diff --git a/tableauserverclient/models/favorites_item.py b/tableauserverclient/models/favorites_item.py index 3d6feff5d..cb3ebdf98 100644 --- a/tableauserverclient/models/favorites_item.py +++ b/tableauserverclient/models/favorites_item.py @@ -4,21 +4,39 @@ from .view_item import ViewItem from .project_item import ProjectItem from .datasource_item import DatasourceItem +from .flow_item import FlowItem logger = logging.getLogger("tableau.models.favorites_item") +from typing import Dict, List, Union + +FavoriteType = Dict[ + str, + List[ + Union[ + DatasourceItem, + ProjectItem, + FlowItem, + ViewItem, + WorkbookItem, + ] + ], +] + class FavoriteItem: class Type: - Workbook = "workbook" - Datasource = "datasource" - View = "view" - Project = "project" + Workbook: str = "workbook" + Datasource: str = "datasource" + View: str = "view" + Project: str = "project" + Flow: str = "flow" @classmethod - def from_response(cls, xml, namespace): - favorites = { + def from_response(cls, xml: str, namespace: Dict) -> FavoriteType: + favorites: FavoriteType = { "datasources": [], + "flows": [], "projects": [], "views": [], "workbooks": [], @@ -45,5 +63,10 @@ def from_response(cls, xml, namespace): fav_project._set_values(*fav_project._parse_element(project)) if fav_project: favorites["projects"].append(fav_project) + for flow in parsed_response.findall(".//t:favorite/t:flow", namespace): + fav_flow = FlowItem("flows") + fav_flow._set_values(*fav_flow._parse_element(flow, namespace)) + if fav_flow: + favorites["flows"].append(fav_flow) return favorites diff --git a/tableauserverclient/server/endpoint/favorites_endpoint.py b/tableauserverclient/server/endpoint/favorites_endpoint.py index 459d852e6..70c1ebfef 100644 --- a/tableauserverclient/server/endpoint/favorites_endpoint.py +++ b/tableauserverclient/server/endpoint/favorites_endpoint.py @@ -1,23 +1,25 @@ 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") +from typing import Optional, TYPE_CHECKING + +if TYPE_CHECKING: + from ...models import DatasourceItem, FlowItem, ProjectItem, UserItem, ViewItem, WorkbookItem + from ..request_options import RequestOptions + class Favorites(Endpoint): @property - def baseurl(self): + def baseurl(self) -> str: 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): + def get(self, user_item: "UserItem", req_options: Optional["RequestOptions"] = None) -> 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) @@ -25,53 +27,66 @@ def get(self, user_item, req_options=None): 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): + def add_favorite_workbook(self, user_item: "UserItem", workbook_item: "WorkbookItem") -> None: 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): + def add_favorite_view(self, user_item: "UserItem", view_item: "ViewItem") -> None: 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): + def add_favorite_datasource(self, user_item: "UserItem", datasource_item: "DatasourceItem") -> None: 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): + def add_favorite_project(self, user_item: "UserItem", project_item: "ProjectItem") -> None: 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="3.3") + def add_favorite_flow(self, user_item: "UserItem", flow_item: "FlowItem") -> None: + url = "{0}/{1}".format(self.baseurl, user_item.id) + add_req = RequestFactory.Favorite.add_flow_req(flow_item.id, flow_item.name) + server_response = self.put_request(url, add_req) + logger.info("Favorited {0} for user (ID: {1})".format(flow_item.name, user_item.id)) + @api(version="2.0") - def delete_favorite_workbook(self, user_item, workbook_item): + def delete_favorite_workbook(self, user_item: "UserItem", workbook_item: "WorkbookItem") -> None: 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): + def delete_favorite_view(self, user_item: "UserItem", view_item: "ViewItem") -> None: 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): + def delete_favorite_datasource(self, user_item: "UserItem", datasource_item: "DatasourceItem") -> None: 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): + def delete_favorite_project(self, user_item: "UserItem", project_item: "ProjectItem") -> None: 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) + + @api(version="3.3") + def delete_favorite_flow(self, user_item: "UserItem", flow_item: "FlowItem") -> None: + url = "{0}/{1}/projects/{2}".format(self.baseurl, user_item.id, flow_item.id) + logger.info("Removing favorite {0} for user (ID: {1})".format(flow_item.id, user_item.id)) + self.delete_request(url) diff --git a/tableauserverclient/server/request_factory.py b/tableauserverclient/server/request_factory.py index 16a11a018..b9ea36980 100644 --- a/tableauserverclient/server/request_factory.py +++ b/tableauserverclient/server/request_factory.py @@ -5,7 +5,7 @@ from ..models import TaskItem, UserItem, GroupItem, PermissionsRule, FavoriteItem -from typing import TYPE_CHECKING +from typing import Optional, TYPE_CHECKING if TYPE_CHECKING: from ..models import DataAlertItem @@ -242,7 +242,7 @@ def update_req(self, database_item): class FavoriteRequest(object): - def _add_to_req(self, id_, target_type, label): + def _add_to_req(self, id_: str, target_type: str, label: str) -> bytes: """ @@ -256,16 +256,39 @@ def _add_to_req(self, id_, target_type, label): return ET.tostring(xml_request) - def add_datasource_req(self, id_, name): + def add_datasource_req(self, id_: Optional[str], name: Optional[str]) -> bytes: + if id_ is None: + raise ValueError("id must exist to add to favorites") + if name is None: + raise ValueError("Name must exist to add to favorites.") return self._add_to_req(id_, FavoriteItem.Type.Datasource, name) - def add_project_req(self, id_, name): + def add_flow_req(self, id_: Optional[str], name: Optional[str]) -> bytes: + if id_ is None: + raise ValueError("id must exist to add to favorites") + if name is None: + raise ValueError("Name must exist to add to favorites.") + return self._add_to_req(id_, FavoriteItem.Type.Flow, name) + + def add_project_req(self, id_: Optional[str], name: Optional[str]) -> bytes: + if id_ is None: + raise ValueError("id must exist to add to favorites") + if name is None: + raise ValueError("Name must exist to add to favorites.") return self._add_to_req(id_, FavoriteItem.Type.Project, name) - def add_view_req(self, id_, name): + def add_view_req(self, id_: Optional[str], name: Optional[str]) -> bytes: + if id_ is None: + raise ValueError("id must exist to add to favorites") + if name is None: + raise ValueError("Name must exist to add to favorites.") return self._add_to_req(id_, FavoriteItem.Type.View, name) - def add_workbook_req(self, id_, name): + def add_workbook_req(self, id_: Optional[str], name: Optional[str]) -> bytes: + if id_ is None: + raise ValueError("id must exist to add to favorites") + if name is None: + raise ValueError("Name must exist to add to favorites.") return self._add_to_req(id_, FavoriteItem.Type.Workbook, name) diff --git a/test/test_favorites.py b/test/test_favorites.py index d92fa7392..cd019fe75 100644 --- a/test/test_favorites.py +++ b/test/test_favorites.py @@ -28,7 +28,7 @@ def setUp(self): self.user = TSC.UserItem("alice", TSC.UserItem.Roles.Viewer) self.user._id = "dd2239f6-ddf1-4107-981a-4cf94e415794" - def test_get(self): + def test_get(self) -> None: response_xml = read_xml_asset(GET_FAVORITES_XML) with requests_mock.mock() as m: m.get("{0}/{1}".format(self.baseurl, self.user.id), text=response_xml) @@ -49,7 +49,7 @@ def test_get(self): self.assertEqual(datasource.id, "e76a1461-3b1d-4588-bf1b-17551a879ad9") self.assertEqual(project.id, "1d0304cd-3796-429f-b815-7258370b9b74") - def test_add_favorite_workbook(self): + def test_add_favorite_workbook(self) -> None: response_xml = read_xml_asset(ADD_FAVORITE_WORKBOOK_XML) workbook = TSC.WorkbookItem("") workbook._id = "6d13b0ca-043d-4d42-8c9d-3f3313ea3a00" @@ -58,7 +58,7 @@ def test_add_favorite_workbook(self): m.put("{0}/{1}".format(self.baseurl, self.user.id), text=response_xml) self.server.favorites.add_favorite_workbook(self.user, workbook) - def test_add_favorite_view(self): + def test_add_favorite_view(self) -> None: response_xml = read_xml_asset(ADD_FAVORITE_VIEW_XML) view = TSC.ViewItem() view._id = "d79634e1-6063-4ec9-95ff-50acbf609ff5" @@ -67,7 +67,7 @@ def test_add_favorite_view(self): m.put("{0}/{1}".format(self.baseurl, self.user.id), text=response_xml) self.server.favorites.add_favorite_view(self.user, view) - def test_add_favorite_datasource(self): + def test_add_favorite_datasource(self) -> None: response_xml = read_xml_asset(ADD_FAVORITE_DATASOURCE_XML) datasource = TSC.DatasourceItem("ee8c6e70-43b6-11e6-af4f-f7b0d8e20760") datasource._id = "e76a1461-3b1d-4588-bf1b-17551a879ad9" @@ -76,7 +76,7 @@ def test_add_favorite_datasource(self): m.put("{0}/{1}".format(self.baseurl, self.user.id), text=response_xml) self.server.favorites.add_favorite_datasource(self.user, datasource) - def test_add_favorite_project(self): + def test_add_favorite_project(self) -> None: self.server.version = "3.1" baseurl = self.server.favorites.baseurl response_xml = read_xml_asset(ADD_FAVORITE_PROJECT_XML) @@ -86,7 +86,7 @@ def test_add_favorite_project(self): m.put("{0}/{1}".format(baseurl, self.user.id), text=response_xml) self.server.favorites.add_favorite_project(self.user, project) - def test_delete_favorite_workbook(self): + def test_delete_favorite_workbook(self) -> None: workbook = TSC.WorkbookItem("") workbook._id = "6d13b0ca-043d-4d42-8c9d-3f3313ea3a00" workbook.name = "Superstore" @@ -94,7 +94,7 @@ def test_delete_favorite_workbook(self): m.delete("{0}/{1}/workbooks/{2}".format(self.baseurl, self.user.id, workbook.id)) self.server.favorites.delete_favorite_workbook(self.user, workbook) - def test_delete_favorite_view(self): + def test_delete_favorite_view(self) -> None: view = TSC.ViewItem() view._id = "d79634e1-6063-4ec9-95ff-50acbf609ff5" view._name = "ENDANGERED SAFARI" @@ -102,7 +102,7 @@ def test_delete_favorite_view(self): m.delete("{0}/{1}/views/{2}".format(self.baseurl, self.user.id, view.id)) self.server.favorites.delete_favorite_view(self.user, view) - def test_delete_favorite_datasource(self): + def test_delete_favorite_datasource(self) -> None: datasource = TSC.DatasourceItem("ee8c6e70-43b6-11e6-af4f-f7b0d8e20760") datasource._id = "e76a1461-3b1d-4588-bf1b-17551a879ad9" datasource.name = "SampleDS" @@ -110,7 +110,7 @@ def test_delete_favorite_datasource(self): m.delete("{0}/{1}/datasources/{2}".format(self.baseurl, self.user.id, datasource.id)) self.server.favorites.delete_favorite_datasource(self.user, datasource) - def test_delete_favorite_project(self): + def test_delete_favorite_project(self) -> None: self.server.version = "3.1" baseurl = self.server.favorites.baseurl project = TSC.ProjectItem("Tableau")