tableauserverclient 0.32__py3-none-any.whl → 0.33__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.
- tableauserverclient/__init__.py +10 -0
- tableauserverclient/_version.py +3 -3
- tableauserverclient/config.py +16 -4
- tableauserverclient/models/__init__.py +12 -0
- tableauserverclient/models/connection_item.py +4 -2
- tableauserverclient/models/database_item.py +6 -0
- tableauserverclient/models/flow_item.py +6 -6
- tableauserverclient/models/groupset_item.py +53 -0
- tableauserverclient/models/interval_item.py +27 -14
- tableauserverclient/models/job_item.py +18 -2
- tableauserverclient/models/linked_tasks_item.py +102 -0
- tableauserverclient/models/permissions_item.py +7 -4
- tableauserverclient/models/tableau_types.py +9 -7
- tableauserverclient/models/virtual_connection_item.py +77 -0
- tableauserverclient/server/endpoint/__init__.py +8 -0
- tableauserverclient/server/endpoint/auth_endpoint.py +3 -3
- tableauserverclient/server/endpoint/custom_views_endpoint.py +65 -4
- tableauserverclient/server/endpoint/databases_endpoint.py +21 -7
- tableauserverclient/server/endpoint/datasources_endpoint.py +105 -9
- tableauserverclient/server/endpoint/endpoint.py +32 -14
- tableauserverclient/server/endpoint/fileuploads_endpoint.py +2 -2
- tableauserverclient/server/endpoint/flow_runs_endpoint.py +44 -4
- tableauserverclient/server/endpoint/flows_endpoint.py +43 -6
- tableauserverclient/server/endpoint/groups_endpoint.py +82 -14
- tableauserverclient/server/endpoint/groupsets_endpoint.py +127 -0
- tableauserverclient/server/endpoint/jobs_endpoint.py +74 -7
- tableauserverclient/server/endpoint/linked_tasks_endpoint.py +45 -0
- tableauserverclient/server/endpoint/projects_endpoint.py +43 -0
- tableauserverclient/server/endpoint/resource_tagger.py +135 -3
- tableauserverclient/server/endpoint/tables_endpoint.py +19 -6
- tableauserverclient/server/endpoint/users_endpoint.py +39 -0
- tableauserverclient/server/endpoint/views_endpoint.py +94 -9
- tableauserverclient/server/endpoint/virtual_connections_endpoint.py +173 -0
- tableauserverclient/server/endpoint/workbooks_endpoint.py +91 -10
- tableauserverclient/server/pager.py +6 -7
- tableauserverclient/server/query.py +2 -1
- tableauserverclient/server/request_factory.py +178 -7
- tableauserverclient/server/request_options.py +4 -2
- tableauserverclient/server/server.py +8 -0
- {tableauserverclient-0.32.dist-info → tableauserverclient-0.33.dist-info}/METADATA +15 -15
- {tableauserverclient-0.32.dist-info → tableauserverclient-0.33.dist-info}/RECORD +45 -39
- {tableauserverclient-0.32.dist-info → tableauserverclient-0.33.dist-info}/WHEEL +1 -1
- {tableauserverclient-0.32.dist-info → tableauserverclient-0.33.dist-info}/LICENSE +0 -0
- {tableauserverclient-0.32.dist-info → tableauserverclient-0.33.dist-info}/LICENSE.versioneer +0 -0
- {tableauserverclient-0.32.dist-info → tableauserverclient-0.33.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import io
|
|
1
2
|
import logging
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
from
|
|
5
|
-
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import List, Optional, Tuple, Union
|
|
6
|
+
|
|
7
|
+
from tableauserverclient.config import BYTES_PER_MB, FILESIZE_LIMIT_MB
|
|
8
|
+
from tableauserverclient.filesys_helpers import get_file_object_size
|
|
9
|
+
from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api
|
|
10
|
+
from tableauserverclient.server.endpoint.exceptions import MissingRequiredFieldError
|
|
6
11
|
from tableauserverclient.models import CustomViewItem, PaginationItem
|
|
7
12
|
from tableauserverclient.server import RequestFactory, RequestOptions, ImageRequestOptions
|
|
8
13
|
|
|
@@ -16,6 +21,15 @@ Delete a custom view
|
|
|
16
21
|
update the name or owner of a custom view.
|
|
17
22
|
"""
|
|
18
23
|
|
|
24
|
+
FilePath = Union[str, os.PathLike]
|
|
25
|
+
FileObject = Union[io.BufferedReader, io.BytesIO]
|
|
26
|
+
FileObjectR = Union[io.BufferedReader, io.BytesIO]
|
|
27
|
+
FileObjectW = Union[io.BufferedWriter, io.BytesIO]
|
|
28
|
+
PathOrFileR = Union[FilePath, FileObjectR]
|
|
29
|
+
PathOrFileW = Union[FilePath, FileObjectW]
|
|
30
|
+
io_types_r = (io.BufferedReader, io.BytesIO)
|
|
31
|
+
io_types_w = (io.BufferedWriter, io.BytesIO)
|
|
32
|
+
|
|
19
33
|
|
|
20
34
|
class CustomViews(QuerysetEndpoint[CustomViewItem]):
|
|
21
35
|
def __init__(self, parent_srv):
|
|
@@ -25,6 +39,10 @@ class CustomViews(QuerysetEndpoint[CustomViewItem]):
|
|
|
25
39
|
def baseurl(self) -> str:
|
|
26
40
|
return "{0}/sites/{1}/customviews".format(self.parent_srv.baseurl, self.parent_srv.site_id)
|
|
27
41
|
|
|
42
|
+
@property
|
|
43
|
+
def expurl(self) -> str:
|
|
44
|
+
return f"{self.parent_srv._server_address}/api/exp/sites/{self.parent_srv.site_id}/customviews"
|
|
45
|
+
|
|
28
46
|
"""
|
|
29
47
|
If the request has no filter parameters: Administrators will see all custom views.
|
|
30
48
|
Other users will see only custom views that they own.
|
|
@@ -102,3 +120,46 @@ class CustomViews(QuerysetEndpoint[CustomViewItem]):
|
|
|
102
120
|
url = "{0}/{1}".format(self.baseurl, view_id)
|
|
103
121
|
self.delete_request(url)
|
|
104
122
|
logger.info("Deleted single custom view (ID: {0})".format(view_id))
|
|
123
|
+
|
|
124
|
+
@api(version="3.21")
|
|
125
|
+
def download(self, view_item: CustomViewItem, file: PathOrFileW) -> PathOrFileW:
|
|
126
|
+
url = f"{self.expurl}/{view_item.id}/content"
|
|
127
|
+
server_response = self.get_request(url)
|
|
128
|
+
if isinstance(file, io_types_w):
|
|
129
|
+
file.write(server_response.content)
|
|
130
|
+
return file
|
|
131
|
+
|
|
132
|
+
with open(file, "wb") as f:
|
|
133
|
+
f.write(server_response.content)
|
|
134
|
+
|
|
135
|
+
return file
|
|
136
|
+
|
|
137
|
+
@api(version="3.21")
|
|
138
|
+
def publish(self, view_item: CustomViewItem, file: PathOrFileR) -> Optional[CustomViewItem]:
|
|
139
|
+
url = self.expurl
|
|
140
|
+
if isinstance(file, io_types_r):
|
|
141
|
+
size = get_file_object_size(file)
|
|
142
|
+
elif isinstance(file, (str, Path)) and (p := Path(file)).is_file():
|
|
143
|
+
size = p.stat().st_size
|
|
144
|
+
else:
|
|
145
|
+
raise ValueError("File path or file object required for publishing custom view.")
|
|
146
|
+
|
|
147
|
+
if size >= FILESIZE_LIMIT_MB * BYTES_PER_MB:
|
|
148
|
+
upload_session_id = self.parent_srv.fileuploads.upload(file)
|
|
149
|
+
url = f"{url}?uploadSessionId={upload_session_id}"
|
|
150
|
+
xml_request, content_type = RequestFactory.CustomView.publish_req_chunked(view_item)
|
|
151
|
+
else:
|
|
152
|
+
if isinstance(file, io_types_r):
|
|
153
|
+
file.seek(0)
|
|
154
|
+
contents = file.read()
|
|
155
|
+
if view_item.name is None:
|
|
156
|
+
raise MissingRequiredFieldError("Custom view item missing name.")
|
|
157
|
+
filename = view_item.name
|
|
158
|
+
elif isinstance(file, (str, Path)):
|
|
159
|
+
filename = Path(file).name
|
|
160
|
+
contents = Path(file).read_bytes()
|
|
161
|
+
|
|
162
|
+
xml_request, content_type = RequestFactory.CustomView.publish_req(view_item, filename, contents)
|
|
163
|
+
|
|
164
|
+
server_response = self.post_request(url, xml_request, content_type)
|
|
165
|
+
return CustomViewItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
from .
|
|
5
|
-
from .endpoint import
|
|
6
|
-
from .
|
|
7
|
-
from .
|
|
2
|
+
from typing import Union, Iterable, Set
|
|
3
|
+
|
|
4
|
+
from tableauserverclient.server.endpoint.default_permissions_endpoint import _DefaultPermissionsEndpoint
|
|
5
|
+
from tableauserverclient.server.endpoint.dqw_endpoint import _DataQualityWarningEndpoint
|
|
6
|
+
from tableauserverclient.server.endpoint.endpoint import api, Endpoint
|
|
7
|
+
from tableauserverclient.server.endpoint.exceptions import MissingRequiredFieldError
|
|
8
|
+
from tableauserverclient.server.endpoint.permissions_endpoint import _PermissionsEndpoint
|
|
9
|
+
from tableauserverclient.server.endpoint.resource_tagger import TaggingMixin
|
|
8
10
|
from tableauserverclient.server import RequestFactory
|
|
9
11
|
from tableauserverclient.models import DatabaseItem, TableItem, PaginationItem, Resource
|
|
10
12
|
|
|
11
13
|
from tableauserverclient.helpers.logging import logger
|
|
12
14
|
|
|
13
15
|
|
|
14
|
-
class Databases(Endpoint):
|
|
16
|
+
class Databases(Endpoint, TaggingMixin):
|
|
15
17
|
def __init__(self, parent_srv):
|
|
16
18
|
super(Databases, self).__init__(parent_srv)
|
|
17
19
|
|
|
@@ -123,3 +125,15 @@ class Databases(Endpoint):
|
|
|
123
125
|
@api(version="3.5")
|
|
124
126
|
def delete_dqw(self, item):
|
|
125
127
|
self._data_quality_warnings.clear(item)
|
|
128
|
+
|
|
129
|
+
@api(version="3.9")
|
|
130
|
+
def add_tags(self, item: Union[DatabaseItem, str], tags: Iterable[str]) -> Set[str]:
|
|
131
|
+
return super().add_tags(item, tags)
|
|
132
|
+
|
|
133
|
+
@api(version="3.9")
|
|
134
|
+
def delete_tags(self, item: Union[DatabaseItem, str], tags: Iterable[str]) -> None:
|
|
135
|
+
super().delete_tags(item, tags)
|
|
136
|
+
|
|
137
|
+
@api(version="3.9")
|
|
138
|
+
def update_tags(self, item: DatabaseItem) -> None:
|
|
139
|
+
raise NotImplementedError("Update tags is not supported for databases.")
|
|
@@ -6,9 +6,10 @@ import os
|
|
|
6
6
|
|
|
7
7
|
from contextlib import closing
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from typing import List, Mapping, Optional, Sequence, Tuple, TYPE_CHECKING, Union
|
|
9
|
+
from typing import Iterable, List, Mapping, Optional, Sequence, Set, Tuple, TYPE_CHECKING, Union
|
|
10
10
|
|
|
11
11
|
from tableauserverclient.helpers.headers import fix_filename
|
|
12
|
+
from tableauserverclient.server.query import QuerySet
|
|
12
13
|
|
|
13
14
|
if TYPE_CHECKING:
|
|
14
15
|
from tableauserverclient.server import Server
|
|
@@ -19,9 +20,9 @@ from tableauserverclient.server.endpoint.dqw_endpoint import _DataQualityWarning
|
|
|
19
20
|
from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api, parameter_added_in
|
|
20
21
|
from tableauserverclient.server.endpoint.exceptions import InternalServerError, MissingRequiredFieldError
|
|
21
22
|
from tableauserverclient.server.endpoint.permissions_endpoint import _PermissionsEndpoint
|
|
22
|
-
from tableauserverclient.server.endpoint.resource_tagger import
|
|
23
|
+
from tableauserverclient.server.endpoint.resource_tagger import TaggingMixin
|
|
23
24
|
|
|
24
|
-
from tableauserverclient.config import ALLOWED_FILE_EXTENSIONS, FILESIZE_LIMIT_MB, BYTES_PER_MB,
|
|
25
|
+
from tableauserverclient.config import ALLOWED_FILE_EXTENSIONS, FILESIZE_LIMIT_MB, BYTES_PER_MB, config
|
|
25
26
|
from tableauserverclient.filesys_helpers import (
|
|
26
27
|
make_download_path,
|
|
27
28
|
get_file_type,
|
|
@@ -54,10 +55,9 @@ PathOrFileR = Union[FilePath, FileObjectR]
|
|
|
54
55
|
PathOrFileW = Union[FilePath, FileObjectW]
|
|
55
56
|
|
|
56
57
|
|
|
57
|
-
class Datasources(QuerysetEndpoint[DatasourceItem]):
|
|
58
|
+
class Datasources(QuerysetEndpoint[DatasourceItem], TaggingMixin[DatasourceItem]):
|
|
58
59
|
def __init__(self, parent_srv: "Server") -> None:
|
|
59
60
|
super(Datasources, self).__init__(parent_srv)
|
|
60
|
-
self._resource_tagger = _ResourceTagger(parent_srv)
|
|
61
61
|
self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
|
|
62
62
|
self._data_quality_warnings = _DataQualityWarningEndpoint(self.parent_srv, "datasource")
|
|
63
63
|
|
|
@@ -126,7 +126,7 @@ class Datasources(QuerysetEndpoint[DatasourceItem]):
|
|
|
126
126
|
datasource_id: str,
|
|
127
127
|
filepath: Optional[PathOrFileW] = None,
|
|
128
128
|
include_extract: bool = True,
|
|
129
|
-
) ->
|
|
129
|
+
) -> PathOrFileW:
|
|
130
130
|
return self.download_revision(
|
|
131
131
|
datasource_id,
|
|
132
132
|
None,
|
|
@@ -149,7 +149,7 @@ class Datasources(QuerysetEndpoint[DatasourceItem]):
|
|
|
149
149
|
)
|
|
150
150
|
raise MissingRequiredFieldError(error)
|
|
151
151
|
|
|
152
|
-
self.
|
|
152
|
+
self.update_tags(datasource_item)
|
|
153
153
|
|
|
154
154
|
# Update the datasource itself
|
|
155
155
|
url = "{0}/{1}".format(self.baseurl, datasource_item.id)
|
|
@@ -272,7 +272,7 @@ class Datasources(QuerysetEndpoint[DatasourceItem]):
|
|
|
272
272
|
if file_size >= FILESIZE_LIMIT_MB * BYTES_PER_MB:
|
|
273
273
|
logger.info(
|
|
274
274
|
"Publishing {} to server with chunking method (datasource over {}MB, chunk size {}MB)".format(
|
|
275
|
-
filename, FILESIZE_LIMIT_MB, CHUNK_SIZE_MB
|
|
275
|
+
filename, FILESIZE_LIMIT_MB, config.CHUNK_SIZE_MB
|
|
276
276
|
)
|
|
277
277
|
)
|
|
278
278
|
upload_session_id = self.parent_srv.fileuploads.upload(file)
|
|
@@ -405,7 +405,7 @@ class Datasources(QuerysetEndpoint[DatasourceItem]):
|
|
|
405
405
|
def download_revision(
|
|
406
406
|
self,
|
|
407
407
|
datasource_id: str,
|
|
408
|
-
revision_number: str,
|
|
408
|
+
revision_number: Optional[str],
|
|
409
409
|
filepath: Optional[PathOrFileW] = None,
|
|
410
410
|
include_extract: bool = True,
|
|
411
411
|
) -> PathOrFileW:
|
|
@@ -459,3 +459,99 @@ class Datasources(QuerysetEndpoint[DatasourceItem]):
|
|
|
459
459
|
self, schedule_id: str, item: DatasourceItem
|
|
460
460
|
) -> List["AddResponse"]: # actually should return a task
|
|
461
461
|
return self.parent_srv.schedules.add_to_schedule(schedule_id, datasource=item)
|
|
462
|
+
|
|
463
|
+
@api(version="1.0")
|
|
464
|
+
def add_tags(self, item: Union[DatasourceItem, str], tags: Union[Iterable[str], str]) -> Set[str]:
|
|
465
|
+
return super().add_tags(item, tags)
|
|
466
|
+
|
|
467
|
+
@api(version="1.0")
|
|
468
|
+
def delete_tags(self, item: Union[DatasourceItem, str], tags: Union[Iterable[str], str]) -> None:
|
|
469
|
+
return super().delete_tags(item, tags)
|
|
470
|
+
|
|
471
|
+
@api(version="1.0")
|
|
472
|
+
def update_tags(self, item: DatasourceItem) -> None:
|
|
473
|
+
return super().update_tags(item)
|
|
474
|
+
|
|
475
|
+
def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[DatasourceItem]:
|
|
476
|
+
"""
|
|
477
|
+
Queries the Tableau Server for items using the specified filters. Page
|
|
478
|
+
size can be specified to limit the number of items returned in a single
|
|
479
|
+
request. If not specified, the default page size is 100. Page size can
|
|
480
|
+
be an integer between 1 and 1000.
|
|
481
|
+
|
|
482
|
+
No positional arguments are allowed. All filters must be specified as
|
|
483
|
+
keyword arguments. If you use the equality operator, you can specify it
|
|
484
|
+
through <field_name>=<value>. If you want to use a different operator,
|
|
485
|
+
you can specify it through <field_name>__<operator>=<value>. Field
|
|
486
|
+
names can either be in snake_case or camelCase.
|
|
487
|
+
|
|
488
|
+
This endpoint supports the following fields and operators:
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
authentication_type=...
|
|
492
|
+
authentication_type__in=...
|
|
493
|
+
connected_workbook_type=...
|
|
494
|
+
connected_workbook_type__gt=...
|
|
495
|
+
connected_workbook_type__gte=...
|
|
496
|
+
connected_workbook_type__lt=...
|
|
497
|
+
connected_workbook_type__lte=...
|
|
498
|
+
connection_to=...
|
|
499
|
+
connection_to__in=...
|
|
500
|
+
connection_type=...
|
|
501
|
+
connection_type__in=...
|
|
502
|
+
content_url=...
|
|
503
|
+
content_url__in=...
|
|
504
|
+
created_at=...
|
|
505
|
+
created_at__gt=...
|
|
506
|
+
created_at__gte=...
|
|
507
|
+
created_at__lt=...
|
|
508
|
+
created_at__lte=...
|
|
509
|
+
database_name=...
|
|
510
|
+
database_name__in=...
|
|
511
|
+
database_user_name=...
|
|
512
|
+
database_user_name__in=...
|
|
513
|
+
description=...
|
|
514
|
+
description__in=...
|
|
515
|
+
favorites_total=...
|
|
516
|
+
favorites_total__gt=...
|
|
517
|
+
favorites_total__gte=...
|
|
518
|
+
favorites_total__lt=...
|
|
519
|
+
favorites_total__lte=...
|
|
520
|
+
has_alert=...
|
|
521
|
+
has_embedded_password=...
|
|
522
|
+
has_extracts=...
|
|
523
|
+
is_certified=...
|
|
524
|
+
is_connectable=...
|
|
525
|
+
is_default_port=...
|
|
526
|
+
is_hierarchical=...
|
|
527
|
+
is_published=...
|
|
528
|
+
name=...
|
|
529
|
+
name__in=...
|
|
530
|
+
owner_domain=...
|
|
531
|
+
owner_domain__in=...
|
|
532
|
+
owner_email=...
|
|
533
|
+
owner_name=...
|
|
534
|
+
owner_name__in=...
|
|
535
|
+
project_name=...
|
|
536
|
+
project_name__in=...
|
|
537
|
+
server_name=...
|
|
538
|
+
server_name__in=...
|
|
539
|
+
server_port=...
|
|
540
|
+
size=...
|
|
541
|
+
size__gt=...
|
|
542
|
+
size__gte=...
|
|
543
|
+
size__lt=...
|
|
544
|
+
size__lte=...
|
|
545
|
+
table_name=...
|
|
546
|
+
table_name__in=...
|
|
547
|
+
tags=...
|
|
548
|
+
tags__in=...
|
|
549
|
+
type=...
|
|
550
|
+
updated_at=...
|
|
551
|
+
updated_at__gt=...
|
|
552
|
+
updated_at__gte=...
|
|
553
|
+
updated_at__lt=...
|
|
554
|
+
updated_at__lte=...
|
|
555
|
+
"""
|
|
556
|
+
|
|
557
|
+
return super().filter(*invalid, page_size=page_size, **kwargs)
|
|
@@ -1,30 +1,41 @@
|
|
|
1
|
+
from typing_extensions import Concatenate, ParamSpec
|
|
1
2
|
from tableauserverclient import datetime_helpers as datetime
|
|
2
3
|
|
|
3
4
|
import abc
|
|
4
5
|
from packaging.version import Version
|
|
5
6
|
from functools import wraps
|
|
6
7
|
from xml.etree.ElementTree import ParseError
|
|
7
|
-
from typing import
|
|
8
|
+
from typing import (
|
|
9
|
+
Any,
|
|
10
|
+
Callable,
|
|
11
|
+
Dict,
|
|
12
|
+
Generic,
|
|
13
|
+
List,
|
|
14
|
+
Optional,
|
|
15
|
+
TYPE_CHECKING,
|
|
16
|
+
Tuple,
|
|
17
|
+
TypeVar,
|
|
18
|
+
Union,
|
|
19
|
+
)
|
|
8
20
|
|
|
9
21
|
from tableauserverclient.models.pagination_item import PaginationItem
|
|
10
22
|
from tableauserverclient.server.request_options import RequestOptions
|
|
11
23
|
|
|
12
|
-
from .exceptions import (
|
|
24
|
+
from tableauserverclient.server.endpoint.exceptions import (
|
|
13
25
|
ServerResponseError,
|
|
14
26
|
InternalServerError,
|
|
15
27
|
NonXMLResponseError,
|
|
16
28
|
NotSignedInError,
|
|
17
29
|
)
|
|
18
|
-
from
|
|
30
|
+
from tableauserverclient.server.exceptions import EndpointUnavailableError
|
|
19
31
|
|
|
20
32
|
from tableauserverclient.server.query import QuerySet
|
|
21
33
|
from tableauserverclient import helpers, get_versions
|
|
22
34
|
|
|
23
35
|
from tableauserverclient.helpers.logging import logger
|
|
24
|
-
from tableauserverclient.config import DELAY_SLEEP_SECONDS
|
|
25
36
|
|
|
26
37
|
if TYPE_CHECKING:
|
|
27
|
-
from
|
|
38
|
+
from tableauserverclient.server.server import Server
|
|
28
39
|
from requests import Response
|
|
29
40
|
|
|
30
41
|
|
|
@@ -38,7 +49,7 @@ TABLEAU_AUTH_HEADER = "x-tableau-auth"
|
|
|
38
49
|
USER_AGENT_HEADER = "User-Agent"
|
|
39
50
|
|
|
40
51
|
|
|
41
|
-
class Endpoint
|
|
52
|
+
class Endpoint:
|
|
42
53
|
def __init__(self, parent_srv: "Server"):
|
|
43
54
|
self.parent_srv = parent_srv
|
|
44
55
|
|
|
@@ -133,7 +144,9 @@ class Endpoint(object):
|
|
|
133
144
|
|
|
134
145
|
loggable_response = self.log_response_safely(server_response)
|
|
135
146
|
logger.debug("Server response from {0}".format(url))
|
|
136
|
-
#
|
|
147
|
+
# uncomment the following to log full responses in debug mode
|
|
148
|
+
# BE CAREFUL WHEN SHARING THESE RESULTS - MAY CONTAIN YOUR SENSITIVE DATA
|
|
149
|
+
# logger.debug(loggable_response)
|
|
137
150
|
|
|
138
151
|
if content_type == "application/xml":
|
|
139
152
|
self.parent_srv._namespace.detect(server_response.content)
|
|
@@ -232,7 +245,12 @@ class Endpoint(object):
|
|
|
232
245
|
)
|
|
233
246
|
|
|
234
247
|
|
|
235
|
-
|
|
248
|
+
E = TypeVar("E", bound="Endpoint")
|
|
249
|
+
P = ParamSpec("P")
|
|
250
|
+
R = TypeVar("R")
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def api(version: str) -> Callable[[Callable[Concatenate[E, P], R]], Callable[Concatenate[E, P], R]]:
|
|
236
254
|
"""Annotate the minimum supported version for an endpoint.
|
|
237
255
|
|
|
238
256
|
Checks the version on the server object and compares normalized versions.
|
|
@@ -251,9 +269,9 @@ def api(version):
|
|
|
251
269
|
>>> ...
|
|
252
270
|
"""
|
|
253
271
|
|
|
254
|
-
def _decorator(func):
|
|
272
|
+
def _decorator(func: Callable[Concatenate[E, P], R]) -> Callable[Concatenate[E, P], R]:
|
|
255
273
|
@wraps(func)
|
|
256
|
-
def wrapper(self, *args, **kwargs):
|
|
274
|
+
def wrapper(self: E, *args: P.args, **kwargs: P.kwargs) -> R:
|
|
257
275
|
self.parent_srv.assert_at_least_version(version, self.__class__.__name__)
|
|
258
276
|
return func(self, *args, **kwargs)
|
|
259
277
|
|
|
@@ -262,7 +280,7 @@ def api(version):
|
|
|
262
280
|
return _decorator
|
|
263
281
|
|
|
264
282
|
|
|
265
|
-
def parameter_added_in(**params):
|
|
283
|
+
def parameter_added_in(**params: str) -> Callable[[Callable[Concatenate[E, P], R]], Callable[Concatenate[E, P], R]]:
|
|
266
284
|
"""Annotate minimum versions for new parameters or request options on an endpoint.
|
|
267
285
|
|
|
268
286
|
The api decorator documents when an endpoint was added, this decorator annotates
|
|
@@ -285,9 +303,9 @@ def parameter_added_in(**params):
|
|
|
285
303
|
>>> ...
|
|
286
304
|
"""
|
|
287
305
|
|
|
288
|
-
def _decorator(func):
|
|
306
|
+
def _decorator(func: Callable[Concatenate[E, P], R]) -> Callable[Concatenate[E, P], R]:
|
|
289
307
|
@wraps(func)
|
|
290
|
-
def wrapper(self, *args, **kwargs):
|
|
308
|
+
def wrapper(self: E, *args: P.args, **kwargs: P.kwargs) -> R:
|
|
291
309
|
import warnings
|
|
292
310
|
|
|
293
311
|
server_ver = Version(self.parent_srv.version or "0.0")
|
|
@@ -335,5 +353,5 @@ class QuerysetEndpoint(Endpoint, Generic[T]):
|
|
|
335
353
|
return queryset
|
|
336
354
|
|
|
337
355
|
@abc.abstractmethod
|
|
338
|
-
def get(self, request_options: RequestOptions) -> Tuple[List[T], PaginationItem]:
|
|
356
|
+
def get(self, request_options: Optional[RequestOptions] = None) -> Tuple[List[T], PaginationItem]:
|
|
339
357
|
raise NotImplementedError(f".get has not been implemented for {self.__class__.__qualname__}")
|
|
@@ -2,7 +2,7 @@ from .endpoint import Endpoint, api
|
|
|
2
2
|
from tableauserverclient import datetime_helpers as datetime
|
|
3
3
|
from tableauserverclient.helpers.logging import logger
|
|
4
4
|
|
|
5
|
-
from tableauserverclient.config import BYTES_PER_MB,
|
|
5
|
+
from tableauserverclient.config import BYTES_PER_MB, config
|
|
6
6
|
from tableauserverclient.models import FileuploadItem
|
|
7
7
|
from tableauserverclient.server import RequestFactory
|
|
8
8
|
|
|
@@ -41,7 +41,7 @@ class Fileuploads(Endpoint):
|
|
|
41
41
|
|
|
42
42
|
try:
|
|
43
43
|
while True:
|
|
44
|
-
chunked_content = file_content.read(CHUNK_SIZE_MB * BYTES_PER_MB)
|
|
44
|
+
chunked_content = file_content.read(config.CHUNK_SIZE_MB * BYTES_PER_MB)
|
|
45
45
|
if not chunked_content:
|
|
46
46
|
break
|
|
47
47
|
yield chunked_content
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from typing import List, Optional, Tuple, TYPE_CHECKING
|
|
3
3
|
|
|
4
|
-
from .endpoint import QuerysetEndpoint, api
|
|
5
|
-
from .exceptions import FlowRunFailedException, FlowRunCancelledException
|
|
4
|
+
from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api
|
|
5
|
+
from tableauserverclient.server.endpoint.exceptions import FlowRunFailedException, FlowRunCancelledException
|
|
6
6
|
from tableauserverclient.models import FlowRunItem, PaginationItem
|
|
7
7
|
from tableauserverclient.exponential_backoff import ExponentialBackoffTimer
|
|
8
8
|
|
|
9
9
|
from tableauserverclient.helpers.logging import logger
|
|
10
|
+
from tableauserverclient.server.query import QuerySet
|
|
10
11
|
|
|
11
12
|
if TYPE_CHECKING:
|
|
12
|
-
from
|
|
13
|
-
from
|
|
13
|
+
from tableauserverclient.server.server import Server
|
|
14
|
+
from tableauserverclient.server.request_options import RequestOptions
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class FlowRuns(QuerysetEndpoint[FlowRunItem]):
|
|
@@ -78,3 +79,42 @@ class FlowRuns(QuerysetEndpoint[FlowRunItem]):
|
|
|
78
79
|
raise FlowRunCancelledException(flow_run)
|
|
79
80
|
else:
|
|
80
81
|
raise AssertionError("Unexpected status in flow_run", flow_run)
|
|
82
|
+
|
|
83
|
+
def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[FlowRunItem]:
|
|
84
|
+
"""
|
|
85
|
+
Queries the Tableau Server for items using the specified filters. Page
|
|
86
|
+
size can be specified to limit the number of items returned in a single
|
|
87
|
+
request. If not specified, the default page size is 100. Page size can
|
|
88
|
+
be an integer between 1 and 1000.
|
|
89
|
+
|
|
90
|
+
No positional arguments are allowed. All filters must be specified as
|
|
91
|
+
keyword arguments. If you use the equality operator, you can specify it
|
|
92
|
+
through <field_name>=<value>. If you want to use a different operator,
|
|
93
|
+
you can specify it through <field_name>__<operator>=<value>. Field
|
|
94
|
+
names can either be in snake_case or camelCase.
|
|
95
|
+
|
|
96
|
+
This endpoint supports the following fields and operators:
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
complete_at=...
|
|
100
|
+
complete_at__gt=...
|
|
101
|
+
complete_at__gte=...
|
|
102
|
+
complete_at__lt=...
|
|
103
|
+
complete_at__lte=...
|
|
104
|
+
flow_id=...
|
|
105
|
+
flow_id__in=...
|
|
106
|
+
progress=...
|
|
107
|
+
progress__gt=...
|
|
108
|
+
progress__gte=...
|
|
109
|
+
progress__lt=...
|
|
110
|
+
progress__lte=...
|
|
111
|
+
started_at=...
|
|
112
|
+
started_at__gt=...
|
|
113
|
+
started_at__gte=...
|
|
114
|
+
started_at__lt=...
|
|
115
|
+
started_at__lte=...
|
|
116
|
+
user_id=...
|
|
117
|
+
user_id__in=...
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
return super().filter(*invalid, page_size=page_size, **kwargs)
|
|
@@ -9,11 +9,11 @@ from typing import Iterable, List, Optional, TYPE_CHECKING, Tuple, Union
|
|
|
9
9
|
|
|
10
10
|
from tableauserverclient.helpers.headers import fix_filename
|
|
11
11
|
|
|
12
|
-
from .dqw_endpoint import _DataQualityWarningEndpoint
|
|
13
|
-
from .endpoint import QuerysetEndpoint, api
|
|
14
|
-
from .exceptions import InternalServerError, MissingRequiredFieldError
|
|
15
|
-
from .permissions_endpoint import _PermissionsEndpoint
|
|
16
|
-
from .resource_tagger import _ResourceTagger
|
|
12
|
+
from tableauserverclient.server.endpoint.dqw_endpoint import _DataQualityWarningEndpoint
|
|
13
|
+
from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api
|
|
14
|
+
from tableauserverclient.server.endpoint.exceptions import InternalServerError, MissingRequiredFieldError
|
|
15
|
+
from tableauserverclient.server.endpoint.permissions_endpoint import _PermissionsEndpoint
|
|
16
|
+
from tableauserverclient.server.endpoint.resource_tagger import _ResourceTagger, TaggingMixin
|
|
17
17
|
from tableauserverclient.models import FlowItem, PaginationItem, ConnectionItem, JobItem
|
|
18
18
|
from tableauserverclient.server import RequestFactory
|
|
19
19
|
from tableauserverclient.filesys_helpers import (
|
|
@@ -22,6 +22,7 @@ from tableauserverclient.filesys_helpers import (
|
|
|
22
22
|
get_file_type,
|
|
23
23
|
get_file_object_size,
|
|
24
24
|
)
|
|
25
|
+
from tableauserverclient.server.query import QuerySet
|
|
25
26
|
|
|
26
27
|
io_types_r = (io.BytesIO, io.BufferedReader)
|
|
27
28
|
io_types_w = (io.BytesIO, io.BufferedWriter)
|
|
@@ -50,7 +51,7 @@ PathOrFileR = Union[FilePath, FileObjectR]
|
|
|
50
51
|
PathOrFileW = Union[FilePath, FileObjectW]
|
|
51
52
|
|
|
52
53
|
|
|
53
|
-
class Flows(QuerysetEndpoint[FlowItem]):
|
|
54
|
+
class Flows(QuerysetEndpoint[FlowItem], TaggingMixin[FlowItem]):
|
|
54
55
|
def __init__(self, parent_srv):
|
|
55
56
|
super(Flows, self).__init__(parent_srv)
|
|
56
57
|
self._resource_tagger = _ResourceTagger(parent_srv)
|
|
@@ -295,3 +296,39 @@ class Flows(QuerysetEndpoint[FlowItem]):
|
|
|
295
296
|
self, schedule_id: str, item: FlowItem
|
|
296
297
|
) -> List["AddResponse"]: # actually should return a task
|
|
297
298
|
return self.parent_srv.schedules.add_to_schedule(schedule_id, flow=item)
|
|
299
|
+
|
|
300
|
+
def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[FlowItem]:
|
|
301
|
+
"""
|
|
302
|
+
Queries the Tableau Server for items using the specified filters. Page
|
|
303
|
+
size can be specified to limit the number of items returned in a single
|
|
304
|
+
request. If not specified, the default page size is 100. Page size can
|
|
305
|
+
be an integer between 1 and 1000.
|
|
306
|
+
|
|
307
|
+
No positional arguments are allowed. All filters must be specified as
|
|
308
|
+
keyword arguments. If you use the equality operator, you can specify it
|
|
309
|
+
through <field_name>=<value>. If you want to use a different operator,
|
|
310
|
+
you can specify it through <field_name>__<operator>=<value>. Field
|
|
311
|
+
names can either be in snake_case or camelCase.
|
|
312
|
+
|
|
313
|
+
This endpoint supports the following fields and operators:
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
created_at=...
|
|
317
|
+
created_at__gt=...
|
|
318
|
+
created_at__gte=...
|
|
319
|
+
created_at__lt=...
|
|
320
|
+
created_at__lte=...
|
|
321
|
+
name=...
|
|
322
|
+
name__in=...
|
|
323
|
+
owner_name=...
|
|
324
|
+
project_id=...
|
|
325
|
+
project_name=...
|
|
326
|
+
project_name__in=...
|
|
327
|
+
updated=...
|
|
328
|
+
updated__gt=...
|
|
329
|
+
updated__gte=...
|
|
330
|
+
updated__lt=...
|
|
331
|
+
updated__lte=...
|
|
332
|
+
"""
|
|
333
|
+
|
|
334
|
+
return super().filter(*invalid, page_size=page_size, **kwargs)
|