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')