From 24e47cea7c1ee78804ebd179445527d6504ad8b6 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 15 Jan 2026 18:16:45 -0800 Subject: [PATCH 1/8] add check for 409 error code --- .../server/endpoint/workbooks_endpoint.py | 11 ++++++++++- test/assets/workbook_refresh_duplicate.xml | 3 +++ test/test_workbook.py | 16 ++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 test/assets/workbook_refresh_duplicate.xml diff --git a/tableauserverclient/server/endpoint/workbooks_endpoint.py b/tableauserverclient/server/endpoint/workbooks_endpoint.py index 5f9695829..16b239c00 100644 --- a/tableauserverclient/server/endpoint/workbooks_endpoint.py +++ b/tableauserverclient/server/endpoint/workbooks_endpoint.py @@ -14,6 +14,7 @@ from tableauserverclient.server.endpoint.exceptions import ( InternalServerError, MissingRequiredFieldError, + ServerResponseError, UnsupportedAttributeError, ) from tableauserverclient.server.endpoint.permissions_endpoint import _PermissionsEndpoint @@ -144,7 +145,15 @@ def refresh(self, workbook_item: Union[WorkbookItem, str], incremental: bool = F id_ = getattr(workbook_item, "id", workbook_item) url = f"{self.baseurl}/{id_}/refresh" refresh_req = RequestFactory.Task.refresh_req(incremental, self.parent_srv) - server_response = self.post_request(url, refresh_req) + server_response = None + try: + server_response = self.post_request(url, refresh_req) + except ServerResponseError as e: + if e.code.startswith("409") and e.detail.find("already"): + print(e.summary + " " + e.detail) + return None + else: + raise e new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0] return new_job diff --git a/test/assets/workbook_refresh_duplicate.xml b/test/assets/workbook_refresh_duplicate.xml new file mode 100644 index 000000000..eca4b4bcc --- /dev/null +++ b/test/assets/workbook_refresh_duplicate.xml @@ -0,0 +1,3 @@ + + +Resource ConflictJob for \'extract\' is already queued. Not queuing a duplicate. \ No newline at end of file diff --git a/test/test_workbook.py b/test/test_workbook.py index e6e807f89..a684630fa 100644 --- a/test/test_workbook.py +++ b/test/test_workbook.py @@ -34,6 +34,7 @@ PUBLISH_XML = TEST_ASSET_DIR / "workbook_publish.xml" PUBLISH_ASYNC_XML = TEST_ASSET_DIR / "workbook_publish_async.xml" REFRESH_XML = TEST_ASSET_DIR / "workbook_refresh.xml" +WORKBOOK_REFRESH_DUPLICATE_XML = TEST_ASSET_DIR / "workbook_refresh_duplicate.xml" REVISION_XML = TEST_ASSET_DIR / "workbook_revision.xml" UPDATE_XML = TEST_ASSET_DIR / "workbook_update.xml" UPDATE_PERMISSIONS = TEST_ASSET_DIR / "workbook_update_permissions.xml" @@ -177,6 +178,21 @@ def test_refresh_id(server: TSC.Server) -> None: ) server.workbooks.refresh("3cc6cd06-89ce-4fdc-b935-5294135d6d42") +def test_refresh_already_running(server: TSC.Server) -> None: + server.version = "2.8" + server.workbooks.baseurl + response_xml = WORKBOOK_REFRESH_DUPLICATE_XML.read_text() + with requests_mock.mock() as m: + m.post( + server.workbooks.baseurl + "/3cc6cd06-89ce-4fdc-b935-5294135d6d42/refresh", + status_code=409, + text=response_xml, + ) + refresh_job = server.workbooks.refresh("3cc6cd06-89ce-4fdc-b935-5294135d6d42") + assert refresh_job is None + + + def test_refresh_object(server: TSC.Server) -> None: server.version = "2.8" From 019e46e8d8f5b6ccbbdb8e759efdc052ab9abe3f Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 15 Jan 2026 18:20:30 -0800 Subject: [PATCH 2/8] format black --- test/test_workbook.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test_workbook.py b/test/test_workbook.py index a684630fa..b210e8402 100644 --- a/test/test_workbook.py +++ b/test/test_workbook.py @@ -178,6 +178,7 @@ def test_refresh_id(server: TSC.Server) -> None: ) server.workbooks.refresh("3cc6cd06-89ce-4fdc-b935-5294135d6d42") + def test_refresh_already_running(server: TSC.Server) -> None: server.version = "2.8" server.workbooks.baseurl @@ -192,8 +193,6 @@ def test_refresh_already_running(server: TSC.Server) -> None: assert refresh_job is None - - def test_refresh_object(server: TSC.Server) -> None: server.version = "2.8" server.workbooks.baseurl From c788327722e74ffd14e3705a4ddf14f99263f8b4 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 15 Jan 2026 18:22:52 -0800 Subject: [PATCH 3/8] mypy --- tableauserverclient/server/endpoint/workbooks_endpoint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tableauserverclient/server/endpoint/workbooks_endpoint.py b/tableauserverclient/server/endpoint/workbooks_endpoint.py index 16b239c00..5e63e7b05 100644 --- a/tableauserverclient/server/endpoint/workbooks_endpoint.py +++ b/tableauserverclient/server/endpoint/workbooks_endpoint.py @@ -126,7 +126,7 @@ def get_by_id(self, workbook_id: str) -> WorkbookItem: return WorkbookItem.from_response(server_response.content, self.parent_srv.namespace)[0] @api(version="2.8") - def refresh(self, workbook_item: Union[WorkbookItem, str], incremental: bool = False) -> JobItem: + def refresh(self, workbook_item: Union[WorkbookItem, str], incremental: bool = False) -> JobItem | None: """ Refreshes the extract of an existing workbook. From 3fa5ce7769a56ef654e89c6b4dc471e3d88b72f7 Mon Sep 17 00:00:00 2001 From: Jac Date: Thu, 15 Jan 2026 20:27:35 -0800 Subject: [PATCH 4/8] Update tableauserverclient/server/endpoint/workbooks_endpoint.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tableauserverclient/server/endpoint/workbooks_endpoint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tableauserverclient/server/endpoint/workbooks_endpoint.py b/tableauserverclient/server/endpoint/workbooks_endpoint.py index 5e63e7b05..85c0f62ce 100644 --- a/tableauserverclient/server/endpoint/workbooks_endpoint.py +++ b/tableauserverclient/server/endpoint/workbooks_endpoint.py @@ -149,7 +149,7 @@ def refresh(self, workbook_item: Union[WorkbookItem, str], incremental: bool = F try: server_response = self.post_request(url, refresh_req) except ServerResponseError as e: - if e.code.startswith("409") and e.detail.find("already"): + if e.code.startswith("409") and "already" in e.detail: print(e.summary + " " + e.detail) return None else: From a4e85be45e9fbbfe1eed3fe7dfe5d0c7f5758629 Mon Sep 17 00:00:00 2001 From: Jac Date: Thu, 15 Jan 2026 20:30:09 -0800 Subject: [PATCH 5/8] Update tableauserverclient/server/endpoint/workbooks_endpoint.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tableauserverclient/server/endpoint/workbooks_endpoint.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tableauserverclient/server/endpoint/workbooks_endpoint.py b/tableauserverclient/server/endpoint/workbooks_endpoint.py index 85c0f62ce..9ecbda670 100644 --- a/tableauserverclient/server/endpoint/workbooks_endpoint.py +++ b/tableauserverclient/server/endpoint/workbooks_endpoint.py @@ -152,8 +152,7 @@ def refresh(self, workbook_item: Union[WorkbookItem, str], incremental: bool = F if e.code.startswith("409") and "already" in e.detail: print(e.summary + " " + e.detail) return None - else: - raise e + raise new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0] return new_job From a7cbf408860f7f65fe4b295974b5948a391274bc Mon Sep 17 00:00:00 2001 From: Jac Date: Thu, 15 Jan 2026 20:31:32 -0800 Subject: [PATCH 6/8] Update tableauserverclient/server/endpoint/workbooks_endpoint.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tableauserverclient/server/endpoint/workbooks_endpoint.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tableauserverclient/server/endpoint/workbooks_endpoint.py b/tableauserverclient/server/endpoint/workbooks_endpoint.py index 9ecbda670..4bf429638 100644 --- a/tableauserverclient/server/endpoint/workbooks_endpoint.py +++ b/tableauserverclient/server/endpoint/workbooks_endpoint.py @@ -145,7 +145,6 @@ def refresh(self, workbook_item: Union[WorkbookItem, str], incremental: bool = F id_ = getattr(workbook_item, "id", workbook_item) url = f"{self.baseurl}/{id_}/refresh" refresh_req = RequestFactory.Task.refresh_req(incremental, self.parent_srv) - server_response = None try: server_response = self.post_request(url, refresh_req) except ServerResponseError as e: From b529f7208386ed16f63abd1bc5f5098ac70c6a84 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 21:03:16 -0800 Subject: [PATCH 7/8] Update refresh() docstring to document None return type (#1735) * Initial plan * Update refresh() docstring to document None return type Co-authored-by: jacalata <2009720+jacalata@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jacalata <2009720+jacalata@users.noreply.github.com> --- tableauserverclient/server/endpoint/workbooks_endpoint.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tableauserverclient/server/endpoint/workbooks_endpoint.py b/tableauserverclient/server/endpoint/workbooks_endpoint.py index 4bf429638..2e3c035e1 100644 --- a/tableauserverclient/server/endpoint/workbooks_endpoint.py +++ b/tableauserverclient/server/endpoint/workbooks_endpoint.py @@ -139,8 +139,8 @@ def refresh(self, workbook_item: Union[WorkbookItem, str], incremental: bool = F Returns ------- - JobItem - The job item. + JobItem | None + The job item, or None if a refresh job is already queued for this workbook. """ id_ = getattr(workbook_item, "id", workbook_item) url = f"{self.baseurl}/{id_}/refresh" From 2cb20432344792bd17432937a1b887c50ff282ad Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 21:03:37 -0800 Subject: [PATCH 8/8] Replace print() with logger.warning() for 409 conflict handling (#1734) * Initial plan * Replace print() with logger.warning() for 409 error handling Co-authored-by: jacalata <2009720+jacalata@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jacalata <2009720+jacalata@users.noreply.github.com> --- tableauserverclient/server/endpoint/workbooks_endpoint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tableauserverclient/server/endpoint/workbooks_endpoint.py b/tableauserverclient/server/endpoint/workbooks_endpoint.py index 2e3c035e1..218a4016f 100644 --- a/tableauserverclient/server/endpoint/workbooks_endpoint.py +++ b/tableauserverclient/server/endpoint/workbooks_endpoint.py @@ -149,7 +149,7 @@ def refresh(self, workbook_item: Union[WorkbookItem, str], incremental: bool = F server_response = self.post_request(url, refresh_req) except ServerResponseError as e: if e.code.startswith("409") and "already" in e.detail: - print(e.summary + " " + e.detail) + logger.warning(f"{e.summary} {e.detail}") return None raise new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]