10000 Add custom session injection, Fix bug for http_options by jacalata · Pull Request #1119 · tableau/server-client-python · GitHub
[go: up one dir, main page]

Skip to content

Add custom session injection, Fix bug for http_options #1119

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

Merged
merged 5 commits into from
Sep 28, 2022
Merged
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
10 changes: 7 additions & 3 deletions contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,18 +66,22 @@ pytest
pip install .
```

### Debugging Tools
See what your outgoing requests look like: https://requestbin.net/ (unaffiliated link not under our control)


### Before Committing

Our CI runs include a Python lint run, so you should run this locally and fix complaints before committing as this will fail your checkin.

```shell
# this will run the formatter without making changes
black --line-length 120 tableauserverclient test samples --check
black . --check

# this will format the directory and code for you
black --line-length 120 tableauserverclient test samples
black .

# this will run type checking
pip install mypy
mypy --show-error-codes --disable-error-code misc --disable-error-code import tableauserverclient test
mypy tableauserverclient test samples
```
2 changes: 1 addition & 1 deletion samples/create_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def main():
logging.basicConfig(level=logging_level)

tableau_auth = TSC.PersonalAccessTokenAuth(args.token_name, args.token_value, site_id=args.site)
server = TSC.Server(args.server, use_server_version=True)
server = TSC.Server(args.server, use_server_version=True, http_options={"verify": False})
with server.auth.sign_in(tableau_auth):
# this code shows 3 different error codes that mean "resource is already in collection"
# 409009: group already exists on server
Expand Down
6 changes: 3 additions & 3 deletions samples/initialize_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@ def main():

# Create the site if it doesn't exist
if existing_site is None:
print("Site not found: {0} Creating it...").format(args.site_id)
print("Site not found: {0} Creating it...".format(args.site_id))
new_site = TSC.SiteItem(
name=args.site_id,
content_url=args.site_id.replace(" ", ""),
admin_mode=TSC.SiteItem.AdminMode.ContentAndUsers,
)
server.sites.create(new_site)
else:
print("Site {0} exists. Moving on...").format(args.site_id)
print("Site {0} exists. Moving on...".format(args.site_id))

################################################################################
# Step 3: Sign-in to our target site
Expand All @@ -87,7 +87,7 @@ def main():

# Create our project if it doesn't exist
if project is None:
print("Project not found: {0} Creating it...").format(args.project)
print("Project not found: {0} Creating it...".format(args.project))
new_project = TSC.ProjectItem(name=args.project)
project = server_upload.projects.create(new_project)

Expand Down
1 change: 1 addition & 0 deletions tableauserverclient/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
)
from .server import (
CSVRequestOptions,
ExcelRequestOptions,
ImageRequestOptions,
PDFRequestOptions,
RequestOptions,
Expand Down
4 changes: 0 additions & 4 deletions tableauserverclient/models/flow_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,6 @@ def description(self, value: str) -> None:
def project_name(self) -> Optional[str]:
return self._project_name

@property
def flow_type(self): # What is this? It doesn't seem to get set anywhere.
return self._flow_type

@property
def updated_at(self) -> Optional["datetime.datetime"]:
return self._updated_at
Expand Down
2 changes: 1 addition & 1 deletion tableauserverclient/models/permissions_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def from_response(cls, resp, ns=None) -> List["PermissionsRule"]:
mode = capability_xml.get("mode")

if name is None or mode is None:
logger.error("Capability was not valid: ", capability_xml)
logger.error("Capability was not valid: {}".format(capability_xml))
raise UnpopulatedPropertyError()
else:
capability_dict[name] = mode
Expand Down
5 changes: 3 additions & 2 deletions tableauserverclient/models/revision_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ def user_name(self) -> Optional[str]:

def __repr__(self):
return (
"<RevisionItem# revisionNumber={_revision_number} " "current={_current} deleted={_deleted} user={_user_id}>"
).format(**self.__dict__)
"<RevisionItem# revisionNumber={_revision_number} "
"current={_current} deleted={_deleted} user={_user_id}>".format(**self.__dict__)
)

@classmethod
def from_response(cls, resp: bytes, ns, resource_item) -> List["RevisionItem"]:
Expand Down
9 changes: 8 additions & 1 deletion tableauserverclient/models/server_info_item.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import warnings
import xml

from defusedxml.ElementTree import fromstring


Expand Down Expand Up @@ -32,7 +35,11 @@ def rest_api_version(self):

@classmethod
def from_response(cls, resp, ns):
parsed_response = fromstring(resp)
try:
parsed_response = fromstring(resp)
except xml.etree.ElementTree.ParseError as error:
warnings.warn("Unexpected response for ServerInfo: {}".format(resp))
return cls("Unknown", "Unknown", "Unknown")
product_version_tag = parsed_response.find(".//t:productVersion", namespaces=ns)
rest_api_version_tag = parsed_response.find(".//t:restApiVersion", namespaces=ns)

Expand Down
1 change: 0 additions & 1 deletion tableauserverclient/models/site_item.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import warnings
import xml.etree.ElementTree as ET

from distutils.version import Version
from defusedxml.ElementTree import fromstring
from .property_decorators import (
property_is_enum,
Expand Down
2 changes: 1 addition & 1 deletion tableauserverclient/models/tableau_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def credentials(self):
+"This method returns values to set as an attribute on the credentials element of the request"

def __repr__(self):
display = "All Credentials types must have a debug display that does not print secrets"
return "All Credentials types must have a debug display that does not print secrets"


def deprecate_site_attribute():
Expand Down
1 change: 1 addition & 0 deletions tableauserverclient/server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from .request_factory import RequestFactory
from .request_options import (
CSVRequestOptions,
ExcelRequestOptions,
ImageRequestOptions,
PDFRequestOptions,
RequestOptions,
Expand Down
2 changes: 1 addition & 1 deletion tableauserverclient/server/endpoint/databases_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def update_table_default_permissions(self, item):

@api(version="3.5")
def delete_table_default_permissions(self, item):
self._default_permissions.delete_default_permissions(item, Resource.Table)
self._default_permissions.delete_default_permission(item, Resource.Table)

@api(version="3.5")
def populate_dqw(self, item):
Expand Down
34 changes: 13 additions & 21 deletions tableauserverclient/server/endpoint/endpoint.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import requests
import logging
from distutils.version import LooseVersion as Version
from packaging.version import Version
from functools import wraps
from xml.etree.ElementTree import ParseError
from typing import Any, Callable, Dict, Optional, TYPE_CHECKING
Expand All @@ -11,9 +11,12 @@
NonXMLResponseError,
EndpointUnavailableError,
)
from .. import endpoint
from ..query import QuerySet
from ... import helpers
from ..._version import get_versions

__TSC_VERSION__ = get_versions()["version"]
del get_versions

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

Expand All @@ -22,34 +25,25 @@
XML_CONTENT_TYPE = "text/xml"
JSON_CONTENT_TYPE = "application/json"

USERAGENT_HEADER = "User-Agent"

if TYPE_CHECKING:
from ..server import Server
from requests import Response


_version_header: Optional[str] = None


class Endpoint(object):
def __init__(self, parent_srv: "Server"):
global _version_header
self.parent_srv = parent_srv

@staticmethod
def _make_common_headers(auth_token, content_type):
global _version_header

if not _version_header:
from ..server import __TSC_VERSION__

_version_header = __TSC_VERSION__

headers = {}
if auth_token is not None:
headers["x-tableau-auth"] = auth_token
if content_type is not None:
headers["content-type"] = content_type
headers["User-Agent"] = "Tableau Server Client/{}".format(_version_header)
headers["User-Agent"] = "Tableau Server Client/{}".format(__TSC_ F987 VERSION__)
return headers

def _make_request(
Expand All @@ -62,9 +56,9 @@ def _make_request(
parameters: Optional[Dict[str, Any]] = None,
) -> "Response":
parameters = parameters or {}
parameters.update(self.parent_srv.http_options)
if "headers" not in parameters:
parameters["headers"] = {}
parameters.update(self.parent_srv.http_options)
parameters["headers"].update(Endpoint._make_common_headers(auth_token, content_type))

if content is not None:
Expand All @@ -89,14 +83,12 @@ def _check_status(self, server_response, url: str = None):
if server_response.status_code >= 500:
raise InternalServerError(server_response, url)
elif server_response.status_code not in Success_codes:
# todo: is an error reliably of content-type application/xml?
try:
raise ServerResponseError.from_response(server_response.content, self.parent_srv.namespace, url)
except ParseError:
# This will happen if we get a non-success HTTP code that
# doesn't return an xml error object (like metadata endpoints or 503 pages)
# we convert this to a better exception and pass through the raw
# response body
# This will happen if we get a non-success HTTP code that doesn't return an xml error object
# e.g metadata endpoints, 503 pages, totally different servers
# we convert this to a better exception and pass through the raw response body
raise NonXMLResponseError(server_response.content)
except Exception:
# anything else re-raise here
Expand Down Expand Up @@ -194,7 +186,7 @@ def api(version):
def _decorator(func):
@wraps(func)
def wrapper(self, *args, **kwargs):
self.parent_srv.assert_at_least_version(version, "endpoint")
self.parent_srv.assert_at_least_version(version, self.__class__.__name__)
return func(self, *args, **kwargs)

return wrapper
Expand Down
21 changes: 17 additions & 4 deletions tableauserverclient/server/endpoint/server_info_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@


class ServerInfo(Endpoint):
def __init__(self, server):
self.parent_srv = server
self._info = None

@property
def serverInfo(self):
if not self._info:
self.get()
return self._info

def __repr__(self):
return "<Endpoint {}>".format(self.serverInfo)

@property
def baseurl(self):
return "{0}/serverInfo".format(self.parent_srv.baseurl)
Expand All @@ -23,10 +36,10 @@ def get(self):
server_response = self.get_unauthenticated_request(self.baseurl)
except ServerResponseError as e:
if e.code == "404003":
raise ServerInfoEndpointNotFoundError
raise ServerInfoEndpointNotFoundError(e)
if e.code == "404001":
raise EndpointUnavailableError
raise EndpointUnavailableError(e)
raise e

server_info = ServerInfoItem.from_response(server_response.content, self.parent_srv.namespace)
return server_info
self._info = ServerInfoItem.from_response(server_response.content, self.parent_srv.namespace)
return self._info
Loading
0