8000 Squashed commit of the following: · jorwoods/server-client-python@53be5b7 · GitHub
[go: up one dir, main page]

Skip to content

Commit 53be5b7

Browse files
jorwoodsJordan Woods
authored and
Jordan Woods
committed
Squashed commit of the following:
commit ad57851 Author: jorwoods <jorwoods@users.noreply.github.com> Date: Thu Nov 11 15:55:01 2021 -0600 Jorwoods/type hint revisions (tableau#956) Add type hints and tests for workbook and data source revisions commit bb0feb8 Author: elsherif <selsherif@tableau.com> Date: Wed Nov 10 01:21:16 2021 +0100 Add support to datasource and workbook revisions (tableau#931) * add support to datasource and workbook revisions (get all item revisions / downlad item by revision number) * add type hints * remove wrong type hints commit 31d420f Author: Stephen Mitchell <scum@mac.com> Date: Tue Nov 9 01:02:14 2021 -0500 Clean up hidden_views by making it an attribute of WorkbookItem (tableau#617) * Clean up hidden views and make it an attribute of workbookitem * Pycodestyle error fix for one letter variable Co-authored-by: Jac <jacalata@users.noreply.github.com> Co-authored by: Stephen Mitchell https://github.com/scuml commit 7443f68 Author: jorwoods <jorwoods@users.noreply.github.com> Date: Sun Nov 7 05:57:42 2021 -0600 Add type hints to `Job` (tableau#939) commit c8170ae Author: Tyler Doyle <doyle.tyler@gmail.com> Date: Mon Nov 1 20:36:17 2021 -0400 Fix slack once and for all (tableau#946) The red X keeps coming back so I'd like to mark this as allowably fail-able -- that way the innards of Slack/OAuth/Tableau credentials don't keep polluting test run reports :)
1 parent 3067923 commit 53be5b7

17 files changed

+496
-94
lines changed

.github/workflows/slack.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ jobs:
99
name: Sends a message to Slack when a push, a pull request or an issue is made
1010
steps:
1111
- name: Send message to Slack API
12+
continue-on-error: true
1213
uses: archive/github-actions-slack@v2.2.2
1314
id: notify
1415
with:

tableauserverclient/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
FlowItem,
3535
WebhookItem,
3636
PersonalAccessTokenAuth,
37-
FlowRunItem
37+
FlowRunItem,
38+
RevisionItem
3839
)
3940
from .server import (
4041
RequestOptions,

tableauserverclient/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,4 @@
3636
from .permissions_item import PermissionsRule, Permission
3737
from .webhook_item import WebhookItem
3838
from .personal_access_token_auth import PersonalAccessTokenAuth
39+
from .revision_item import RevisionItem

tableauserverclient/models/datasource_item.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
if TYPE_CHECKING:
1515
from .permissions_item import PermissionsRule
1616
from .connection_item import ConnectionItem
17+
from .revision_item import RevisionItem
1718
import datetime
1819

1920

@@ -37,6 +38,7 @@ def __init__(self, project_id: str, name: str = None) -> None:
3738
self._id: Optional[str] = None
3839
self._initial_tags: Set = set()
3940
self._project_name: Optional[str] = None
41+
self._revisions = None
4042
self._updated_at = None
4143
self._use_remote_query_agent = None
4244
self._webpage_url = None
@@ -166,6 +168,13 @@ def use_remote_query_agent(self, value: bool):
166168
def webpage_url(self) -> < F438 span class=pl-v>Optional[str]:
167169
return self._webpage_url
168170

171+
@property
172+
def revisions(self) -> List["RevisionItem"]:
173+
if self._revisions is None:
174+
error = "Datasource item must be populated with revisions first."
175+
raise UnpopulatedPropertyError(error)
176+
return self._revisions()
177+
169178
def _set_connections(self, connections):
170179
self._connections = connections
171180

@@ -175,6 +184,9 @@ def _set_permissions(self, permissions):
175184
def _set_data_quality_warnings(self, dqws):
176185
self._data_quality_warnings = dqws
177186

187+
def _set_revisions(self, revisions):
188+
self._revisions = revisions
189+
178190
def _parse_common_elements(self, datasource_xml, ns):
179191
if not isinstance(datasource_xml, ET.Element):
180192
datasource_xml = ET.fromstring(datasource_xml).find(".//t:datasource", namespaces=ns)

tableauserverclient/models/job_item.py

Lines changed: 56 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,35 @@
33
from ..datetime_helpers import parse_datetime
44

55

6+
from typing import List, Optional, TYPE_CHECKING
7+
8+
if TYPE_CHECKING:
9+
import datetime
10+
11+
612
class JobItem(object):
713
class FinishCode:
814
"""
915
Status codes as documented on
1016
https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_jobs_tasks_and_schedules.htm#query_job
1117
"""
1218

13-
Success = 0
14-
Failed = 1
15-
Cancelled = 2
19+
Success: int = 0
20+
Failed: int = 1
21+
Cancelled: int = 2
1622

1723
def __init__(
1824
self,
19-
id_,
20-
job_type,
21-
progress,
22-
created_at,
23-
started_at=None,
24-
completed_at=None,
25-
finish_code=0,
26-
notes=None,
27-
mode=None,
28-
flow_run=None,
25+
id_: str,
26+
job_type: str,
27+
progress: str,
28+
created_at: "datetime.datetime",
29+
started_at: Optional["datetime.datetime"] = None,
30+
completed_at: Optional["datetime.datetime"] = None,
31+
finish_code: int = 0,
32+
notes: Optional[List[str]] = None,
33+
mode: Optional[str] = None,
34+
flow_run: Optional[FlowRunItem] = None
2935
):
3036
self._id = id_
3137
self._type = job_type
@@ -34,48 +40,48 @@ def __init__(
3440
self._started_at = started_at
3541
self._completed_at = completed_at
3642
self._finish_code = finish_code
37-
self._notes = notes or []
43+
self._notes: List[str] = notes or []
3844
self._mode = mode
3945
self._flow_run = flow_run
4046

4147
@property
42-
def id(self):
48+
def id(self) -> str:
4349
return self._id
4450

4551
@property
46-
def type(self):
52+
def type(self) -> str:
4753
return self._type
4854

4955
@property
50-
def progress(self):
56+
def progress(self) -> str:
5157
return self._progress
5258

5359
@property
54-
def created_at(self):
60+
def created_at(self) -> "datetime.datetime":
5561
return self._created_at
5662

5763
@property
58-
def started_at(self):
64+
def started_at(self) -> Optional["datetime.datetime"]:
5965
return self._started_at
6066

6167
@property
62-
def completed_at(self):
68+
def completed_at(self) -> Optional["datetime.datetime"]:
6369
return self._completed_at
6470

6571
@property
66-
def finish_code(self):
72+
def finish_code(self) -> int:
6773
return self._finish_code
6874

6975
@property
70-
def notes(self):
76+
def notes(self) -> List[str]:
7177
return self._notes
7278

7379
@property
74-
def mode(self):
80+
def mode(self) -> Optional[str]:
7581
return self._mode
7682

7783
@mode.setter
78-
def mode(self, value):
84+
def mode(self, value: str) -> None:
7985
# check for valid data here
8086
self._mode = value
8187

@@ -94,7 +100,7 @@ def __repr__(self):
94100
)
95101

96102
@classmethod
97-
def from_response(cls, xml, ns):
103+
def from_response(cls, xml, ns) -> List["JobItem"]:
98104
parsed_response = ET.fromstring(xml)
99105
all_tasks_xml = parsed_response.findall(".//t:job", namespaces=ns)
100106

@@ -136,23 +142,23 @@ def _parse_element(cls, element, ns):
136142

137143
class BackgroundJobItem(object):
138144
class Status:
139-
Pending = "Pending"
140-
InProgress = "InProgress"
141-
Success = "Success"
142-
Failed = "Failed"
143-
Cancelled = "Cancelled"
145+
Pending: str = "Pending"
146+
InProgress: str = "InProgress"
147+
Success: str = "Success"
148+
Failed: str = "Failed"
149+
Cancelled: str = "Cancelled"
144150

145151
def __init__(
146152
self,
147-
id_,
148-
created_at,
149-
priority,
150-
job_type,
151-
status,
152-
title=None,
153-
subtitle=None,
154-
started_at=None,
155-
ended_at=None,
153+
id_: str,
154+
created_at: "datetime.datetime",
155+
priority: int,
156+
job_type: str,
157+
status: str,
158+
title: Optional[str] = None,
159+
subtitle: Optional[str] = None,
160+
started_at: Optional["datetime.datetime"] = None,
161+
ended_at: Optional["datetime.datetime"] = None,
156162
):
157163
self._id = id_
158164
self._type = job_type
@@ -165,49 +171,49 @@ def __init__(
165171
self._subtitle = subtitle
166172

167173
@property
168-
def id(self):
174+
def id(self) -> str:
169175
return self._id
170176

171177
@property
172-
def name(self):
178+
def name(self) -> Optional[str]:
173179
"""For API consistency - all other resource endpoints have a name attribute which is used to display what
174180
they are. Alias title as name to allow consistent handling of resources in the list sample."""
175181
return self._title
176182

177183
@property
178-
def status(self):
184+
def status(self) -> str:
179185
return self._status
180186

181187
@property
182-
def type(self):
188+
def type(self) -> str:
183189
return self._type
184190

185191
@property
186-
def created_at(self):
192+
def created_at(self) -> "datetime.datetime":
187193
return self._created_at
188194

189195
@property
190-
def started_at(self):
196+
def started_at(self) -> Optional["datetime.datetime"]:
191197
return self._started_at
192198

193199
@property
194-
def ended_at(self):
200+
def ended_at(self) -> Optional["datetime.datetime"]:
195201
return self._ended_at
196202

197203
@property
198-
def title(self):
204+
def title(self) -> Optional[str]:
199205
return self._title
200206

201207
@property
202-
def subtitle(self):
208+
def subtitle(self) -> Optional[str]:
203209
return self._subtitle
204210

205211
@property
206-
def priority(self):
212+
def priority(self) -> int:
207213
return self._priority
208214

209215
@classmethod
210-
def from_response(cls, xml, ns):
216+
def from_response(cls, xml, ns) -> List["BackgroundJobItem"]:
211217
parsed_response = ET.fromstring(xml)
212218
all_tasks_xml = parsed_response.findall(".//t:backgroundJob", namespaces=ns)
213219
return [cls._parse_element(x, ns) for x in all_tasks_xml]
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import xml.etree.ElementTree as ET
2+
from ..datetime_helpers import parse_datetime
3+
from typing import List, Optional, TYPE_CHECKING, Type
4+
5+
if TYPE_CHECKING:
6+
from datetime import datetime
7+
8+
class RevisionItem(object):
9+
def __init__(self):
10+
self._resource_id: Optional[str] = None
11+
self._resource_name: Optional[str] = None
12+
self._revision_number: Optional[str] = None
13+
self._current: Optional[bool] = None
14+
self._deleted: Optional[bool] = None
15+
self._created_at: Optional["datetime"] = None
16+
self._user_id: Optional[str] = None
17+
self._user_name: Optional[str] = None
18+
19+
@property
20+
def resource_id(self) -> Optional[str]:
21+
return self._resource_id
22+
23+
@property
24+
def resource_name(self) -> Optional[str]:
25+
return self._resource_name
26+
27+
@property
28+
def revision_number(self) -> Optional[str]:
29+
return self._revision_number
30+
31+
@property
32+
def current(self) -> Optional[bool]:
33+
return self._current
34+
35+
@property
36+
def deleted(self) -> Optional[bool]:
37+
return self._deleted
38+
39+
@property
40+
def created_at(self) -> Optional["datetime"]:
41+
return self._created_at
42+
43+
@property
44+
def user_id(self) -> Optional[str]:
45+
return self._user_id
46+
47+
@property
48+
def user_name(self) -> Optional[str]:
49+
return self._user_name
50+
51+
def __repr__(self):
52+
return (
53+
"<RevisionItem# revisionNumber={_revision_number} "
54+
"current={_current} deleted={_deleted} user={_user_id}>".format(**self.__dict__)
55+
)
56+
57+
@classmethod
58+
def from_response(
59+
cls,
60+
resp: bytes,
61+
ns,
62+
resource_item
63+
) -> List["RevisionItem"]:
64+
all_revision_items = list()
65+
parsed_response = ET.fromstring(resp)
66+
all_revision_xml = parsed_response.findall(".//t:revision", namespaces=ns)
67+
for revision_xml in all_revision_xml:
68+
revision_item = cls()
69+
revision_item._resource_id = resource_item.id
70+
revision_item._resource_name = resource_item.name
71+
revision_item._revision_number = revision_xml.get("revisionNumber", None)
72+
revision_item._current = string_to_bool(revision_xml.get("isCurrent", ""))
73+
revision_item._deleted = string_to_bool(revision_xml.get("isDeleted", ""))
74+
revision_item._created_at = parse_datetime(revision_xml.get("createdAt", None))
75+
for user in revision_xml.findall('.//t:user', namespaces=ns):
76+
revision_item._user_id = user.get("id", None)
77+
revision_item._user_name = user.get("name", None)
78+
79+
all_revision_items.append(revision_item)
80+
return all_revision_items
81+
82+
83+
# Used to convert string represented boolean to a boolean type
84+
def string_to_bool(s: str) -> bool:
85+
return s.lower() == "true"

0 commit comments

Comments
 (0)
0