Skip to content
Open
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
9 changes: 8 additions & 1 deletion tableauserverclient/server/endpoint/custom_views_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def populate_image(self, view_item: CustomViewItem, req_options: Optional["Image
view_item : CustomViewItem

req_options : ImageRequestOptions, optional
Options to customize the image returned, by default None
Options to customize the image returned, including format (PNG or SVG), by default None

Returns
-------
Expand All @@ -139,6 +139,13 @@ def populate_image(self, view_item: CustomViewItem, req_options: Optional["Image
def image_fetcher():
return self._get_view_image(view_item, req_options)

if req_options is not None:
if not self.parent_srv.check_at_least_version("3.29"):
if req_options.format:
from tableauserverclient.server.endpoint.exceptions import UnsupportedAttributeError

raise UnsupportedAttributeError("format parameter is only supported in 3.29+")

view_item._set_image(image_fetcher)
logger.info(f"Populated image for custom view (ID: {view_item.id})")

Expand Down
12 changes: 8 additions & 4 deletions tableauserverclient/server/endpoint/views_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def populate_image(self, view_item: ViewItem, req_options: Optional["ImageReques

req_options: Optional[ImageRequestOptions], default None
Optional request options for the request. These options can include
parameters such as image resolution and max age.
parameters such as image resolution, max age, and format (PNG or SVG).

Returns
-------
Expand All @@ -171,9 +171,13 @@ def populate_image(self, view_item: ViewItem, req_options: Optional["ImageReques
def image_fetcher():
return self._get_view_image(view_item, req_options)

if not self.parent_srv.check_at_least_version("3.23") and req_options is not None:
if req_options.viz_height or req_options.viz_width:
raise UnsupportedAttributeError("viz_height and viz_width are only supported in 3.23+")
if req_options is not None:
if not self.parent_srv.check_at_least_version("3.23"):
if req_options.viz_height or req_options.viz_width:
raise UnsupportedAttributeError("viz_height and viz_width are only supported in 3.23+")
if not self.parent_srv.check_at_least_version("3.29"):
if req_options.format:
raise UnsupportedAttributeError("format parameter is only supported in 3.29+")

view_item._set_image(image_fetcher)
logger.info(f"Populated image for view (ID: {view_item.id})")
Expand Down
13 changes: 12 additions & 1 deletion tableauserverclient/server/request_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,10 @@ class ImageRequestOptions(_ImagePDFCommonExportOptions):
viz_width: int, optional
The width of the viz in pixels. If specified, viz_height must also be specified.

format: str, optional
The format of the image to export. Use Format.PNG, Format.SVG, Format.png, or Format.svg.
Default is "PNG". Available in API version 3.29+.

"""

extension = "png"
Expand All @@ -505,14 +509,21 @@ class ImageRequestOptions(_ImagePDFCommonExportOptions):
class Resolution:
High = "high"

def __init__(self, imageresolution=None, maxage=-1, viz_height=None, viz_width=None):
class Format:
PNG = "PNG"
SVG = "SVG"

def __init__(self, imageresolution=None, maxage=-1, viz_height=None, viz_width=None, format=None):
super().__init__(maxage=maxage, viz_height=viz_height, viz_width=viz_width)
self.image_resolution = imageresolution
self.format = format

def get_query_params(self):
params = super().get_query_params()
if self.image_resolution:
params["resolution"] = self.image_resolution
if self.format:
params["format"] = self.format
return params


Expand Down
48 changes: 48 additions & 0 deletions test/test_custom_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,54 @@ def test_populate_image_with_options(server: TSC.Server) -> None:
assert response == single_view.image


def test_populate_image_svg_format(server: TSC.Server) -> None:
server.version = "3.29"
response = b"<svg>test</svg>"
with requests_mock.mock() as m:
m.get(
server.custom_views.baseurl + "/d79634e1-6063-4ec9-95ff-50acbf609ff5/image?format=SVG",
content=response,
)
single_view = TSC.CustomViewItem()
single_view._id = "d79634e1-6063-4ec9-95ff-50acbf609ff5"
req_option = TSC.ImageRequestOptions(format=TSC.ImageRequestOptions.Format.SVG)
server.custom_views.populate_image(single_view, req_option)
assert response == single_view.image


def test_populate_image_png_format(server: TSC.Server) -> None:
server.version = "3.29"
response = POPULATE_PREVIEW_IMAGE.read_bytes()
with requests_mock.mock() as m:
m.get(
server.custom_views.baseurl + "/d79634e1-6063-4ec9-95ff-50acbf609ff5/image?format=PNG",
content=response,
)
single_view = TSC.CustomViewItem()
single_view._id = "d79634e1-6063-4ec9-95ff-50acbf609ff5"
req_option = TSC.ImageRequestOptions(format=TSC.ImageRequestOptions.Format.PNG)
server.custom_views.populate_image(single_view, req_option)
assert response == single_view.image


def test_populate_image_format_unsupported_version(server: TSC.Server) -> None:
from tableauserverclient.server.endpoint.exceptions import UnsupportedAttributeError

server.version = "3.28"
response = POPULATE_PREVIEW_IMAGE.read_bytes()
with requests_mock.mock() as m:
m.get(
server.custom_views.baseurl + "/d79634e1-6063-4ec9-95ff-50acbf609ff5/image?format=SVG",
content=response,
)
single_view = TSC.CustomViewItem()
single_view._id = "d79634e1-6063-4ec9-95ff-50acbf609ff5"
req_option = TSC.ImageRequestOptions(format=TSC.ImageRequestOptions.Format.SVG)

with pytest.raises(UnsupportedAttributeError):
server.custom_views.populate_image(single_view, req_option)


def test_populate_image_missing_id(server: TSC.Server) -> None:
single_view = TSC.CustomViewItem()
single_view._id = None
Expand Down
46 changes: 46 additions & 0 deletions test/test_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,52 @@ def test_populate_image_with_options(server: TSC.Server) -> None:
assert response == single_view.image


def test_populate_image_svg_format(server: TSC.Server) -> None:
server.version = "3.29"
response = b"<svg>test</svg>"
with requests_mock.mock() as m:
m.get(
server.views.baseurl + "/d79634e1-6063-4ec9-95ff-50acbf609ff5/image?format=SVG",
content=response,
)
single_view = TSC.ViewItem()
single_view._id = "d79634e1-6063-4ec9-95ff-50acbf609ff5"
req_option = TSC.ImageRequestOptions(format=TSC.ImageRequestOptions.Format.SVG)
server.views.populate_image(single_view, req_option)
assert response == single_view.image


def test_populate_image_png_format(server: TSC.Server) -> None:
server.version = "3.29"
response = POPULATE_PREVIEW_IMAGE.read_bytes()
with requests_mock.mock() as m:
m.get(
server.views.baseurl + "/d79634e1-6063-4ec9-95ff-50acbf609ff5/image?format=PNG",
content=response,
)
single_view = TSC.ViewItem()
single_view._id = "d79634e1-6063-4ec9-95ff-50acbf609ff5"
req_option = TSC.ImageRequestOptions(format=TSC.ImageRequestOptions.Format.PNG)
server.views.populate_image(single_view, req_option)
assert response == single_view.image


def test_populate_image_format_unsupported_version(server: TSC.Server) -> None:
server.version = "3.28"
response = POPULATE_PREVIEW_IMAGE.read_bytes()
with requests_mock.mock() as m:
m.get(
server.views.baseurl + "/d79634e1-6063-4ec9-95ff-50acbf609ff5/image?format=SVG",
content=response,
)
single_view = TSC.ViewItem()
single_view._id = "d79634e1-6063-4ec9-95ff-50acbf609ff5"
req_option = TSC.ImageRequestOptions(format=TSC.ImageRequestOptions.Format.SVG)

with pytest.raises(UnsupportedAttributeError):
server.views.populate_image(single_view, req_option)


def test_populate_pdf(server: TSC.Server) -> None:
response = POPULATE_PDF.read_bytes()
with requests_mock.mock() as m:
Expand Down