You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by po...@apache.org on 2021/03/03 09:35:12 UTC
[airflow] 35/38: Add Tableau provider separate from Salesforce
Provider (#14030)
This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch v2-0-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
commit 641a7c5fbb168794e2ae6925e6bb622ae2a03ceb
Author: Jyoti Dhiman <36...@users.noreply.github.com>
AuthorDate: Thu Feb 25 17:52:54 2021 +0530
Add Tableau provider separate from Salesforce Provider (#14030)
Closes #13614
(cherry picked from commit 45e72ca83049a7db526b1f0fbd94c75f5f92cc75)
---
CONTRIBUTING.rst | 1 +
airflow/providers/dependencies.json | 3 +
airflow/providers/salesforce/CHANGELOG.rst | 16 ++++
airflow/providers/salesforce/hooks/tableau.py | 104 ++-------------------
.../operators/tableau_refresh_workbook.py | 88 ++---------------
airflow/providers/salesforce/provider.yaml | 6 +-
.../salesforce/sensors/tableau_job_status.py | 68 +++-----------
.../{salesforce => tableau}/CHANGELOG.rst | 0
.../provider.yaml => tableau/__init__.py} | 34 +------
.../example_dags/__init__.py} | 33 -------
.../example_tableau_refresh_workbook.py | 4 +-
.../provider.yaml => tableau/hooks/__init__.py} | 34 +------
.../{salesforce => tableau}/hooks/tableau.py | 0
.../operators/__init__.py} | 33 -------
.../operators/tableau_refresh_workbook.py | 4 +-
.../{salesforce => tableau}/provider.yaml | 26 +++---
.../provider.yaml => tableau/sensors/__init__.py} | 33 -------
.../sensors/tableau_job_status.py | 2 +-
.../apache-airflow-providers-tableau/index.rst | 29 +++++-
docs/integration-logos/tableau/tableau.png | Bin 0 -> 4142 bytes
docs/spelling_wordlist.txt | 1 +
.../run_install_and_test_provider_packages.sh | 2 +-
setup.py | 4 +-
tests/core/test_providers_manager.py | 1 +
.../providers/tableau/hooks/__init__.py | 34 +------
.../{salesforce => tableau}/hooks/test_tableau.py | 32 +++++--
.../providers/tableau/operators/__init__.py | 33 -------
.../operators/test_tableau_refresh_workbook.py | 26 +++++-
.../providers/tableau/sensors/__init__.py | 33 -------
.../sensors/test_tableau_job_status.py | 16 +++-
30 files changed, 162 insertions(+), 538 deletions(-)
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 0a6f381..857d3bb 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -654,6 +654,7 @@ microsoft.mssql odbc
mysql amazon,presto,vertica
opsgenie http
postgres amazon
+salesforce tableau
sftp ssh
slack http
snowflake slack
diff --git a/airflow/providers/dependencies.json b/airflow/providers/dependencies.json
index 836020c..b01e96c 100644
--- a/airflow/providers/dependencies.json
+++ b/airflow/providers/dependencies.json
@@ -67,6 +67,9 @@
"postgres": [
"amazon"
],
+ "salesforce": [
+ "tableau"
+ ],
"sftp": [
"ssh"
],
diff --git a/airflow/providers/salesforce/CHANGELOG.rst b/airflow/providers/salesforce/CHANGELOG.rst
index cef7dda..b4eb0ed 100644
--- a/airflow/providers/salesforce/CHANGELOG.rst
+++ b/airflow/providers/salesforce/CHANGELOG.rst
@@ -19,6 +19,22 @@
Changelog
---------
+1.0.2
+.....
+
+Tableau provider moved to separate 'tableau' provider
+
+Things done:
+
+ - Tableau classes imports classes from 'tableau' provider with deprecation warning
+
+
+1.0.1
+.....
+
+Updated documentation and readme files.
+
+
1.0.0
.....
diff --git a/airflow/providers/salesforce/hooks/tableau.py b/airflow/providers/salesforce/hooks/tableau.py
index 51c2f98..cf5f7f3 100644
--- a/airflow/providers/salesforce/hooks/tableau.py
+++ b/airflow/providers/salesforce/hooks/tableau.py
@@ -14,102 +14,14 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-from enum import Enum
-from typing import Any, Optional
-from tableauserverclient import Pager, PersonalAccessTokenAuth, Server, TableauAuth
-from tableauserverclient.server import Auth
+import warnings
-from airflow.hooks.base import BaseHook
+# pylint: disable=unused-import
+from airflow.providers.tableau.hooks.tableau import TableauHook, TableauJobFinishCode # noqa
-
-class TableauJobFinishCode(Enum):
- """
- The finish code indicates the status of the job.
-
- .. seealso:: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#query_job
-
- """
-
- PENDING = -1
- SUCCESS = 0
- ERROR = 1
- CANCELED = 2
-
-
-class TableauHook(BaseHook):
- """
- Connects to the Tableau Server Instance and allows to communicate with it.
-
- .. seealso:: https://tableau.github.io/server-client-python/docs/
-
- :param site_id: The id of the site where the workbook belongs to.
- It will connect to the default site if you don't provide an id.
- :type site_id: Optional[str]
- :param tableau_conn_id: The Tableau Connection id containing the credentials
- to authenticate to the Tableau Server.
- :type tableau_conn_id: str
- """
-
- conn_name_attr = 'tableau_conn_id'
- default_conn_name = 'tableau_default'
- conn_type = 'tableau'
- hook_name = 'Tableau'
-
- def __init__(self, site_id: Optional[str] = None, tableau_conn_id: str = default_conn_name) -> None:
- super().__init__()
- self.tableau_conn_id = tableau_conn_id
- self.conn = self.get_connection(self.tableau_conn_id)
- self.site_id = site_id or self.conn.extra_dejson.get('site_id', '')
- self.server = Server(self.conn.host, use_server_version=True)
- self.tableau_conn = None
-
- def __enter__(self):
- if not self.tableau_conn:
- self.tableau_conn = self.get_conn()
- return self
-
- def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
- self.server.auth.sign_out()
-
- def get_conn(self) -> Auth.contextmgr:
- """
- Signs in to the Tableau Server and automatically signs out if used as ContextManager.
-
- :return: an authorized Tableau Server Context Manager object.
- :rtype: tableauserverclient.server.Auth.contextmgr
- """
- if self.conn.login and self.conn.password:
- return self._auth_via_password()
- if 'token_name' in self.conn.extra_dejson and 'personal_access_token' in self.conn.extra_dejson:
- return self._auth_via_token()
- raise NotImplementedError('No Authentication method found for given Credentials!')
-
- def _auth_via_password(self) -> Auth.contextmgr:
- tableau_auth = TableauAuth(
- username=self.conn.login, password=self.conn.password, site_id=self.site_id
- )
- return self.server.auth.sign_in(tableau_auth)
-
- def _auth_via_token(self) -> Auth.contextmgr:
- tableau_auth = PersonalAccessTokenAuth(
- token_name=self.conn.extra_dejson['token_name'],
- personal_access_token=self.conn.extra_dejson['personal_access_token'],
- site_id=self.site_id,
- )
- return self.server.auth.sign_in_with_personal_access_token(tableau_auth)
-
- def get_all(self, resource_name: str) -> Pager:
- """
- Get all items of the given resource.
-
- .. seealso:: https://tableau.github.io/server-client-python/docs/page-through-results
-
- :param resource_name: The name of the resource to paginate.
- For example: jobs or workbooks
- :type resource_name: str
- :return: all items by returning a Pager.
- :rtype: tableauserverclient.Pager
- """
- resource = getattr(self.server, resource_name)
- return Pager(resource.get)
+warnings.warn(
+ "This module is deprecated. Please use `airflow.providers.tableau.hooks.tableau`.",
+ DeprecationWarning,
+ stacklevel=2,
+)
diff --git a/airflow/providers/salesforce/operators/tableau_refresh_workbook.py b/airflow/providers/salesforce/operators/tableau_refresh_workbook.py
index 7d4ffdc..309af33 100644
--- a/airflow/providers/salesforce/operators/tableau_refresh_workbook.py
+++ b/airflow/providers/salesforce/operators/tableau_refresh_workbook.py
@@ -14,84 +14,16 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-from typing import Optional
-from tableauserverclient import WorkbookItem
+import warnings
-from airflow.exceptions import AirflowException
-from airflow.models import BaseOperator
-from airflow.providers.salesforce.hooks.tableau import TableauHook
-from airflow.utils.decorators import apply_defaults
+# pylint: disable=unused-import
+from airflow.providers.tableau.operators.tableau_refresh_workbook import ( # noqa
+ TableauRefreshWorkbookOperator,
+)
-
-class TableauRefreshWorkbookOperator(BaseOperator):
- """
- Refreshes a Tableau Workbook/Extract
-
- .. seealso:: https://tableau.github.io/server-client-python/docs/api-ref#workbooks
-
- :param workbook_name: The name of the workbook to refresh.
- :type workbook_name: str
- :param site_id: The id of the site where the workbook belongs to.
- :type site_id: Optional[str]
- :param blocking: By default the extract refresh will be blocking means it will wait until it has finished.
- :type blocking: bool
- :param tableau_conn_id: The Tableau Connection id containing the credentials
- to authenticate to the Tableau Server.
- :type tableau_conn_id: str
- """
-
- @apply_defaults
- def __init__(
- self,
- *,
- workbook_name: str,
- site_id: Optional[str] = None,
- blocking: bool = True,
- tableau_conn_id: str = 'tableau_default',
- **kwargs,
- ) -> None:
- super().__init__(**kwargs)
- self.workbook_name = workbook_name
- self.site_id = site_id
- self.blocking = blocking
- self.tableau_conn_id = tableau_conn_id
-
- def execute(self, context: dict) -> str:
- """
- Executes the Tableau Extract Refresh and pushes the job id to xcom.
-
- :param context: The task context during execution.
- :type context: dict
- :return: the id of the job that executes the extract refresh
- :rtype: str
- """
- with TableauHook(self.site_id, self.tableau_conn_id) as tableau_hook:
- workbook = self._get_workbook_by_name(tableau_hook)
-
- job_id = self._refresh_workbook(tableau_hook, workbook.id)
- if self.blocking:
- from airflow.providers.salesforce.sensors.tableau_job_status import TableauJobStatusSensor
-
- TableauJobStatusSensor(
- job_id=job_id,
- site_id=self.site_id,
- tableau_conn_id=self.tableau_conn_id,
- task_id='wait_until_succeeded',
- dag=None,
- ).execute(context={})
- self.log.info('Workbook %s has been successfully refreshed.', self.workbook_name)
- return job_id
-
- def _get_workbook_by_name(self, tableau_hook: TableauHook) -> WorkbookItem:
- for workbook in tableau_hook.get_all(resource_name='workbooks'):
- if workbook.name == self.workbook_name:
- self.log.info('Found matching workbook with id %s', workbook.id)
- return workbook
-
- raise AirflowException(f'Workbook {self.workbook_name} not found!')
-
- def _refresh_workbook(self, tableau_hook: TableauHook, workbook_id: str) -> str:
- job = tableau_hook.server.workbooks.refresh(workbook_id)
- self.log.info('Refreshing Workbook %s...', self.workbook_name)
- return job.id
+warnings.warn(
+ "This module is deprecated. Please use `airflow.providers.tableau.operators.tableau_refresh_workbook`.",
+ DeprecationWarning,
+ stacklevel=2,
+)
diff --git a/airflow/providers/salesforce/provider.yaml b/airflow/providers/salesforce/provider.yaml
index fe739ff..c0992d8 100644
--- a/airflow/providers/salesforce/provider.yaml
+++ b/airflow/providers/salesforce/provider.yaml
@@ -22,6 +22,8 @@ description: |
`Salesforce <https://www.salesforce.com/>`__
versions:
+ - 1.0.2
+ - 1.0.1
- 1.0.0
integrations:
@@ -40,10 +42,12 @@ sensors:
- airflow.providers.salesforce.sensors.tableau_job_status
hooks:
+ - integration-name: Tableau
+ python-modules:
+ - airflow.providers.salesforce.hooks.tableau
- integration-name: Salesforce
python-modules:
- airflow.providers.salesforce.hooks.salesforce
- - airflow.providers.salesforce.hooks.tableau
hook-class-names:
- airflow.providers.salesforce.hooks.tableau.TableauHook
diff --git a/airflow/providers/salesforce/sensors/tableau_job_status.py b/airflow/providers/salesforce/sensors/tableau_job_status.py
index 4939203..076159e 100644
--- a/airflow/providers/salesforce/sensors/tableau_job_status.py
+++ b/airflow/providers/salesforce/sensors/tableau_job_status.py
@@ -14,63 +14,17 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-from typing import Optional
-from airflow.exceptions import AirflowException
-from airflow.providers.salesforce.hooks.tableau import TableauHook, TableauJobFinishCode
-from airflow.sensors.base import BaseSensorOperator
-from airflow.utils.decorators import apply_defaults
+import warnings
+# pylint: disable=unused-import
+from airflow.providers.tableau.sensors.tableau_job_status import ( # noqa
+ TableauJobFailedException,
+ TableauJobStatusSensor,
+)
-class TableauJobFailedException(AirflowException):
- """An exception that indicates that a Job failed to complete."""
-
-
-class TableauJobStatusSensor(BaseSensorOperator):
- """
- Watches the status of a Tableau Server Job.
-
- .. seealso:: https://tableau.github.io/server-client-python/docs/api-ref#jobs
-
- :param job_id: The job to watch.
- :type job_id: str
- :param site_id: The id of the site where the workbook belongs to.
- :type site_id: Optional[str]
- :param tableau_conn_id: The Tableau Connection id containing the credentials
- to authenticate to the Tableau Server.
- :type tableau_conn_id: str
- """
-
- template_fields = ('job_id',)
-
- @apply_defaults
- def __init__(
- self,
- *,
- job_id: str,
- site_id: Optional[str] = None,
- tableau_conn_id: str = 'tableau_default',
- **kwargs,
- ) -> None:
- super().__init__(**kwargs)
- self.tableau_conn_id = tableau_conn_id
- self.job_id = job_id
- self.site_id = site_id
-
- def poke(self, context: dict) -> bool:
- """
- Pokes until the job has successfully finished.
-
- :param context: The task context during execution.
- :type context: dict
- :return: True if it succeeded and False if not.
- :rtype: bool
- """
- with TableauHook(self.site_id, self.tableau_conn_id) as tableau_hook:
- finish_code = TableauJobFinishCode(
- int(tableau_hook.server.jobs.get_by_id(self.job_id).finish_code)
- )
- self.log.info('Current finishCode is %s (%s)', finish_code.name, finish_code.value)
- if finish_code in [TableauJobFinishCode.ERROR, TableauJobFinishCode.CANCELED]:
- raise TableauJobFailedException('The Tableau Refresh Workbook Job failed!')
- return finish_code == TableauJobFinishCode.SUCCESS
+warnings.warn(
+ "This module is deprecated. Please use `airflow.providers.tableau.sensors.tableau_job_status`.",
+ DeprecationWarning,
+ stacklevel=2,
+)
diff --git a/airflow/providers/salesforce/CHANGELOG.rst b/airflow/providers/tableau/CHANGELOG.rst
similarity index 100%
copy from airflow/providers/salesforce/CHANGELOG.rst
copy to airflow/providers/tableau/CHANGELOG.rst
diff --git a/airflow/providers/salesforce/provider.yaml b/airflow/providers/tableau/__init__.py
similarity index 50%
copy from airflow/providers/salesforce/provider.yaml
copy to airflow/providers/tableau/__init__.py
index fe739ff..217e5db 100644
--- a/airflow/providers/salesforce/provider.yaml
+++ b/airflow/providers/tableau/__init__.py
@@ -1,3 +1,4 @@
+#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
@@ -14,36 +15,3 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-
----
-package-name: apache-airflow-providers-salesforce
-name: Salesforce
-description: |
- `Salesforce <https://www.salesforce.com/>`__
-
-versions:
- - 1.0.0
-
-integrations:
- - integration-name: Salesforce
- external-doc-url: https://www.salesforce.com/
- tags: [service]
-
-operators:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.operators.tableau_refresh_workbook
-
-sensors:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.sensors.tableau_job_status
-
-hooks:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.hooks.salesforce
- - airflow.providers.salesforce.hooks.tableau
-
-hook-class-names:
- - airflow.providers.salesforce.hooks.tableau.TableauHook
diff --git a/airflow/providers/salesforce/provider.yaml b/airflow/providers/tableau/example_dags/__init__.py
similarity index 50%
copy from airflow/providers/salesforce/provider.yaml
copy to airflow/providers/tableau/example_dags/__init__.py
index fe739ff..13a8339 100644
--- a/airflow/providers/salesforce/provider.yaml
+++ b/airflow/providers/tableau/example_dags/__init__.py
@@ -14,36 +14,3 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-
----
-package-name: apache-airflow-providers-salesforce
-name: Salesforce
-description: |
- `Salesforce <https://www.salesforce.com/>`__
-
-versions:
- - 1.0.0
-
-integrations:
- - integration-name: Salesforce
- external-doc-url: https://www.salesforce.com/
- tags: [service]
-
-operators:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.operators.tableau_refresh_workbook
-
-sensors:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.sensors.tableau_job_status
-
-hooks:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.hooks.salesforce
- - airflow.providers.salesforce.hooks.tableau
-
-hook-class-names:
- - airflow.providers.salesforce.hooks.tableau.TableauHook
diff --git a/airflow/providers/salesforce/example_dags/example_tableau_refresh_workbook.py b/airflow/providers/tableau/example_dags/example_tableau_refresh_workbook.py
similarity index 92%
rename from airflow/providers/salesforce/example_dags/example_tableau_refresh_workbook.py
rename to airflow/providers/tableau/example_dags/example_tableau_refresh_workbook.py
index 32b347c..da1cc8b 100644
--- a/airflow/providers/salesforce/example_dags/example_tableau_refresh_workbook.py
+++ b/airflow/providers/tableau/example_dags/example_tableau_refresh_workbook.py
@@ -23,8 +23,8 @@ when the operation actually finishes. That's why we have another task that check
from datetime import timedelta
from airflow import DAG
-from airflow.providers.salesforce.operators.tableau_refresh_workbook import TableauRefreshWorkbookOperator
-from airflow.providers.salesforce.sensors.tableau_job_status import TableauJobStatusSensor
+from airflow.providers.tableau.operators.tableau_refresh_workbook import TableauRefreshWorkbookOperator
+from airflow.providers.tableau.sensors.tableau_job_status import TableauJobStatusSensor
from airflow.utils.dates import days_ago
DEFAULT_ARGS = {
diff --git a/airflow/providers/salesforce/provider.yaml b/airflow/providers/tableau/hooks/__init__.py
similarity index 50%
copy from airflow/providers/salesforce/provider.yaml
copy to airflow/providers/tableau/hooks/__init__.py
index fe739ff..217e5db 100644
--- a/airflow/providers/salesforce/provider.yaml
+++ b/airflow/providers/tableau/hooks/__init__.py
@@ -1,3 +1,4 @@
+#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
@@ -14,36 +15,3 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-
----
-package-name: apache-airflow-providers-salesforce
-name: Salesforce
-description: |
- `Salesforce <https://www.salesforce.com/>`__
-
-versions:
- - 1.0.0
-
-integrations:
- - integration-name: Salesforce
- external-doc-url: https://www.salesforce.com/
- tags: [service]
-
-operators:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.operators.tableau_refresh_workbook
-
-sensors:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.sensors.tableau_job_status
-
-hooks:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.hooks.salesforce
- - airflow.providers.salesforce.hooks.tableau
-
-hook-class-names:
- - airflow.providers.salesforce.hooks.tableau.TableauHook
diff --git a/airflow/providers/salesforce/hooks/tableau.py b/airflow/providers/tableau/hooks/tableau.py
similarity index 100%
copy from airflow/providers/salesforce/hooks/tableau.py
copy to airflow/providers/tableau/hooks/tableau.py
diff --git a/airflow/providers/salesforce/provider.yaml b/airflow/providers/tableau/operators/__init__.py
similarity index 50%
copy from airflow/providers/salesforce/provider.yaml
copy to airflow/providers/tableau/operators/__init__.py
index fe739ff..13a8339 100644
--- a/airflow/providers/salesforce/provider.yaml
+++ b/airflow/providers/tableau/operators/__init__.py
@@ -14,36 +14,3 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-
----
-package-name: apache-airflow-providers-salesforce
-name: Salesforce
-description: |
- `Salesforce <https://www.salesforce.com/>`__
-
-versions:
- - 1.0.0
-
-integrations:
- - integration-name: Salesforce
- external-doc-url: https://www.salesforce.com/
- tags: [service]
-
-operators:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.operators.tableau_refresh_workbook
-
-sensors:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.sensors.tableau_job_status
-
-hooks:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.hooks.salesforce
- - airflow.providers.salesforce.hooks.tableau
-
-hook-class-names:
- - airflow.providers.salesforce.hooks.tableau.TableauHook
diff --git a/airflow/providers/salesforce/operators/tableau_refresh_workbook.py b/airflow/providers/tableau/operators/tableau_refresh_workbook.py
similarity index 95%
copy from airflow/providers/salesforce/operators/tableau_refresh_workbook.py
copy to airflow/providers/tableau/operators/tableau_refresh_workbook.py
index 7d4ffdc..25ca77b 100644
--- a/airflow/providers/salesforce/operators/tableau_refresh_workbook.py
+++ b/airflow/providers/tableau/operators/tableau_refresh_workbook.py
@@ -20,7 +20,7 @@ from tableauserverclient import WorkbookItem
from airflow.exceptions import AirflowException
from airflow.models import BaseOperator
-from airflow.providers.salesforce.hooks.tableau import TableauHook
+from airflow.providers.tableau.hooks.tableau import TableauHook
from airflow.utils.decorators import apply_defaults
@@ -71,7 +71,7 @@ class TableauRefreshWorkbookOperator(BaseOperator):
job_id = self._refresh_workbook(tableau_hook, workbook.id)
if self.blocking:
- from airflow.providers.salesforce.sensors.tableau_job_status import TableauJobStatusSensor
+ from airflow.providers.tableau.sensors.tableau_job_status import TableauJobStatusSensor
TableauJobStatusSensor(
job_id=job_id,
diff --git a/airflow/providers/salesforce/provider.yaml b/airflow/providers/tableau/provider.yaml
similarity index 61%
copy from airflow/providers/salesforce/provider.yaml
copy to airflow/providers/tableau/provider.yaml
index fe739ff..e777947 100644
--- a/airflow/providers/salesforce/provider.yaml
+++ b/airflow/providers/tableau/provider.yaml
@@ -16,34 +16,34 @@
# under the License.
---
-package-name: apache-airflow-providers-salesforce
-name: Salesforce
+package-name: apache-airflow-providers-tableau
+name: Tableau
description: |
- `Salesforce <https://www.salesforce.com/>`__
+ `Tableau <https://www.tableau.com/>`__
versions:
- 1.0.0
integrations:
- - integration-name: Salesforce
- external-doc-url: https://www.salesforce.com/
+ - integration-name: Tableau
+ external-doc-url: https://www.tableau.com/
+ logo: /integration-logos/tableau/tableau.png
tags: [service]
operators:
- - integration-name: Salesforce
+ - integration-name: Tableau
python-modules:
- - airflow.providers.salesforce.operators.tableau_refresh_workbook
+ - airflow.providers.tableau.operators.tableau_refresh_workbook
sensors:
- - integration-name: Salesforce
+ - integration-name: Tableau
python-modules:
- - airflow.providers.salesforce.sensors.tableau_job_status
+ - airflow.providers.tableau.sensors.tableau_job_status
hooks:
- - integration-name: Salesforce
+ - integration-name: Tableau
python-modules:
- - airflow.providers.salesforce.hooks.salesforce
- - airflow.providers.salesforce.hooks.tableau
+ - airflow.providers.tableau.hooks.tableau
hook-class-names:
- - airflow.providers.salesforce.hooks.tableau.TableauHook
+ - airflow.providers.tableau.hooks.tableau.TableauHook
diff --git a/airflow/providers/salesforce/provider.yaml b/airflow/providers/tableau/sensors/__init__.py
similarity index 50%
copy from airflow/providers/salesforce/provider.yaml
copy to airflow/providers/tableau/sensors/__init__.py
index fe739ff..13a8339 100644
--- a/airflow/providers/salesforce/provider.yaml
+++ b/airflow/providers/tableau/sensors/__init__.py
@@ -14,36 +14,3 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-
----
-package-name: apache-airflow-providers-salesforce
-name: Salesforce
-description: |
- `Salesforce <https://www.salesforce.com/>`__
-
-versions:
- - 1.0.0
-
-integrations:
- - integration-name: Salesforce
- external-doc-url: https://www.salesforce.com/
- tags: [service]
-
-operators:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.operators.tableau_refresh_workbook
-
-sensors:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.sensors.tableau_job_status
-
-hooks:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.hooks.salesforce
- - airflow.providers.salesforce.hooks.tableau
-
-hook-class-names:
- - airflow.providers.salesforce.hooks.tableau.TableauHook
diff --git a/airflow/providers/salesforce/sensors/tableau_job_status.py b/airflow/providers/tableau/sensors/tableau_job_status.py
similarity index 96%
copy from airflow/providers/salesforce/sensors/tableau_job_status.py
copy to airflow/providers/tableau/sensors/tableau_job_status.py
index 4939203..518e2f0 100644
--- a/airflow/providers/salesforce/sensors/tableau_job_status.py
+++ b/airflow/providers/tableau/sensors/tableau_job_status.py
@@ -17,7 +17,7 @@
from typing import Optional
from airflow.exceptions import AirflowException
-from airflow.providers.salesforce.hooks.tableau import TableauHook, TableauJobFinishCode
+from airflow.providers.tableau.hooks.tableau import TableauHook, TableauJobFinishCode
from airflow.sensors.base import BaseSensorOperator
from airflow.utils.decorators import apply_defaults
diff --git a/airflow/providers/salesforce/CHANGELOG.rst b/docs/apache-airflow-providers-tableau/index.rst
similarity index 56%
copy from airflow/providers/salesforce/CHANGELOG.rst
copy to docs/apache-airflow-providers-tableau/index.rst
index cef7dda..47ace94 100644
--- a/airflow/providers/salesforce/CHANGELOG.rst
+++ b/docs/apache-airflow-providers-tableau/index.rst
@@ -1,3 +1,4 @@
+
.. Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
@@ -15,11 +16,29 @@
specific language governing permissions and limitations
under the License.
+``apache-airflow-providers-tableau``
+=======================================
+
+Content
+-------
+
+.. toctree::
+ :maxdepth: 1
+ :caption: Guides
+
+ Connection types <connections/tableau>
+
+.. toctree::
+ :maxdepth: 1
+ :caption: References
+
+ Python API <_api/airflow/providers/tableau/index>
-Changelog
----------
+.. toctree::
+ :maxdepth: 1
+ :caption: Resources
-1.0.0
-.....
+ Example DAGs <https://github.com/apache/airflow/tree/master/airflow/providers/tableau/example_dags>
+ PyPI Repository <https://pypi.org/project/apache-airflow-providers-tableau/>
-Initial version of the provider.
+.. THE REMINDER OF THE FILE IS AUTOMATICALLY GENERATED. IT WILL BE OVERWRITTEN AT RELEASE TIME!
diff --git a/docs/integration-logos/tableau/tableau.png b/docs/integration-logos/tableau/tableau.png
new file mode 100644
index 0000000..4ec356c
Binary files /dev/null and b/docs/integration-logos/tableau/tableau.png differ
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index 71f9e34..0e89285 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -1280,6 +1280,7 @@ sync'ed
sys
syspath
systemd
+tableau
tableauserverclient
tablefmt
tagKey
diff --git a/scripts/in_container/run_install_and_test_provider_packages.sh b/scripts/in_container/run_install_and_test_provider_packages.sh
index 76d41e4..5eb039a 100755
--- a/scripts/in_container/run_install_and_test_provider_packages.sh
+++ b/scripts/in_container/run_install_and_test_provider_packages.sh
@@ -95,7 +95,7 @@ function discover_all_provider_packages() {
# Columns is to force it wider, so it doesn't wrap at 80 characters
COLUMNS=180 airflow providers list
- local expected_number_of_providers=63
+ local expected_number_of_providers=64
local actual_number_of_providers
actual_providers=$(airflow providers list --output yaml | grep package_name)
actual_number_of_providers=$(wc -l <<<"$actual_providers")
diff --git a/setup.py b/setup.py
index 2867b36..4ee7a5c 100644
--- a/setup.py
+++ b/setup.py
@@ -444,7 +444,7 @@ statsd = [
'statsd>=3.3.0, <4.0',
]
tableau = [
- 'tableauserverclient~=0.12',
+ 'tableauserverclient',
]
telegram = [
'python-telegram-bot==13.0',
@@ -576,6 +576,7 @@ PROVIDERS_REQUIREMENTS: Dict[str, List[str]] = {
'snowflake': snowflake,
'sqlite': [],
'ssh': ssh,
+ 'tableau': tableau,
'telegram': telegram,
'vertica': vertica,
'yandex': yandex,
@@ -608,7 +609,6 @@ CORE_EXTRAS_REQUIREMENTS: Dict[str, List[str]] = {
'rabbitmq': rabbitmq,
'sentry': sentry,
'statsd': statsd,
- 'tableau': tableau,
'virtualenv': virtualenv,
}
diff --git a/tests/core/test_providers_manager.py b/tests/core/test_providers_manager.py
index 39ee588..9112d5e 100644
--- a/tests/core/test_providers_manager.py
+++ b/tests/core/test_providers_manager.py
@@ -81,6 +81,7 @@ ALL_PROVIDERS = [
# 'apache-airflow-providers-snowflake',
'apache-airflow-providers-sqlite',
'apache-airflow-providers-ssh',
+ 'apache-airflow-providers-tableau',
'apache-airflow-providers-telegram',
'apache-airflow-providers-vertica',
'apache-airflow-providers-yandex',
diff --git a/airflow/providers/salesforce/provider.yaml b/tests/providers/tableau/hooks/__init__.py
similarity index 50%
copy from airflow/providers/salesforce/provider.yaml
copy to tests/providers/tableau/hooks/__init__.py
index fe739ff..217e5db 100644
--- a/airflow/providers/salesforce/provider.yaml
+++ b/tests/providers/tableau/hooks/__init__.py
@@ -1,3 +1,4 @@
+#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
@@ -14,36 +15,3 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-
----
-package-name: apache-airflow-providers-salesforce
-name: Salesforce
-description: |
- `Salesforce <https://www.salesforce.com/>`__
-
-versions:
- - 1.0.0
-
-integrations:
- - integration-name: Salesforce
- external-doc-url: https://www.salesforce.com/
- tags: [service]
-
-operators:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.operators.tableau_refresh_workbook
-
-sensors:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.sensors.tableau_job_status
-
-hooks:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.hooks.salesforce
- - airflow.providers.salesforce.hooks.tableau
-
-hook-class-names:
- - airflow.providers.salesforce.hooks.tableau.TableauHook
diff --git a/tests/providers/salesforce/hooks/test_tableau.py b/tests/providers/tableau/hooks/test_tableau.py
similarity index 81%
rename from tests/providers/salesforce/hooks/test_tableau.py
rename to tests/providers/tableau/hooks/test_tableau.py
index 130746d..66ecdf7 100644
--- a/tests/providers/salesforce/hooks/test_tableau.py
+++ b/tests/providers/tableau/hooks/test_tableau.py
@@ -19,12 +19,19 @@ import unittest
from unittest.mock import patch
from airflow import configuration, models
-from airflow.providers.salesforce.hooks.tableau import TableauHook
+from airflow.providers.tableau.hooks.tableau import TableauHook
from airflow.utils import db
class TestTableauHook(unittest.TestCase):
+ """
+ Test class for TableauHook
+ """
+
def setUp(self):
+ """
+ setup
+ """
configuration.conf.load_test_config()
db.merge_conn(
@@ -46,9 +53,12 @@ class TestTableauHook(unittest.TestCase):
)
)
- @patch('airflow.providers.salesforce.hooks.tableau.TableauAuth')
- @patch('airflow.providers.salesforce.hooks.tableau.Server')
+ @patch('airflow.providers.tableau.hooks.tableau.TableauAuth')
+ @patch('airflow.providers.tableau.hooks.tableau.Server')
def test_get_conn_auth_via_password_and_site_in_connection(self, mock_server, mock_tableau_auth):
+ """
+ Test get conn auth via password
+ """
with TableauHook(tableau_conn_id='tableau_test_password') as tableau_hook:
mock_server.assert_called_once_with(tableau_hook.conn.host, use_server_version=True)
mock_tableau_auth.assert_called_once_with(
@@ -59,9 +69,12 @@ class TestTableauHook(unittest.TestCase):
mock_server.return_value.auth.sign_in.assert_called_once_with(mock_tableau_auth.return_value)
mock_server.return_value.auth.sign_out.assert_called_once_with()
- @patch('airflow.providers.salesforce.hooks.tableau.PersonalAccessTokenAuth')
- @patch('airflow.providers.salesforce.hooks.tableau.Server')
+ @patch('airflow.providers.tableau.hooks.tableau.PersonalAccessTokenAuth')
+ @patch('airflow.providers.tableau.hooks.tableau.Server')
def test_get_conn_auth_via_token_and_site_in_init(self, mock_server, mock_tableau_auth):
+ """
+ Test get conn auth via token
+ """
with TableauHook(site_id='test', tableau_conn_id='tableau_test_token') as tableau_hook:
mock_server.assert_called_once_with(tableau_hook.conn.host, use_server_version=True)
mock_tableau_auth.assert_called_once_with(
@@ -74,10 +87,13 @@ class TestTableauHook(unittest.TestCase):
)
mock_server.return_value.auth.sign_out.assert_called_once_with()
- @patch('airflow.providers.salesforce.hooks.tableau.TableauAuth')
- @patch('airflow.providers.salesforce.hooks.tableau.Server')
- @patch('airflow.providers.salesforce.hooks.tableau.Pager', return_value=[1, 2, 3])
+ @patch('airflow.providers.tableau.hooks.tableau.TableauAuth')
+ @patch('airflow.providers.tableau.hooks.tableau.Server')
+ @patch('airflow.providers.tableau.hooks.tableau.Pager', return_value=[1, 2, 3])
def test_get_all(self, mock_pager, mock_server, mock_tableau_auth): # pylint: disable=unused-argument
+ """
+ Test get all
+ """
with TableauHook(tableau_conn_id='tableau_test_password') as tableau_hook:
jobs = tableau_hook.get_all(resource_name='jobs')
assert jobs == mock_pager.return_value
diff --git a/airflow/providers/salesforce/provider.yaml b/tests/providers/tableau/operators/__init__.py
similarity index 50%
copy from airflow/providers/salesforce/provider.yaml
copy to tests/providers/tableau/operators/__init__.py
index fe739ff..13a8339 100644
--- a/airflow/providers/salesforce/provider.yaml
+++ b/tests/providers/tableau/operators/__init__.py
@@ -14,36 +14,3 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-
----
-package-name: apache-airflow-providers-salesforce
-name: Salesforce
-description: |
- `Salesforce <https://www.salesforce.com/>`__
-
-versions:
- - 1.0.0
-
-integrations:
- - integration-name: Salesforce
- external-doc-url: https://www.salesforce.com/
- tags: [service]
-
-operators:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.operators.tableau_refresh_workbook
-
-sensors:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.sensors.tableau_job_status
-
-hooks:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.hooks.salesforce
- - airflow.providers.salesforce.hooks.tableau
-
-hook-class-names:
- - airflow.providers.salesforce.hooks.tableau.TableauHook
diff --git a/tests/providers/salesforce/operators/test_tableau_refresh_workbook.py b/tests/providers/tableau/operators/test_tableau_refresh_workbook.py
similarity index 80%
rename from tests/providers/salesforce/operators/test_tableau_refresh_workbook.py
rename to tests/providers/tableau/operators/test_tableau_refresh_workbook.py
index 77139c1..72377a5 100644
--- a/tests/providers/salesforce/operators/test_tableau_refresh_workbook.py
+++ b/tests/providers/tableau/operators/test_tableau_refresh_workbook.py
@@ -21,11 +21,18 @@ from unittest.mock import Mock, patch
import pytest
from airflow.exceptions import AirflowException
-from airflow.providers.salesforce.operators.tableau_refresh_workbook import TableauRefreshWorkbookOperator
+from airflow.providers.tableau.operators.tableau_refresh_workbook import TableauRefreshWorkbookOperator
class TestTableauRefreshWorkbookOperator(unittest.TestCase):
+ """
+ Test class for TableauRefreshWorkbookOperator
+ """
+
def setUp(self):
+ """
+ setup
+ """
self.mocked_workbooks = []
for i in range(3):
mock_workbook = Mock()
@@ -34,8 +41,11 @@ class TestTableauRefreshWorkbookOperator(unittest.TestCase):
self.mocked_workbooks.append(mock_workbook)
self.kwargs = {'site_id': 'test_site', 'task_id': 'task', 'dag': None}
- @patch('airflow.providers.salesforce.operators.tableau_refresh_workbook.TableauHook')
+ @patch('airflow.providers.tableau.operators.tableau_refresh_workbook.TableauHook')
def test_execute(self, mock_tableau_hook):
+ """
+ Test Execute
+ """
mock_tableau_hook.get_all = Mock(return_value=self.mocked_workbooks)
mock_tableau_hook.return_value.__enter__ = Mock(return_value=mock_tableau_hook)
operator = TableauRefreshWorkbookOperator(blocking=False, workbook_name='wb_2', **self.kwargs)
@@ -45,9 +55,12 @@ class TestTableauRefreshWorkbookOperator(unittest.TestCase):
mock_tableau_hook.server.workbooks.refresh.assert_called_once_with(2)
assert mock_tableau_hook.server.workbooks.refresh.return_value.id == job_id
- @patch('airflow.providers.salesforce.sensors.tableau_job_status.TableauJobStatusSensor')
- @patch('airflow.providers.salesforce.operators.tableau_refresh_workbook.TableauHook')
+ @patch('airflow.providers.tableau.sensors.tableau_job_status.TableauJobStatusSensor')
+ @patch('airflow.providers.tableau.operators.tableau_refresh_workbook.TableauHook')
def test_execute_blocking(self, mock_tableau_hook, mock_tableau_job_status_sensor):
+ """
+ Test execute blocking
+ """
mock_tableau_hook.get_all = Mock(return_value=self.mocked_workbooks)
mock_tableau_hook.return_value.__enter__ = Mock(return_value=mock_tableau_hook)
operator = TableauRefreshWorkbookOperator(workbook_name='wb_2', **self.kwargs)
@@ -64,8 +77,11 @@ class TestTableauRefreshWorkbookOperator(unittest.TestCase):
dag=None,
)
- @patch('airflow.providers.salesforce.operators.tableau_refresh_workbook.TableauHook')
+ @patch('airflow.providers.tableau.operators.tableau_refresh_workbook.TableauHook')
def test_execute_missing_workbook(self, mock_tableau_hook):
+ """
+ Test execute missing workbook
+ """
mock_tableau_hook.get_all = Mock(return_value=self.mocked_workbooks)
mock_tableau_hook.return_value.__enter__ = Mock(return_value=mock_tableau_hook)
operator = TableauRefreshWorkbookOperator(workbook_name='test', **self.kwargs)
diff --git a/airflow/providers/salesforce/provider.yaml b/tests/providers/tableau/sensors/__init__.py
similarity index 50%
copy from airflow/providers/salesforce/provider.yaml
copy to tests/providers/tableau/sensors/__init__.py
index fe739ff..13a8339 100644
--- a/airflow/providers/salesforce/provider.yaml
+++ b/tests/providers/tableau/sensors/__init__.py
@@ -14,36 +14,3 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-
----
-package-name: apache-airflow-providers-salesforce
-name: Salesforce
-description: |
- `Salesforce <https://www.salesforce.com/>`__
-
-versions:
- - 1.0.0
-
-integrations:
- - integration-name: Salesforce
- external-doc-url: https://www.salesforce.com/
- tags: [service]
-
-operators:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.operators.tableau_refresh_workbook
-
-sensors:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.sensors.tableau_job_status
-
-hooks:
- - integration-name: Salesforce
- python-modules:
- - airflow.providers.salesforce.hooks.salesforce
- - airflow.providers.salesforce.hooks.tableau
-
-hook-class-names:
- - airflow.providers.salesforce.hooks.tableau.TableauHook
diff --git a/tests/providers/salesforce/sensors/test_tableau_job_status.py b/tests/providers/tableau/sensors/test_tableau_job_status.py
similarity index 84%
rename from tests/providers/salesforce/sensors/test_tableau_job_status.py
rename to tests/providers/tableau/sensors/test_tableau_job_status.py
index 7f01011..ea6eeb2 100644
--- a/tests/providers/salesforce/sensors/test_tableau_job_status.py
+++ b/tests/providers/tableau/sensors/test_tableau_job_status.py
@@ -21,18 +21,25 @@ from unittest.mock import Mock, patch
import pytest
from parameterized import parameterized
-from airflow.providers.salesforce.sensors.tableau_job_status import (
+from airflow.providers.tableau.sensors.tableau_job_status import (
TableauJobFailedException,
TableauJobStatusSensor,
)
class TestTableauJobStatusSensor(unittest.TestCase):
+ """
+ Test Class for JobStatusSensor
+ """
+
def setUp(self):
self.kwargs = {'job_id': 'job_2', 'site_id': 'test_site', 'task_id': 'task', 'dag': None}
- @patch('airflow.providers.salesforce.sensors.tableau_job_status.TableauHook')
+ @patch('airflow.providers.tableau.sensors.tableau_job_status.TableauHook')
def test_poke(self, mock_tableau_hook):
+ """
+ Test poke
+ """
mock_tableau_hook.return_value.__enter__ = Mock(return_value=mock_tableau_hook)
mock_get = mock_tableau_hook.server.jobs.get_by_id
mock_get.return_value.finish_code = '0'
@@ -44,8 +51,11 @@ class TestTableauJobStatusSensor(unittest.TestCase):
mock_get.assert_called_once_with(sensor.job_id)
@parameterized.expand([('1',), ('2',)])
- @patch('airflow.providers.salesforce.sensors.tableau_job_status.TableauHook')
+ @patch('airflow.providers.tableau.sensors.tableau_job_status.TableauHook')
def test_poke_failed(self, finish_code, mock_tableau_hook):
+ """
+ Test poke failed
+ """
mock_tableau_hook.return_value.__enter__ = Mock(return_value=mock_tableau_hook)
mock_get = mock_tableau_hook.server.jobs.get_by_id
mock_get.return_value.finish_code = finish_code