tableauserverclient 0.32__py3-none-any.whl → 0.34__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. tableauserverclient/__init__.py +34 -18
  2. tableauserverclient/_version.py +3 -3
  3. tableauserverclient/config.py +20 -6
  4. tableauserverclient/models/__init__.py +12 -0
  5. tableauserverclient/models/column_item.py +1 -1
  6. tableauserverclient/models/connection_credentials.py +1 -1
  7. tableauserverclient/models/connection_item.py +10 -8
  8. tableauserverclient/models/custom_view_item.py +29 -6
  9. tableauserverclient/models/data_acceleration_report_item.py +2 -2
  10. tableauserverclient/models/data_alert_item.py +5 -5
  11. tableauserverclient/models/data_freshness_policy_item.py +6 -6
  12. tableauserverclient/models/database_item.py +8 -2
  13. tableauserverclient/models/datasource_item.py +10 -10
  14. tableauserverclient/models/dqw_item.py +1 -1
  15. tableauserverclient/models/favorites_item.py +5 -6
  16. tableauserverclient/models/fileupload_item.py +1 -1
  17. tableauserverclient/models/flow_item.py +12 -12
  18. tableauserverclient/models/flow_run_item.py +3 -3
  19. tableauserverclient/models/group_item.py +4 -4
  20. tableauserverclient/models/groupset_item.py +53 -0
  21. tableauserverclient/models/interval_item.py +36 -23
  22. tableauserverclient/models/job_item.py +26 -10
  23. tableauserverclient/models/linked_tasks_item.py +102 -0
  24. tableauserverclient/models/metric_item.py +5 -5
  25. tableauserverclient/models/pagination_item.py +1 -1
  26. tableauserverclient/models/permissions_item.py +19 -14
  27. tableauserverclient/models/project_item.py +35 -19
  28. tableauserverclient/models/property_decorators.py +12 -11
  29. tableauserverclient/models/reference_item.py +2 -2
  30. tableauserverclient/models/revision_item.py +3 -3
  31. tableauserverclient/models/schedule_item.py +2 -2
  32. tableauserverclient/models/server_info_item.py +26 -6
  33. tableauserverclient/models/site_item.py +69 -3
  34. tableauserverclient/models/subscription_item.py +3 -3
  35. tableauserverclient/models/table_item.py +1 -1
  36. tableauserverclient/models/tableau_auth.py +115 -5
  37. tableauserverclient/models/tableau_types.py +11 -9
  38. tableauserverclient/models/tag_item.py +3 -4
  39. tableauserverclient/models/task_item.py +4 -4
  40. tableauserverclient/models/user_item.py +47 -17
  41. tableauserverclient/models/view_item.py +11 -10
  42. tableauserverclient/models/virtual_connection_item.py +78 -0
  43. tableauserverclient/models/webhook_item.py +6 -6
  44. tableauserverclient/models/workbook_item.py +90 -12
  45. tableauserverclient/namespace.py +1 -1
  46. tableauserverclient/server/__init__.py +2 -1
  47. tableauserverclient/server/endpoint/__init__.py +8 -0
  48. tableauserverclient/server/endpoint/auth_endpoint.py +68 -11
  49. tableauserverclient/server/endpoint/custom_views_endpoint.py +124 -19
  50. tableauserverclient/server/endpoint/data_acceleration_report_endpoint.py +2 -2
  51. tableauserverclient/server/endpoint/data_alert_endpoint.py +14 -14
  52. tableauserverclient/server/endpoint/databases_endpoint.py +32 -17
  53. tableauserverclient/server/endpoint/datasources_endpoint.py +150 -59
  54. tableauserverclient/server/endpoint/default_permissions_endpoint.py +19 -18
  55. tableauserverclient/server/endpoint/dqw_endpoint.py +9 -9
  56. tableauserverclient/server/endpoint/endpoint.py +47 -31
  57. tableauserverclient/server/endpoint/exceptions.py +23 -7
  58. tableauserverclient/server/endpoint/favorites_endpoint.py +31 -31
  59. tableauserverclient/server/endpoint/fileuploads_endpoint.py +11 -13
  60. tableauserverclient/server/endpoint/flow_runs_endpoint.py +59 -17
  61. tableauserverclient/server/endpoint/flow_task_endpoint.py +2 -2
  62. tableauserverclient/server/endpoint/flows_endpoint.py +73 -35
  63. tableauserverclient/server/endpoint/groups_endpoint.py +96 -27
  64. tableauserverclient/server/endpoint/groupsets_endpoint.py +127 -0
  65. tableauserverclient/server/endpoint/jobs_endpoint.py +79 -12
  66. tableauserverclient/server/endpoint/linked_tasks_endpoint.py +45 -0
  67. tableauserverclient/server/endpoint/metadata_endpoint.py +2 -2
  68. tableauserverclient/server/endpoint/metrics_endpoint.py +10 -10
  69. tableauserverclient/server/endpoint/permissions_endpoint.py +13 -15
  70. tableauserverclient/server/endpoint/projects_endpoint.py +124 -30
  71. tableauserverclient/server/endpoint/resource_tagger.py +139 -6
  72. tableauserverclient/server/endpoint/schedules_endpoint.py +17 -18
  73. tableauserverclient/server/endpoint/server_info_endpoint.py +40 -5
  74. tableauserverclient/server/endpoint/sites_endpoint.py +282 -17
  75. tableauserverclient/server/endpoint/subscriptions_endpoint.py +10 -10
  76. tableauserverclient/server/endpoint/tables_endpoint.py +33 -19
  77. tableauserverclient/server/endpoint/tasks_endpoint.py +8 -8
  78. tableauserverclient/server/endpoint/users_endpoint.py +405 -19
  79. tableauserverclient/server/endpoint/views_endpoint.py +111 -25
  80. tableauserverclient/server/endpoint/virtual_connections_endpoint.py +174 -0
  81. tableauserverclient/server/endpoint/webhooks_endpoint.py +11 -11
  82. tableauserverclient/server/endpoint/workbooks_endpoint.py +735 -68
  83. tableauserverclient/server/filter.py +2 -2
  84. tableauserverclient/server/pager.py +8 -10
  85. tableauserverclient/server/query.py +70 -20
  86. tableauserverclient/server/request_factory.py +213 -41
  87. tableauserverclient/server/request_options.py +125 -145
  88. tableauserverclient/server/server.py +73 -9
  89. tableauserverclient/server/sort.py +2 -2
  90. {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/METADATA +17 -17
  91. tableauserverclient-0.34.dist-info/RECORD +106 -0
  92. {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/WHEEL +1 -1
  93. tableauserverclient-0.32.dist-info/RECORD +0 -100
  94. {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/LICENSE +0 -0
  95. {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/LICENSE.versioneer +0 -0
  96. {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/top_level.txt +0 -0
@@ -1,19 +1,31 @@
1
+ import abc
1
2
  import copy
3
+ from typing import Generic, Optional, Protocol, TypeVar, Union, TYPE_CHECKING, runtime_checkable
4
+ from collections.abc import Iterable
2
5
  import urllib.parse
3
6
 
4
- from .endpoint import Endpoint
5
- from .exceptions import ServerResponseError
6
- from ..exceptions import EndpointUnavailableError
7
+ from tableauserverclient.server.endpoint.endpoint import Endpoint, api
8
+ from tableauserverclient.server.endpoint.exceptions import ServerResponseError
9
+ from tableauserverclient.server.exceptions import EndpointUnavailableError
7
10
  from tableauserverclient.server import RequestFactory
8
11
  from tableauserverclient.models import TagItem
9
12
 
10
13
  from tableauserverclient.helpers.logging import logger
11
14
 
15
+ if TYPE_CHECKING:
16
+ from tableauserverclient.models.column_item import ColumnItem
17
+ from tableauserverclient.models.database_item import DatabaseItem
18
+ from tableauserverclient.models.datasource_item import DatasourceItem
19
+ from tableauserverclient.models.flow_item import FlowItem
20
+ from tableauserverclient.models.table_item import TableItem
21
+ from tableauserverclient.models.workbook_item import WorkbookItem
22
+ from tableauserverclient.server.server import Server
23
+
12
24
 
13
25
  class _ResourceTagger(Endpoint):
14
26
  # Add new tags to resource
15
27
  def _add_tags(self, baseurl, resource_id, tag_set):
16
- url = "{0}/{1}/tags".format(baseurl, resource_id)
28
+ url = f"{baseurl}/{resource_id}/tags"
17
29
  add_req = RequestFactory.Tag.add_req(tag_set)
18
30
 
19
31
  try:
@@ -28,7 +40,7 @@ class _ResourceTagger(Endpoint):
28
40
  # Delete a resource's tag by name
29
41
  def _delete_tag(self, baseurl, resource_id, tag_name):
30
42
  encoded_tag_name = urllib.parse.quote(tag_name)
31
- url = "{0}/{1}/tags/{2}".format(baseurl, resource_id, encoded_tag_name)
43
+ url = f"{baseurl}/{resource_id}/tags/{encoded_tag_name}"
32
44
 
33
45
  try:
34
46
  self.delete_request(url)
@@ -48,4 +60,125 @@ class _ResourceTagger(Endpoint):
48
60
  if add_set:
49
61
  resource_item.tags = self._add_tags(baseurl, resource_item.id, add_set)
50
62
  resource_item._initial_tags = copy.copy(resource_item.tags)
51
- logger.info("Updated tags to {0}".format(resource_item.tags))
63
+ logger.info(f"Updated tags to {resource_item.tags}")
64
+
65
+
66
+ class Response(Protocol):
67
+ content: bytes
68
+
69
+
70
+ @runtime_checkable
71
+ class Taggable(Protocol):
72
+ tags: set[str]
73
+ _initial_tags: set[str]
74
+
75
+ @property
76
+ def id(self) -> Optional[str]:
77
+ pass
78
+
79
+
80
+ T = TypeVar("T")
81
+
82
+
83
+ class TaggingMixin(abc.ABC, Generic[T]):
84
+ parent_srv: "Server"
85
+
86
+ @property
87
+ @abc.abstractmethod
88
+ def baseurl(self) -> str:
89
+ pass
90
+
91
+ @abc.abstractmethod
92
+ def put_request(self, url, request) -> Response:
93
+ pass
94
+
95
+ @abc.abstractmethod
96
+ def delete_request(self, url) -> None:
97
+ pass
98
+
99
+ def add_tags(self, item: Union[T, str], tags: Union[Iterable[str], str]) -> set[str]:
100
+ item_id = getattr(item, "id", item)
101
+
102
+ if not isinstance(item_id, str):
103
+ raise ValueError("ID not found.")
104
+
105
+ if isinstance(tags, str):
106
+ tag_set = {tags}
107
+ else:
108
+ tag_set = set(tags)
109
+
110
+ url = f"{self.baseurl}/{item_id}/tags"
111
+ add_req = RequestFactory.Tag.add_req(tag_set)
112
+ server_response = self.put_request(url, add_req)
113
+ return TagItem.from_response(server_response.content, self.parent_srv.namespace)
114
+
115
+ def delete_tags(self, item: Union[T, str], tags: Union[Iterable[str], str]) -> None:
116
+ item_id = getattr(item, "id", item)
117
+
118
+ if not isinstance(item_id, str):
119
+ raise ValueError("ID not found.")
120
+
121
+ if isinstance(tags, str):
122
+ tag_set = {tags}
123
+ else:
124
+ tag_set = set(tags)
125
+
126
+ for tag in tag_set:
127
+ encoded_tag_name = urllib.parse.quote(tag)
128
+ url = f"{self.baseurl}/{item_id}/tags/{encoded_tag_name}"
129
+ self.delete_request(url)
130
+
131
+ def update_tags(self, item: T) -> None:
132
+ if (initial_tags := getattr(item, "_initial_tags", None)) is None:
133
+ raise ValueError(f"{item} does not have initial tags.")
134
+ if (tags := getattr(item, "tags", None)) is None:
135
+ raise ValueError(f"{item} does not have tags.")
136
+ if tags == initial_tags:
137
+ return
138
+
139
+ add_set = tags - initial_tags
140
+ remove_set = initial_tags - tags
141
+ self.delete_tags(item, remove_set)
142
+ if add_set:
143
+ tags = self.add_tags(item, add_set)
144
+ setattr(item, "tags", tags)
145
+
146
+ setattr(item, "_initial_tags", copy.copy(tags))
147
+ logger.info(f"Updated tags to {tags}")
148
+
149
+
150
+ content = Iterable[Union["ColumnItem", "DatabaseItem", "DatasourceItem", "FlowItem", "TableItem", "WorkbookItem"]]
151
+
152
+
153
+ class Tags(Endpoint):
154
+ def __init__(self, parent_srv: "Server"):
155
+ super().__init__(parent_srv)
156
+
157
+ @property
158
+ def baseurl(self):
159
+ return f"{self.parent_srv.baseurl}/tags"
160
+
161
+ @api(version="3.9")
162
+ def batch_add(self, tags: Union[Iterable[str], str], content: content) -> set[str]:
163
+ if isinstance(tags, str):
164
+ tag_set = {tags}
165
+ else:
166
+ tag_set = set(tags)
167
+
168
+ url = f"{self.baseurl}:batchCreate"
169
+ batch_create_req = RequestFactory.Tag.batch_create(tag_set, content)
170
+ server_response = self.put_request(url, batch_create_req)
171
+ return TagItem.from_response(server_response.content, self.parent_srv.namespace)
172
+
173
+ @api(version="3.9")
174
+ def batch_delete(self, tags: Union[Iterable[str], str], content: content) -> set[str]:
175
+ if isinstance(tags, str):
176
+ tag_set = {tags}
177
+ else:
178
+ tag_set = set(tags)
179
+
180
+ url = f"{self.baseurl}:batchDelete"
181
+ # The batch delete XML is the same as the batch create XML.
182
+ batch_delete_req = RequestFactory.Tag.batch_create(tag_set, content)
183
+ server_response = self.put_request(url, batch_delete_req)
184
+ return TagItem.from_response(server_response.content, self.parent_srv.namespace)
@@ -2,7 +2,7 @@ import copy
2
2
  import logging
3
3
  import warnings
4
4
  from collections import namedtuple
5
- from typing import TYPE_CHECKING, Callable, List, Optional, Tuple, Union
5
+ from typing import TYPE_CHECKING, Callable, Optional, Union
6
6
 
7
7
  from .endpoint import Endpoint, api, parameter_added_in
8
8
  from .exceptions import MissingRequiredFieldError
@@ -22,14 +22,14 @@ if TYPE_CHECKING:
22
22
  class Schedules(Endpoint):
23
23
  @property
24
24
  def baseurl(self) -> str:
25
- return "{0}/schedules".format(self.parent_srv.baseurl)
25
+ return f"{self.parent_srv.baseurl}/schedules"
26
26
 
27
27
  @property
28
28
  def siteurl(self) -> str:
29
- return "{0}/sites/{1}/schedules".format(self.parent_srv.baseurl, self.parent_srv.site_id)
29
+ return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/schedules"
30
30
 
31
31
  @api(version="2.3")
32
- def get(self, req_options: Optional["RequestOptions"] = None) -> Tuple[List[ScheduleItem], PaginationItem]:
32
+ def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[ScheduleItem], PaginationItem]:
33
33
  logger.info("Querying all schedules")
34
34
  url = self.baseurl
35
35
  server_response = self.get_request(url, req_options)
@@ -42,8 +42,8 @@ class Schedules(Endpoint):
42
42
  if not schedule_id:
43
43
  error = "No Schedule ID provided"
44
44
  raise ValueError(error)
45
- logger.info("Querying a single schedule by id ({})".format(schedule_id))
46
- url = "{0}/{1}".format(self.baseurl, schedule_id)
45
+ logger.info(f"Querying a single schedule by id ({schedule_id})")
46
+ url = f"{self.baseurl}/{schedule_id}"
47
47
  server_response = self.get_request(url)
48
48
  return ScheduleItem.from_response(server_response.content, self.parent_srv.namespace)[0]
49
49
 
@@ -52,9 +52,9 @@ class Schedules(Endpoint):
52
52
  if not schedule_id:
53
53
  error = "Schedule ID undefined"
54
54
  raise ValueError(error)
55
- url = "{0}/{1}".format(self.baseurl, schedule_id)
55
+ url = f"{self.baseurl}/{schedule_id}"
56
56
  self.delete_request(url)
57
- logger.info("Deleted single schedule (ID: {0})".format(schedule_id))
57
+ logger.info(f"Deleted single schedule (ID: {schedule_id})")
58
58
 
59
59
  @api(version="2.3")
60
60
  def update(self, schedule_item: ScheduleItem) -> ScheduleItem:
@@ -62,10 +62,10 @@ class Schedules(Endpoint):
62
62
  error = "Schedule item missing ID."
63
63
  raise MissingRequiredFieldError(error)
64
64
 
65
- url = "{0}/{1}".format(self.baseurl, schedule_item.id)
65
+ url = f"{self.baseurl}/{schedule_item.id}"
66
66
  update_req = RequestFactory.Schedule.update_req(schedule_item)
67
67
  server_response = self.put_request(url, update_req)
68
- logger.info("Updated schedule item (ID: {})".format(schedule_item.id))
68
+ logger.info(f"Updated schedule item (ID: {schedule_item.id})")
69
69
  updated_schedule = copy.copy(schedule_item)
70
70
  return updated_schedule._parse_common_tags(server_response.content, self.parent_srv.namespace)
71
71
 
@@ -79,7 +79,7 @@ class Schedules(Endpoint):
79
79
  create_req = RequestFactory.Schedule.create_req(schedule_item)
80
80
  server_response = self.post_request(url, create_req)
81
81
  new_schedule = ScheduleItem.from_response(server_response.content, self.parent_srv.namespace)[0]
82
- logger.info("Created new schedule (ID: {})".format(new_schedule.id))
82
+ logger.info(f"Created new schedule (ID: {new_schedule.id})")
83
83
  return new_schedule
84
84
 
85
85
  @api(version="2.8")
@@ -91,12 +91,12 @@ class Schedules(Endpoint):
91
91
  datasource: Optional["DatasourceItem"] = None,
92
92
  flow: Optional["FlowItem"] = None,
93
93
  task_type: Optional[str] = None,
94
- ) -> List[AddResponse]:
94
+ ) -> list[AddResponse]:
95
95
  # There doesn't seem to be a good reason to allow one item of each type?
96
96
  if workbook and datasource:
97
97
  warnings.warn("Passing in multiple items for add_to_schedule will be deprecated", PendingDeprecationWarning)
98
- items: List[
99
- Tuple[str, Union[WorkbookItem, FlowItem, DatasourceItem], str, Callable[[Optional[str], str], bytes], str]
98
+ items: list[
99
+ tuple[str, Union[WorkbookItem, FlowItem, DatasourceItem], str, Callable[[Optional[str], str], bytes], str]
100
100
  ] = []
101
101
 
102
102
  if workbook is not None:
@@ -115,8 +115,7 @@ class Schedules(Endpoint):
115
115
  ) # type:ignore[arg-type]
116
116
 
117
117
  results = (self._add_to(*x) for x in items)
118
- # list() is needed for python 3.x compatibility
119
- return list(filter(lambda x: not x.result, results)) # type:ignore[arg-type]
118
+ return [x for x in results if not x.result]
120
119
 
121
120
  def _add_to(
122
121
  self,
@@ -133,13 +132,13 @@ class Schedules(Endpoint):
133
132
  item_task_type,
134
133
  ) -> AddResponse:
135
134
  id_ = resource.id
136
- url = "{0}/{1}/{2}s".format(self.siteurl, schedule_id, type_)
135
+ url = f"{self.siteurl}/{schedule_id}/{type_}s"
137
136
  add_req = req_factory(id_, task_type=item_task_type) # type: ignore[call-arg, arg-type]
138
137
  response = self.put_request(url, add_req)
139
138
 
140
139
  error, warnings, task_created = ScheduleItem.parse_add_to_schedule_response(response, self.parent_srv.namespace)
141
140
  if task_created:
142
- logger.info("Added {} to {} to schedule {}".format(type_, id_, schedule_id))
141
+ logger.info(f"Added {type_} to {id_} to schedule {schedule_id}")
143
142
 
144
143
  if error is not None or warnings is not None:
145
144
  return AddResponse(
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ from typing import Union
2
3
 
3
4
  from .endpoint import Endpoint, api
4
5
  from .exceptions import ServerResponseError
@@ -21,15 +22,49 @@ class ServerInfo(Endpoint):
21
22
  return self._info
22
23
 
23
24
  def __repr__(self):
24
- return "<Endpoint {}>".format(self.serverInfo)
25
+ return f"<Endpoint {self.serverInfo}>"
25
26
 
26
27
  @property
27
- def baseurl(self):
28
- return "{0}/serverInfo".format(self.parent_srv.baseurl)
28
+ def baseurl(self) -> str:
29
+ return f"{self.parent_srv.baseurl}/serverInfo"
29
30
 
30
31
  @api(version="2.4")
31
- def get(self):
32
- """Retrieve the server info for the server. This is an unauthenticated call"""
32
+ def get(self) -> Union[ServerInfoItem, None]:
33
+ """
34
+ Retrieve the build and version information for the server.
35
+
36
+ This method makes an unauthenticated call, so no sign in or
37
+ authentication token is required.
38
+
39
+ Returns
40
+ -------
41
+ :class:`~tableauserverclient.models.ServerInfoItem`
42
+
43
+ Raises
44
+ ------
45
+ :class:`~tableauserverclient.exceptions.ServerInfoEndpointNotFoundError`
46
+ Raised when the server info endpoint is not found.
47
+
48
+ :class:`~tableauserverclient.exceptions.EndpointUnavailableError`
49
+ Raised when the server info endpoint is not available.
50
+
51
+ Examples
52
+ --------
53
+ >>> import tableauserverclient as TSC
54
+
55
+ >>> # create a instance of server
56
+ >>> server = TSC.Server('https://MY-SERVER')
57
+
58
+ >>> # set the version number > 2.3
59
+ >>> # the server_info.get() method works in 2.4 and later
60
+ >>> server.version = '2.5'
61
+
62
+ >>> s_info = server.server_info.get()
63
+ >>> print("\nServer info:")
64
+ >>> print("\tProduct version: {0}".format(s_info.product_version))
65
+ >>> print("\tREST API version: {0}".format(s_info.rest_api_version))
66
+ >>> print("\tBuild number: {0}".format(s_info.build_number))
67
+ """
33
68
  try:
34
69
  server_response = self.get_unauthenticated_request(self.baseurl)
35
70
  except ServerResponseError as e: