Skip to content

Commit 4873a58

Browse files
jacalataMrwanBaghdadjorwoodsbcantoniTrimPeachu
authored
Patch fix: 0.23.1 (#1129)
* Allow injection of session_factory to allow use of a custom session * Jac/show server info (#1118) * Fix bug in exposing ExcelRequestOptions and test (#1123) * Fix a few pylint errors (#1124) * fix behavior when url has no protocol (#1125) * smoke test for pypi * Add permission control for Data Roles and Metrics (Issue #1063) (#1120) Co-authored-by: Marwan Baghdad <[email protected]> Co-authored-by: jorwoods <[email protected]> Co-authored-by: Brian Cantoni <[email protected]> Co-authored-by: TrimPeachu <[email protected]>
1 parent ef9e7fd commit 4873a58

File tree

12 files changed

+156
-31
lines changed

12 files changed

+156
-31
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# This workflow will install TSC from pypi and validate that it runs. For more information see:
2+
# https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
3+
4+
name: Pypi smoke tests
5+
6+
on:
7+
workflow_dispatch:
8+
schedule:
9+
- cron: 0 11 * * * # Every day at 11AM UTC (7AM EST)
10+
11+
permissions:
12+
contents: read
13+
14+
jobs:
15+
build:
16+
strategy:
17+
fail-fast: false
18+
matrix:
19+
os: [ubuntu-latest, macos-latest, windows-latest]
20+
python-version: ['3.x']
21+
22+
runs-on: ${{ matrix.os }}
23+
24+
steps:
25+
- name: Set up Python ${{ matrix.python-version }} on ${{ matrix.os }}
26+
uses: actions/setup-python@v4
27+
with:
28+
python-version: ${{ matrix.python-version }}
29+
- name: pip install
30+
run: |
31+
pip uninstall tableauserverclient
32+
pip install tableauserverclient
33+
- name: Launch app
34+
run: |
35+
python -c "import tableauserverclient as TSC
36+
server = TSC.Server('http://example.com', use_server_version=False)"

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2016 Tableau
3+
Copyright (c) 2022 Tableau
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

samples/smoke_test.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# This sample verifies that tableau server client is installed
2+
# and you can run it. It also shows the version of the client.
3+
4+
import tableauserverclient as TSC
5+
6+
server = TSC.Server("Fake-Server-Url", use_server_version=False)
7+
print("Client details:")
8+
print(TSC.server.endpoint.Endpoint._make_common_headers("fake-token", "any-content"))

tableauserverclient/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from ._version import get_versions
12
from .namespace import NEW_NAMESPACE as DEFAULT_NAMESPACE
23
from .models import (
34
BackgroundJobItem,

tableauserverclient/models/tableau_auth.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,6 @@ def credentials(self):
6565
}
6666

6767
def __repr__(self):
68-
return "<PersonalAccessToken name={} token={} site={}>".format(
68+
return "<PersonalAccessToken name={} token={}>(site={})".format(
6969
self.token_name, self.personal_access_token[:2] + "...", self.site_id
7070
)

tableauserverclient/models/tableau_types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111

1212
class Resource:
1313
Database = "database"
14+
Datarole = "datarole"
1415
Datasource = "datasource"
1516
Flow = "flow"
1617
Lens = "lens"
18+
Metric = "metric"
1719
Project = "project"
1820
Table = "table"
1921
View = "view"

tableauserverclient/server/endpoint/auth_endpoint.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,18 @@ def baseurl(self):
2828
def sign_in(self, auth_req):
2929
url = "{0}/{1}".format(self.baseurl, "signin")
3030
signin_req = RequestFactory.Auth.signin_req(auth_req)
31-
server_response = self.parent_srv.session.post(url, data=signin_req, **self.parent_srv.http_options)
31+
server_response = self.parent_srv.session.post(
32+
url, data=signin_req, **self.parent_srv.http_options, allow_redirects=False
33+
)
34+
# manually handle a redirect so that we send the correct POST request instead of GET
35+
# this will make e.g http://online.tableau.com work to redirect to http://east.online.tableau.com
36+
if server_response.status_code == 301:
37+
server_response = self.parent_srv.session.post(
38+
server_response.headers["Location"],
39+
data=signin_req,
40+
**self.parent_srv.http_options,
41+
allow_redirects=False,
42+
)
3243
self.parent_srv._namespace.detect(server_response.content)
3344
self._check_status(server_response, url)
3445
parsed_response = fromstring(server_response.content)

tableauserverclient/server/endpoint/endpoint.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
EndpointUnavailableError,
1313
)
1414
from ..query import QuerySet
15-
from ... import helpers
16-
from ..._version import get_versions
15+
from ... import helpers, get_versions
1716

18-
__TSC_VERSION__ = get_versions()["version"]
19-
del get_versions
17+
if TYPE_CHECKING:
18+
from ..server import Server
19+
from requests import Response
2020

2121
logger = logging.getLogger("tableau.endpoint")
2222

@@ -25,11 +25,9 @@
2525
XML_CONTENT_TYPE = "text/xml"
2626
JSON_CONTENT_TYPE = "application/json"
2727

28-
USERAGENT_HEADER = "User-Agent"
29-
30-
if TYPE_CHECKING:
31-
from ..server import Server
32-
from requests import Response
28+
CONTENT_TYPE_HEADER = "content-type"
29+
TABLEAU_AUTH_HEADER = "x-tableau-auth"
30+
USER_AGENT_HEADER = "User-Agent"
3331

3432

3533
class Endpoint(object):
@@ -38,12 +36,13 @@ def __init__(self, parent_srv: "Server"):
3836

3937
@staticmethod
4038
def _make_common_headers(auth_token, content_type):
39+
_client_version: Optional[str] = get_versions()["version"]
4140
headers = {}
4241
if auth_token is not None:
43-
headers["x-tableau-auth"] = auth_token
42+
headers[TABLEAU_AUTH_HEADER] = auth_token
4443
if content_type is not None:
45-
headers["content-type"] = content_type
46-
headers["User-Agent"] = "Tableau Server Client/{}".format(__TSC_VERSION__)
44+
headers[CONTENT_TYPE_HEADER] = content_type
45+
headers[USER_AGENT_HEADER] = "Tableau Server Client/{}".format(_client_version)
4746
return headers
4847

4948
def _make_request(

tableauserverclient/server/endpoint/projects_endpoint.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,14 @@ def populate_workbook_default_permissions(self, item):
9999
def populate_datasource_default_permissions(self, item):
100100
self._default_permissions.populate_default_permissions(item, Resource.Datasource)
101101

102+
@api(version="3.2")
103+
def populate_metric_default_permissions(self, item):
104+
self._default_permissions.populate_default_permissions(item, Resource.Metric)
105+
106+
@api(version="3.4")
107+
def populate_datarole_default_permissions(self, item):
108+
self._default_permissions.populate_default_permissions(item, Resource.Datarole)
109+
102110
@api(version="3.4")
103111
def populate_flow_default_permissions(self, item):
104112
self._default_permissions.populate_default_permissions(item, Resource.Flow)
@@ -115,6 +123,14 @@ def update_workbook_default_permissions(self, item, rules):
115123
def update_datasource_default_permissions(self, item, rules):
116124
return self._default_permissions.update_default_permissions(item, rules, Resource.Datasource)
117125

126+
@api(version="3.2")
127+
def update_metric_default_permissions(self, item, rules):
128+
return self._default_permissions.update_default_permissions(item, rules, Resource.Metric)
129+
130+
@api(version="3.4")
131+
def update_datarole_default_permissions(self, item, rules):
132+
return self._default_permissions.update_default_permissions(item, rules, Resource.Datarole)
133+
118134
@api(version="3.4")
119135
def update_flow_default_permissions(self, item, rules):
120136
return self._default_permissions.update_default_permissions(item, rules, Resource.Flow)
@@ -131,6 +147,14 @@ def delete_workbook_default_permissions(self, item, rule):
131147
def delete_datasource_default_permissions(self, item, rule):
132148
self._default_permissions.delete_default_permission(item, rule, Resource.Datasource)
133149

150+
@api(version="3.2")
151+
def delete_metric_default_permissions(self, item, rule):
152+
self._default_permissions.delete_default_permission(item, rule, Resource.Metric)
153+
154+
@api(version="3.4")
155+
def delete_datarole_default_permissions(self, item, rule):
156+
self._default_permissions.delete_default_permission(item, rule, Resource.Datarole)
157+
134158
@api(version="3.4")
135159
def delete_flow_default_permissions(self, item, rule):
136160
self._default_permissions.delete_default_permission(item, rule, Resource.Flow)

tableauserverclient/server/server.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import logging
12
import warnings
23

34
import requests
45
import urllib3
56

6-
from defusedxml.ElementTree import fromstring
7+
from defusedxml.ElementTree import fromstring, ParseError
78
from packaging.version import Version
89
from .endpoint import (
910
Sites,
@@ -61,7 +62,7 @@ def __init__(self, server_address, use_server_version=False, http_options=None,
6162
self._site_id = None
6263
self._user_id = None
6364

64-
self._server_address = server_address
65+
self._server_address: str = server_address
6566
self._session_factory = session_factory or requests.session
6667

6768
self.auth = Auth(self)
@@ -103,10 +104,13 @@ def __init__(self, server_address, use_server_version=False, http_options=None,
103104

104105
def validate_server_connection(self):
105106
try:
106-
self._session.prepare_request(requests.Request("GET", url=self._server_address, params=self._http_options))
107+
if not self._server_address.startswith("http://") and not self._server_address.startswith("https://"):
108+
self._server_address = "http://" + self._server_address
109+
self._session.prepare_request(
110+
requests.Request("GET", url=self._server_address, params=self._http_options)
111+
)
107112
except Exception as req_ex:
108-
warnings.warn("Invalid server initialization\n {}".format(req_ex.__str__()), UserWarning)
109-
print("==================")
113+
raise ValueError("Invalid server initialization", req_ex)
110114

111115
def __repr__(self):
112116
return "<TableauServerClient> [Connection: {}, {}]".format(self.baseurl, self.server_info.serverInfo)
@@ -140,7 +144,13 @@ def _set_auth(self, site_id, user_id, auth_token):
140144

141145
def _get_legacy_version(self):
142146
response = self._session.get(self.server_address + "/auth?format=xml")
143-
info_xml = fromstring(response.content)
147+
try:
148+
info_xml = fromstring(response.content)
149+
except ParseError as parseError:
150+
logging.getLogger("TSC.server").info(
151+
"Could not read server version info. The server may not be running or configured."
152+
)
153+
return self.version
144154
prod_version = info_xml.find(".//product_version").text
145155
version = _PRODUCT_TO_REST_VERSION.get(prod_version, "2.1") # 2.1
146156
return version
@@ -164,8 +174,6 @@ def use_server_version(self):
164174

165175
def use_highest_version(self):
166176
self.use_server_version()
167-
import warnings
168-
169177
warnings.warn("use use_server_version instead", DeprecationWarning)
170178

171179
def check_at_least_version(self, target: str):

0 commit comments

Comments
 (0)