diff --git a/tableauserverclient/models/__init__.py b/tableauserverclient/models/__init__.py index 415c84147..b248ea399 100644 --- a/tableauserverclient/models/__init__.py +++ b/tableauserverclient/models/__init__.py @@ -7,6 +7,7 @@ from .pagination_item import PaginationItem from .project_item import ProjectItem from .schedule_item import ScheduleItem +from .server_info_item import ServerInfoItem from .site_item import SiteItem from .tableau_auth import TableauAuth from .user_item import UserItem diff --git a/tableauserverclient/models/server_info_item.py b/tableauserverclient/models/server_info_item.py new file mode 100644 index 000000000..91900f850 --- /dev/null +++ b/tableauserverclient/models/server_info_item.py @@ -0,0 +1,33 @@ +import xml.etree.ElementTree as ET +from .. import NAMESPACE + + +class ServerInfoItem(object): + def __init__(self, product_version, build_number, rest_api_version): + self._product_version = product_version + self._build_number = build_number + self._rest_api_version = rest_api_version + + @property + def product_version(self): + return self._product_version + + @property + def build_number(self): + return self._build_number + + @property + def rest_api_version(self): + return self._rest_api_version + + @classmethod + def from_response(cls, resp): + parsed_response = ET.fromstring(resp) + product_version_tag = parsed_response.find('.//t:productVersion', namespaces=NAMESPACE) + rest_api_version_tag = parsed_response.find('.//t:restApiVersion', namespaces=NAMESPACE) + + build_number = product_version_tag.get('build', None) + product_version = product_version_tag.text + rest_api_version = rest_api_version_tag.text + + return cls(product_version, build_number, rest_api_version) diff --git a/tableauserverclient/server/endpoint/__init__.py b/tableauserverclient/server/endpoint/__init__.py index 65e15c683..63d69510c 100644 --- a/tableauserverclient/server/endpoint/__init__.py +++ b/tableauserverclient/server/endpoint/__init__.py @@ -5,6 +5,7 @@ from .groups_endpoint import Groups from .projects_endpoint import Projects from .schedules_endpoint import Schedules +from .server_info_endpoint import ServerInfo from .sites_endpoint import Sites from .users_endpoint import Users from .views_endpoint import Views diff --git a/tableauserverclient/server/endpoint/endpoint.py b/tableauserverclient/server/endpoint/endpoint.py index 98f451211..fd8cb3ac7 100644 --- a/tableauserverclient/server/endpoint/endpoint.py +++ b/tableauserverclient/server/endpoint/endpoint.py @@ -1,6 +1,7 @@ from .exceptions import ServerResponseError import logging + logger = logging.getLogger('tableau.endpoint') Success_codes = [200, 201, 204] @@ -15,6 +16,16 @@ def _check_status(server_response): if server_response.status_code not in Success_codes: raise ServerResponseError.from_response(server_response.content) + def get_unauthenticated_request(self, url, request_object=None): + if request_object is not None: + url = request_object.apply_query_params(url) + server_response = self.parent_srv.session.get(url, **self.parent_srv.http_options) + self._check_status(server_response) + if server_response.encoding: + logger.debug(u'Server response from {0}:\n\t{1}'.format( + url, server_response.content.decode(server_response.encoding))) + return server_response + def get_request(self, url, request_object=None): if request_object is not None: url = request_object.apply_query_params(url) diff --git a/tableauserverclient/server/endpoint/server_info_endpoint.py b/tableauserverclient/server/endpoint/server_info_endpoint.py new file mode 100644 index 000000000..901778c58 --- /dev/null +++ b/tableauserverclient/server/endpoint/server_info_endpoint.py @@ -0,0 +1,21 @@ +from .endpoint import Endpoint +from ...models import ServerInfoItem +import logging + +logger = logging.getLogger('tableau.endpoint.server_info') + + +class ServerInfo(Endpoint): + def __init__(self, parent_srv): + super(Endpoint, self).__init__() + self.parent_srv = parent_srv + + @property + def baseurl(self): + return "{0}/serverInfo".format(self.parent_srv.baseurl) + + def get(self): + """ Retrieve the server info for the server. This is an unauthenticated call """ + server_response = self.get_unauthenticated_request(self.baseurl) + server_info = ServerInfoItem.from_response(server_response.content) + return server_info diff --git a/tableauserverclient/server/server.py b/tableauserverclient/server/server.py index 8c28c1825..5e85063d2 100644 --- a/tableauserverclient/server/server.py +++ b/tableauserverclient/server/server.py @@ -1,5 +1,5 @@ from .exceptions import NotSignedInError -from .endpoint import Sites, Views, Users, Groups, Workbooks, Datasources, Projects, Auth, Schedules +from .endpoint import Sites, Views, Users, Groups, Workbooks, Datasources, Projects, Auth, Schedules, ServerInfo import requests @@ -27,6 +27,7 @@ def __init__(self, server_address): self.datasources = Datasources(self) self.projects = Projects(self) self.schedules = Schedules(self) + self.server_info = ServerInfo(self) def add_http_options(self, options_dict): self._http_options.update(options_dict) diff --git a/test/assets/server_info_get.xml b/test/assets/server_info_get.xml new file mode 100644 index 000000000..ce4e0b322 --- /dev/null +++ b/test/assets/server_info_get.xml @@ -0,0 +1,6 @@ + + +10.1.0 +2.4 + + \ No newline at end of file diff --git a/test/test_server_info.py b/test/test_server_info.py new file mode 100644 index 000000000..03e39210f --- /dev/null +++ b/test/test_server_info.py @@ -0,0 +1,26 @@ +import unittest +import os.path +import requests_mock +import tableauserverclient as TSC + +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') + + +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) + 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)