Skip to content

Commit c84f921

Browse files
committed
feat: download custom views
1 parent 3a53d00 commit c84f921

File tree

2 files changed

+47
-5
lines changed

2 files changed

+47
-5
lines changed

tableauserverclient/server/endpoint/custom_views_endpoint.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import io
12
import logging
2-
from typing import List, Optional, Tuple
3+
import os
4+
from typing import List, Optional, Tuple, Union
35

4-
from .endpoint import QuerysetEndpoint, api
5-
from .exceptions import MissingRequiredFieldError
6+
from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api
7+
from tableauserverclient.server.endpoint.exceptions import MissingRequiredFieldError
68
from tableauserverclient.models import CustomViewItem, PaginationItem
79
from tableauserverclient.server import RequestFactory, RequestOptions, ImageRequestOptions
810

@@ -16,6 +18,15 @@
1618
update the name or owner of a custom view.
1719
"""
1820

21+
FilePath = Union[str, os.PathLike]
22+
FileObject = Union[io.BufferedReader, io.BytesIO]
23+
FileObjectR = Union[io.BufferedReader, io.BytesIO]
24+
FileObjectW = Union[io.BufferedWriter, io.BytesIO]
25+
PathOrFileR = Union[FilePath, FileObjectR]
26+
PathOrFileW = Union[FilePath, FileObjectW]
27+
io_types_r = (io.BufferedReader, io.BytesIO)
28+
io_types_w = (io.BufferedWriter, io.BytesIO)
29+
1930

2031
class CustomViews(QuerysetEndpoint[CustomViewItem]):
2132
def __init__(self, parent_srv):
@@ -25,6 +36,10 @@ def __init__(self, parent_srv):
2536
def baseurl(self) -> str:
2637
return "{0}/sites/{1}/customviews".format(self.parent_srv.baseurl, self.parent_srv.site_id)
2738

39+
@property
40+
def expurl(self) -> str:
41+
return f"{self.parent_srv._server_address}/api/exp/sites/{self.parent_srv.site_id}/customviews"
42+
2843
"""
2944
If the request has no filter parameters: Administrators will see all custom views.
3045
Other users will see only custom views that they own.
@@ -102,3 +117,16 @@ def delete(self, view_id: str) -> None:
102117
url = "{0}/{1}".format(self.baseurl, view_id)
103118
self.delete_request(url)
104119
logger.info("Deleted single custom view (ID: {0})".format(view_id))
120+
121+
@api(version="3.21")
122+
def download(self, view_item: CustomViewItem, file: PathOrFileW) -> PathOrFileW:
123+
url = f"{self.expurl}/{view_item.id}/content"
124+
server_response = self.get_request(url)
125+
if isinstance(file, io_types_w):
126+
file.write(server_response.content)
127+
return file
128+
129+
with open(file, "wb") as f:
130+
f.write(server_response.content)
131+
132+
return file

test/test_custom_view.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
1+
import io
12
import os
3+
from pathlib import Path
24
import unittest
35

46
import requests_mock
57

68
import tableauserverclient as TSC
79
from tableauserverclient.datetime_helpers import format_datetime
810

9-
TEST_ASSET_DIR = os.path.join(os.path.dirname(__file__), "assets")
11+
TEST_ASSET_DIR = Path(__file__).parent / "assets"
1012

1113
GET_XML = os.path.join(TEST_ASSET_DIR, "custom_view_get.xml")
1214
GET_XML_ID = os.path.join(TEST_ASSET_DIR, "custom_view_get_id.xml")
1315
POPULATE_PREVIEW_IMAGE = os.path.join(TEST_ASSET_DIR, "Sample View Image.png")
1416
CUSTOM_VIEW_UPDATE_XML = os.path.join(TEST_ASSET_DIR, "custom_view_update.xml")
17+
CUSTOM_VIEW_DOWNLOAD = TEST_ASSET_DIR / "custom_view_download.json"
1518

1619

1720
class CustomViewTests(unittest.TestCase):
1821
def setUp(self):
1922
self.server = TSC.Server("http://test", False)
20-
self.server.version = "3.19" # custom views only introduced in 3.19
23+
self.server.version = "3.21" # custom views only introduced in 3.19
2124

2225
# Fake sign in
2326
self.server._site_id = "dad65087-b08b-4603-af4e-2887b8aafc67"
@@ -132,3 +135,14 @@ def test_update(self) -> None:
132135
def test_update_missing_id(self) -> None:
133136
cv = TSC.CustomViewItem(name="test")
134137
self.assertRaises(TSC.MissingRequiredFieldError, self.server.custom_views.update, cv)
138+
139+
def test_download(self) -> None:
140+
cv = TSC.CustomViewItem(name="test")
141+
cv._id = "1f951daf-4061-451a-9df1-69a8062664f2"
142+
content = CUSTOM_VIEW_DOWNLOAD.read_bytes()
143+
data = io.BytesIO()
144+
with requests_mock.mock() as m:
145+
m.get(f"{self.server.custom_views.expurl}/1f951daf-4061-451a-9df1-69a8062664f2/content", content=content)
146+
self.server.custom_views.download(cv, data)
147+
148+
assert data.getvalue() == content

0 commit comments

Comments
 (0)