8000 User favorites endpoint by jorwoods · Pull Request #638 · tableau/server-client-python · GitHub
[go: up one dir, main page]

Skip to content

User favorites endpoint #638

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Jun 26, 2020
Merged
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
1 change: 1 addition & 0 deletions tableauserverclient/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from .datasource_item import DatasourceItem
from .database_item import DatabaseItem
from .exceptions import UnpopulatedPropertyError
from .favorites_item import FavoriteItem
from .group_item import GroupItem
from .flow_item import FlowItem
from .interval_item import IntervalItem, DailyInterval, WeeklyInterval, MonthlyInterval, HourlyInterval
Expand Down
49 changes: 49 additions & 0 deletions tableauserverclient/models/favorites_item.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import xml.etree.ElementTree as ET
import logging
from .workbook_item import WorkbookItem
from .view_item import ViewItem
from .project_item import ProjectItem
from .datasource_item import DatasourceItem

logger = logging.getLogger('tableau.models.favorites_item')


class FavoriteItem:
class Type:
Workbook = 'workbook'
Datasource = 'datasource'
View = 'view'
Project = 'project'

@classmethod
def from_response(cls, xml, namespace):
favorites = {
'datasources': [],
'projects': [],
'views': [],
'workbooks': [],
}

parsed_response = ET.fromstring(xml)
for workbook in parsed_response.findall('.//t:favorite/t:workbook', namespace):
fav_workbook = WorkbookItem('')
fav_workbook._set_values(*fav_workbook._parse_element(workbook, namespace))
if fav_workbook:
favorites['workbooks'].append(fav_workbook)
for view in parsed_response.findall('.//t:favorite[t:view]', namespace):
fav_views = ViewItem.from_xml_element(view, namespace)
if fav_views:
for fav_view in fav_views:
favorites['views'].append(fav_view)
for datasource in parsed_response.findall('.//t:favorite/t:datasource', namespace):
fav_datasource = DatasourceItem('')
fav_datasource._set_values(*fav_datasource._parse_element(datasource, namespace))
if fav_datasource:
favorites['datasources'].append(fav_datasource)
for project in parsed_response.findall('.//t:favorite/t:project', namespace):
fav_project = ProjectItem('p')
fav_project._set_values(*fav_project._parse_element(project))
if fav_project:
favorites['projects'].append(fav_project)

return favorites
8 changes: 8 additions & 0 deletions tableauserverclient/models/user_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def __init__(self, name=None, site_role=None, auth_setting=None):
self._id = None
self._last_login = None
self._workbooks = None
self._favorites = None
self.email = None
self.fullname = None
self.name = name
Expand Down Expand Up @@ -99,6 +100,13 @@ def workbooks(self):
raise UnpopulatedPropertyError(error)
return self._workbooks()

@property
def favorites(self):
if self._favorites is None:
error = "User item must be populated with favorites first."
raise UnpopulatedPropertyError(error)
return self._favorites

def to_reference(self):
return ResourceReference(id_=self.id, tag_name=self.tag_name)

Expand Down
2 changes: 1 addition & 1 deletion tableauserverclient/server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
PermissionsRule, Permission, ColumnItem, FlowItem, WebhookItem
from .endpoint import Auth, Datasources, Endpoint, Groups, Projects, Schedules, \
Sites, Tables, Users, Views, Workbooks, Subscriptions, ServerResponseError, \
MissingRequiredFieldError, Flows
MissingRequiredFieldError, Flows, Favorites
from .server import Server
from .pager import Pager
from .exceptions import NotSignedInError
1 change: 1 addition & 0 deletions tableauserverclient/server/endpoint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from .datasources_endpoint import Datasources
from .databases_endpoint import Databases
from .endpoint import Endpoint
from .favorites_endpoint import Favorites
from .flows_endpoint import Flows
from .exceptions import ServerResponseError, MissingRequiredFieldError, ServerInfoEndpointNotFoundError
from .groups_endpoint import Groups
Expand Down
78 changes: 78 additions & 0 deletions tableauserverclient/server/endpoint/favorites_endpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from .endpoint import Endpoint, api
from .exceptions import MissingRequiredFieldError
from .. import RequestFactory
from ...models import FavoriteItem
from ..pager import Pager
import xml.etree.ElementTree as ET
import logging
import copy

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


class Favorites(Endpoint):
@property
def baseurl(self):
return "{0}/sites/{1}/favorites".format(self.parent_srv.baseurl, self.parent_srv.site_id)

# Gets all favorites
@api(version="2.5")
def get(self, user_item, req_options=None):
logger.info('Querying all favorites for user {0}'.format(user_item.name))
url = '{0}/{1}'.format(self.baseurl, user_item.id)
server_response = self.get_request(url, req_options)

user_item._favorites = FavoriteItem.from_response(server_response.content,
self.parent_srv.namespace)

