Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/_includes/analytics.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-BVCN');</script>
<!-- End Google Tag Manager -->
2 changes: 2 additions & 0 deletions docs/_includes/head.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@

<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>

{% if jekyll.environment == "production" %}{% include analytics.html %}{% endif %}
33 changes: 33 additions & 0 deletions tableauserverclient/models/access_permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class AccessPermissions(object):
class Mode(object):
Allow = 'Allow'
Deny = 'Deny'

class _Base_Permssions(object):
Read = 'Read'
Write = 'Write'

class Workbook(_Base_Permssions):
AddComment = 'AddComment'
ChangeHierarchy = 'ChangeHierarchy'
ChangePermissions = 'ChangePermissions'
Delete = 'Delete'
ExportData = 'ExportData'
ExportImage = 'ExportImage'
ExportXml = 'ExportXml'
Filter = 'Filter'
ShareView = 'ShareView'
ViewComments = 'ViewComments'
ViewUnderlyingData = 'ViewUnderlyingData'
WebAuthoring = 'WebAuthoring'


class Datasource(_Base_Permssions):
ChangePermissions = 'ChangePermissions'
Connect = 'Connect'
Delete = 'Delete'
ExportXml = 'ExportXml'


class Project(_Base_Permssions):
ProjectLeader = 'ProjectLeader'
75 changes: 75 additions & 0 deletions tableauserverclient/models/favorite_item.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import xml.etree.ElementTree as ET
import re
from .. import NAMESPACE


class FavoriteItem(object):
# Note that name must be the same as string value
class Types:
Workbook = 'Workbook'
View = 'View'
Datasource = 'Datasource'

def __init__(self, label, id, type):
self._label = None
self._id = None
self._type = None
self.label = label
self.id = id
self.type = type

@property
def label(self):
return self._label

@label.setter
def label(self, value):
if not value:
error = 'Label must be defined.'
raise ValueError(error)
else:
self._label = value

@property
def id(self):
return self._id

@id.setter
def id(self, value):
if not value:
error = 'ID must be defined.'
raise ValueError(error)
else:
self._id = value

@property
def type(self):
return self._type

@type.setter
def type(self, value):
if not value:
error = 'Type must be defined.'
raise ValueError(error)
elif not hasattr(FavoriteItem.Types, value):
error = 'Invalid type for favorite.'
raise ValueError(error)
else:
self._type = value

@classmethod
def from_response(cls, resp):
all_favorite_items = set()
parsed_response = ET.fromstring(resp)
all_favorite_xml = parsed_response.findall('.//t:favorite', namespaces=NAMESPACE)
for favorite_xml in all_favorite_xml:
child_elem = favorite_xml.getchildren()[0]
leading_namespace = '{{{0}}}'.format(NAMESPACE['t'])
favorite_type = child_elem.tag.replace(leading_namespace, '')
subject_elem = favorite_xml.find('.//t:' + favorite_type, namespaces=NAMESPACE)
id = subject_elem.get('id', None)
label = favorite_xml.get('label', None)
type = favorite_type.title()
favorite_item = cls(label, id, type)
all_favorite_items.add(favorite_item)
return all_favorite_items
44 changes: 44 additions & 0 deletions tableauserverclient/models/permission_item.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import xml.etree.ElementTree as ET
from .. import NAMESPACE


class PermissionItem(object):
Group = 'group'
User = 'user'

def __init__(self):
self._grantee_type = None
self._grantee_id = None
self.permissions = {}

@property
def grantee_type(self):
return self._grantee_type

@property
def grantee_id(self):
return self._grantee_id

@classmethod
def from_response(cls, resp):
all_permission_items = list()
parsed_response = ET.fromstring(resp)
all_grantee_xml = parsed_response.findall('.//t:granteeCapabilities', namespaces=NAMESPACE)
for grantee_xml in all_grantee_xml:
permission_item = cls()
all_capability_xml = grantee_xml.findall('.//t:capability', namespaces=NAMESPACE)
for capability_xml in all_capability_xml:
name = capability_xml.get('name', None)
mode = capability_xml.get('mode', None)
permission_item.permissions[name] = mode

