Expose refreshExtractTriggered on SubscriptionItem - #1861
Open
jacalata wants to merge 2 commits into
Open
Conversation
The Tableau REST API supports a `refreshExtractTriggered="true"` attribute on subscription payloads that makes the subscription fire when its referenced schedule's extract refresh completes, rather than on the schedule's time trigger. On Tableau Cloud, this is the wire form of an "On Extract Refresh" subscription. TSC never exposed this attribute; users trying to create these subscriptions were passing `schedule_id=None` and hitting a confusing wire error deep in the endpoint layer. Changes: - `SubscriptionItem.on_extract_refresh(...)` classmethod factory constructs a subscription with an extract-refresh schedule id and the flag set. - `refresh_extract_triggered` exposed as a property with a docstring covering the two ways the server surprises callers (server rejects True with a non-extract schedule; server silently clears the flag when a schedule change is included in an update). - `Subscriptions.create()` and `.update()` now raise `ValueError` up front when `schedule_id` is missing, so the wire error becomes an actionable client-side message. - `create_req` emits `refreshExtractTriggered="true"` only when set; `update_req` emits both true and false so callers can turn the flag off on an existing subscription. - `_parse_element` reads the attribute back into the property; parse continues to accept inline-schedule responses (schedule_id=None). Tests cover: factory sets flag + schedule id; default false; create_req emit-when-set/omit-when-false; update_req always emits; parse round-trip for both true and missing; parse of inline-schedule responses; create() and update() reject missing schedule_id. Related to #1658. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds first-class support for Tableau REST API’s refreshExtractTriggered subscription attribute (Tableau Cloud “On Extract Refresh” subscriptions) by exposing it on SubscriptionItem, emitting/parsing it in XML requests/responses, and improving client-side validation/errors when schedule_id is missing.
Changes:
- Introduces
SubscriptionItem.refresh_extract_triggeredplusSubscriptionItem.on_extract_refresh(...)factory for extract-refresh-triggered subscriptions. - Updates subscription request XML generation/parsing to handle
refreshExtractTriggered(create emits only when true; update always emits true/false). - Adds endpoint-level validation for missing
schedule_idand expands test coverage for the new behavior and regression cases.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
test/test_subscription.py |
Adds tests for factory, defaults, XML emit/omit behavior, parsing, and create/update validation. |
tableauserverclient/server/request_factory.py |
Emits refreshExtractTriggered in create/update subscription request payloads. |
tableauserverclient/server/endpoint/subscriptions_endpoint.py |
Validates schedule_id presence for create/update and raises clearer ValueErrors. |
tableauserverclient/models/subscription_item.py |
Adds property + factory, documents behavior, and parses refreshExtractTriggered from responses. |
Suppressed comments (1)
tableauserverclient/server/endpoint/subscriptions_endpoint.py:75
- update() uses a truthiness check for schedule_id, which won’t reject whitespace-only values and is slightly inconsistent with update_req (which treats schedule_id as optional and would emit a without an id). Making the validation explicit for None/empty/whitespace keeps failures predictable and avoids generating invalid XML.
if not subscription_item.schedule_id:
# A subscription round-tripped from an inline-schedule response
# (Cloud/TOL) has schedule_id=None. Updating it in that state
# sends <schedule/> with no id and hits the same wire-layer error
# that create() guards against. See tableau/server-client-python#1658.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+46
to
+50
| if not subscription_item.schedule_id: | ||
| # See tableau/server-client-python#1658: users trying to create an | ||
| # "On Extract Refresh" subscription pass schedule_id=None and hit | ||
| # a confusing wire-layer error. Point them at the factory. | ||
| raise ValueError("schedule_id is required; see SubscriptionItem.on_extract_refresh") |
- Docstring on `refresh_extract_triggered` now warns about the manual- build update() footgun: because every subscriptions.update() payload carries the attribute, a caller who builds a fresh SubscriptionItem locally, stamps _id, and updates will silently flip an existing on-extract-refresh subscription off. Fetch first. - Soften create()'s "schedule_id is required" error so someone who just forgot to set schedule_id on a time-based subscription doesn't get steered exclusively toward SubscriptionItem.on_extract_refresh(...); the factory is now mentioned as a conditional pointer. - __init__'s schedule_id parameter is now typed str | None, matching the real state: _parse_element sets it to None on inline-schedule responses. Drop the two `# type: ignore` markers in test/test_subscription.py that were papering over the earlier lie. - create_req asserts schedule_id non-None to satisfy mypy after the parameter widening; subscriptions.create() already guards this path before request emission. - Add samples/create_extract_refresh_subscription.py demonstrating the full flow: sign in, resolve view/workbook and user by name, pick an extract-refresh schedule from the schedules list, build the subscription via on_extract_refresh(), post it. Highest-leverage discoverability artifact for callers searching "on extract refresh". - CHANGELOG entry.
jacalata
added a commit
that referenced
this pull request
Aug 18, 2026
Three fresh-eyes findings: - Remove the "Subscriptions cannot use On Extract Refresh" entry. The REST API's refreshExtractTriggered attribute does support this, and TSC support is landing in #1861 (which closes the tracker #1658). The entry is about to be factually wrong on both counts. - Soften the intro: it promised "follow the appropriate issues" for every item, but the vf_-silently-dropped entry has no tracker filed. New wording says "where filed" so the doc doesn't overpromise. - Reword the daily-schedules note to describe behavior neutrally ("currently runs daily schedules hourly instead") rather than calling it a "bug", which reads punchy on a public docs page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds support for the REST API's
refreshExtractTriggered="true"attribute on subscriptions -- the wire form of Tableau Cloud's "On Extract Refresh" subscriptions. When True, the subscription fires when the referenced schedule's extract refresh completes, rather than on the schedule's time trigger.Closes #1658.
What changes
SubscriptionItem.on_extract_refresh(subject, extract_refresh_schedule_id, user_id, target)classmethod factory -- the recommended way to construct these subscriptions.refresh_extract_triggeredis now a boolean property onSubscriptionItem. Its docstring covers the two ways the server can surprise callers:schedule_idcause the server to silently clear the flag to False on that same call; converting an existing time-based subscription requires two updates.Subscriptions.create()and.update()now raiseValueErrorup front ifschedule_idis missing, so what used to be a confusing wire-layer error becomes an actionable client-side message.create_reqemitsrefreshExtractTriggered="true"only when set.update_reqemits both"true"and"false"unconditionally, so callers can turn the flag off on an existing subscription (the server retains the prior value when the attribute is absent)._parse_elementreads the attribute back into the property.Compatibility note
Every
subscriptions.update()payload now carriesrefreshExtractTriggered="true|false". Older servers that don't know the attribute should ignore unknown attrs on the<subscription>element per the schema's<xs:anyAttribute processContents="skip"/>; the attribute has been on the server since long before TSC's current minimum version.Test plan
pytest test/test_subscription.py -q-- 16 pass, including 12 new tests covering the factory, defaults,create_reqemit-when-set / omit-when-false,update_reqalways-emits in both directions, parse round-trip with and without the attribute, parse of inline-schedule responses (noschedule_id), andcreate()/update()rejection of missing schedule_id.on_extract_refresh(), confirm it appears as "On Extract Refresh" in the web UI, and confirm it fires when the referenced extract refresh runs.🤖 Generated with Claude Code