diff --git a/tableauserverclient/models/permissions_item.py b/tableauserverclient/models/permissions_item.py index 6487b6ca5..40049e3c4 100644 --- a/tableauserverclient/models/permissions_item.py +++ b/tableauserverclient/models/permissions_item.py @@ -38,6 +38,7 @@ class Resource: Flow = 'flow' Table = 'table' Database = 'database' + View = 'view' class PermissionsRule(object): diff --git a/tableauserverclient/models/view_item.py b/tableauserverclient/models/view_item.py index 3dd9e065b..958cef7a2 100644 --- a/tableauserverclient/models/view_item.py +++ b/tableauserverclient/models/view_item.py @@ -21,6 +21,7 @@ def __init__(self): self._sheet_type = None self._updated_at = None self._workbook_id = None + self._permissions = None self.tags = set() def _set_preview_image(self, preview_image): @@ -106,6 +107,16 @@ def updated_at(self): def workbook_id(self): return self._workbook_id + @property + def permissions(self): + if self._permissions is None: + error = "View item must be populated with permissions first." + raise UnpopulatedPropertyError(error) + return self._permissions() + + def _set_permissions(self, permissions): + self._permissions = permissions + @classmethod def from_response(cls, resp, ns, workbook_id=''): return cls.from_xml_element(ET.fromstring(resp), ns, workbook_id) diff --git a/tableauserverclient/server/endpoint/views_endpoint.py b/tableauserverclient/server/endpoint/views_endpoint.py index 62cd3af50..85ae70f93 100644 --- a/tableauserverclient/server/endpoint/views_endpoint.py +++ b/tableauserverclient/server/endpoint/views_endpoint.py @@ -1,6 +1,7 @@ from .endpoint import Endpoint, api from .exceptions import MissingRequiredFieldError from .resource_tagger import _ResourceTagger +from .permissions_endpoint import _PermissionsEndpoint from .. import RequestFactory, ViewItem, PaginationItem from ...models.tag_item import TagItem import logging @@ -13,6 +14,7 @@ class Views(Endpoint): def __init__(self, parent_srv): super(Views, self).__init__(parent_srv) self._resource_tagger = _ResourceTagger(parent_srv) + self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl) # Used because populate_preview_image functionaliy requires workbook endpoint @property @@ -109,6 +111,18 @@ def _get_view_csv(self, view_item, req_options): csv = server_response.iter_content(1024) return csv + @api(version='3.2') + def populate_permissions(self, item): + self._permissions.populate(item) + + @api(version='3.2') + def update_permissions(self, resource, rules): + return self._permissions.update(resource, rules) + + @api(version='3.2') + def delete_permission(self, item, capability_item): + return self._permissions.delete(item, capability_item) + # Update view. Currently only tags can be updated def update(self, view_item): if not view_item.id: diff --git a/test/assets/view_populate_permissions.xml b/test/assets/view_populate_permissions.xml new file mode 100644 index 000000000..e73616f46 --- /dev/null +++ b/test/assets/view_populate_permissions.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/assets/view_update_permissions.xml b/test/assets/view_update_permissions.xml new file mode 100644 index 000000000..2e78a4a90 --- /dev/null +++ b/test/assets/view_update_permissions.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/test/test_view.py b/test/test_view.py index fcf7d986c..350be83fd 100644 --- a/test/test_view.py +++ b/test/test_view.py @@ -4,6 +4,7 @@ import tableauserverclient as TSC from tableauserverclient.datetime_helpers import format_datetime +from tableauserverclient import UserItem, GroupItem, PermissionsRule TEST_ASSET_DIR = os.path.join(os.path.dirname(__file__), 'assets') @@ -13,13 +14,15 @@ POPULATE_PREVIEW_IMAGE = os.path.join(TEST_ASSET_DIR, 'Sample View Image.png') POPULATE_PDF = os.path.join(TEST_ASSET_DIR, 'populate_pdf.pdf') POPULATE_CSV = os.path.join(TEST_ASSET_DIR, 'populate_csv.csv') +POPULATE_PERMISSIONS_XML = os.path.join(TEST_ASSET_DIR, 'view_populate_permissions.xml') +UPDATE_PERMISSIONS = os.path.join(TEST_ASSET_DIR, 'view_update_permissions.xml') UPDATE_XML = os.path.join(TEST_ASSET_DIR, 'workbook_update.xml') class ViewTests(unittest.TestCase): def setUp(self): self.server = TSC.Server('http://test') - self.server.version = '2.7' + self.server.version = '3.2' # Fake sign in self.server._site_id = 'dad65087-b08b-4603-af4e-2887b8aafc67' @@ -170,6 +173,59 @@ def test_populate_image_missing_id(self): single_view._id = None self.assertRaises(TSC.MissingRequiredFieldError, self.server.views.populate_image, single_view) + def test_populate_permissions(self): + with open(POPULATE_PERMISSIONS_XML, 'rb') as f: + response_xml = f.read().decode('utf-8') + with requests_mock.mock() as m: + m.get(self.baseurl + "/e490bec4-2652-4fda-8c4e-f087db6fa328/permissions", text=response_xml) + single_view = TSC.ViewItem() + single_view._id = "e490bec4-2652-4fda-8c4e-f087db6fa328" + + self.server.views.populate_permissions(single_view) + permissions = single_view.permissions + + self.assertEqual(permissions[0].grantee.tag_name, 'group') + self.assertEqual(permissions[0].grantee.id, 'c8f2773a-c83a-11e8-8c8f-33e6d787b506') + self.assertDictEqual(permissions[0].capabilities, { + TSC.Permission.Capability.ViewComments: TSC.Permission.Mode.Allow, + TSC.Permission.Capability.Read: TSC.Permission.Mode.Allow, + TSC.Permission.Capability.AddComment: TSC.Permission.Mode.Allow, + TSC.Permission.Capability.ExportData: TSC.Permission.Mode.Allow, + TSC.Permission.Capability.ExportImage: TSC.Permission.Mode.Allow, + + }) + + def test_add_permissions(self): + with open(UPDATE_PERMISSIONS, 'rb') as f: + response_xml = f.read().decode('utf-8') + + single_view = TSC.ViewItem() + single_view._id = '21778de4-b7b9-44bc-a599-1506a2639ace' + + bob = UserItem.as_reference("7c37ee24-c4b1-42b6-a154-eaeab7ee330a") + group_of_people = GroupItem.as_reference("5e5e1978-71fa-11e4-87dd-7382f5c437af") + + new_permissions = [ + PermissionsRule(bob, {'Write': 'Allow'}), + PermissionsRule(group_of_people, {'Read': 'Deny'}) + ] + + with requests_mock.mock() as m: + m.put(self.baseurl + "/21778de4-b7b9-44bc-a599-1506a2639ace/permissions", text=response_xml) + permissions = self.server.views.update_permissions(single_view, new_permissions) + + self.assertEqual(permissions[0].grantee.tag_name, 'group') + self.assertEqual(permissions[0].grantee.id, '5e5e1978-71fa-11e4-87dd-7382f5c437af') + self.assertDictEqual(permissions[0].capabilities, { + TSC.Permission.Capability.Read: TSC.Permission.Mode.Deny + }) + + self.assertEqual(permissions[1].grantee.tag_name, 'user') + self.assertEqual(permissions[1].grantee.id, '7c37ee24-c4b1-42b6-a154-eaeab7ee330a') + self.assertDictEqual(permissions[1].capabilities, { + TSC.Permission.Capability.Write: TSC.Permission.Mode.Allow + }) + def test_update_tags(self): with open(ADD_TAGS_XML, 'rb') as f: add_tags_xml = f.read().decode('utf-8')