@api(version="2.0")
def add_favorite_workbook(self, user_item, workbook_item):
url = '{0}/{1}'.format(self.baseurl, user_item.id)
add_req = RequestFactory.Favorite.add_workbook_req(workbook_item.id, workbook_item.name)
server_response = self.put_request(url, add_req)
logger.info('Favorited {0} for user (ID: {1})'.format(workbook_item.name, user_item.id))

@api(version="2.0")
def add_favorite_view(self, user_item, view_item):
url = '{0}/{1}'.format(self.baseurl, user_item.id)
add_req = RequestFactory.Favorite.add_view_req(view_item.id, view_item.name)
server_response = self.put_request(url, add_req)
logger.info('Favorited {0} for user (ID: {1})'.format(view_item.name, user_item.id))

@api(version="2.3")
def add_favorite_datasource(self, user_item, datasource_item):
url = '{0}/{1}'.format(self.baseurl, user_item.id)
add_req = RequestFactory.Favorite.add_datasource_req(datasource_item.id, datasource_item.name)
server_response = self.put_request(url, add_req)
logger.info('Favorited {0} for user (ID: {1})'.format(datasource_item.name, user_item.id))

@api(version="3.1")
def add_favorite_project(self, user_item, project_item):
url = '{0}/{1}'.format(self.baseurl, user_item.id)
add_req = RequestFactory.Favorite.add_project_req(project_item.id, project_item.name)
server_response = self.put_request(url, add_req)
logger.info('Favorited {0} for user (ID: {1})'.format(project_item.name, user_item.id))

@api(version="2.0")
def delete_favorite_workbook(self, user_item, workbook_item):
url = '{0}/{1}/workbooks/{2}'.format(self.baseurl, user_item.id, workbook_item.id)
logger.info('Removing favorite {0} for user (ID: {1})'.format(workbook_item.id, user_item.id))
self.delete_request(url)

@api(version="2.0")
def delete_favorite_view(self, user_item, view_item):
url = '{0}/{1}/views/{2}'.format(self.baseurl, user_item.id, view_item.id)
logger.info('Removing favorite {0} for user (ID: {1})'.format(view_item.id, user_item.id))
self.delete_request(url)

@api(version="2.3")
def delete_favorite_datasource(self, user_item, datasource_item):
url = '{0}/{1}/datasources/{2}'.format(self.baseurl, user_item.id, datasource_item.id)
logger.info('Removing favorite {0} for user (ID: {1})'.format(datasource_item.id, user_item.id))
self.delete_request(url)

@api(version="3.1")
def delete_favorite_project(self, user_item, project_item):
url = '{0}/{1}/projects/{2}'.format(self.baseurl, user_item.id, project_item.id)
logger.info('Removing favorite {0} for user (ID: {1})'.format(project_item.id, user_item.id))
self.delete_request(url)
31 changes: 30 additions & 1 deletion tableauserverclient/server/request_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from requests.packages.urllib3.fields import RequestField
from requests.packages.urllib3.filepost import encode_multipart_formdata

from ..models import TaskItem
from ..models import TaskItem, UserItem, GroupItem, PermissionsRule, FavoriteItem


