Skip to content

Commit 6f171fa

Browse files
jacalataclaudebcantoniCopilot
authored
feat(samples): sample for reading connector-specific connection attrs (#1880)
* feat(samples): add inspect_connection_details.py for reading connector-specific attrs The REST API's <connection> element only surfaces generic fields (id, type, serverAddress, serverPort, userName, embedPassword, authenticationType, queryTaggingEnabled). Connector-specific attributes -- Oracle service, Snowflake schema, Teradata query-band, initial SQL, etc. -- live inside the workbook or datasource file itself. This sample downloads the file via TSC and parses it with the community `tableaudocumentapi` library to reach the extra attributes. Verified against a live Tableau Cloud site: on a SQL Server datasource, the sample returns dbname/schema values that are None on the REST ConnectionItem. Addresses the recurring backlog around connector-specific connection metadata (#1807, #1571, #569, #353, #160) by pointing users at the existing download-and-parse path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Adopt _shared.py credential helper in inspect_connection_details Fold the inline sign-in argparse block into the shared helper landed in #1843. Users of this sample now get env / .env / interactive-prompt credential resolution, JWT + username/password auth in addition to PAT, and a clear ValueError when --server is missing under non-TTY, instead of an opaque 400/401 inside TSC. Also update the header's Python-version claim from 3.7 to 3.10 to match `pyproject.toml`'s `requires-python = ">=3.10"`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Fix inspect connection sample review comments Co-authored-by: bcantoni <58372+bcantoni@users.noreply.github.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Brian Cantoni <bcantoni@salesforce.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: bcantoni <58372+bcantoni@users.noreply.github.com>
1 parent 7bb9852 commit 6f171fa

1 file changed

Lines changed: 108 additions & 0 deletions

File tree

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
####
2+
# This script demonstrates how to read connector-specific connection attributes
3+
# (Oracle service, Snowflake schema, Teradata query-band, initial SQL, etc.) that
4+
# the Tableau REST API's `<connection>` element does NOT expose.
5+
#
6+
# The REST API's `.populate_connections()` surface only carries generic fields
7+
# (id, type, server_address, server_port, username, embed_password, auth_type).
8+
# The full set of connector-specific attributes lives inside the workbook or
9+
# datasource file itself. This sample downloads the file via TSC, then parses it
10+
# with the community `tableaudocumentapi` library to reach the extra attributes.
11+
#
12+
# Requires:
13+
# pip install tableauserverclient tableaudocumentapi
14+
#
15+
# Related GitHub issues:
16+
# - server-client-python#1807 (Oracle service on ConnectionItem)
17+
# - server-client-python#1571 (database_name on ConnectionItem)
18+
# - server-client-python#160 (queryband, initial_sql)
19+
# - server-client-python#353 (SQL Server database + schema + table)
20+
#
21+
# To run the script, you must have installed Python 3.10 or later.
22+
####
23+
24+
import argparse
25+
import logging
26+
import tempfile
27+
28+
import tableauserverclient as TSC
29+
30+
from _shared import add_common_arguments, build_auth, resolve_credentials
31+
32+
try:
33+
from tableaudocumentapi import Datasource, Workbook
34+
except ImportError:
35+
raise SystemExit(
36+
"This sample requires the tableaudocumentapi package.\n" "Install it with: pip install tableaudocumentapi"
37+
)
38+
39+
40+
def dump_connections_from_datasource_file(path):
41+
"""Print connector-specific attributes for every connection inside a .tds/.tdsx file."""
42+
ds = Datasource.from_file(path)
43+
for i, c in enumerate(ds.connections, start=1):
44+
_print_connection(f"datasource conn #{i}", c)
45+
46+
47+
def dump_connections_from_workbook_file(path):
48+
"""Print connector-specific attributes for every connection across every
49+
embedded datasource inside a .twb/.twbx file."""
50+
wb = Workbook(path)
51+
for di, ds in enumerate(wb.datasources, start=1):
52+
for ci, c in enumerate(ds.connections, start=1):
53+
_print_connection(f"workbook datasource #{di}, conn #{ci}", c)
54+
55+
56+
def _print_connection(label, c):
57+
print(f"--- {label} ---")
58+
print(f" dbclass: {c.dbclass}")
59+
print(f" server: {c.server}")
60+
print(f" port: {c.port}")
61+
print(f" username: {c.username}")
62+
print(f" dbname: {c.dbname}")
63+
# Schema, service, query-band, and initial-SQL fields are connector-specific
64+
# and are not exposed by TSC's ConnectionItem today.
65+
print(f" schema: {c.schema}")
66+
print(f" service: {c.service}")
67+
print(f" authentication: {c.authentication}")
68+
print(f" query_band: {c.query_band}")
69+
print(f" initial_sql: {c.initial_sql}")
70+
71+
72+
def main():
73+
parser = argparse.ArgumentParser(
74+
description=(
75+
"Inspect connector-specific connection attributes for a datasource or workbook. "
76+
"Downloads the item via TSC, then parses its embedded XML with tableau-document-api."
77+
)
78+
)
79+
# Common options -- credentials come from CLI args, env vars, a .env file,
80+
# or an interactive prompt. See samples/_shared.py.
81+
add_common_arguments(parser)
82+
# Options specific to this sample
83+
parser.add_argument("resource_type", choices=["workbook", "datasource"])
84+
parser.add_argument("resource_id", help="LUID of the workbook or datasource")
85+
86+
args = parser.parse_args()
87+
88+
resolve_credentials(args)
89+
logging.basicConfig(level=getattr(logging, args.logging_level.upper()))
90+
91+
tableau_auth = build_auth(args)
92+
server = TSC.Server(args.server, use_server_version=True)
93+
94+
with server.auth.sign_in(tableau_auth):
95+
endpoint = server.workbooks if args.resource_type == "workbook" else server.datasources
96+
with tempfile.TemporaryDirectory() as tmp:
97+
# include_extract=False keeps downloads small; extract bytes are not
98+
# needed to read the <connection> XML.
99+
path = endpoint.download(args.resource_id, filepath=tmp, include_extract=False)
100+
print(f"Downloaded to {path}")
101+
if args.resource_type == "workbook":
102+
dump_connections_from_workbook_file(path)
103+
else:
104+
dump_connections_from_datasource_file(path)
105+
106+
107+
if __name__ == "__main__":
108+
main()

0 commit comments

Comments
 (0)