From 61501755f24c1676971e1d7eb19f6584264d3384 Mon Sep 17 00:00:00 2001 From: T8y8 Date: Mon, 7 Nov 2016 16:33:43 -0600 Subject: [PATCH] Implement call to move to highest supported REST API version --- .../server/endpoint/__init__.py | 2 +- .../server/endpoint/exceptions.py | 4 +++ .../server/endpoint/server_info_endpoint.py | 8 ++++- tableauserverclient/server/server.py | 36 ++++++++++++++++++- test/assets/server_info_404.xml | 7 ++++ test/assets/server_info_auth_info.xml | 12 +++++++ test/test_server_info.py | 31 ++++++++++++++-- 7 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 test/assets/server_info_404.xml create mode 100644 test/assets/server_info_auth_info.xml diff --git a/tableauserverclient/server/endpoint/__init__.py b/tableauserverclient/server/endpoint/__init__.py index 63d69510c..d9dca0f42 100644 --- a/tableauserverclient/server/endpoint/__init__.py +++ b/tableauserverclient/server/endpoint/__init__.py @@ -1,7 +1,7 @@ from .auth_endpoint import Auth from .datasources_endpoint import Datasources from .endpoint import Endpoint -from .exceptions import ServerResponseError, MissingRequiredFieldError +from .exceptions import ServerResponseError, MissingRequiredFieldError, ServerInfoEndpointNotFoundError from .groups_endpoint import Groups from .projects_endpoint import Projects from .schedules_endpoint import Schedules diff --git a/tableauserverclient/server/endpoint/exceptions.py b/tableauserverclient/server/endpoint/exceptions.py index 7907a6dab..3eadd5ce5 100644 --- a/tableauserverclient/server/endpoint/exceptions.py +++ b/tableauserverclient/server/endpoint/exceptions.py @@ -24,3 +24,7 @@ def from_response(cls, resp): class MissingRequiredFieldError(Exception): pass + + +class ServerInfoEndpointNotFoundError(Exception): + pass diff --git a/tableauserverclient/server/endpoint/server_info_endpoint.py b/tableauserverclient/server/endpoint/server_info_endpoint.py index 1fb17f26f..d6b2b7d96 100644 --- a/tableauserverclient/server/endpoint/server_info_endpoint.py +++ b/tableauserverclient/server/endpoint/server_info_endpoint.py @@ -1,4 +1,5 @@ from .endpoint import Endpoint +from .exceptions import ServerResponseError, ServerInfoEndpointNotFoundError from ...models import ServerInfoItem import logging @@ -12,6 +13,11 @@ def baseurl(self): def get(self): """ Retrieve the server info for the server. This is an unauthenticated call """ - server_response = self.get_unauthenticated_request(self.baseurl) + try: + server_response = self.get_unauthenticated_request(self.baseurl) + except ServerResponseError as e: + if e.code == "404003": + raise ServerInfoEndpointNotFoundError + server_info = ServerInfoItem.from_response(server_response.content) return server_info diff --git a/tableauserverclient/server/server.py b/tableauserverclient/server/server.py index 2cb08a892..b233377fe 100644 --- a/tableauserverclient/server/server.py +++ b/tableauserverclient/server/server.py @@ -1,8 +1,19 @@ +import xml.etree.ElementTree as ET + from .exceptions import NotSignedInError -from .endpoint import Sites, Views, Users, Groups, Workbooks, Datasources, Projects, Auth, Schedules, ServerInfo +from .endpoint import Sites, Views, Users, Groups, Workbooks, Datasources, Projects, Auth, \ + Schedules, ServerInfo, ServerInfoEndpointNotFoundError import requests +_PRODUCT_TO_REST_VERSION = { + '10.0': '2.3', + '9.3': '2.2', + '9.2': '2.1', + '9.1': '2.0', + '9.0': '2.0' +} + class Server(object): class PublishMode: @@ -47,6 +58,29 @@ def _set_auth(self, site_id, user_id, auth_token): self._user_id = user_id self._auth_token = auth_token + def _get_legacy_version(self): + response = self._session.get(self.server_address + "/auth?format=xml") + info_xml = ET.fromstring(response.content) + prod_version = info_xml.find('.//product_version').text + version = _PRODUCT_TO_REST_VERSION.get(prod_version, '2.1') # 2.1 + return version + + def _determine_highest_version(self): + try: + old_version = self.version + self.version = "2.4" + version = self.server_info.get().rest_api_version + except ServerInfoEndpointNotFoundError: + version = self._get_legacy_version() + + finally: + self.version = old_version + + return version + + def use_highest_version(self): + self.version = self._determine_highest_version() + @property def baseurl(self): return "{0}/api/{1}".format(self._server_address, str(self.version)) diff --git a/test/assets/server_info_404.xml b/test/assets/server_info_404.xml new file mode 100644 index 000000000..a23abf9ae --- /dev/null +++ b/test/assets/server_info_404.xml @@ -0,0 +1,7 @@ + + + + Resource Not Found + Unknown resource '/2.4/serverInfo' specified in URI. + + diff --git a/test/assets/server_info_auth_info.xml b/test/assets/server_info_auth_info.xml new file mode 100644 index 000000000..58d9c5baf --- /dev/null +++ b/test/assets/server_info_auth_info.xml @@ -0,0 +1,12 @@ + + +0.31 +0.31 +9.2 +9.3 +9.3.4 +hello.16.1106.2025 +unrestricted +2.6 + + diff --git a/test/test_server_info.py b/test/test_server_info.py index 03e39210f..084e6c91f 100644 --- a/test/test_server_info.py +++ b/test/test_server_info.py @@ -6,21 +6,48 @@ TEST_ASSET_DIR = os.path.join(os.path.dirname(__file__), 'assets') SERVER_INFO_GET_XML = os.path.join(TEST_ASSET_DIR, 'server_info_get.xml') +SERVER_INFO_404 = os.path.join(TEST_ASSET_DIR, 'server_info_404.xml') +SERVER_INFO_AUTH_INFO_XML = os.path.join(TEST_ASSET_DIR, 'server_info_auth_info.xml') class ServerInfoTests(unittest.TestCase): def setUp(self): self.server = TSC.Server('http://test') - self.server.version = '2.4' self.baseurl = self.server.server_info.baseurl def test_server_info_get(self): with open(SERVER_INFO_GET_XML, 'rb') as f: response_xml = f.read().decode('utf-8') with requests_mock.mock() as m: - m.get(self.baseurl, text=response_xml) + self.server.version = '2.4' + m.get(self.server.server_info.baseurl, text=response_xml) actual = self.server.server_info.get() self.assertEqual('10.1.0', actual.product_version) self.assertEqual('10100.16.1024.2100', actual.build_number) self.assertEqual('2.4', actual.rest_api_version) + + def test_server_info_use_highest_version_downgrades(self): + with open(SERVER_INFO_AUTH_INFO_XML, 'rb') as f: + # This is the auth.xml endpoint present back to 9.0 Servers + auth_response_xml = f.read().decode('utf-8') + with open(SERVER_INFO_404, 'rb') as f: + # 10.1 serverInfo response + si_response_xml = f.read().decode('utf-8') + with requests_mock.mock() as m: + # Return a 404 for serverInfo so we can pretend this is an old Server + m.get(self.server.server_address + "/api/2.4/serverInfo", text=si_response_xml, status_code=404) + m.get(self.server.server_address + "/auth?format=xml", text=auth_response_xml) + self.server.use_highest_version() + self.assertEqual(self.server.version, '2.2') + + def test_server_info_use_highest_version_upgrades(self): + with open(SERVER_INFO_GET_XML, 'rb') as f: + si_response_xml = f.read().decode('utf-8') + with requests_mock.mock() as m: + m.get(self.server.server_address + "/api/2.4/serverInfo", text=si_response_xml) + # Pretend we're old + self.server.version = '2.0' + self.server.use_highest_version() + # Did we upgrade to 2.4? + self.assertEqual(self.server.version, '2.4')