def _add_multipart(parts):
Expand Down Expand Up @@ -149,6 +149,34 @@ def publish_req_chunked(self, datasource_item, connection_credentials=None, conn
return _add_multipart(parts)


class FavoriteRequest(object):
def _add_to_req(self, id_, target_type, label):
'''
<favorite label="...">
<target_type id="..." />
</favorite>
'''
xml_request = ET.Element('tsRequest')
favorite_element = ET.SubElement(xml_request, 'favorite')
target = ET.SubElement(favorite_element, target_type)
favorite_element.attrib['label'] = label
target.attrib['id'] = id_

return ET.tostring(xml_request)

def add_datasource_req(self, id_, name):
return self._add_to_req(id_, FavoriteItem.Type.Datasource, name)

def add_project_req(self, id_, name):
return self._add_to_req(id_, FavoriteItem.Type.Project, name)

def add_view_req(self, id_, name):
return self._add_to_req(id_, FavoriteItem.Type.View, name)

def add_workbook_req(self, id_, name):
return self._add_to_req(id_, FavoriteItem.Type.Workbook, name)


class FileuploadRequest(object):
def chunk_req(self, chunk):
parts = {'request_payload': ('', '', 'text/xml'),
Expand Down Expand Up @@ -605,6 +633,7 @@ class RequestFactory(object):
Datasource = DatasourceRequest()
Database = DatabaseRequest()
Empty = EmptyRequest()
Favorite = FavoriteRequest()
Fileupload = FileuploadRequest()
Flow = FlowRequest()
Group = GroupRequest()
Expand Down
3 changes: 2 additions & 1 deletion tableauserverclient/server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from ..namespace import Namespace
from .endpoint import Sites, Views, Users, Groups, Workbooks, Datasources, Projects, Auth, \
Schedules, ServerInfo, Tasks, Subscriptions, Jobs, Metadata,\
Databases, Tables, Flows, Webhooks, DataAccelerationReport
Databases, Tables, Flows, Webhooks, DataAccelerationReport, Favorites
from .endpoint.exceptions import EndpointUnavailableError, ServerInfoEndpointNotFoundError

import requests
Expand Down Expand Up @@ -46,6 +46,7 @@ def __init__(self, server_address, use_server_version=False):
self.jobs = Jobs(self)
self.workbooks = Workbooks(self)
self.datasources = Datasources(self)
self.favorites = Favorites(self)
self.flows = Flows(self)
self.projects = Projects(self)
self.schedules = Schedules(self)
Expand Down
17 changes: 17 additions & 0 deletions test/assets/favorites_add_datasource.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?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">
<favorites>
<favorite>
<datasource id="e76a1461-3b1d-4588-bf1b-17551a879ad9"
name="SampleDS"
contentUrl="SampleDS"
type="dataengine"
createdAt="2016-08-11T21:22:40Z"
updatedAt="2016-08-11T21:34:17Z">
<project id="ee8c6e70-43b6-11e6-af4f-f7b0d8e20760" name="default" />
<owner id="5de011f8-5aa9-4d5b-b991-f462c8dd6bb7" />
<tags />
</datasource>
</favorite>
</favorites>
</tsResponse>
11 changes: 11 additions & 0 deletions test/assets/favorites_add_project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?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">
<favorites>
<favorite>
<project id="1d0304cd-3796-429f-b815-7258370b9b74"
name="Tableau"
description=""
contentPermissions="ManagedByOwner" />
</favorite>
</favorites>
</tsResponse>
14 changes: 14 additions & 0 deletions test/assets/favorites_add_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<tsResponse>
<favorites>
<favorite label="favorite-labe1">
<view id="d79634e1-6063-4ec9-95ff-50acbf609ff5"
name="ENDANGERED SAFARI"
contentUrl="SafariSample/sheets/ENDANGEREDSAFARI"
createdAt="2016-08-03T20:34:04Z"
updatedAt="2016-08-04T17:56:41Z"
viewUrlName="view-url-name">
<workbook id="3cc6cd06-89ce-4fdc-b935-5294135d6d42"/>
<project id="5241e88d-d384-4fd7-9c2f-648b5247efc5"/>
<tags />
</view>
</tsResponse>
20 changes: 20 additions & 0 deletions test/assets/favorites_add_workbook.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?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">
<favorites>
<favorite label="Superstore">
<workbook id="6d13b0ca-043d-4d42-8c9d-3f3313ea3a00"
name="Superstore"
description="description for Superstore"
contentUrl="Superstore"
showTabs="false"
size="1"
createdAt="2016-08-03T20:34:04Z"
updatedAt="2016-08-04T17:56:41Z">
<project id="ee8c6e70-43b6-11e6-af4f-f7b0d8e20760" name="default" />
<owner id="5de011f8-5aa9-4d5b-b991-f462c8dd6bb7" />
<tags />
</workbook>
</favorite>
</favorites>
</tsResponse>
47 changes: 47 additions & 0 deletions test/assets/favorites_get.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?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">
<favorites>
<favorite label="favorite-label">
<view id="d79634e1-6063-4ec9-95ff-50acbf609ff5"
name="ENDANGERED SAFARI"
contentUrl="SafariSample/sheets/ENDANGEREDSAFARI">
<workbook id="3cc6cd06-89ce-4fdc-b935-5294135d6d42" />
<owner id="5de011f8-5aa9-4d5b-b991-f462c8dd6bb7" />
<project id="5241e88d-d384-4fd7-9c2f-648b5247efc5" />
<tags />
</view>
</favorite>
<favorite>
<workbook id="6d13b0ca-043d-4d42-8c9d-3f3313ea3a00"
name="Superstore"
description="description for Superstore"
contentUrl="Superstore"
showTabs="false"
size="1"
createdAt="2016-08-03T20:34:04Z"
updatedAt="2016-08-04T17:56:41Z">
<project id="ee8c6e70-43b6-11e6-af4f-f7b0d8e20760" name="default" />
<owner id="5de011f8-5aa9-4d5b-b991-f462c8dd6bb7" />
<tags />
</workbook>
</favorite>
<favorite>
<project id="1d0304cd-3796-429f-b815-7258370b9b74"
name="Tableau"
description=""
contentPermissions="ManagedByOwner" />
</favorite>
<favorite>
<datasource id="e76a1461-3b1d-4588-bf1b-17551a879ad9"
name="SampleDS"
contentUrl="SampleDS"
type="dataengine"
createdAt="2016-08-11T21:22:40Z"
updatedAt="2016-08-11T21:34:17Z">
<project id="ee8c6e70-43b6-11e6-af4f-f7b0d8e20760" name="default" />
<owner id="5de011f8-5aa9-4d5b-b991-f462c8dd6bb7" />
<tags />
</datasource>
</favorite>
</favorites>
</tsResponse>
Loading
0