From 241effd56e2b65890ad9ef1d83f6de6f18954ad5 Mon Sep 17 00:00:00 2001 From: Tyler Doyle Date: Sat, 23 Sep 2017 14:46:20 -0700 Subject: [PATCH 01/10] Add a Connection Item Fetcher and test coverage --- tableauserverclient/models/datasource_item.py | 2 +- .../server/endpoint/datasources_endpoint.py | 15 +++++++++---- test/test_datasource.py | 21 +++++++++++++++++++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/tableauserverclient/models/datasource_item.py b/tableauserverclient/models/datasource_item.py index 3ecbe9e4f..b00e6cbea 100644 --- a/tableauserverclient/models/datasource_item.py +++ b/tableauserverclient/models/datasource_item.py @@ -28,7 +28,7 @@ def connections(self): if self._connections is None: error = 'Datasource item must be populated with connections first.' raise UnpopulatedPropertyError(error) - return self._connections + return self._connections() @property def content_url(self): diff --git a/tableauserverclient/server/endpoint/datasources_endpoint.py b/tableauserverclient/server/endpoint/datasources_endpoint.py index b50d5d543..27c5981a1 100644 --- a/tableauserverclient/server/endpoint/datasources_endpoint.py +++ b/tableauserverclient/server/endpoint/datasources_endpoint.py @@ -55,12 +55,19 @@ def populate_connections(self, datasource_item): if not datasource_item.id: error = 'Datasource item missing ID. Datasource must be retrieved from server first.' raise MissingRequiredFieldError(error) - url = '{0}/{1}/connections'.format(self.baseurl, datasource_item.id) - server_response = self.get_request(url) - datasource_item._set_connections( - ConnectionItem.from_response(server_response.content, self.parent_srv.namespace)) + + def connections_fetcher(): + return self._get_datasource_connections(datasource_item) + + datasource_item._set_connections(connections_fetcher) logger.info('Populated connections for datasource (ID: {0})'.format(datasource_item.id)) + def _get_datasource_connections(self, datasource_item, req_options=None): + url = '{0}/{1}/connections'.format(self.baseurl, datasource_item.id) + server_response = self.get_request(url, req_options) + connections = ConnectionItem.from_response(server_response.content, self.parent_srv.namespace) + return connections + # Delete 1 datasource by id @api(version="2.0") def delete(self, datasource_id): diff --git a/test/test_datasource.py b/test/test_datasource.py index 3b9b29248..681f5fd8b 100644 --- a/test/test_datasource.py +++ b/test/test_datasource.py @@ -10,10 +10,12 @@ GET_XML = os.path.join(TEST_ASSET_DIR, 'datasource_get.xml') GET_EMPTY_XML = os.path.join(TEST_ASSET_DIR, 'datasource_get_empty.xml') GET_BY_ID_XML = os.path.join(TEST_ASSET_DIR, 'datasource_get_by_id.xml') +POPULATE_CONNECTIONS_XML = os.path.join(TEST_ASSET_DIR, 'datasource_populate_connections.xml') PUBLISH_XML = os.path.join(TEST_ASSET_DIR, 'datasource_publish.xml') UPDATE_XML = os.path.join(TEST_ASSET_DIR, 'datasource_update.xml') + class DatasourceTests(unittest.TestCase): def setUp(self): self.server = TSC.Server('http://test') @@ -135,6 +137,25 @@ def test_update_tags(self): self.assertEqual(single_datasource.tags, updated_datasource.tags) self.assertEqual(single_datasource._initial_tags, updated_datasource._initial_tags) + def test_populate_connections(self): + with open(POPULATE_CONNECTIONS_XML, 'rb') as f: + response_xml = f.read().decode('utf-8') + with requests_mock.mock() as m: + m.get(self.baseurl + '/9dbd2263-16b5-46e1-9c43-a76bb8ab65fb/connections', text=response_xml) + single_datasource = TSC.DatasourceItem('test', '1d0304cd-3796-429f-b815-7258370b9b74') + single_datasource.owner_id = 'dd2239f6-ddf1-4107-981a-4cf94e415794' + single_datasource._id = '9dbd2263-16b5-46e1-9c43-a76bb8ab65fb' + self.server.datasources.populate_connections(single_datasource) + + self.assertEqual('9dbd2263-16b5-46e1-9c43-a76bb8ab65fb', single_datasource.id) + + connections = single_datasource.connections + self.assertTrue(connections) + ds1, ds2, ds3 = connections + self.assertEqual(ds1.id, 'be786ae0-d2bf-4a4b-9b34-e2de8d2d4488') + self.assertEqual(ds2.id, '970e24bc-e200-4841-a3e9-66e7d122d77e') + self.assertEqual(ds3.id, '7d85b889-283b-42df-b23e-3c811e402f1f') + def test_publish(self): with open(PUBLISH_XML, 'rb') as f: response_xml = f.read().decode('utf-8') From 24f9c322f2dc0c9df8e9846febc3372963aa156b Mon Sep 17 00:00:00 2001 From: Tyler Doyle Date: Sat, 23 Sep 2017 14:51:11 -0700 Subject: [PATCH 02/10] Add connection fetcher to workbooks --- tableauserverclient/models/workbook_item.py | 2 +- .../server/endpoint/workbooks_endpoint.py | 14 +++++++++++--- test/test_workbook.py | 8 ++++---- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/tableauserverclient/models/workbook_item.py b/tableauserverclient/models/workbook_item.py index 992648f9f..0882109a3 100644 --- a/tableauserverclient/models/workbook_item.py +++ b/tableauserverclient/models/workbook_item.py @@ -30,7 +30,7 @@ def connections(self): if self._connections is None: error = "Workbook item must be populated with connections first." raise UnpopulatedPropertyError(error) - return self._connections + return self._connections() @property def content_url(self): diff --git a/tableauserverclient/server/endpoint/workbooks_endpoint.py b/tableauserverclient/server/endpoint/workbooks_endpoint.py index f86e848fc..5e5edb407 100644 --- a/tableauserverclient/server/endpoint/workbooks_endpoint.py +++ b/tableauserverclient/server/endpoint/workbooks_endpoint.py @@ -135,11 +135,19 @@ def populate_connections(self, workbook_item): if not workbook_item.id: error = "Workbook item missing ID. Workbook must be retrieved from server first." raise MissingRequiredFieldError(error) - url = "{0}/{1}/connections".format(self.baseurl, workbook_item.id) - server_response = self.get_request(url) - workbook_item._set_connections(ConnectionItem.from_response(server_response.content, self.parent_srv.namespace)) + + def connection_fetcher(): + return self._get_workbook_connections(workbook_item) + + workbook_item._set_connections(connection_fetcher) logger.info('Populated connections for workbook (ID: {0})'.format(workbook_item.id)) + def _get_workbook_connections(self, workbook_item, req_options=None): + url = "{0}/{1}/connections".format(self.baseurl, workbook_item.id) + server_response = self.get_request(url, req_options) + connections = ConnectionItem.from_response(server_response.content, self.parent_srv.namespace) + return connections + # Get preview image of workbook @api(version="2.0") def populate_preview_image(self, workbook_item): diff --git a/test/test_workbook.py b/test/test_workbook.py index 7a2131e7f..7b922791b 100644 --- a/test/test_workbook.py +++ b/test/test_workbook.py @@ -233,10 +233,10 @@ def test_populate_connections(self): single_workbook._id = '1f951daf-4061-451a-9df1-69a8062664f2' self.server.workbooks.populate_connections(single_workbook) - self.assertEqual('37ca6ced-58d7-4dcf-99dc-f0a85223cbef', single_workbook.connections[0].id) - self.assertEqual('dataengine', single_workbook.connections[0].connection_type) - self.assertEqual('4506225a-0d32-4ab1-82d3-c24e85f7afba', single_workbook.connections[0].datasource_id) - self.assertEqual('World Indicators', single_workbook.connections[0].datasource_name) + self.assertEqual('37ca6ced-58d7-4dcf-99dc-f0a85223cbef', single_workbook.connections[0].id) + self.assertEqual('dataengine', single_workbook.connections[0].connection_type) + self.assertEqual('4506225a-0d32-4ab1-82d3-c24e85f7afba', single_workbook.connections[0].datasource_id) + self.assertEqual('World Indicators', single_workbook.connections[0].datasource_name) def test_populate_connections_missing_id(self): single_workbook = TSC.WorkbookItem('test') From cc197fe2cad7aa8577144f016c19039797261acf Mon Sep 17 00:00:00 2001 From: Tyler Doyle Date: Sat, 23 Sep 2017 15:08:14 -0700 Subject: [PATCH 03/10] add a fetcher for views on workbooks --- tableauserverclient/models/workbook_item.py | 12 +++++++++- .../server/endpoint/workbooks_endpoint.py | 18 ++++++++++---- test/test_workbook.py | 24 +++++++++---------- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/tableauserverclient/models/workbook_item.py b/tableauserverclient/models/workbook_item.py index 0882109a3..3b6f5f170 100644 --- a/tableauserverclient/models/workbook_item.py +++ b/tableauserverclient/models/workbook_item.py @@ -83,10 +83,20 @@ def updated_at(self): @property def views(self): + # Views can be set in an initial workbook response OR by a call + # to Server. Without getting too fancy, I think we can rely on + # returning a list from the response, until they call + # populate_workbook, in which case we bind the fetcher and + # return a callable. if self._views is None: error = "Workbook item must be populated with views first." raise UnpopulatedPropertyError(error) - return self._views + elif callable(self._views): + # We've called `populate_views` on this model + return self._views() + else: + # We had views included in a WorkbookItem response + return self._views def _set_connections(self, connections): self._connections = connections diff --git a/tableauserverclient/server/endpoint/workbooks_endpoint.py b/tableauserverclient/server/endpoint/workbooks_endpoint.py index 5e5edb407..da19fa92e 100644 --- a/tableauserverclient/server/endpoint/workbooks_endpoint.py +++ b/tableauserverclient/server/endpoint/workbooks_endpoint.py @@ -122,12 +122,20 @@ def populate_views(self, workbook_item): if not workbook_item.id: error = "Workbook item missing ID. Workbook must be retrieved from server first." raise MissingRequiredFieldError(error) + + def view_fetcher(): + return self._get_views_for_workbook(workbook_item) + + workbook_item._set_views(view_fetcher) + logger.info('Populated views for workbook (ID: {0}'.format(workbook_item.id)) + + def _get_views_for_workbook(self, workbook_item): url = "{0}/{1}/views".format(self.baseurl, workbook_item.id) server_response = self.get_request(url) - workbook_item._set_views(ViewItem.from_response(server_response.content, - self.parent_srv.namespace, - workbook_id=workbook_item.id)) - logger.info('Populated views for workbook (ID: {0}'.format(workbook_item.id)) + views = ViewItem.from_response(server_response.content, + self.parent_srv.namespace, + workbook_id=workbook_item.id) + return views # Get all connections of workbook @api(version="2.0") @@ -137,7 +145,7 @@ def populate_connections(self, workbook_item): raise MissingRequiredFieldError(error) def connection_fetcher(): - return self._get_workbook_connections(workbook_item) + return self._get_workbook_connections(workbook_item) workbook_item._set_connections(connection_fetcher) logger.info('Populated connections for workbook (ID: {0})'.format(workbook_item.id)) diff --git a/test/test_workbook.py b/test/test_workbook.py index 7b922791b..f42d69fb6 100644 --- a/test/test_workbook.py +++ b/test/test_workbook.py @@ -207,18 +207,18 @@ def test_populate_views(self): single_workbook._id = '1f951daf-4061-451a-9df1-69a8062664f2' self.server.workbooks.populate_views(single_workbook) - views_list = single_workbook.views - self.assertEqual('097dbe13-de89-445f-b2c3-02f28bd010c1', views_list[0].id) - self.assertEqual('GDP per capita', views_list[0].name) - self.assertEqual('RESTAPISample/sheets/GDPpercapita', views_list[0].content_url) - - self.assertEqual('2c1ab9d7-8d64-4cc6-b495-52e40c60c330', views_list[1].id) - self.assertEqual('Country ranks', views_list[1].name) - self.assertEqual('RESTAPISample/sheets/Countryranks', views_list[1].content_url) - - self.assertEqual('0599c28c-6d82-457e-a453-e52c1bdb00f5', views_list[2].id) - self.assertEqual('Interest rates', views_list[2].name) - self.assertEqual('RESTAPISample/sheets/Interestrates', views_list[2].content_url) + views_list = single_workbook.views + self.assertEqual('097dbe13-de89-445f-b2c3-02f28bd010c1', views_list[0].id) + self.assertEqual('GDP per capita', views_list[0].name) + self.assertEqual('RESTAPISample/sheets/GDPpercapita', views_list[0].content_url) + + self.assertEqual('2c1ab9d7-8d64-4cc6-b495-52e40c60c330', views_list[1].id) + self.assertEqual('Country ranks', views_list[1].name) + self.assertEqual('RESTAPISample/sheets/Countryranks', views_list[1].content_url) + + self.assertEqual('0599c28c-6d82-457e-a453-e52c1bdb00f5', views_list[2].id) + self.assertEqual('Interest rates', views_list[2].name) + self.assertEqual('RESTAPISample/sheets/Interestrates', views_list[2].content_url) def test_populate_views_missing_id(self): single_workbook = TSC.WorkbookItem('test') From 0a179ade528e13d92de13aaa87588e59977de6c8 Mon Sep 17 00:00:00 2001 From: Tyler Doyle Date: Sat, 23 Sep 2017 15:14:16 -0700 Subject: [PATCH 04/10] Add preview image fetcher --- tableauserverclient/models/workbook_item.py | 2 +- .../server/endpoint/workbooks_endpoint.py | 12 ++++++++++-- test/test_workbook.py | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/tableauserverclient/models/workbook_item.py b/tableauserverclient/models/workbook_item.py index 3b6f5f170..bcf13b9ac 100644 --- a/tableauserverclient/models/workbook_item.py +++ b/tableauserverclient/models/workbook_item.py @@ -49,7 +49,7 @@ def preview_image(self): if self._preview_image is None: error = "Workbook item must be populated with its preview image first." raise UnpopulatedPropertyError(error) - return self._preview_image + return self._preview_image() @property def project_id(self): diff --git a/tableauserverclient/server/endpoint/workbooks_endpoint.py b/tableauserverclient/server/endpoint/workbooks_endpoint.py index da19fa92e..d7a2ae4a7 100644 --- a/tableauserverclient/server/endpoint/workbooks_endpoint.py +++ b/tableauserverclient/server/endpoint/workbooks_endpoint.py @@ -162,10 +162,18 @@ def populate_preview_image(self, workbook_item): if not workbook_item.id: error = "Workbook item missing ID. Workbook must be retrieved from server first." raise MissingRequiredFieldError(error) + + def image_fetcher(): + return self._get_wb_preview_image(workbook_item) + + workbook_item._set_preview_image(image_fetcher) + logger.info('Populated preview image for workbook (ID: {0})'.format(workbook_item.id)) + + def _get_wb_preview_image(self, workbook_item): url = "{0}/{1}/previewImage".format(self.baseurl, workbook_item.id) server_response = self.get_request(url) - workbook_item._set_preview_image(server_response.content) - logger.info('Populated preview image for workbook (ID: {0})'.format(workbook_item.id)) + preview_image = server_response.content + return preview_image # Publishes workbook. Chunking method if file over 64MB @api(version="2.0") diff --git a/test/test_workbook.py b/test/test_workbook.py index f42d69fb6..91ca3d679 100644 --- a/test/test_workbook.py +++ b/test/test_workbook.py @@ -253,7 +253,7 @@ def test_populate_preview_image(self): single_workbook._id = '1f951daf-4061-451a-9df1-69a8062664f2' self.server.workbooks.populate_preview_image(single_workbook) - self.assertEqual(response, single_workbook.preview_image) + self.assertEqual(response, single_workbook.preview_image) def test_populate_preview_image_missing_id(self): single_workbook = TSC.WorkbookItem('test') From e7c678789d6dd5e1ff492e0f5c6e6bf474a2070e Mon Sep 17 00:00:00 2001 From: Tyler Doyle Date: Sat, 23 Sep 2017 15:24:45 -0700 Subject: [PATCH 05/10] add sub-pager to workbooks for user --- tableauserverclient/models/user_item.py | 2 +- .../server/endpoint/users_endpoint.py | 13 +++++++-- test/test_user.py | 29 +++++++++---------- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/tableauserverclient/models/user_item.py b/tableauserverclient/models/user_item.py index fafb8d058..b0da7b3d0 100644 --- a/tableauserverclient/models/user_item.py +++ b/tableauserverclient/models/user_item.py @@ -82,7 +82,7 @@ def workbooks(self): if self._workbooks is None: error = "User item must be populated with workbooks first." raise UnpopulatedPropertyError(error) - return self._workbooks + return self._workbooks() def _set_workbooks(self, workbooks): self._workbooks = workbooks diff --git a/tableauserverclient/server/endpoint/users_endpoint.py b/tableauserverclient/server/endpoint/users_endpoint.py index 21a3d4742..54ab07212 100644 --- a/tableauserverclient/server/endpoint/users_endpoint.py +++ b/tableauserverclient/server/endpoint/users_endpoint.py @@ -70,15 +70,24 @@ def add(self, user_item): # Get workbooks for user @api(version="2.0") def populate_workbooks(self, user_item, req_options=None): + from .. import Pager + if not user_item.id: error = "User item missing ID." raise MissingRequiredFieldError(error) + + def wb_pager(): + return Pager(lambda options: self._get_wbs_for_user(user_item, options), req_options) + + user_item._set_workbooks(wb_pager) + + def _get_wbs_for_user(self, user_item, req_options=None): url = "{0}/{1}/workbooks".format(self.baseurl, user_item.id) server_response = self.get_request(url, req_options) logger.info('Populated workbooks for user (ID: {0})'.format(user_item.id)) - user_item._set_workbooks(WorkbookItem.from_response(server_response.content, self.parent_srv.namespace)) + workbook_item = WorkbookItem.from_response(server_response.content, self.parent_srv.namespace) pagination_item = PaginationItem.from_response(server_response.content, self.parent_srv.namespace) - return pagination_item + return workbook_item, pagination_item def populate_favorites(self, user_item): raise NotImplementedError('REST API currently does not support the ability to query favorites') diff --git a/test/test_user.py b/test/test_user.py index fa8344371..8df2f2b2e 100644 --- a/test/test_user.py +++ b/test/test_user.py @@ -128,21 +128,20 @@ def test_populate_workbooks(self): text=response_xml) single_user = TSC.UserItem('test', 'Interactor') single_user._id = 'dd2239f6-ddf1-4107-981a-4cf94e415794' - pagination_item = self.server.users.populate_workbooks(single_user) - - workbook_list = single_user.workbooks - self.assertEqual(1, pagination_item.total_available) - self.assertEqual('3cc6cd06-89ce-4fdc-b935-5294135d6d42', workbook_list[0].id) - self.assertEqual('SafariSample', workbook_list[0].name) - self.assertEqual('SafariSample', workbook_list[0].content_url) - self.assertEqual(False, workbook_list[0].show_tabs) - self.assertEqual(26, workbook_list[0].size) - self.assertEqual('2016-07-26T20:34:56Z', format_datetime(workbook_list[0].created_at)) - self.assertEqual('2016-07-26T20:35:05Z', format_datetime(workbook_list[0].updated_at)) - self.assertEqual('ee8c6e70-43b6-11e6-af4f-f7b0d8e20760', workbook_list[0].project_id) - self.assertEqual('default', workbook_list[0].project_name) - self.assertEqual('5de011f8-5aa9-4d5b-b991-f462c8dd6bb7', workbook_list[0].owner_id) - self.assertEqual(set(['Safari', 'Sample']), workbook_list[0].tags) + self.server.users.populate_workbooks(single_user) + + workbook_list = list(single_user.workbooks) + self.assertEqual('3cc6cd06-89ce-4fdc-b935-5294135d6d42', workbook_list[0].id) + self.assertEqual('SafariSample', workbook_list[0].name) + self.assertEqual('SafariSample', workbook_list[0].content_url) + self.assertEqual(False, workbook_list[0].show_tabs) + self.assertEqual(26, workbook_list[0].size) + self.assertEqual('2016-07-26T20:34:56Z', format_datetime(workbook_list[0].created_at)) + self.assertEqual('2016-07-26T20:35:05Z', format_datetime(workbook_list[0].updated_at)) + self.assertEqual('ee8c6e70-43b6-11e6-af4f-f7b0d8e20760', workbook_list[0].project_id) + self.assertEqual('default', workbook_list[0].project_name) + self.assertEqual('5de011f8-5aa9-4d5b-b991-f462c8dd6bb7', workbook_list[0].owner_id) + self.assertEqual(set(['Safari', 'Sample']), workbook_list[0].tags) def test_populate_workbooks_missing_id(self): single_user = TSC.UserItem('test', 'Interactor') From 08179890eaaed18647cd481c7098660c0a93c672 Mon Sep 17 00:00:00 2001 From: Tyler Doyle Date: Sat, 23 Sep 2017 15:29:17 -0700 Subject: [PATCH 06/10] And make sure the test asset is there --- test/assets/datasource_populate_connections.xml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 test/assets/datasource_populate_connections.xml diff --git a/test/assets/datasource_populate_connections.xml b/test/assets/datasource_populate_connections.xml new file mode 100644 index 000000000..442a78323 --- /dev/null +++ b/test/assets/datasource_populate_connections.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file From 6b603cf07f574135bee0e3df074e7eb31137a197 Mon Sep 17 00:00:00 2001 From: Tyler Doyle Date: Sat, 23 Sep 2017 15:30:46 -0700 Subject: [PATCH 07/10] pep8 fix --- test/test_datasource.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_datasource.py b/test/test_datasource.py index 681f5fd8b..af3143203 100644 --- a/test/test_datasource.py +++ b/test/test_datasource.py @@ -15,7 +15,6 @@ UPDATE_XML = os.path.join(TEST_ASSET_DIR, 'datasource_update.xml') - class DatasourceTests(unittest.TestCase): def setUp(self): self.server = TSC.Server('http://test') From 05b70ff8bdf27f09aeece6be1064e5a432818059 Mon Sep 17 00:00:00 2001 From: Tyler Doyle Date: Sat, 23 Sep 2017 15:32:31 -0700 Subject: [PATCH 08/10] remove a random debug print --- tableauserverclient/models/schedule_item.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tableauserverclient/models/schedule_item.py b/tableauserverclient/models/schedule_item.py index 5531ef090..3e97ccc15 100644 --- a/tableauserverclient/models/schedule_item.py +++ b/tableauserverclient/models/schedule_item.py @@ -103,7 +103,6 @@ def updated_at(self): def _parse_common_tags(self, schedule_xml, ns): if not isinstance(schedule_xml, ET.Element): - print(ns) schedule_xml = ET.fromstring(schedule_xml).find('.//t:schedule', namespaces=ns) if schedule_xml is not None: (_, name, _, _, updated_at, _, next_run_at, end_schedule_at, execution_order, From 649464d8cd5a9742f4b31486f7c21f4608848751 Mon Sep 17 00:00:00 2001 From: Tyler Doyle Date: Sat, 23 Sep 2017 15:47:03 -0700 Subject: [PATCH 09/10] add fetcher for view images and preview images --- tableauserverclient/models/view_item.py | 10 ++++++-- .../server/endpoint/views_endpoint.py | 24 +++++++++++++++---- test/test_view.py | 6 ++--- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/tableauserverclient/models/view_item.py b/tableauserverclient/models/view_item.py index ed42e21ff..80de88352 100644 --- a/tableauserverclient/models/view_item.py +++ b/tableauserverclient/models/view_item.py @@ -15,6 +15,12 @@ def __init__(self): self._workbook_id = None self.tags = set() + def _set_preview_image(self, preview_image): + self._preview_image = preview_image + + def _set_image(self, image): + self._image = image + @property def content_url(self): return self._content_url @@ -25,7 +31,7 @@ def id(self): @property def image(self): - return self._image + return self._image() @property def name(self): @@ -40,7 +46,7 @@ def preview_image(self): if self._preview_image is None: error = "View item must be populated with its preview image first." raise UnpopulatedPropertyError(error) - return self._preview_image + return self._preview_image() @property def total_views(self): diff --git a/tableauserverclient/server/endpoint/views_endpoint.py b/tableauserverclient/server/endpoint/views_endpoint.py index 4d9909a6c..43349aec0 100644 --- a/tableauserverclient/server/endpoint/views_endpoint.py +++ b/tableauserverclient/server/endpoint/views_endpoint.py @@ -35,21 +35,37 @@ def populate_preview_image(self, view_item): if not view_item.id or not view_item.workbook_id: error = "View item missing ID or workbook ID." raise MissingRequiredFieldError(error) + + def image_fetcher(): + return self._get_preview_for_view(view_item) + + view_item._set_preview_image(image_fetcher) + logger.info('Populated preview image for view (ID: {0})'.format(view_item.id)) + + def _get_preview_for_view(self, view_item): url = "{0}/workbooks/{1}/views/{2}/previewImage".format(self.siteurl, view_item.workbook_id, view_item.id) server_response = self.get_request(url) - view_item._preview_image = server_response.content - logger.info('Populated preview image for view (ID: {0})'.format(view_item.id)) + image = server_response.content + return image def populate_image(self, view_item, req_options=None): if not view_item.id: error = "View item missing ID." raise MissingRequiredFieldError(error) + + def image_fetcher(): + return self._get_view_image(view_item, req_options) + + view_item._set_image(image_fetcher) + logger.info("Populated image for view (ID: {0})".format(view_item.id)) + + def _get_view_image(self, view_item, req_options): url = "{0}/{1}/image".format(self.baseurl, view_item.id) server_response = self.get_request(url, req_options) - view_item._image = server_response.content - logger.info("Populated image for view (ID: {0})".format(view_item.id)) + image = server_response.content + return image # Update view. Currently only tags can be updated def update(self, view_item): diff --git a/test/test_view.py b/test/test_view.py index b1340ff60..54972745a 100644 --- a/test/test_view.py +++ b/test/test_view.py @@ -56,7 +56,7 @@ def test_populate_preview_image(self): single_view._id = 'd79634e1-6063-4ec9-95ff-50acbf609ff5' single_view._workbook_id = '3cc6cd06-89ce-4fdc-b935-5294135d6d42' self.server.views.populate_preview_image(single_view) - self.assertEqual(response, single_view.preview_image) + self.assertEqual(response, single_view.preview_image) def test_populate_preview_image_missing_id(self): single_view = TSC.ViewItem() @@ -75,7 +75,7 @@ def test_populate_image(self): single_view = TSC.ViewItem() single_view._id = 'd79634e1-6063-4ec9-95ff-50acbf609ff5' self.server.views.populate_image(single_view) - self.assertEqual(response, single_view.image) + self.assertEqual(response, single_view.image) def test_populate_image_high_resolution(self): with open(POPULATE_PREVIEW_IMAGE, 'rb') as f: @@ -86,7 +86,7 @@ def test_populate_image_high_resolution(self): single_view._id = 'd79634e1-6063-4ec9-95ff-50acbf609ff5' req_option = TSC.ImageRequestOptions(imageresolution=TSC.ImageRequestOptions.Resolution.High) self.server.views.populate_image(single_view, req_option) - self.assertEqual(response, single_view.image) + self.assertEqual(response, single_view.image) def test_populate_image_missing_id(self): single_view = TSC.ViewItem() From 989056abc13d4db0501b09efcbab04acb54613b1 Mon Sep 17 00:00:00 2001 From: Tyler Doyle Date: Mon, 25 Sep 2017 11:56:00 -0700 Subject: [PATCH 10/10] Fix import issues, and cleanup a deprecation in tests --- tableauserverclient/server/endpoint/groups_endpoint.py | 3 +-- tableauserverclient/server/endpoint/users_endpoint.py | 3 +-- test/test_server_info.py | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/tableauserverclient/server/endpoint/groups_endpoint.py b/tableauserverclient/server/endpoint/groups_endpoint.py index 0e16eaf3a..bee595b25 100644 --- a/tableauserverclient/server/endpoint/groups_endpoint.py +++ b/tableauserverclient/server/endpoint/groups_endpoint.py @@ -2,6 +2,7 @@ from .exceptions import MissingRequiredFieldError from ...models.exceptions import UnpopulatedPropertyError from .. import RequestFactory, GroupItem, UserItem, PaginationItem +from ..pager import Pager import logging logger = logging.getLogger('tableau.endpoint.groups') @@ -25,8 +26,6 @@ def get(self, req_options=None): # Gets all users in a given group @api(version="2.0") def populate_users(self, group_item, req_options=None): - from .. import Pager - if not group_item.id: error = "Group item missing ID. Group must be retrieved from server first." raise MissingRequiredFieldError(error) diff --git a/tableauserverclient/server/endpoint/users_endpoint.py b/tableauserverclient/server/endpoint/users_endpoint.py index 54ab07212..64b296543 100644 --- a/tableauserverclient/server/endpoint/users_endpoint.py +++ b/tableauserverclient/server/endpoint/users_endpoint.py @@ -1,6 +1,7 @@ from .endpoint import Endpoint, api from .exceptions import MissingRequiredFieldError from .. import RequestFactory, UserItem, WorkbookItem, PaginationItem +from ..pager import Pager import logging import copy @@ -70,8 +71,6 @@ def add(self, user_item): # Get workbooks for user @api(version="2.0") def populate_workbooks(self, user_item, req_options=None): - from .. import Pager - if not user_item.id: error = "User item missing ID." raise MissingRequiredFieldError(error) diff --git a/test/test_server_info.py b/test/test_server_info.py index 2eb763a80..3dadff7c1 100644 --- a/test/test_server_info.py +++ b/test/test_server_info.py @@ -39,7 +39,7 @@ def test_server_info_use_highest_version_downgrades(self): # 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.server.use_server_version() self.assertEqual(self.server.version, '2.2') def test_server_info_use_highest_version_upgrades(self): @@ -49,7 +49,7 @@ def test_server_info_use_highest_version_upgrades(self): 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() + self.server.use_server_version() # Did we upgrade to 2.4? self.assertEqual(self.server.version, '2.4')