Skip to content

Commit 3a0583a

Browse files
committed
Implement call to move to highest supported REST API version
1 parent c0c31cc commit 3a0583a

File tree

7 files changed

+95
-5
lines changed

7 files changed

+95
-5
lines changed

tableauserverclient/server/endpoint/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from .auth_endpoint import Auth
22
from .datasources_endpoint import Datasources
33
from .endpoint import Endpoint
4-
from .exceptions import ServerResponseError, MissingRequiredFieldError
4+
from .exceptions import ServerResponseError, MissingRequiredFieldError, ServerInfoEndpointNotFoundError
55
from .groups_endpoint import Groups
66
from .projects_endpoint import Projects
77
from .schedules_endpoint import Schedules

tableauserverclient/server/endpoint/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,7 @@ def from_response(cls, resp):
2424

2525
class MissingRequiredFieldError(Exception):
2626
pass
27+
28+
29+
class ServerInfoEndpointNotFoundError(Exception):
30+
pass

tableauserverclient/server/endpoint/server_info_endpoint.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from .endpoint import Endpoint
2+
from .exceptions import ServerResponseError, ServerInfoEndpointNotFoundError
23
from ...models import ServerInfoItem
34
import logging
45

@@ -12,6 +13,11 @@ def baseurl(self):
1213

1314
def get(self):
1415
""" Retrieve the server info for the server. This is an unauthenticated call """
15-
server_response = self.get_unauthenticated_request(self.baseurl)
16+
try:
17+
server_response = self.get_unauthenticated_request(self.baseurl)
18+
except ServerResponseError as e:
19+
if e.code == "404003":
20+
raise ServerInfoEndpointNotFoundError
21+
1622
server_info = ServerInfoItem.from_response(server_response.content)
1723
return server_info

tableauserverclient/server/server.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
1+
import xml.etree.ElementTree as ET
2+
13
from .exceptions import NotSignedInError
2-
from .endpoint import Sites, Views, Users, Groups, Workbooks, Datasources, Projects, Auth, Schedules, ServerInfo
4+
from .endpoint import Sites, Views, Users, Groups, Workbooks, Datasources, Projects, Auth, \
5+
Schedules, ServerInfo, ServerInfoEndpointNotFoundError
36

47
import requests
58

9+
_PRODUCT_TO_REST_VERSION = {
10+
'10.0': '2.3',
11+
'9.3': '2.2',
12+
'9.2': '2.1',
13+
'9.1': '2.0',
14+
'9.0': '2.0'
15+
}
16+
617

718
class Server(object):
819
class PublishMode:
@@ -47,6 +58,29 @@ def _set_auth(self, site_id, user_id, auth_token):
4758
self._user_id = user_id
4859
self._auth_token = auth_token
4960

61+
def _get_legacy_version(self):
62+
response = self._session.get(self.server_address + "/auth?format=xml")
63+
info_xml = ET.fromstring(response.content)
64+
prod_version = info_xml.find('.//product_version').text
65+
version = _PRODUCT_TO_REST_VERSION.get(prod_version, '2.1') # 2.1
66+
return version
67+
68+
def _determine_highest_version(self):
69+
try:
70+
old_version = self.version
71+
self.version = "2.4"
72+
version = self.server_info.get().rest_api_version
73+
except ServerInfoEndpointNotFoundError:
74+
version = self._get_legacy_version()
75+
76+
finally:
77+
self.version = old_version
78+
79+
return version
80+
81+
def use_highest_version(self):
82+
self.version = self._determine_highest_version()
83+
5084
@property
5185
def baseurl(self):
5286
return "{0}/api/{1}".format(self._server_address, str(self.version))

test/assets/server_info_404.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<tsResponse xmlns="http://tableau.com/api" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tableau.com/api http://tableau.com/api/ts-api-2.3.xsd">
3+
<error code="404003">
4+
<summary>Resource Not Found</summary>
5+
<detail>Unknown resource '/2.4/serverInfo' specified in URI.</detail>
6+
</error>
7+
</tsResponse>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<authinfo>
2+
<version version="0.31">
3+
<api_version>0.31</api_version>
4+
<server_api_version>0.31</server_api_version>
5+
<document_version>9.2</document_version>
6+
<product_version>9.3</product_version>
7+
<external_version>9.3.4</external_version>
8+
<build>hello.16.1106.2025</build>
9+
<publishing_method>unrestricted</publishing_method>
10+
<mobile_api_version>2.6</mobile_api_version>
11+
</version>
12+
</authinfo>

test/test_server_info.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,48 @@
66
TEST_ASSET_DIR = os.path.join(os.path.dirname(__file__), 'assets')
77

88
SERVER_INFO_GET_XML = os.path.join(TEST_ASSET_DIR, 'server_info_get.xml')
9+
SERVER_INFO_404 = os.path.join(TEST_ASSET_DIR, 'server_info_404.xml')
10+
SERVER_INFO_AUTH_INFO_XML = os.path.join(TEST_ASSET_DIR, 'server_info_auth_info.xml')
911

1012

1113
class ServerInfoTests(unittest.TestCase):
1214
def setUp(self):
1315
self.server = TSC.Server('http://test')
14-
self.server.version = '2.4'
1516
self.baseurl = self.server.server_info.baseurl
1617

1718
def test_server_info_get(self):
1819
with open(SERVER_INFO_GET_XML, 'rb') as f:
1920
response_xml = f.read().decode('utf-8')
2021
with requests_mock.mock() as m:
21-
m.get(self.baseurl, text=response_xml)
22+
self.server.version = '2.4'
23+
m.get(self.server.server_info.baseurl, text=response_xml)
2224
actual = self.server.server_info.get()
2325

2426
self.assertEqual('10.1.0', actual.product_version)
2527
self.assertEqual('10100.16.1024.2100', actual.build_number)
2628
self.assertEqual('2.4', actual.rest_api_version)
29+
30+
def test_server_info_use_highest_version_downgrades(self):
31+
with open(SERVER_INFO_AUTH_INFO_XML, 'rb') as f:
32+
# This is the auth.xml endpoint present back to 9.0 Servers
33+
auth_response_xml = f.read().decode('utf-8')
34+
with open(SERVER_INFO_404, 'rb') as f:
35+
# 10.1 serverInfo response
36+
si_response_xml = f.read().decode('utf-8')
37+
with requests_mock.mock() as m:
38+
# Return a 404 for serverInfo so we can pretend this is an old Server
39+
m.get(self.server.server_address + "/api/2.4/serverInfo", text=si_response_xml, status_code=404)
40+
m.get(self.server.server_address + "/auth?format=xml", text=auth_response_xml)
41+
self.server.use_highest_version()
42+
self.assertEqual(self.server.version, '2.2')
43+
44+
def test_server_info_use_highest_version_upgrades(self):
45+
with open(SERVER_INFO_GET_XML, 'rb') as f:
46+
si_response_xml = f.read().decode('utf-8')
47+
with requests_mock.mock() as m:
48+
m.get(self.server.server_address + "/api/2.4/serverInfo", text=si_response_xml)
49+
# Pretend we're old
50+
self.server.version = '2.0'
51+
self.server.use_highest_version()
52+
# Did we upgrade to 2.4?
53+
self.assertEqual(self.server.version, '2.4')

0 commit comments

Comments
 (0)