Skip to content

Add the VDS endpoint into the TSC #1603

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
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
72 changes: 72 additions & 0 deletions samples/vds.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
####
# Getting Started Part Three of Three
# This script demonstrates all the different types of 'content' a server contains
#
# To make it easy to run, it doesn't take any arguments - you need to edit the code with your info
####

from typing import Dict, Any
from rich import print

import getpass
import tableauserverclient as TSC


def main():
# 1 - replace with your server domain: stop at the slash
server_url = ""

# 2 - optional - change to false **for testing only** if you get a certificate error
use_ssl = True

server = TSC.Server(server_url, use_server_version=True, http_options={"verify": use_ssl})
print(f"Connected to {server.server_info.baseurl}")

# 3 - replace with your site name exactly as it looks in the url
# e.g https://my-server/#/site/this-is-your-site-url-name/not-this-part
site_url_name = "" # leave empty if there is no site name in the url (you are on the default site)

# 4 - replace with your username.
# REMEMBER: if you are using Tableau Online, your username is the entire email address
username = ""
password = "" #getpass.getpass("Enter your password:") # so you don't save it in this file
tableau_auth = TSC.TableauAuth(username, password, site_id=site_url_name)

# OR instead of username+password, use a Personal Access Token (PAT) (required by Tableau Cloud)
# token_name = "your-token-name"
# token_value = "your-token-value-long-random-string"
# tableau_auth = TSC.PersonalAccessTokenAuth(token_name, token_value, site_id=site_url_name)

with server.auth.sign_in(tableau_auth):
# schema - may be used to understand the schema of the VDS API
print(server.vizql.VizQL_Schema)
# query
query_dict: Dict[str, Any] = {
"fields": [{"fieldCaption": "School Code"}],
"filters": [
{
"field": {"fieldCaption": "Enrollment (K-12)", "function": "SUM"},
"filterType": "QUANTITATIVE_NUMERICAL",
"quantitativeFilterType": "MIN",
"min": 500
}
]
}
datasource_id = "e4d75fd4-af1c-4fb8-a049-058e3fef57bc"

# query metadata
vds_metadata = server.vizql.query_vds_metadata(
datasource_id=datasource_id
)
if vds_metadata:
print(vds_metadata)

# query data
vds_data = server.vizql.query_vds_data(
query=query_dict,
datasource_id=datasource_id
)
if vds_data:
print(vds_data)
if __name__ == "__main__":
main()
6 changes: 6 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[versioneer]
VCS = git
style = pep440-pre
versionfile_source = tableauserverclient/bin/_version.py
versionfile_build = tableauserverclient/bin/_version.py
tag_prefix = v
2 changes: 2 additions & 0 deletions tableauserverclient/server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
Views,
Webhooks,
Workbooks,
VizQL,
)

__all__ = [
Expand Down Expand Up @@ -91,4 +92,5 @@
"Views",
"Webhooks",
"Workbooks",
"VizQL",
]
3 changes: 2 additions & 1 deletion tableauserverclient/server/endpoint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from tableauserverclient.server.endpoint.virtual_connections_endpoint import VirtualConnections
from tableauserverclient.server.endpoint.webhooks_endpoint import Webhooks
from tableauserverclient.server.endpoint.workbooks_endpoint import Workbooks

from tableauserverclient.server.endpoint.vizql_endpoint import VizQL
__all__ = [
"Auth",
"CustomViews",
Expand Down Expand Up @@ -66,4 +66,5 @@
"VirtualConnections",
"Webhooks",
"Workbooks",
"VizQL",
]
31 changes: 16 additions & 15 deletions tableauserverclient/server/endpoint/endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def set_parameters(http_options, auth_token, content, content_type, parameters)
parameters["headers"][CONTENT_TYPE_HEADER] = content_type

Endpoint.set_user_agent(parameters)
if content is not None:
if content is not None and content_type != JSON_CONTENT_TYPE:
parameters["data"] = content
return parameters or {}

Expand All @@ -86,21 +86,20 @@ def set_user_agent(parameters):
# return explicitly for testing only
return parameters

def _blocking_request(self, method, url, parameters={}) -> Optional[Union["Response", Exception]]:
def _blocking_request(self, method, url, parameters={}, content=None, content_type=None) -> Optional[Union["Response", Exception]]:
response = None
logger.debug(f"[{datetime.timestamp()}] Begin blocking request to {url}")
try:
response = method(url, **parameters)
if content and content_type == JSON_CONTENT_TYPE:
response = method(url, json=content, **parameters)
else:
response = method(url, **parameters)
logger.debug(f"[{datetime.timestamp()}] Call finished")
except Exception as e:
logger.debug(f"Error making request to server: {e}")
raise e
return response

def send_request_while_show_progress_threaded(
self, method, url, parameters={}, request_timeout=None
) -> Optional[Union["Response", Exception]]:
return self._blocking_request(method, url, parameters)

def _make_request(
self,
Expand All @@ -116,17 +115,19 @@ def _make_request(
)

logger.debug(f"request method {method.__name__}, url: {url}")
if content:
redacted = helpers.strings.redact_xml(content[:200])
# this needs to be under a trace or something, it's a LOT
# logger.debug("request content: {}".format(redacted))

# a request can, for stuff like publishing, spin for ages waiting for a response.
# we need some user-facing activity so they know it's not dead.
request_timeout = self.parent_srv.http_options.get("timeout") or 0
server_response: Optional[Union["Response", Exception]] = self.send_request_while_show_progress_threaded(
method, url, parameters, request_timeout
)

if content and content_type == JSON_CONTENT_TYPE:
server_response: Optional[Union["Response", Exception]] = self._blocking_request(
method, url, parameters, content, content_type
)
else:
server_response: Optional[Union["Response", Exception]] = self._blocking_request(
method, url, parameters
)

logger.debug(f"[{datetime.timestamp()}] Async request returned: received {server_response}")
# is this blocking retry really necessary? I guess if it was just the threading messing it up?
if server_response is None:
Expand Down
2 changes: 1 addition & 1 deletion tableauserverclient/server/endpoint/metadata_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def query(self, query, variables=None, abort_on_error=False, parameters=None):
raise InvalidGraphQLQuery("Must provide a string")

# Setting content type because post_reuqest defaults to text/xml
server_response = self.post_request(url, graphql_query, content_type="application/json", parameters=parameters)
server_response = self.post_request(url, graphql_query, parameters=parameters)
results = server_response.json()

if abort_on_error and results.get("errors", None):
Expand Down
Loading