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