group_elem = grantee_xml.find('.//t:group', namespaces=NAMESPACE)
user_elem = grantee_xml.find('.//t:user', namespaces=NAMESPACE)
if group_elem is not None:
permission_item._grantee_id = group_elem.get('id', None)
permission_item._grantee_type = permission_item.Group
elif user_elem is not None:
permission_item._grantee_id = user_elem.get('id', None)
permission_item._grantee_type = permission_item.User
all_permission_items.append(permission_item)
return all_permission_items
35 changes: 35 additions & 0 deletions tableauserverclient/server/endpoint/favorites_endpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from endpoint import Endpoint
from exceptions import MissingRequiredFieldError
from .. import RequestFactory, FavoriteItem
import logging

logger = logging.getLogger('tableau.endpoint.favorites')


class Favorites(Endpoint):
def __init__(self, parent_srv):
super(Endpoint, self).__init__()
self.baseurl = "{0}/sites/{1}/favorites"
self.parent_srv = parent_srv

def _construct_url(self):
return self.baseurl.format(self.parent_srv.baseurl, self.parent_srv.site_id)

def add(self, favorite_item, user_item):
url = "{0}/{1}".format(self._construct_url(), user_item.id)
add_req = RequestFactory().favorite.add_req(favorite_item)
server_response = self.put_request(url, add_req)
logger.info('Added {0} (ID: {1}) to {2}\'s favorites'.format(favorite_item.type,
favorite_item.id, user_item.name))
return FavoriteItem.from_response(server_response.text)

def delete(self, favorite_item, user_item):
if not user_item.id:
error = "User item missing ID."
raise MissingRequiredFieldError(error)

url = "{0}/{1}/{2}s/{3}".format(self._construct_url(), user_item.id,
favorite_item.type.lower(), favorite_item.id)
self.delete_request(url)
logger.info('Deleted {0} (ID: {1}) from {2}\'s favorites'.format(favorite_item.type,
favorite_item.id, user_item.name))
10 changes: 10 additions & 0 deletions tableauserverclient/server/endpoint/sites_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ def get_by_id(self, site_id):
server_response = self.get_request(url)
return SiteItem.from_response(server_response.content)[0]

# Gets 1 site by name
def get_by_name(self, site_name):
if not site_name:
error = "Site Name undefined."
raise ValueError(error)
logger.info('Querying single site (Name: {0})'.format(site_name))
url = "{0}/{1}?key=name".format(self.baseurl, site_name)
server_response = self.get_request(url)
return SiteItem.from_response(server_response.content)[0]

# Update site
def update(self, site_item):
if not site_item.id:
Expand Down
5 changes: 5 additions & 0 deletions test/assets/site_get_by_name.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<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">
<site id="dad65087-b08b-4603-af4e-2887b8aafc67" name="testsite" contentUrl="testsite"
adminMode="ContentOnly" disableSubscriptions="false" state="Active" revisionHistoryEnabled="false" subscribeOthersEnabled="true" />
</tsResponse>
19 changes: 19 additions & 0 deletions test/test_site.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

GET_XML = os.path.join(TEST_ASSET_DIR, 'site_get.xml')
GET_BY_ID_XML = os.path.join(TEST_ASSET_DIR, 'site_get_by_id.xml')
GET_BY_NAME_XML = os.path.join(TEST_ASSET_DIR, 'site_get_by_name.xml')
UPDATE_XML = os.path.join(TEST_ASSET_DIR, 'site_update.xml')
CREATE_XML = os.path.join(TEST_ASSET_DIR, 'site_create.xml')

Expand Down Expand Up @@ -64,6 +65,24 @@ def test_get_by_id(self):
def test_get_by_id_missing_id(self):
self.assertRaises(ValueError, self.server.sites.get_by_id, '')

def test_get_by_name(self):
with open(GET_BY_NAME_XML, 'rb') as f:
response_xml = f.read().decode('utf-8')
with requests_mock.mock() as m:
m.get(self.baseurl + '/testsite?key=name', text=response_xml)
single_site = self.server.sites.get_by_name('testsite')

self.assertEqual('dad65087-b08b-4603-af4e-2887b8aafc67', single_site.id)
self.assertEqual('Active', single_site.state)
self.assertEqual('testsite', single_site.name)
self.assertEqual('ContentOnly', single_site.admin_mode)
self.assertEqual(False, single_site.revision_history_enabled)
self.assertEqual(True, single_site.subscribe_others_enabled)
self.assertEqual(False, single_site.disable_subscriptions)

def test_get_by_name_missing_name(self):
self.assertRaises(ValueError, self.server.sites.get_by_name, '')

def test_update(self):
with open(UPDATE_XML, 'rb') as f:
response_xml = f.read().decode('utf-8')
Expand Down