tableauserverclient 0.33__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 (94) hide show
  1. tableauserverclient/__init__.py +28 -22
  2. tableauserverclient/_version.py +3 -3
  3. tableauserverclient/config.py +5 -3
  4. tableauserverclient/models/column_item.py +1 -1
  5. tableauserverclient/models/connection_credentials.py +1 -1
  6. tableauserverclient/models/connection_item.py +6 -6
  7. tableauserverclient/models/custom_view_item.py +29 -6
  8. tableauserverclient/models/data_acceleration_report_item.py +2 -2
  9. tableauserverclient/models/data_alert_item.py +5 -5
  10. tableauserverclient/models/data_freshness_policy_item.py +6 -6
  11. tableauserverclient/models/database_item.py +3 -3
  12. tableauserverclient/models/datasource_item.py +10 -10
  13. tableauserverclient/models/dqw_item.py +1 -1
  14. tableauserverclient/models/favorites_item.py +5 -6
  15. tableauserverclient/models/fileupload_item.py +1 -1
  16. tableauserverclient/models/flow_item.py +6 -6
  17. tableauserverclient/models/flow_run_item.py +3 -3
  18. tableauserverclient/models/group_item.py +4 -4
  19. tableauserverclient/models/groupset_item.py +4 -4
  20. tableauserverclient/models/interval_item.py +9 -9
  21. tableauserverclient/models/job_item.py +8 -8
  22. tableauserverclient/models/linked_tasks_item.py +5 -5
  23. tableauserverclient/models/metric_item.py +5 -5
  24. tableauserverclient/models/pagination_item.py +1 -1
  25. tableauserverclient/models/permissions_item.py +12 -10
  26. tableauserverclient/models/project_item.py +35 -19
  27. tableauserverclient/models/property_decorators.py +12 -11
  28. tableauserverclient/models/reference_item.py +2 -2
  29. tableauserverclient/models/revision_item.py +3 -3
  30. tableauserverclient/models/schedule_item.py +2 -2
  31. tableauserverclient/models/server_info_item.py +26 -6
  32. tableauserverclient/models/site_item.py +69 -3
  33. tableauserverclient/models/subscription_item.py +3 -3
  34. tableauserverclient/models/table_item.py +1 -1
  35. tableauserverclient/models/tableau_auth.py +115 -5
  36. tableauserverclient/models/tableau_types.py +2 -2
  37. tableauserverclient/models/tag_item.py +3 -4
  38. tableauserverclient/models/task_item.py +4 -4
  39. tableauserverclient/models/user_item.py +47 -17
  40. tableauserverclient/models/view_item.py +11 -10
  41. tableauserverclient/models/virtual_connection_item.py +6 -5
  42. tableauserverclient/models/webhook_item.py +6 -6
  43. tableauserverclient/models/workbook_item.py +90 -12
  44. tableauserverclient/namespace.py +1 -1
  45. tableauserverclient/server/__init__.py +2 -1
  46. tableauserverclient/server/endpoint/auth_endpoint.py +65 -8
  47. tableauserverclient/server/endpoint/custom_views_endpoint.py +62 -18
  48. tableauserverclient/server/endpoint/data_acceleration_report_endpoint.py +2 -2
  49. tableauserverclient/server/endpoint/data_alert_endpoint.py +14 -14
  50. tableauserverclient/server/endpoint/databases_endpoint.py +13 -12
  51. tableauserverclient/server/endpoint/datasources_endpoint.py +49 -54
  52. tableauserverclient/server/endpoint/default_permissions_endpoint.py +19 -18
  53. tableauserverclient/server/endpoint/dqw_endpoint.py +9 -9
  54. tableauserverclient/server/endpoint/endpoint.py +19 -21
  55. tableauserverclient/server/endpoint/exceptions.py +23 -7
  56. tableauserverclient/server/endpoint/favorites_endpoint.py +31 -31
  57. tableauserverclient/server/endpoint/fileuploads_endpoint.py +9 -11
  58. tableauserverclient/server/endpoint/flow_runs_endpoint.py +15 -13
  59. tableauserverclient/server/endpoint/flow_task_endpoint.py +2 -2
  60. tableauserverclient/server/endpoint/flows_endpoint.py +30 -29
  61. tableauserverclient/server/endpoint/groups_endpoint.py +18 -17
  62. tableauserverclient/server/endpoint/groupsets_endpoint.py +2 -2
  63. tableauserverclient/server/endpoint/jobs_endpoint.py +7 -7
  64. tableauserverclient/server/endpoint/linked_tasks_endpoint.py +2 -2
  65. tableauserverclient/server/endpoint/metadata_endpoint.py +2 -2
  66. tableauserverclient/server/endpoint/metrics_endpoint.py +10 -10
  67. tableauserverclient/server/endpoint/permissions_endpoint.py +13 -15
  68. tableauserverclient/server/endpoint/projects_endpoint.py +81 -30
  69. tableauserverclient/server/endpoint/resource_tagger.py +14 -13
  70. tableauserverclient/server/endpoint/schedules_endpoint.py +17 -18
  71. tableauserverclient/server/endpoint/server_info_endpoint.py +40 -5
  72. tableauserverclient/server/endpoint/sites_endpoint.py +282 -17
  73. tableauserverclient/server/endpoint/subscriptions_endpoint.py +10 -10
  74. tableauserverclient/server/endpoint/tables_endpoint.py +15 -14
  75. tableauserverclient/server/endpoint/tasks_endpoint.py +8 -8
  76. tableauserverclient/server/endpoint/users_endpoint.py +366 -19
  77. tableauserverclient/server/endpoint/views_endpoint.py +19 -18
  78. tableauserverclient/server/endpoint/virtual_connections_endpoint.py +6 -5
  79. tableauserverclient/server/endpoint/webhooks_endpoint.py +11 -11
  80. tableauserverclient/server/endpoint/workbooks_endpoint.py +647 -61
  81. tableauserverclient/server/filter.py +2 -2
  82. tableauserverclient/server/pager.py +5 -6
  83. tableauserverclient/server/query.py +68 -19
  84. tableauserverclient/server/request_factory.py +37 -36
  85. tableauserverclient/server/request_options.py +123 -145
  86. tableauserverclient/server/server.py +65 -9
  87. tableauserverclient/server/sort.py +2 -2
  88. {tableauserverclient-0.33.dist-info → tableauserverclient-0.34.dist-info}/METADATA +6 -6
  89. tableauserverclient-0.34.dist-info/RECORD +106 -0
  90. {tableauserverclient-0.33.dist-info → tableauserverclient-0.34.dist-info}/WHEEL +1 -1
  91. tableauserverclient-0.33.dist-info/RECORD +0 -106
  92. {tableauserverclient-0.33.dist-info → tableauserverclient-0.34.dist-info}/LICENSE +0 -0
  93. {tableauserverclient-0.33.dist-info → tableauserverclient-0.34.dist-info}/LICENSE.versioneer +0 -0
  94. {tableauserverclient-0.33.dist-info → tableauserverclient-0.34.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- from typing import Dict, List, Optional
1
+ from typing import Optional
2
2
  import xml.etree.ElementTree as ET
3
3
 
4
4
  from defusedxml.ElementTree import fromstring
@@ -13,7 +13,7 @@ class GroupSetItem:
13
13
  def __init__(self, name: Optional[str] = None) -> None:
14
14
  self.name = name
15
15
  self.id: Optional[str] = None
16
- self.groups: List["GroupItem"] = []
16
+ self.groups: list["GroupItem"] = []
17
17
  self.group_count: int = 0
18
18
 
19
19
  def __str__(self) -> str:
@@ -25,13 +25,13 @@ class GroupSetItem:
25
25
  return self.__str__()
26
26
 
27
27
  @classmethod
28
- def from_response(cls, response: bytes, ns: Dict[str, str]) -> List["GroupSetItem"]:
28
+ def from_response(cls, response: bytes, ns: dict[str, str]) -> list["GroupSetItem"]:
29
29
  parsed_response = fromstring(response)
30
30
  all_groupset_xml = parsed_response.findall(".//t:groupSet", namespaces=ns)
31
31
  return [cls.from_xml(xml, ns) for xml in all_groupset_xml]
32
32
 
33
33
  @classmethod
34
- def from_xml(cls, groupset_xml: ET.Element, ns: Dict[str, str]) -> "GroupSetItem":
34
+ def from_xml(cls, groupset_xml: ET.Element, ns: dict[str, str]) -> "GroupSetItem":
35
35
  def get_group(group_xml: ET.Element) -> GroupItem:
36
36
  group_item = GroupItem()
37
37
  group_item._id = group_xml.get("id")
@@ -1,7 +1,7 @@
1
1
  from .property_decorators import property_is_valid_time, property_not_nullable
2
2
 
3
3
 
4
- class IntervalItem(object):
4
+ class IntervalItem:
5
5
  class Frequency:
6
6
  Hourly = "Hourly"
7
7
  Daily = "Daily"
@@ -25,7 +25,7 @@ class IntervalItem(object):
25
25
  LastDay = "LastDay"
26
26
 
27
27
 
28
- class HourlyInterval(object):
28
+ class HourlyInterval:
29
29
  def __init__(self, start_time, end_time, interval_value):
30
30
  self.start_time = start_time
31
31
  self.end_time = end_time
@@ -73,12 +73,12 @@ class HourlyInterval(object):
73
73
  for interval in intervals:
74
74
  # if an hourly interval is a string, then it is a weekDay interval
75
75
  if isinstance(interval, str) and not interval.isnumeric() and not hasattr(IntervalItem.Day, interval):
76
- error = "Invalid weekDay interval {}".format(interval)
76
+ error = f"Invalid weekDay interval {interval}"
77
77
  raise ValueError(error)
78
78
 
79
79
  # if an hourly interval is a number, it is an hours or minutes interval
80
80
  if isinstance(interval, (int, float)) and float(interval) not in VALID_INTERVALS:
81
- error = "Invalid interval {} not in {}".format(interval, str(VALID_INTERVALS))
81
+ error = f"Invalid interval {interval} not in {str(VALID_INTERVALS)}"
82
82
  raise ValueError(error)
83
83
 
84
84
  self._interval = intervals
@@ -108,7 +108,7 @@ class HourlyInterval(object):
108
108
  return interval_type_pairs
109
109
 
110
110
 
111
- class DailyInterval(object):
111
+ class DailyInterval:
112
112
  def __init__(self, start_time, *interval_values):
113
113
  self.start_time = start_time
114
114
  self.interval = interval_values
@@ -141,12 +141,12 @@ class DailyInterval(object):
141
141
  for interval in intervals:
142
142
  # if an hourly interval is a string, then it is a weekDay interval
143
143
  if isinstance(interval, str) and not interval.isnumeric() and not hasattr(IntervalItem.Day, interval):
144
- error = "Invalid weekDay interval {}".format(interval)
144
+ error = f"Invalid weekDay interval {interval}"
145
145
  raise ValueError(error)
146
146
 
147
147
  # if an hourly interval is a number, it is an hours or minutes interval
148
148
  if isinstance(interval, (int, float)) and float(interval) not in VALID_INTERVALS:
149
- error = "Invalid interval {} not in {}".format(interval, str(VALID_INTERVALS))
149
+ error = f"Invalid interval {interval} not in {str(VALID_INTERVALS)}"
150
150
  raise ValueError(error)
151
151
 
152
152
  self._interval = intervals
@@ -176,7 +176,7 @@ class DailyInterval(object):
176
176
  return interval_type_pairs
177
177
 
178
178
 
179
- class WeeklyInterval(object):
179
+ class WeeklyInterval:
180
180
  def __init__(self, start_time, *interval_values):
181
181
  self.start_time = start_time
182
182
  self.interval = interval_values
@@ -213,7 +213,7 @@ class WeeklyInterval(object):
213
213
  return [(IntervalItem.Occurrence.WeekDay, day) for day in self.interval]
214
214
 
215
215
 
216
- class MonthlyInterval(object):
216
+ class MonthlyInterval:
217
217
  def __init__(self, start_time, interval_value):
218
218
  self.start_time = start_time
219
219
 
@@ -1,5 +1,5 @@
1
1
  import datetime
2
- from typing import List, Optional
2
+ from typing import Optional
3
3
 
4
4
  from defusedxml.ElementTree import fromstring
5
5
 
@@ -7,7 +7,7 @@ from tableauserverclient.datetime_helpers import parse_datetime
7
7
  from tableauserverclient.models.flow_run_item import FlowRunItem
8
8
 
9
9
 
10
- class JobItem(object):
10
+ class JobItem:
11
11
  class FinishCode:
12
12
  """
13
13
  Status codes as documented on
@@ -27,7 +27,7 @@ class JobItem(object):
27
27
  started_at: Optional[datetime.datetime] = None,
28
28
  completed_at: Optional[datetime.datetime] = None,
29
29
  finish_code: int = 0,
30
- notes: Optional[List[str]] = None,
30
+ notes: Optional[list[str]] = None,
31
31
  mode: Optional[str] = None,
32
32
  workbook_id: Optional[str] = None,
33
33
  datasource_id: Optional[str] = None,
@@ -43,7 +43,7 @@ class JobItem(object):
43
43
  self._started_at = started_at
44
44
  self._completed_at = completed_at
45
45
  self._finish_code = finish_code
46
- self._notes: List[str] = notes or []
46
+ self._notes: list[str] = notes or []
47
47
  self._mode = mode
48
48
  self._workbook_id = workbook_id
49
49
  self._datasource_id = datasource_id
@@ -81,7 +81,7 @@ class JobItem(object):
81
81
  return self._finish_code
82
82
 
83
83
  @property
84
- def notes(self) -> List[str]:
84
+ def notes(self) -> list[str]:
85
85
  return self._notes
86
86
 
87
87
  @property
@@ -139,7 +139,7 @@ class JobItem(object):
139
139
  return self.__str__() + " { " + ", ".join(" % s: % s" % item for item in vars(self).items()) + "}"
140
140
 
141
141
  @classmethod
142
- def from_response(cls, xml, ns) -> List["JobItem"]:
142
+ def from_response(cls, xml, ns) -> list["JobItem"]:
143
143
  parsed_response = fromstring(xml)
144
144
  all_tasks_xml = parsed_response.findall(".//t:job", namespaces=ns)
145
145
 
@@ -191,7 +191,7 @@ class JobItem(object):
191
191
  )
192
192
 
193
193
 
194
- class BackgroundJobItem(object):
194
+ class BackgroundJobItem:
195
195
  class Status:
196
196
  Pending: str = "Pending"
197
197
  InProgress: str = "InProgress"
@@ -270,7 +270,7 @@ class BackgroundJobItem(object):
270
270
  return self._priority
271
271
 
272
272
  @classmethod
273
- def from_response(cls, xml, ns) -> List["BackgroundJobItem"]:
273
+ def from_response(cls, xml, ns) -> list["BackgroundJobItem"]:
274
274
  parsed_response = fromstring(xml)
275
275
  all_tasks_xml = parsed_response.findall(".//t:backgroundJob", namespaces=ns)
276
276
  return [cls._parse_element(x, ns) for x in all_tasks_xml]
@@ -1,5 +1,5 @@
1
1
  import datetime as dt
2
- from typing import List, Optional
2
+ from typing import Optional
3
3
 
4
4
  from defusedxml.ElementTree import fromstring
5
5
 
@@ -14,7 +14,7 @@ class LinkedTaskItem:
14
14
  self.schedule: Optional[ScheduleItem] = None
15
15
 
16
16
  @classmethod
17
- def from_response(cls, resp: bytes, namespace) -> List["LinkedTaskItem"]:
17
+ def from_response(cls, resp: bytes, namespace) -> list["LinkedTaskItem"]:
18
18
  parsed_response = fromstring(resp)
19
19
  return [
20
20
  cls._parse_element(x, namespace)
@@ -35,10 +35,10 @@ class LinkedTaskStepItem:
35
35
  self.id: Optional[str] = None
36
36
  self.step_number: Optional[int] = None
37
37
  self.stop_downstream_on_failure: Optional[bool] = None
38
- self.task_details: List[LinkedTaskFlowRunItem] = []
38
+ self.task_details: list[LinkedTaskFlowRunItem] = []
39
39
 
40
40
  @classmethod
41
- def from_task_xml(cls, xml, namespace) -> List["LinkedTaskStepItem"]:
41
+ def from_task_xml(cls, xml, namespace) -> list["LinkedTaskStepItem"]:
42
42
  return [cls._parse_element(x, namespace) for x in xml.findall(".//t:linkedTaskSteps[@id]", namespace)]
43
43
 
44
44
  @classmethod
@@ -61,7 +61,7 @@ class LinkedTaskFlowRunItem:
61
61
  self.flow_name: Optional[str] = None
62
62
 
63
63
  @classmethod
64
- def _parse_element(cls, xml, namespace) -> List["LinkedTaskFlowRunItem"]:
64
+ def _parse_element(cls, xml, namespace) -> list["LinkedTaskFlowRunItem"]:
65
65
  all_tasks = []
66
66
  for flow_run in xml.findall(".//t:flowRun[@id]", namespace):
67
67
  task = cls()
@@ -1,6 +1,6 @@
1
1
  import xml.etree.ElementTree as ET
2
2
  from datetime import datetime
3
- from typing import List, Optional, Set
3
+ from typing import Optional
4
4
 
5
5
  from tableauserverclient.datetime_helpers import parse_datetime
6
6
  from .property_decorators import property_is_boolean, property_is_datetime
@@ -8,7 +8,7 @@ from .tag_item import TagItem
8
8
  from .permissions_item import Permission
9
9
 
10
10
 
11
- class MetricItem(object):
11
+ class MetricItem:
12
12
  def __init__(self, name: Optional[str] = None):
13
13
  self._id: Optional[str] = None
14
14
  self._name: Optional[str] = name
@@ -21,8 +21,8 @@ class MetricItem(object):
21
21
  self._project_name: Optional[str] = None
22
22
  self._owner_id: Optional[str] = None
23
23
  self._view_id: Optional[str] = None
24
- self._initial_tags: Set[str] = set()
25
- self.tags: Set[str] = set()
24
+ self._initial_tags: set[str] = set()
25
+ self.tags: set[str] = set()
26
26
  self._permissions: Optional[Permission] = None
27
27
 
28
28
  @property
@@ -126,7 +126,7 @@ class MetricItem(object):
126
126
  cls,
127
127
  resp: bytes,
128
128
  ns,
129
- ) -> List["MetricItem"]:
129
+ ) -> list["MetricItem"]:
130
130
  all_metric_items = list()
131
131
  parsed_response = ET.fromstring(resp)
132
132
  all_metric_xml = parsed_response.findall(".//t:metric", namespaces=ns)
@@ -1,7 +1,7 @@
1
1
  from defusedxml.ElementTree import fromstring
2
2
 
3
3
 
4
- class PaginationItem(object):
4
+ class PaginationItem:
5
5
  def __init__(self):
6
6
  self._page_number = None
7
7
  self._page_size = None
@@ -1,5 +1,5 @@
1
1
  import xml.etree.ElementTree as ET
2
- from typing import Dict, List, Optional
2
+ from typing import Optional
3
3
 
4
4
  from defusedxml.ElementTree import fromstring
5
5
 
@@ -36,23 +36,25 @@ class Permission:
36
36
  ShareView = "ShareView"
37
37
  ViewComments = "ViewComments"
38
38
  ViewUnderlyingData = "ViewUnderlyingData"
39
+ VizqlDataApiAccess = "VizqlDataApiAccess"
39
40
  WebAuthoring = "WebAuthoring"
40
41
  Write = "Write"
41
42
  RunExplainData = "RunExplainData"
42
43
  CreateRefreshMetrics = "CreateRefreshMetrics"
43
44
  SaveAs = "SaveAs"
45
+ PulseMetricDefine = "PulseMetricDefine"
44
46
 
45
47
  def __repr__(self):
46
48
  return "<Enum Capability: AddComment | ChangeHierarchy | ChangePermission ... (17 more) >"
47
49
 
48
50
 
49
51
  class PermissionsRule:
50
- def __init__(self, grantee: ResourceReference, capabilities: Dict[str, str]) -> None:
52
+ def __init__(self, grantee: ResourceReference, capabilities: dict[str, str]) -> None:
51
53
  self.grantee = grantee
52
54
  self.capabilities = capabilities
53
55
 
54
56
  def __repr__(self):
55
- return "<PermissionsRule grantee={}, capabilities={}>".format(self.grantee, self.capabilities)
57
+ return f"<PermissionsRule grantee={self.grantee}, capabilities={self.capabilities}>"
56
58
 
57
59
  def __eq__(self, other: object) -> bool:
58
60
  if not hasattr(other, "grantee") or not hasattr(other, "capabilities"):
@@ -66,7 +68,7 @@ class PermissionsRule:
66
68
  if self.capabilities == other.capabilities:
67
69
  return self
68
70
 
69
- capabilities = set((*self.capabilities.keys(), *other.capabilities.keys()))
71
+ capabilities = {*self.capabilities.keys(), *other.capabilities.keys()}
70
72
  new_capabilities = {}
71
73
  for capability in capabilities:
72
74
  if (self.capabilities.get(capability), other.capabilities.get(capability)) == (
@@ -86,7 +88,7 @@ class PermissionsRule:
86
88
  if self.capabilities == other.capabilities:
87
89
  return self
88
90
 
89
- capabilities = set((*self.capabilities.keys(), *other.capabilities.keys()))
91
+ capabilities = {*self.capabilities.keys(), *other.capabilities.keys()}
90
92
  new_capabilities = {}
91
93
  for capability in capabilities:
92
94
  if Permission.Mode.Allow in (self.capabilities.get(capability), other.capabilities.get(capability)):
@@ -100,14 +102,14 @@ class PermissionsRule:
100
102
  return PermissionsRule(self.grantee, new_capabilities)
101
103
 
102
104
  @classmethod
103
- def from_response(cls, resp, ns=None) -> List["PermissionsRule"]:
105
+ def from_response(cls, resp, ns=None) -> list["PermissionsRule"]:
104
106
  parsed_response = fromstring(resp)
105
107
 
106
108
  rules = []
107
109
  permissions_rules_list_xml = parsed_response.findall(".//t:granteeCapabilities", namespaces=ns)
108
110
 
109
111
  for grantee_capability_xml in permissions_rules_list_xml:
110
- capability_dict: Dict[str, str] = {}
112
+ capability_dict: dict[str, str] = {}
111
113
 
112
114
  grantee = PermissionsRule._parse_grantee_element(grantee_capability_xml, ns)
113
115
 
@@ -116,7 +118,7 @@ class PermissionsRule:
116
118
  mode = capability_xml.get("mode")
117
119
 
118
120
  if name is None or mode is None:
119
- logger.error("Capability was not valid: {}".format(capability_xml))
121
+ logger.error(f"Capability was not valid: {capability_xml}")
120
122
  raise UnpopulatedPropertyError()
121
123
  else:
122
124
  capability_dict[name] = mode
@@ -127,7 +129,7 @@ class PermissionsRule:
127
129
  return rules
128
130
 
129
131
  @staticmethod
130
- def _parse_grantee_element(grantee_capability_xml: ET.Element, ns: Optional[Dict[str, str]]) -> ResourceReference:
132
+ def _parse_grantee_element(grantee_capability_xml: ET.Element, ns: Optional[dict[str, str]]) -> ResourceReference:
131
133
  """Use Xpath magic and some string splitting to get the right object type from the xml"""
132
134
 
133
135
  # Get the first element in the tree with an 'id' attribute
@@ -146,6 +148,6 @@ class PermissionsRule:
146
148
  elif grantee_type == "groupSet":
147
149
  grantee = GroupSetItem.as_reference(grantee_id)
148
150
  else:
149
- raise UnknownGranteeTypeError("No support for grantee type of {}".format(grantee_type))
151
+ raise UnknownGranteeTypeError(f"No support for grantee type of {grantee_type}")
150
152
 
151
153
  return grantee
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  import xml.etree.ElementTree as ET
3
- from typing import List, Optional
3
+ from typing import Optional
4
4
 
5
5
  from defusedxml.ElementTree import fromstring
6
6
 
@@ -8,14 +8,16 @@ from tableauserverclient.models.exceptions import UnpopulatedPropertyError
8
8
  from tableauserverclient.models.property_decorators import property_is_enum, property_not_empty
9
9
 
10
10
 
11
- class ProjectItem(object):
11
+ class ProjectItem:
12
+ ERROR_MSG = "Project item must be populated with permissions first."
13
+
12
14
  class ContentPermissions:
13
15
  LockedToProject: str = "LockedToProject"
14
16
  ManagedByOwner: str = "ManagedByOwner"
15
17
  LockedToProjectWithoutNested: str = "LockedToProjectWithoutNested"
16
18
 
17
19
  def __repr__(self):
18
- return "<Project {0} {1} parent={2} permissions={3}>".format(
20
+ return "<Project {} {} parent={} permissions={}>".format(
19
21
  self._id, self.name, self.parent_id or "None (Top level)", self.content_permissions or "Not Set"
20
22
  )
21
23
 
@@ -43,6 +45,9 @@ class ProjectItem(object):
43
45
  self._default_lens_permissions = None
44
46
  self._default_datarole_permissions = None
45
47
  self._default_metric_permissions = None
48
+ self._default_virtualconnection_permissions = None
49
+ self._default_database_permissions = None
50
+ self._default_table_permissions = None
46
51
 
47
52
  @property
48
53
  def content_permissions(self):
@@ -56,52 +61,63 @@ class ProjectItem(object):
56
61
  @property
57
62
  def permissions(self):
58
63
  if self._permissions is None:
59
- error = "Project item must be populated with permissions first."
60
- raise UnpopulatedPropertyError(error)
64
+ raise UnpopulatedPropertyError(self.ERROR_MSG)
61
65
  return self._permissions()
62
66
 
63
67
  @property
64
68
  def default_datasource_permissions(self):
65
69
  if self._default_datasource_permissions is None:
66
- error = "Project item must be populated with permissions first."
67
- raise UnpopulatedPropertyError(error)
70
+ raise UnpopulatedPropertyError(self.ERROR_MSG)
68
71
  return self._default_datasource_permissions()
69
72
 
70
73
  @property
71
74
  def default_workbook_permissions(self):
72
75
  if self._default_workbook_permissions is None:
73
- error = "Project item must be populated with permissions first."
74
- raise UnpopulatedPropertyError(error)
76
+ raise UnpopulatedPropertyError(self.ERROR_MSG)
75
77
  return self._default_workbook_permissions()
76
78
 
77
79
  @property
78
80
  def default_flow_permissions(self):
79
81
  if self._default_flow_permissions is None:
80
- error = "Project item must be populated with permissions first."
81
- raise UnpopulatedPropertyError(error)
82
+ raise UnpopulatedPropertyError(self.ERROR_MSG)
82
83
  return self._default_flow_permissions()
83
84
 
84
85
  @property
85
86
  def default_lens_permissions(self):
86
87
  if self._default_lens_permissions is None:
87
- error = "Project item must be populated with permissions first."
88
- raise UnpopulatedPropertyError(error)
88
+ raise UnpopulatedPropertyError(self.ERROR_MSG)
89
89
  return self._default_lens_permissions()
90
90
 
91
91
  @property
92
92
  def default_datarole_permissions(self):
93
93
  if self._default_datarole_permissions is None:
94
- error = "Project item must be populated with permissions first."
95
- raise UnpopulatedPropertyError(error)
94
+ raise UnpopulatedPropertyError(self.ERROR_MSG)
96
95
  return self._default_datarole_permissions()
97
96
 
98
97
  @property
99
98
  def default_metric_permissions(self):
100
99
  if self._default_metric_permissions is None:
101
- error = "Project item must be populated with permissions first."
102
- raise UnpopulatedPropertyError(error)
100
+ raise UnpopulatedPropertyError(self.ERROR_MSG)
103
101
  return self._default_metric_permissions()
104
102
 
103
+ @property
104
+ def default_virtualconnection_permissions(self):
105
+ if self._default_virtualconnection_permissions is None:
106
+ raise UnpopulatedPropertyError(self.ERROR_MSG)
107
+ return self._default_virtualconnection_permissions()
108
+
109
+ @property
110
+ def default_database_permissions(self):
111
+ if self._default_database_permissions is None:
112
+ raise UnpopulatedPropertyError(self.ERROR_MSG)
113
+ return self._default_database_permissions()
114
+
115
+ @property
116
+ def default_table_permissions(self):
117
+ if self._default_table_permissions is None:
118
+ raise UnpopulatedPropertyError(self.ERROR_MSG)
119
+ return self._default_table_permissions()
120
+
105
121
  @property
106
122
  def id(self) -> Optional[str]:
107
123
  return self._id
@@ -158,7 +174,7 @@ class ProjectItem(object):
158
174
  self._permissions = permissions
159
175
 
160
176
  def _set_default_permissions(self, permissions, content_type):
161
- attr = "_default_{content}_permissions".format(content=content_type)
177
+ attr = f"_default_{content_type}_permissions"
162
178
  setattr(
163
179
  self,
164
180
  attr,
@@ -166,7 +182,7 @@ class ProjectItem(object):
166
182
  )
167
183
 
168
184
  @classmethod
169
- def from_response(cls, resp, ns) -> List["ProjectItem"]:
185
+ def from_response(cls, resp, ns) -> list["ProjectItem"]:
170
186
  all_project_items = list()
171
187
  parsed_response = fromstring(resp)
172
188
  all_project_xml = parsed_response.findall(".//t:project", namespaces=ns)
@@ -1,7 +1,8 @@
1
1
  import datetime
2
2
  import re
3
3
  from functools import wraps
4
- from typing import Any, Container, Optional, Tuple
4
+ from typing import Any, Optional
5
+ from collections.abc import Container
5
6
 
6
7
  from tableauserverclient.datetime_helpers import parse_datetime
7
8
 
@@ -11,7 +12,7 @@ def property_is_enum(enum_type):
11
12
  @wraps(func)
12
13
  def wrapper(self, value):
13
14
  if value is not None and not hasattr(enum_type, value):
14
- error = "Invalid value: {0}. {1} must be of type {2}.".format(value, func.__name__, enum_type.__name__)
15
+ error = f"Invalid value: {value}. {func.__name__} must be of type {enum_type.__name__}."
15
16
  raise ValueError(error)
16
17
  return func(self, value)
17
18
 
@@ -24,7 +25,7 @@ def property_is_boolean(func):
24
25
  @wraps(func)
25
26
  def wrapper(self, value):
26
27
  if not isinstance(value, bool):
27
- error = "Boolean expected for {0} flag.".format(func.__name__)
28
+ error = f"Boolean expected for {func.__name__} flag."
28
29
  raise ValueError(error)
29
30
  return func(self, value)
30
31
 
@@ -35,7 +36,7 @@ def property_not_nullable(func):
35
36
  @wraps(func)
36
37
  def wrapper(self, value):
37
38
  if value is None:
38
- error = "{0} must be defined.".format(func.__name__)
39
+ error = f"{func.__name__} must be defined."
39
40
  raise ValueError(error)
40
41
  return func(self, value)
41
42
 
@@ -46,7 +47,7 @@ def property_not_empty(func):
46
47
  @wraps(func)
47
48
  def wrapper(self, value):
48
49
  if not value:
49
- error = "{0} must not be empty.".format(func.__name__)
50
+ error = f"{func.__name__} must not be empty."
50
51
  raise ValueError(error)
51
52
  return func(self, value)
52
53
 
@@ -66,7 +67,7 @@ def property_is_valid_time(func):
66
67
  return wrapper
67
68
 
68
69
 
69
- def property_is_int(range: Tuple[int, int], allowed: Optional[Container[Any]] = None):
70
+ def property_is_int(range: tuple[int, int], allowed: Optional[Container[Any]] = None):
70
71
  """Takes a range of ints and a list of exemptions to check against
71
72
  when setting a property on a model. The range is a tuple of (min, max) and the
72
73
  allowed list (empty by default) allows values outside that range.
@@ -81,7 +82,7 @@ def property_is_int(range: Tuple[int, int], allowed: Optional[Container[Any]] =
81
82
  def property_type_decorator(func):
82
83
  @wraps(func)
83
84
  def wrapper(self, value):
84
- error = "Invalid property defined: '{}'. Integer value expected.".format(value)
85
+ error = f"Invalid property defined: '{value}'. Integer value expected."
85
86
 
86
87
  if range is None:
87
88
  if isinstance(value, int):
@@ -133,7 +134,7 @@ def property_is_datetime(func):
133
134
  return func(self, value)
134
135
  if not isinstance(value, str):
135
136
  raise ValueError(
136
- "Cannot convert {} into a datetime, cannot update {}".format(value.__class__.__name__, func.__name__)
137
+ f"Cannot convert {value.__class__.__name__} into a datetime, cannot update {func.__name__}"
137
138
  )
138
139
 
139
140
  dt = parse_datetime(value)
@@ -146,11 +147,11 @@ def property_is_data_acceleration_config(func):
146
147
  @wraps(func)
147
148
  def wrapper(self, value):
148
149
  if not isinstance(value, dict):
149
- raise ValueError("{} is not type 'dict', cannot update {})".format(value.__class__.__name__, func.__name__))
150
+ raise ValueError(f"{value.__class__.__name__} is not type 'dict', cannot update {func.__name__})")
150
151
  if len(value) < 2 or not all(attr in value.keys() for attr in ("acceleration_enabled", "accelerate_now")):
151
- error = "{} should have 2 keys ".format(func.__name__)
152
+ error = f"{func.__name__} should have 2 keys "
152
153
  error += "'acceleration_enabled' and 'accelerate_now'"
153
- error += "instead you have {}".format(value.keys())
154
+ error += f"instead you have {value.keys()}"
154
155
  raise ValueError(error)
155
156
  return func(self, value)
156
157
 
@@ -1,10 +1,10 @@
1
- class ResourceReference(object):
1
+ class ResourceReference:
2
2
  def __init__(self, id_, tag_name):
3
3
  self.id = id_
4
4
  self.tag_name = tag_name
5
5
 
6
6
  def __str__(self):
7
- return "<ResourceReference id={} tag={}>".format(self._id, self._tag_name)
7
+ return f"<ResourceReference id={self._id} tag={self._tag_name}>"
8
8
 
9
9
  __repr__ = __str__
10
10
 
@@ -1,12 +1,12 @@
1
1
  from datetime import datetime
2
- from typing import List, Optional
2
+ from typing import Optional
3
3
 
4
4
  from defusedxml.ElementTree import fromstring
5
5
 
6
6
  from tableauserverclient.datetime_helpers import parse_datetime
7
7
 
8
8
 
9
- class RevisionItem(object):
9
+ class RevisionItem:
10
10
  def __init__(self):
11
11
  self._resource_id: Optional[str] = None
12
12
  self._resource_name: Optional[str] = None
@@ -56,7 +56,7 @@ class RevisionItem(object):
56
56
  )
57
57
 
58
58
  @classmethod
59
- def from_response(cls, resp: bytes, ns, resource_item) -> List["RevisionItem"]:
59
+ def from_response(cls, resp: bytes, ns, resource_item) -> list["RevisionItem"]:
60
60
  all_revision_items = list()
61
61
  parsed_response = fromstring(resp)
62
62
  all_revision_xml = parsed_response.findall(".//t:revision", namespaces=ns)
@@ -19,7 +19,7 @@ from .property_decorators import (
19
19
  Interval = Union[HourlyInterval, DailyInterval, WeeklyInterval, MonthlyInterval]
20
20
 
21
21
 
22
- class ScheduleItem(object):
22
+ class ScheduleItem:
23
23
  class Type:
24
24
  Extract = "Extract"
25
25
  Flow = "Flow"
@@ -336,7 +336,7 @@ class ScheduleItem(object):
336
336
  all_task_xml = parsed_response.findall(".//t:task", namespaces=ns)
337
337
 
338
338
  error = (
339
- "Status {}: {}".format(response.status_code, response.reason)
339
+ f"Status {response.status_code}: {response.reason}"
340
340
  if response.status_code < 200 or response.status_code >= 300
341
341
  else None
342
342
  )