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

Skip to content

Commit 3759248

Browse files
committed
feat: add/remove tags endpoints for workbooks
Closes #1421
1 parent 7822be0 commit 3759248

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
@@ -18,6 +18,7 @@
1818

1919
TEST_ASSET_DIR = os.path.join(os.path.dirname(__file__), "assets")
2020

21+
ADD_TAG_XML = os.path.join(TEST_ASSET_DIR, "workbook_add_tag.xml")
2122
ADD_TAGS_XML = os.path.join(TEST_ASSET_DIR, "workbook_add_tags.xml")
2223
GET_BY_ID_XML = os.path.join(TEST_ASSET_DIR, "workbook_get_by_id.xml")
2324
GET_BY_ID_XML_PERSONAL = os.path.join(TEST_ASSET_DIR, "workbook_get_by_id_personal.xml")
@@ -894,3 +895,70 @@ def test_odata_connection(self) -> None:
894895

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

0 commit comments

Comments
 (0)
0