diff --git a/tableauserverclient/models/datasource_item.py b/tableauserverclient/models/datasource_item.py index b5568a778..dbaa0ff91 100644 --- a/tableauserverclient/models/datasource_item.py +++ b/tableauserverclient/models/datasource_item.py @@ -32,7 +32,7 @@ def __repr__(self): self.project_id, ) - def __init__(self, project_id: str, name: Optional[str] = None) -> None: + def __init__(self, project_id: Optional[str] = None, name: Optional[str] = None) -> None: self._ask_data_enablement = None self._certified = None self._certification_note = None @@ -135,12 +135,11 @@ def id(self) -> Optional[str]: return self._id @property - def project_id(self) -> str: + def project_id(self) -> Optional[str]: return self._project_id @project_id.setter - @property_not_nullable - def project_id(self, value: str): + def project_id(self, value: Optional[str]): self._project_id = value @property diff --git a/tableauserverclient/server/endpoint/workbooks_endpoint.py b/tableauserverclient/server/endpoint/workbooks_endpoint.py index 295a4941f..5e2784b55 100644 --- a/tableauserverclient/server/endpoint/workbooks_endpoint.py +++ b/tableauserverclient/server/endpoint/workbooks_endpoint.py @@ -309,6 +309,7 @@ def publish( as_job: bool = False, hidden_views: Optional[Sequence[str]] = None, skip_connection_check: bool = False, + parameters=None, ): if connection_credentials is not None: import warnings @@ -412,7 +413,7 @@ def publish( # Send the publishing request to server try: - server_response = self.post_request(url, xml_request, content_type) + server_response = self.post_request(url, xml_request, content_type, parameters) except InternalServerError as err: if err.code == 504 and not as_job: err.content = "Timeout error while publishing. Please use asynchronous publishing to avoid timeouts." diff --git a/tableauserverclient/server/request_factory.py b/tableauserverclient/server/request_factory.py index b19c3cc56..050874c91 100644 --- a/tableauserverclient/server/request_factory.py +++ b/tableauserverclient/server/request_factory.py @@ -9,6 +9,8 @@ if TYPE_CHECKING: from tableauserverclient.server import Server +# this file could be largely replaced if we were willing to import the huge file from generateDS + def _add_multipart(parts: Dict) -> Tuple[Any, str]: mime_multipart_parts = list() @@ -146,10 +148,11 @@ def update_req(self, database_item): class DatasourceRequest(object): - def _generate_xml(self, datasource_item, connection_credentials=None, connections=None): + def _generate_xml(self, datasource_item: DatasourceItem, connection_credentials=None, connections=None): xml_request = ET.Element("tsRequest") datasource_element = ET.SubElement(xml_request, "datasource") - datasource_element.attrib["name"] = datasource_item.name + if datasource_item.name: + datasource_element.attrib["name"] = datasource_item.name if datasource_item.description: datasource_element.attrib["description"] = str(datasource_item.description) if datasource_item.use_remote_query_agent is not None: @@ -157,10 +160,16 @@ def _generate_xml(self, datasource_item, connection_credentials=None, connection if datasource_item.ask_data_enablement: ask_data_element = ET.SubElement(datasource_element, "askData") - ask_data_element.attrib["enablement"] = datasource_item.ask_data_enablement + ask_data_element.attrib["enablement"] = datasource_item.ask_data_enablement.__str__() - project_element = ET.SubElement(datasource_element, "project") - project_element.attrib["id"] = datasource_item.project_id + if datasource_item.certified: + datasource_element.attrib["isCertified"] = datasource_item.certified.__str__() + if datasource_item.certification_note: + datasource_element.attrib["certificationNote"] = datasource_item.certification_note + + if datasource_item.project_id: + project_element = ET.SubElement(datasource_element, "project") + project_element.attrib["id"] = datasource_item.project_id if connection_credentials is not None and connections is not None: raise RuntimeError("You cannot set both `connections` and `connection_credentials`") diff --git a/tableauserverclient/server/request_options.py b/tableauserverclient/server/request_options.py index baedd74de..299b9db2f 100644 --- a/tableauserverclient/server/request_options.py +++ b/tableauserverclient/server/request_options.py @@ -38,6 +38,7 @@ class Operator: class Field: Args = "args" CompletedAt = "completedAt" + ContentUrl = "contentUrl" CreatedAt = "createdAt" DomainName = "domainName" DomainNickname = "domainNickname" @@ -147,7 +148,7 @@ def get_query_params(self): return params -class ExcelRequestOptions(RequestOptionsBase): +class ExcelRequestOptions(_FilterOptionsBase): def __init__(self, maxage: int = -1) -> None: super().__init__() self.max_age = maxage diff --git a/test/test_datasource_model.py b/test/test_datasource_model.py index 2360574ec..655284194 100644 --- a/test/test_datasource_model.py +++ b/test/test_datasource_model.py @@ -3,11 +3,9 @@ class DatasourceModelTests(unittest.TestCase): - def test_invalid_project_id(self): - self.assertRaises(ValueError, TSC.DatasourceItem, None) - datasource = TSC.DatasourceItem("10") - with self.assertRaises(ValueError): - datasource.project_id = None + def test_nullable_project_id(self): + datasource = TSC.DatasourceItem(name="10") + self.assertEqual(datasource.project_id, None) def test_require_boolean_flag_bridge_fail(self): datasource = TSC.DatasourceItem("10") diff --git a/test/test_view.py b/test/test_view.py index f5d3db47b..1459150bb 100644 --- a/test/test_view.py +++ b/test/test_view.py @@ -299,3 +299,19 @@ def test_populate_excel(self) -> None: excel_file = b"".join(single_view.excel) self.assertEqual(response, excel_file) + + def test_filter_excel(self) -> None: + self.server.version = "3.8" + self.baseurl = self.server.views.baseurl + with open(POPULATE_EXCEL, "rb") as f: + response = f.read() + with requests_mock.mock() as m: + m.get(self.baseurl + "/d79634e1-6063-4ec9-95ff-50acbf609ff5/crosstab/excel?maxAge=1", content=response) + single_view = TSC.ViewItem() + single_view._id = "d79634e1-6063-4ec9-95ff-50acbf609ff5" + request_option = TSC.ExcelRequestOptions(maxage=1) + request_option.vf("stuff", "1") + self.server.views.populate_excel(single_view, request_option) + + excel_file = b"".join(single_view.excel) + self.assertEqual(response, excel_file)