10000 feat: add/remove tags endpoints for workbooks · tableau/server-client-python@dbbe005 · GitHub
[go: up one dir, main page]

Skip to content

Commit dbbe005

Browse files
committed
feat: add/remove tags endpoints for workbooks
Closes #1421
1 parent 79f1788 commit dbbe005

File tree

3 files changed

+109
-4
lines changed

3 files changed

+109
-4
lines changed

tableauserverclient/server/endpoint/workbooks_endpoint.py

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88

99
from tableauserverclient.helpers.headers import fix_filename
1010

11-
from .endpoint import QuerysetEndpoint, api, parameter_added_in
12-
from .exceptions import InternalServerError, MissingRequiredFieldError
13-
from .permissions_endpoint import _PermissionsEndpoint
14-
from .resource_tagger import _ResourceTagger
11+
from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api, parameter_added_in
12+
from tableauserverclient.server.endpoint.exceptions import InternalServerError, MissingRequiredFieldError
13+
from tableauserverclient.server.endpoint.permissions_endpoint import _PermissionsEndpoint
14+
from tableauserverclient.server.endpoint.resource_tagger import _ResourceTagger
1515

1616
from tableauserverclient.filesys_helpers import (
1717
to_filename,
@@ -24,9 +24,11 @@
2424
from tableauserverclient.server import RequestFactory
2525

2626
from typing import (
27+
Iterable,
2728
List,
2829
Optional,
2930
Sequence,
31+
Set,
3032
Tuple,
3133
TYPE_CHECKING,
3234
Union,
@@ -498,3 +500,32 @@ def schedule_extract_refresh(
498500
self, schedule_id: str, item: WorkbookItem
499501
) -> List["AddResponse"]: # actually should return a task
500502
return self.parent_srv.schedules.add_to_schedule(schedule_id, workbook=item)
503+
504+
@api(version="1.0")
505+
def add_tags(self, workbook: Union[WorkbookItem, str], tags: Union[Iterable[str], str]) -> Set[str]:
506+
workbook = getattr(workbook, "id", workbook)
507+
508+
if not isinstance(workbook, str):
509+
raise ValueError("Workbook ID not found.")
510+
511+
if isinstance(tags, str):
512+
tag_set = set([tags])
513+
else:
514+
tag_set = set(tags)
515+
516+
return self._resource_tagger._add_tags(self.baseurl, workbook, tag_set)
517+
518+
@api(version="1.0")
519+
def delete_tags(self, workbook: Union[WorkbookItem, str], tags: Union[Iterable[str], str]) -> None:
520+
workbook = getattr(workbook, "id", workbook)
521+
522+
if not isinstance(workbook, str):
523+
raise ValueError("Workbook ID not found.")
524+
525+
if isinstance(tags, str):
526+
tag_set = set([tags])
527+
else:
528+
tag_set = set(tags)
529+
530+
for tag in tag_set:
531+
self._resource_tagger._delete_tag(self.baseurl, workbook, tag)

test/assets/workbook_add_tag.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version='1.0' encoding='UTF-8'?>
2+
<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">
3+
<tags>
4+
<tag label="a" />
5+
</tags>
6+
</tsResponse>

test/test_workbook.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
57AE TEST_ASSET_DIR = os.path.join(os.path.dirname(__file__), "assets")
1818

19+
ADD_TAG_XML = os.path.join(TEST_ASSET_DIR, "workbook_add_tag.xml")
1920
ADD_TAGS_XML = os.path.join(TEST_ASSET_DIR, "workbook_add_tags.xml")
2021
GET_BY_ID_XML = os.path.join(TEST_ASSET_DIR, "workbook_get_by_id.xml")
2122
GET_BY_ID_XML_PERSONAL = os.path.join(TEST_ASSET_DIR, "workbook_get_by_id_personal.xml")
@@ -891,3 +892,70 @@ def test_odata_connection(self) -> None:
891892

892893
assert xml_connection is not None
893894
self.assertEqual(xml_connection.get("serverAddress"), url)
895+
896+
def test_add_tags(self) -> None:
897+
workbook = TSC.WorkbookItem("project", "test")
898+
workbook._id = "06b944d2-959d-4604-9305-12323c95e70e"
899+
tags = list("abcd")
900+
901+
with requests_mock.mock() as m:
902+
m.put(
903+
f"{self.baseurl}/{workbook.id}/tags",
904+
status_code=200,
905+
text=Path(ADD_TAGS_XML).read_text(),
906+
)
907+
tag_result = self.server.workbooks.add_tags(workbook, tags)
908+
909+
for a, b in zip(sorted(tag_result), sorted(tags)):
910+
self.assertEqual(a, b)
911+
912+
def test_add_tag(self) -> None:
913+
workbook = TSC.WorkbookItem("project", "test")
914+
workbook._id = "06b944d2-959d-4604-9305-12323c95e70e"
915+
tags = "a"
916+
917+
with requests_mock.mock() as m:
918+
m.put(
919+
f"{self.baseurl}/{workbook.id}/tags",
920+
status_code=200,
921+
text=Path(ADD_TAG_XML).read_text(),
922+
)
923+
tag_result = self.server.workbooks.add_tags(workbook, tags)
924+
925+
for a, b in zip(sorted(tag_result), sorted(tags)):
926+
self.assertEqual(a, b)
927+
928+
def test_add_tag_id(self) -> None:
929+
workbook = TSC.WorkbookItem("project", "test")
930+
workbook._id = "06b944d2-959d-4604-9305-12323c95e70e"
931+
tags = "a"
932+
933+
with requests_mock.mock() as m:
934+
m.put(
935+
f"{self.baseurl}/{workbook.id}/tags",
936+
status_code=200,
937+
text=Path(ADD_TAG_XML).read_text(),
938+
)
939+
tag_result = self.server.workbooks.add_tags(workbook.id, tags)
940+
941+
for a, b in zip(sorted(tag_result), sorted(tags)):
942+
self.assertEqual(a, b)
943+
944+
def test_delete_tags(self) -> None:
945+
workbook = TSC.WorkbookItem("project", "test")
946+
workbook._id = "06b944d2-959d-4604-9305-12323c95e70e"
947+
tags = list("abcd")
948+
949+
matcher = re.compile(rf"{self.baseurl}\/{workbook.id}\/tags\/[abcd]")
950+
with requests_mock.mock() as m:
951+
m.delete(
952+
matcher,
953+
status_code=200,
954+
text="",
955+
)
956+
self.server.workbooks.delete_tags(workbook, tags)
957+
history = m.request_history
958+
959+
self.assertEqual(len(history), len(tags))
960+
urls = sorted([r.url.split("/")[-1] for r in history])
961+
self.assertEqual(urls, sorted(tags))

0 commit comments

Comments
 (0)
0