From d2f1711f8dfa8bbc86ea9319da5d40b4f024fcc1 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Mon, 6 Jun 2022 15:43:29 -0700 Subject: [PATCH 1/7] add client version/debug header Allow setting options at server initialization, Add a version header for analytics --- tableauserverclient/__init__.py | 12 +----------- tableauserverclient/server/server.py | 19 ++++++++++++++++++- test/test_group.py | 8 +++----- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/tableauserverclient/__init__.py b/tableauserverclient/__init__.py index 592551b4e..3582386fd 100644 --- a/tableauserverclient/__init__.py +++ b/tableauserverclient/__init__.py @@ -1,4 +1,4 @@ -from ._version import get_versions +from .namespace import NEW_NAMESPACE as DEFAULT_NAMESPACE from .models import ( ConnectionCredentials, ConnectionItem, @@ -36,12 +36,7 @@ PersonalAccessTokenAuth, FlowRunItem, RevisionItem, - MetricItem, - TableauItem, - Resource, - plural_type, ) -from .namespace import NEW_NAMESPACE as DEFAULT_NAMESPACE from .server import ( RequestOptions, CSVRequestOptions, @@ -55,8 +50,3 @@ NotSignedInError, Pager, ) -from .helpers import * - -__version__ = get_versions()["version"] -__VERSION__ = __version__ -del get_versions diff --git a/tableauserverclient/server/server.py b/tableauserverclient/server/server.py index 9103c7a58..c6cf29ffd 100644 --- a/tableauserverclient/server/server.py +++ b/tableauserverclient/server/server.py @@ -35,6 +35,10 @@ from .exceptions import NotSignedInError from ..namespace import Namespace +from tableauserverclient._version import get_versions +__TSC_VERSION__ = get_versions()["version"] +del get_versions + import requests from distutils.version import LooseVersion as Version @@ -46,6 +50,9 @@ "9.1": "2.0", "9.0": "2.0", } +minimum_supported_server_version = "2.3" +default_server_version = "2.3" +client_version_header = "X-TableauServerClient-Version" class Server(object): @@ -62,7 +69,7 @@ def __init__(self, server_address, use_server_version=False, http_options=None): self._session = requests.Session() self._http_options = dict() - self.version = "2.3" + self.version = default_server_version self.auth = Auth(self) self.views = Views(self) self.users = Users(self) @@ -92,6 +99,11 @@ def __init__(self, server_address, use_server_version=False, http_options=None): if http_options: self.add_http_options(http_options) + # must set this before calling use_server_version, because that's a server call + if http_options_dict: + self.add_http_options(http_options_dict) + self.add_http_version_header() + if use_server_version: self.use_server_version() @@ -100,8 +112,13 @@ def add_http_options(self, options_dict): if options_dict.get("verify") == False: urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + def add_http_version_header(self): + if not self._http_options[client_version_header]: + self._http_options.update({client_version_header: __TSC_VERSION__}) + def clear_http_options(self): self._http_options = dict() + self.add_http_version_header() def _clear_auth(self): self._site_id = None diff --git a/test/test_group.py b/test/test_group.py index d948090ca..d884677ab 100644 --- a/test/test_group.py +++ b/test/test_group.py @@ -1,9 +1,7 @@ # encoding=utf-8 -import os import unittest - +import os import requests_mock - import tableauserverclient as TSC from tableauserverclient.datetime_helpers import format_datetime @@ -129,7 +127,7 @@ def test_add_user_before_populating(self) -> None: with requests_mock.mock() as m: m.get(self.baseurl, text=get_xml_response) m.post( - "http://test/api/2.3/sites/dad65087-b08b-4603-af4e-2887b8aafc67/groups/ef8b19c0-43b6-11e6-af50" + self.baseurl + "/dad65087-b08b-4603-af4e-2887b8aafc67/groups/ef8b19c0-43b6-11e6-af50" "-63f5805dbe3c/users", text=add_user_response, ) @@ -163,7 +161,7 @@ def test_remove_user_before_populating(self) -> None: with requests_mock.mock() as m: m.get(self.baseurl, text=response_xml) m.delete( - "http://test/api/2.3/sites/dad65087-b08b-4603-af4e-2887b8aafc67/groups/ef8b19c0-43b6-11e6-af50" + self.baseurl + "/dad65087-b08b-4603-af4e-2887b8aafc67/groups/ef8b19c0-43b6-11e6-af50" "-63f5805dbe3c/users/5de011f8-5aa9-4d5b-b991-f462c8dd6bb7", text="ok", ) From 68115e55d3fbd24050c22c426c2805aa86a609a7 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Fri, 22 Jul 2022 17:52:00 -0700 Subject: [PATCH 2/7] black formatting --- setup.py | 2 +- tableauserverclient/server/endpoint/endpoint.py | 2 +- tableauserverclient/server/server.py | 1 + test/test_requests.py | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index f9b072ec6..240a6a07f 100644 --- a/setup.py +++ b/setup.py @@ -41,7 +41,7 @@ setup_requires=pytest_runner, install_requires=[ "defusedxml>=0.7.1", - "requests>=2.11,<3.0", + "requests>=2.20,<3.0", ], python_requires=">3.7.0", tests_require=test_requirements, diff --git a/tableauserverclient/server/endpoint/endpoint.py b/tableauserverclient/server/endpoint/endpoint.py index 8e691553b..f2666c128 100644 --- a/tableauserverclient/server/endpoint/endpoint.py +++ b/tableauserverclient/server/endpoint/endpoint.py @@ -37,7 +37,7 @@ def _make_common_headers(auth_token, content_type): headers["x-tableau-auth"] = auth_token if content_type is not None: headers["content-type"] = content_type - + headers["User-Agent"] = "Tableau Server Client {}".format(get_versions()["version"]) return headers def _make_request( diff --git a/tableauserverclient/server/server.py b/tableauserverclient/server/server.py index c6cf29ffd..75c376d41 100644 --- a/tableauserverclient/server/server.py +++ b/tableauserverclient/server/server.py @@ -36,6 +36,7 @@ from ..namespace import Namespace from tableauserverclient._version import get_versions + __TSC_VERSION__ = get_versions()["version"] del get_versions diff --git a/test/test_requests.py b/test/test_requests.py index 82859dd26..5c0d090ba 100644 --- a/test/test_requests.py +++ b/test/test_requests.py @@ -41,6 +41,7 @@ def test_make_post_request(self): ) self.assertEqual(resp.request.headers["x-tableau-auth"], "j80k54ll2lfMZ0tv97mlPvvSCRyD0DOM") self.assertEqual(resp.request.headers["content-type"], "multipart/mixed") + self.assertTrue(re.search("Tableau Server Client", resp.request.headers["user-agent"])) self.assertEqual(resp.request.body, b"1337") # Test that 500 server errors are handled properly From 34d075f48b2f3d26644f90d4f5ba34082251f453 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 4 Aug 2022 20:11:38 -0700 Subject: [PATCH 3/7] fix version import for cyclical error also alphabetized --- tableauserverclient/server/endpoint/endpoint.py | 14 +++++++++++++- tableauserverclient/server/server.py | 11 ++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/tableauserverclient/server/endpoint/endpoint.py b/tableauserverclient/server/endpoint/endpoint.py index f2666c128..058d66ad2 100644 --- a/tableauserverclient/server/endpoint/endpoint.py +++ b/tableauserverclient/server/endpoint/endpoint.py @@ -11,6 +11,7 @@ NonXMLResponseError, EndpointUnavailableError, ) +from .. import endpoint from ..query import QuerySet from ... import helpers @@ -26,18 +27,29 @@ from requests import Response +_version_header: Optional[str] = None + + class Endpoint(object): def __init__(self, parent_srv: "Server"): + global _version_header self.parent_srv = parent_srv @staticmethod def _make_common_headers(auth_token, content_type): + global _version_header + + if not _version_header: + from ..server import __TSC_VERSION__ + + _version_header = __TSC_VERSION__ + headers = {} if auth_token is not None: headers["x-tableau-auth"] = auth_token if content_type is not None: headers["content-type"] = content_type - headers["User-Agent"] = "Tableau Server Client {}".format(get_versions()["version"]) + headers["User-Agent"] = "Tableau Server Client {}".format(_version_header) return headers def _make_request( diff --git a/tableauserverclient/server/server.py b/tableauserverclient/server/server.py index 75c376d41..344e30f87 100644 --- a/tableauserverclient/server/server.py +++ b/tableauserverclient/server/server.py @@ -35,15 +35,11 @@ from .exceptions import NotSignedInError from ..namespace import Namespace -from tableauserverclient._version import get_versions +from .._version import get_versions __TSC_VERSION__ = get_versions()["version"] del get_versions -import requests - -from distutils.version import LooseVersion as Version - _PRODUCT_TO_REST_VERSION = { "10.0": "2.3", "9.3": "2.2", @@ -97,12 +93,9 @@ def __init__(self, server_address, use_server_version=False, http_options=None): self.flow_runs = FlowRuns(self) self.metrics = Metrics(self) + # must set this before calling use_server_version, because that's a server call if http_options: self.add_http_options(http_options) - - # must set this before calling use_server_version, because that's a server call - if http_options_dict: - self.add_http_options(http_options_dict) self.add_http_version_header() if use_server_version: From 7183da2f48f7a7abcce613f10bf7ea729ffd77e0 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 4 Aug 2022 20:13:54 -0700 Subject: [PATCH 4/7] fixing imports also alphabetizing --- tableauserverclient/__init__.py | 54 +++++++++++++------------- tableauserverclient/server/__init__.py | 2 +- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/tableauserverclient/__init__.py b/tableauserverclient/__init__.py index 3582386fd..394184120 100644 --- a/tableauserverclient/__init__.py +++ b/tableauserverclient/__init__.py @@ -1,52 +1,52 @@ from .namespace import NEW_NAMESPACE as DEFAULT_NAMESPACE from .models import ( + BackgroundJobItem, + ColumnItem, ConnectionCredentials, ConnectionItem, + DQWItem, + DailyInterval, DataAlertItem, + DatabaseItem, DatasourceItem, - DQWItem, + FlowItem, + FlowRunItem, GroupItem, + HourlyInterval, + IntervalItem, JobItem, - BackgroundJobItem, + MetricItem, + MonthlyInterval, PaginationItem, + Permission, + PermissionsRule, + PersonalAccessTokenAuth, ProjectItem, + RevisionItem, ScheduleItem, SiteItem, + SubscriptionItem, + TableItem, TableauAuth, - PersonalAccessTokenAuth, + Target, + TaskItem, + UnpopulatedPropertyError, UserItem, ViewItem, - WorkbookItem, - UnpopulatedPropertyError, - HourlyInterval, - DailyInterval, - WeeklyInterval, - MonthlyInterval, - IntervalItem, - TaskItem, - SubscriptionItem, - Target, - PermissionsRule, - Permission, - DatabaseItem, - TableItem, - ColumnItem, - FlowItem, WebhookItem, - PersonalAccessTokenAuth, - FlowRunItem, - RevisionItem, + WeeklyInterval, + WorkbookItem, ) from .server import ( - RequestOptions, CSVRequestOptions, ImageRequestOptions, PDFRequestOptions, - Filter, - Sort, - Server, - ServerResponseError, + RequestOptions, MissingRequiredFieldError, NotSignedInError, + ServerResponseError, + Filter, Pager, + Server, + Sort, ) diff --git a/tableauserverclient/server/__init__.py b/tableauserverclient/server/__init__.py index cb680d914..25abb3c9a 100644 --- a/tableauserverclient/server/__init__.py +++ b/tableauserverclient/server/__init__.py @@ -9,7 +9,7 @@ from .filter import Filter from .sort import Sort -from .. import ( +from ..models import ( BackgroundJobItem, ColumnItem, ConnectionItem, From b8d27202dd443619344407634760887a24cec2e6 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 4 Aug 2022 20:14:12 -0700 Subject: [PATCH 5/7] Update test_group.py How was this test passing!??!! --- test/test_group.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/test_group.py b/test/test_group.py index d884677ab..306d42170 100644 --- a/test/test_group.py +++ b/test/test_group.py @@ -127,8 +127,7 @@ def test_add_user_before_populating(self) -> None: with requests_mock.mock() as m: m.get(self.baseurl, text=get_xml_response) m.post( - self.baseurl + "/dad65087-b08b-4603-af4e-2887b8aafc67/groups/ef8b19c0-43b6-11e6-af50" - "-63f5805dbe3c/users", + self.baseurl + "/ef8b19c0-43b6-11e6-af50-63f5805dbe3c/users", text=add_user_response, ) all_groups, pagination_item = self.server.groups.get() @@ -161,8 +160,7 @@ def test_remove_user_before_populating(self) -> None: with requests_mock.mock() as m: m.get(self.baseurl, text=response_xml) m.delete( - self.baseurl + "/dad65087-b08b-4603-af4e-2887b8aafc67/groups/ef8b19c0-43b6-11e6-af50" - "-63f5805dbe3c/users/5de011f8-5aa9-4d5b-b991-f462c8dd6bb7", + self.baseurl + "/ef8b19c0-43b6-11e6-af50-63f5805dbe3c/users/5de011f8-5aa9-4d5b-b991-f462c8dd6bb7", text="ok", ) all_groups, pagination_item = self.server.groups.get() From 9062882b24ba76cdfcd990f52f12e1b5c3f0588f Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Mon, 29 Aug 2022 23:45:47 -0700 Subject: [PATCH 6/7] revert import deleted in merge --- tableauserverclient/server/server.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tableauserverclient/server/server.py b/tableauserverclient/server/server.py index 344e30f87..cd206d7e1 100644 --- a/tableauserverclient/server/server.py +++ b/tableauserverclient/server/server.py @@ -1,7 +1,8 @@ -from distutils.version import LooseVersion as Version +import requests import urllib3 -from defusedxml.ElementTree import fromstring +from defusedxml.ElementTree import fromstring +from distutils.version import LooseVersion as Version from .endpoint import ( Sites, Views, From da9fc648114acaf4f54a509ab1c0dbe95fed4ed4 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Tue, 30 Aug 2022 18:32:11 -0700 Subject: [PATCH 7/7] adjust user agent --- tableauserverclient/server/endpoint/endpoint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tableauserverclient/server/endpoint/endpoint.py b/tableauserverclient/server/endpoint/endpoint.py index 058d66ad2..378c84746 100644 --- a/tableauserverclient/server/endpoint/endpoint.py +++ b/tableauserverclient/server/endpoint/endpoint.py @@ -49,7 +49,7 @@ def _make_common_headers(auth_token, content_type): headers["x-tableau-auth"] = auth_token if content_type is not None: headers["content-type"] = content_type - headers["User-Agent"] = "Tableau Server Client {}".format(_version_header) + headers["User-Agent"] = "Tableau Server Client/{}".format(_version_header) return headers def _make_request(