8000 repr improvements · cbini/server-client-python@4772fe4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 4772fe4

Browse files
LGraberjacalata
authored andcommitted
repr improvements
also: versioning for JWT, user-impersonation
1 parent f56b2c7 commit 4772fe4

File tree

10 files changed

+95
-70
lines changed

10 files changed

+95
-70
lines changed

tableauserverclient/__init__.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,47 @@
11
from ._version import get_versions
22
from .namespace import NEW_NAMESPACE as DEFAULT_NAMESPACE
3-
from .models import *
3+
from .models import (
4+
BackgroundJobItem,
5+
ColumnItem,
6+
ConnectionCredentials,
7+
ConnectionItem,
8+
CustomViewItem,
9+
DQWItem,
10+
DailyInterval,
11+
DataAlertItem,
12+
DatabaseItem,
13+
DatasourceItem,
14+
FavoriteItem,
15+
FlowItem,
16+
FlowRunItem,
17+
FileuploadItem,
18+
GroupItem,
19+
HourlyInterval,
20+
IntervalItem,
21+
JobItem,
22+
JWTAuth,
23+
MetricItem,
24+
MonthlyInterval,
25+
PaginationItem,
26+
Permission,
27+
PermissionsRule,
28+
PersonalAccessTokenAuth,
29+
ProjectItem,
30+
RevisionItem,
31+
ScheduleItem,
32+
SiteItem,
33+
ServerInfoItem,
34+
SubscriptionItem,
35+
TableItem,
36+
TableauAuth,
37+
Target,
38+
TaskItem,
39+
UserItem,
40+
ViewItem,
41+
WebhookItem,
42+
WeeklyInterval,
43+
WorkbookItem,
44+
)
445
from .server import (
546
CSVRequestOptions,
647
ExcelRequestOptions,

tableauserverclient/models/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from .site_item import SiteItem
3232
from .subscription_item import SubscriptionItem
3333
from .table_item import TableItem
34-
from .tableau_auth import Credentials, TableauAuth, PersonalAccessTokenAuth
34+
from .tableau_auth import Credentials, TableauAuth, PersonalAccessTokenAuth, JWTAuth
3535
from .tableau_types import Resource, TableauItem, plural_type
3636
from .tag_item import TagItem
3737
from .target import Target

tableauserverclient/models/group_item.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,9 @@ def __init__(self, name=None, domain_name=None) -> None:
2626
self.name: Optional[str] = name
2727
self.domain_name: Optional[str] = domain_name
2828

29-
def __str__(self):
29+
def __repr__(self):
3030
return "{}({!r})".format(self.__class__.__name__, self.__dict__)
3131

32-
__repr__ = __str__
33-
3432
@property
3533
def domain_name(self) -> Optional[str]:
3634
return self._domain_name

tableauserverclient/models/permissions_item.py

Lines changed: 1 addition & 3 deletions
F438
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,9 @@ def __init__(self, grantee: ResourceReference, capabilities: Dict[str, str]) ->
4545
self.grantee = grantee
4646
self.capabilities = capabilities
4747

48-
def __str__(self):
48+
def __repr__(self):
4949
return "<PermissionsRule grantee={}, capabilities={}>".format(self.grantee, self.capabilities)
5050

51-
__repr__ = __str__
52-
5351
@classmethod
5452
def from_response(cls, resp, ns=None) -> List["PermissionsRule"]:
5553
parsed_response = fromstring(resp)

tableauserverclient/models/server_info_item.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def __init__(self, product_version, build_number, rest_api_version):
1212
self._build_number = build_number
1313
self._rest_api_version = rest_api_version
1414

15-
def __str__(self):
15+
def __repr__(self):
1616
return (
1717
"ServerInfoItem: [product version: "
1818
+ self._product_version

tableauserverclient/models/site_item.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ class SiteItem(object):
2626
_tier_explorer_capacity: Optional[int] = None
2727
_tier_viewer_capacity: Optional[int] = None
2828

29-
def __str__(self):
29+
# TODO should have a debug representation that just lists all the attributes?
30+
def __repr__(self):
3031
return (
3132
"<"
3233
+ __name__

tableauserverclient/models 10000 /tableau_auth.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ def credentials(self):
4343
return {"name": self.username, "password": self.password}
4444

4545
def __repr__(self):
46-
return "<Credentials username={} password={}>".format(self.username, "<redacted>")
46+
if self.user_id_to_impersonate:
47+
uid = f", user_id_to_impersonate=f{self.user_id_to_impersonate}"
48+
else:
49+
uid = ""
50+
return f"<Credentials username={self.username} password=redacted (site={self.site_id}{uid})>"
4751

4852
@property
4953
def site(self):
@@ -56,6 +60,7 @@ def site(self, value):
5660
self.site_id = value
5761

5862

63+
# A Tableau-generated Personal Access Token
5964
class PersonalAccessTokenAuth(Credentials):
6065
def __init__(self, token_name, personal_access_token, site_id=None, user_id_to_impersonate=None):
6166
if personal_access_token is None or token_name is None:
@@ -72,13 +77,18 @@ def credentials(self):
7277
}
7378

7479
def __repr__(self):
75-
return "<PersonalAccessToken name={} token={}>(site={})".format(
76-
self.token_name, self.personal_access_token[:2] + "...", self.site_id
77-
)
80+
81+
if self.user_id_to_impersonate:
82+
uid = f", user_id_to_impersonate=f{self.user_id_to_impersonate}"
83+
else:
84+
uid = ""
85+
return f"<PersonalAccessToken name={self.token_name} token={self.personal_access_token[:2]}..." \
86+
f"(site={self.site_id}{uid} >"
7887

7988

89+
# A standard JWT generated specifically for Tableau
8090
class JWTAuth(Credentials):
81-
def __init__(self, jwt=None, site_id=None, user_id_to_impersonate=None):
91+
def __init__(self, jwt: str, site_id=None, user_id_to_impersonate=None):
8292
if jwt is None:
8393
raise TabError("Must provide a JWT token when using JWT authentication")
8494
super().__init__(site_id, user_id_to_impersonate)
@@ -93,4 +103,4 @@ def __repr__(self):
93103
uid = f", user_id_to_impersonate=f{self.user_id_to_impersonate}"
94104
else:
95105
uid = ""
96-
return f"<{self.__class__.__qualname__}(jwt={self.jwt[:5]}..., site_id={self.site_id}{uid})>"
106+
return f"<{self.__class__.__qualname__} jwt={self.jwt[:5]}... (site={self.site_id}{uid})>"

tableauserverclient/server/endpoint/auth_endpoint.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,14 @@ def sign_in(self, auth_req: "Credentials") -> contextmgr:
6666
logger.info("Signed into {0} as user with id {1}".format(self.parent_srv.server_address, user_id))
6767
return Auth.contextmgr(self.sign_out)
6868

69+
# We use the same request that username/password login uses for all auth types.
70+
# The distinct methods are mostly useful for explicitly showing api version support for each auth type
6971
@api(version="3.6")
7072
def sign_in_with_personal_access_token(self, auth_req: "Credentials") -> contextmgr:
71-
# We use the same request that username/password login uses.
73+
return self.sign_in(auth_req)
74+
75+
@api(version="3.17")
76+
def sign_in_with_json_web_token(self, auth_req: "Credentials") -> contextmgr:
7277
return self.sign_in(auth_req)
7378

7479
@api(version="2.0")

test/models/_models.py

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,59 @@
11
from tableauserverclient import *
22

3-
# mmm. why aren't these available in the tsc namespace?
3+
# TODO why aren't these available in the tsc namespace? Probably a bug.
44
from tableauserverclient.models import (
55
DataAccelerationReportItem,
6-
FavoriteItem,
76
Credentials,
87
ServerInfoItem,
98
Resource,
109
TableauItem,
11-
plural_type,
1210
)
1311

1412

1513
def get_defined_models():
16-
# not clever: copied from tsc/models/__init__.py
14+
# nothing clever here: list was manually copied from tsc/models/__init__.py
1715
return [
18-
ColumnItem,
19-
ConnectionCredentials,
2016
ConnectionItem,
21-
DataAccelerationReportItem,
2217
DataAlertItem,
23-
DatabaseItem,
2418
DatasourceItem,
25-
DQWItem,
26-
UnpopulatedPropertyError,
27-
FavoriteItem,
2819
FlowItem,
29-
FlowRunItem,
3020
GroupItem,
31-
IntervalItem,
32-
DailyInterval,
33-
WeeklyInterval,
34-
MonthlyInterval,
35-
HourlyInterval,
3621
JobItem,
37-
BackgroundJobItem,
3822
MetricItem,
39-
PaginationItem,
4023
PermissionsRule,
41-
Permission,
4224
ProjectItem,
4325
RevisionItem,
4426
ScheduleItem,
45-
ServerInfoItem,
46-
SiteItem,
4727
SubscriptionItem,
48-
TableItem,
4928
Credentials,
29+
JWTAuth,
5030
TableauAuth,
5131
PersonalAccessTokenAuth,
52-
Resource,
53-
TableauItem,
54-
plural_type,
55-
Target,
32+
ServerInfoItem,
33+
SiteItem,
5634
TaskItem,
5735
UserItem,
5836
ViewItem,
5937
WebhookItem,
6038
WorkbookItem,
6139
]
40+
41+
42+
# manually identified. As these are implemented, they should move to the other list.
43+
def get_unimplemented_models():
44+
return [
45+
BackgroundJobItem,
46+
DataAccelerationReportItem,
47+
FavoriteItem,
48+
IntervalItem,
49+
DailyInterval,
50+
WeeklyInterval,
51+
MonthlyInterval,
52+
HourlyInterval,
53+
PaginationItem,
54+
Permission,
55+
Resource,
56+
TableItem,
57+
TableauItem,
58+
Target
59+
]

test/models/test_repr.py

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,11 @@
1-
import pytest
2-
31
from unittest import TestCase
42
import _models
53

64

75
# ensure that all models have a __repr__ method implemented
86
class TestAllModels(TestCase):
97

10-
"""
11-
ColumnItem wrapper_descriptor
12-
ConnectionCredentials wrapper_descriptor
13-
DataAccelerationReportItem wrapper_descriptor
14-
DatabaseItem wrapper_descriptor
15-
DQWItem wrapper_descriptor
16-
UnpopulatedPropertyError wrapper_descriptor
17-
FavoriteItem wrapper_descriptor
18-
FlowRunItem wrapper_descriptor
19-
IntervalItem wrapper_descriptor
20-
DailyInterval wrapper_descriptor
21-
WeeklyInterval wrapper_descriptor
22-
MonthlyInterval wrapper_descriptor
23-
HourlyInterval wrapper_descriptor
24-
BackgroundJobItem wrapper_descriptor
25-
PaginationItem wrapper_descriptor
26-
Permission wrapper_descriptor
27-
ServerInfoItem wrapper_descriptor
28-
SiteItem wrapper_descriptor
29-
TableItem wrapper_descriptor
30-
Resource wrapper_descriptor
31-
"""
32-
338
# not all models have __repr__ yet: see above list
34-
@pytest.mark.xfail()
359
def test_repr_is_implemented(self):
3610
m = _models.get_defined_models()
3711
for model in m:

0 commit comments

Comments
 (0)
0