tableauserverclient 0.38__py3-none-any.whl → 0.39__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/bin/_version.py → _version.py +3 -3
- bin/__init__.py +3 -0
- bin/_version.py +21 -0
- {tableauserverclient/models → models}/__init__.py +9 -0
- models/collection_item.py +52 -0
- {tableauserverclient/models → models}/connection_item.py +16 -2
- {tableauserverclient/models → models}/custom_view_item.py +8 -0
- {tableauserverclient/models → models}/data_freshness_policy_item.py +3 -3
- {tableauserverclient/models → models}/datasource_item.py +3 -1
- models/extensions_item.py +186 -0
- {tableauserverclient/models → models}/favorites_item.py +21 -8
- {tableauserverclient/models → models}/flow_item.py +1 -1
- {tableauserverclient/models → models}/group_item.py +7 -1
- {tableauserverclient/models → models}/groupset_item.py +14 -0
- {tableauserverclient/models → models}/interval_item.py +2 -1
- models/oidc_item.py +82 -0
- {tableauserverclient/models → models}/permissions_item.py +2 -0
- {tableauserverclient/models → models}/project_item.py +3 -2
- {tableauserverclient/models → models}/property_decorators.py +2 -2
- {tableauserverclient/models → models}/reference_item.py +12 -6
- {tableauserverclient/models → models}/schedule_item.py +10 -1
- {tableauserverclient/models → models}/site_item.py +26 -0
- {tableauserverclient/models → models}/tableau_auth.py +13 -6
- {tableauserverclient/models → models}/user_item.py +10 -3
- {tableauserverclient/models → models}/workbook_item.py +2 -2
- {tableauserverclient/server → server}/endpoint/__init__.py +4 -0
- {tableauserverclient/server → server}/endpoint/datasources_endpoint.py +152 -22
- server/endpoint/extensions_endpoint.py +79 -0
- {tableauserverclient/server → server}/endpoint/flow_task_endpoint.py +1 -1
- {tableauserverclient/server → server}/endpoint/flows_endpoint.py +5 -4
- server/endpoint/oidc_endpoint.py +157 -0
- {tableauserverclient/server → server}/endpoint/projects_endpoint.py +12 -0
- {tableauserverclient/server → server}/endpoint/schedules_endpoint.py +48 -1
- {tableauserverclient/server → server}/endpoint/users_endpoint.py +274 -5
- {tableauserverclient/server → server}/endpoint/views_endpoint.py +23 -0
- {tableauserverclient/server → server}/endpoint/workbooks_endpoint.py +124 -9
- {tableauserverclient/server → server}/request_factory.py +281 -2
- {tableauserverclient/server → server}/request_options.py +12 -2
- {tableauserverclient/server → server}/server.py +4 -0
- {tableauserverclient-0.38.dist-info → tableauserverclient-0.39.dist-info}/METADATA +5 -26
- tableauserverclient-0.39.dist-info/RECORD +107 -0
- {tableauserverclient-0.38.dist-info → tableauserverclient-0.39.dist-info}/WHEEL +1 -1
- tableauserverclient-0.39.dist-info/top_level.txt +4 -0
- tableauserverclient/__init__.py +0 -145
- tableauserverclient/config.py +0 -27
- tableauserverclient/datetime_helpers.py +0 -45
- tableauserverclient/exponential_backoff.py +0 -30
- tableauserverclient/filesys_helpers.py +0 -63
- tableauserverclient/namespace.py +0 -37
- tableauserverclient/py.typed +0 -0
- tableauserverclient-0.38.dist-info/RECORD +0 -108
- tableauserverclient-0.38.dist-info/licenses/LICENSE.versioneer +0 -7
- tableauserverclient-0.38.dist-info/top_level.txt +0 -1
- {tableauserverclient/helpers → helpers}/__init__.py +0 -0
- {tableauserverclient/helpers → helpers}/headers.py +0 -0
- {tableauserverclient/helpers → helpers}/logging.py +0 -0
- {tableauserverclient/helpers → helpers}/strings.py +0 -0
- {tableauserverclient/models → models}/column_item.py +0 -0
- {tableauserverclient/models → models}/connection_credentials.py +0 -0
- {tableauserverclient/models → models}/data_acceleration_report_item.py +0 -0
- {tableauserverclient/models → models}/data_alert_item.py +0 -0
- {tableauserverclient/models → models}/database_item.py +0 -0
- {tableauserverclient/models → models}/dqw_item.py +0 -0
- {tableauserverclient/models → models}/exceptions.py +0 -0
- {tableauserverclient/models → models}/extract_item.py +0 -0
- {tableauserverclient/models → models}/fileupload_item.py +0 -0
- {tableauserverclient/models → models}/flow_run_item.py +0 -0
- {tableauserverclient/models → models}/job_item.py +0 -0
- {tableauserverclient/models → models}/linked_tasks_item.py +0 -0
- {tableauserverclient/models → models}/location_item.py +0 -0
- {tableauserverclient/models → models}/metric_item.py +0 -0
- {tableauserverclient/models → models}/pagination_item.py +0 -0
- {tableauserverclient/models → models}/revision_item.py +0 -0
- {tableauserverclient/models → models}/server_info_item.py +0 -0
- {tableauserverclient/models → models}/subscription_item.py +0 -0
- {tableauserverclient/models → models}/table_item.py +0 -0
- {tableauserverclient/models → models}/tableau_types.py +0 -0
- {tableauserverclient/models → models}/tag_item.py +0 -0
- {tableauserverclient/models → models}/target.py +0 -0
- {tableauserverclient/models → models}/task_item.py +0 -0
- {tableauserverclient/models → models}/view_item.py +0 -0
- {tableauserverclient/models → models}/virtual_connection_item.py +0 -0
- {tableauserverclient/models → models}/webhook_item.py +0 -0
- {tableauserverclient/server → server}/__init__.py +0 -0
- {tableauserverclient/server → server}/endpoint/auth_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/custom_views_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/data_acceleration_report_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/data_alert_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/databases_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/default_permissions_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/dqw_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/exceptions.py +0 -0
- {tableauserverclient/server → server}/endpoint/favorites_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/fileuploads_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/flow_runs_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/groups_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/groupsets_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/jobs_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/linked_tasks_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/metadata_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/metrics_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/permissions_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/resource_tagger.py +0 -0
- {tableauserverclient/server → server}/endpoint/server_info_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/sites_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/subscriptions_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/tables_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/tasks_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/virtual_connections_endpoint.py +0 -0
- {tableauserverclient/server → server}/endpoint/webhooks_endpoint.py +0 -0
- {tableauserverclient/server → server}/exceptions.py +0 -0
- {tableauserverclient/server → server}/filter.py +0 -0
- {tableauserverclient/server → server}/pager.py +0 -0
- {tableauserverclient/server → server}/query.py +0 -0
- {tableauserverclient/server → server}/sort.py +0 -0
- {tableauserverclient-0.38.dist-info → tableauserverclient-0.39.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,14 +1,19 @@
|
|
|
1
|
+
from collections.abc import Iterable
|
|
1
2
|
import copy
|
|
3
|
+
import csv
|
|
4
|
+
import io
|
|
5
|
+
import itertools
|
|
2
6
|
import logging
|
|
3
7
|
from typing import Optional
|
|
8
|
+
import warnings
|
|
4
9
|
|
|
5
10
|
from tableauserverclient.server.query import QuerySet
|
|
6
11
|
|
|
7
|
-
from .endpoint import QuerysetEndpoint, api
|
|
8
|
-
from .exceptions import MissingRequiredFieldError, ServerResponseError
|
|
12
|
+
from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api
|
|
13
|
+
from tableauserverclient.server.endpoint.exceptions import MissingRequiredFieldError, ServerResponseError
|
|
9
14
|
from tableauserverclient.server import RequestFactory, RequestOptions
|
|
10
|
-
from tableauserverclient.models import UserItem, WorkbookItem, PaginationItem, GroupItem
|
|
11
|
-
from
|
|
15
|
+
from tableauserverclient.models import UserItem, WorkbookItem, PaginationItem, GroupItem, JobItem
|
|
16
|
+
from tableauserverclient.server.pager import Pager
|
|
12
17
|
|
|
13
18
|
from tableauserverclient.helpers.logging import logger
|
|
14
19
|
|
|
@@ -344,7 +349,34 @@ class Users(QuerysetEndpoint[UserItem]):
|
|
|
344
349
|
|
|
345
350
|
# Add new users to site. This does not actually perform a bulk action, it's syntactic sugar
|
|
346
351
|
@api(version="2.0")
|
|
347
|
-
def add_all(self, users: list[UserItem]):
|
|
352
|
+
def add_all(self, users: list[UserItem]) -> tuple[list[UserItem], list[UserItem]]:
|
|
353
|
+
"""
|
|
354
|
+
Syntactic sugar for calling users.add multiple times. This method has
|
|
355
|
+
been deprecated in favor of using the bulk_add which accomplishes the
|
|
356
|
+
same task in one API call.
|
|
357
|
+
|
|
358
|
+
.. deprecated:: v0.41.0
|
|
359
|
+
`add_all` will be removed as its functionality is replicated via
|
|
360
|
+
the `bulk_add` method.
|
|
361
|
+
|
|
362
|
+
Parameters
|
|
363
|
+
----------
|
|
364
|
+
users: list[UserItem]
|
|
365
|
+
A list of UserItem objects to add to the site. Each UserItem object
|
|
366
|
+
will be passed to the `add` method individually.
|
|
367
|
+
|
|
368
|
+
Returns
|
|
369
|
+
-------
|
|
370
|
+
tuple[list[UserItem], list[UserItem]]
|
|
371
|
+
The first element of the tuple is a list of UserItem objects that
|
|
372
|
+
were successfully added to the site. The second element is a list
|
|
373
|
+
of UserItem objects that failed to be added to the site.
|
|
374
|
+
|
|
375
|
+
Warnings
|
|
376
|
+
--------
|
|
377
|
+
This method is deprecated. Use the `bulk_add` method instead.
|
|
378
|
+
"""
|
|
379
|
+
warnings.warn("This method is deprecated, use bulk_add method instead.", DeprecationWarning)
|
|
348
380
|
created = []
|
|
349
381
|
failed = []
|
|
350
382
|
for user in users:
|
|
@@ -357,8 +389,143 @@ class Users(QuerysetEndpoint[UserItem]):
|
|
|
357
389
|
|
|
358
390
|
# helping the user by parsing a file they could have used to add users through the UI
|
|
359
391
|
# line format: Username [required], password, display name, license, admin, publish
|
|
392
|
+
@api(version="3.15")
|
|
393
|
+
def bulk_add(self, users: Iterable[UserItem]) -> JobItem:
|
|
394
|
+
"""
|
|
395
|
+
When adding users in bulk, the server will return a job item that can be used to track the progress of the
|
|
396
|
+
operation. This method will return the job item that was created when the users were added.
|
|
397
|
+
|
|
398
|
+
For each user, name is required, and other fields are optional. If connected to activte directory and
|
|
399
|
+
the user name is not unique across domains, then the domain attribute must be populated on
|
|
400
|
+
the UserItem.
|
|
401
|
+
|
|
402
|
+
The user's display name is read from the fullname attribute.
|
|
403
|
+
|
|
404
|
+
Email is optional, but if provided, it must be a valid email address.
|
|
405
|
+
|
|
406
|
+
If auth_setting is not provided, and idp_configuration_id is None, then
|
|
407
|
+
default is ServerDefault.
|
|
408
|
+
|
|
409
|
+
If site_role is not provided, the default is Unlicensed.
|
|
410
|
+
|
|
411
|
+
Password is optional, and only used if the server is using local
|
|
412
|
+
authentication. If using any other authentication method, the password
|
|
413
|
+
should not be provided.
|
|
414
|
+
|
|
415
|
+
Details about administrator level and publishing capability are
|
|
416
|
+
inferred from the site_role.
|
|
417
|
+
|
|
418
|
+
If the user belongs to a different IDP configuration, the UserItem's
|
|
419
|
+
idp_configuration_id attribute must be set to the IDP configuration ID
|
|
420
|
+
that the user belongs to.
|
|
421
|
+
|
|
422
|
+
Parameters
|
|
423
|
+
----------
|
|
424
|
+
users: Iterable[UserItem]
|
|
425
|
+
An iterable of UserItem objects to add to the site. See above for
|
|
426
|
+
what fields are required and optional.
|
|
427
|
+
|
|
428
|
+
Returns
|
|
429
|
+
-------
|
|
430
|
+
JobItem
|
|
431
|
+
The job that is started for adding the users in bulk.
|
|
432
|
+
|
|
433
|
+
Examples
|
|
434
|
+
--------
|
|
435
|
+
>>> import tableauserverclient as TSC
|
|
436
|
+
>>> server = TSC.Server('http://localhost')
|
|
437
|
+
>>> # Login to the server
|
|
438
|
+
|
|
439
|
+
>>> # Create a list of UserItem objects to add to the site
|
|
440
|
+
>>> users = [
|
|
441
|
+
>>> TSC.UserItem(name="user1", site_role="Unlicensed"),
|
|
442
|
+
>>> TSC.UserItem(name="user2", site_role="Explorer"),
|
|
443
|
+
>>> TSC.UserItem(name="user3", site_role="Creator"),
|
|
444
|
+
>>> ]
|
|
445
|
+
|
|
446
|
+
>>> # Set the domain name for the users
|
|
447
|
+
>>> for user in users:
|
|
448
|
+
>>> user.domain_name = "example.com"
|
|
449
|
+
|
|
450
|
+
>>> # Add the users to the site
|
|
451
|
+
>>> job = server.users.bulk_add(users)
|
|
452
|
+
|
|
453
|
+
"""
|
|
454
|
+
url = f"{self.baseurl}/import"
|
|
455
|
+
# Allow for iterators to be passed into the function
|
|
456
|
+
csv_users, xml_users = itertools.tee(users, 2)
|
|
457
|
+
csv_content = create_users_csv(csv_users)
|
|
458
|
+
|
|
459
|
+
xml_request, content_type = RequestFactory.User.import_from_csv_req(csv_content, xml_users)
|
|
460
|
+
server_response = self.post_request(url, xml_request, content_type)
|
|
461
|
+
return JobItem.from_response(server_response.content, self.parent_srv.namespace).pop()
|
|
462
|
+
|
|
463
|
+
@api(version="3.15")
|
|
464
|
+
def bulk_remove(self, users: Iterable[UserItem]) -> None:
|
|
465
|
+
"""
|
|
466
|
+
Remove multiple users from the site. The users are identified by their
|
|
467
|
+
domain and name. The users are removed in bulk, so the server will not
|
|
468
|
+
return a job item to track the progress of the operation nor a response
|
|
469
|
+
for each user that was removed.
|
|
470
|
+
|
|
471
|
+
Parameters
|
|
472
|
+
----------
|
|
473
|
+
users: Iterable[UserItem]
|
|
474
|
+
An iterable of UserItem objects to remove from the site. Each
|
|
475
|
+
UserItem object should have the domain and name attributes set.
|
|
476
|
+
|
|
477
|
+
Returns
|
|
478
|
+
-------
|
|
479
|
+
None
|
|
480
|
+
|
|
481
|
+
Examples
|
|
482
|
+
--------
|
|
483
|
+
>>> import tableauserverclient as TSC
|
|
484
|
+
>>> server = TSC.Server('http://localhost')
|
|
485
|
+
>>> # Login to the server
|
|
486
|
+
|
|
487
|
+
>>> # Find the users to remove
|
|
488
|
+
>>> example_users = server.users.filter(domain_name="example.com")
|
|
489
|
+
>>> server.users.bulk_remove(example_users)
|
|
490
|
+
"""
|
|
491
|
+
url = f"{self.baseurl}/delete"
|
|
492
|
+
csv_content = remove_users_csv(users)
|
|
493
|
+
request, content_type = RequestFactory.User.delete_csv_req(csv_content)
|
|
494
|
+
server_response = self.post_request(url, request, content_type)
|
|
495
|
+
return None
|
|
496
|
+
|
|
360
497
|
@api(version="2.0")
|
|
361
498
|
def create_from_file(self, filepath: str) -> tuple[list[UserItem], list[tuple[UserItem, ServerResponseError]]]:
|
|
499
|
+
"""
|
|
500
|
+
Syntactic sugar for calling users.add multiple times. This method has
|
|
501
|
+
been deprecated in favor of using the bulk_add which accomplishes the
|
|
502
|
+
same task in one API call.
|
|
503
|
+
|
|
504
|
+
.. deprecated:: v0.41.0
|
|
505
|
+
`add_all` will be removed as its functionality is replicated via
|
|
506
|
+
the `bulk_add` method.
|
|
507
|
+
|
|
508
|
+
Parameters
|
|
509
|
+
----------
|
|
510
|
+
filepath: str
|
|
511
|
+
The path to the CSV file containing the users to add to the site.
|
|
512
|
+
The file is read in line by line and each line is passed to the
|
|
513
|
+
`add` method.
|
|
514
|
+
|
|
515
|
+
Returns
|
|
516
|
+
-------
|
|
517
|
+
tuple[list[UserItem], list[tuple[UserItem, ServerResponseError]]]
|
|
518
|
+
The first element of the tuple is a list of UserItem objects that
|
|
519
|
+
were successfully added to the site. The second element is a list
|
|
520
|
+
of tuples where the first element is the UserItem object that failed
|
|
521
|
+
to be added to the site and the second element is the ServerResponseError
|
|
522
|
+
that was raised when attempting to add the user.
|
|
523
|
+
|
|
524
|
+
Warnings
|
|
525
|
+
--------
|
|
526
|
+
This method is deprecated. Use the `bulk_add` method instead.
|
|
527
|
+
"""
|
|
528
|
+
warnings.warn("This method is deprecated, use bulk_add instead", DeprecationWarning)
|
|
362
529
|
created = []
|
|
363
530
|
failed = []
|
|
364
531
|
if not filepath.find("csv"):
|
|
@@ -569,3 +736,105 @@ class Users(QuerysetEndpoint[UserItem]):
|
|
|
569
736
|
"""
|
|
570
737
|
|
|
571
738
|
return super().filter(*invalid, page_size=page_size, **kwargs)
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
def create_users_csv(users: Iterable[UserItem]) -> bytes:
|
|
742
|
+
"""
|
|
743
|
+
Create a CSV byte string from an Iterable of UserItem objects. The CSV will
|
|
744
|
+
have the following columns, and no header row:
|
|
745
|
+
|
|
746
|
+
- Username
|
|
747
|
+
- Password
|
|
748
|
+
- Display Name
|
|
749
|
+
- License
|
|
750
|
+
- Admin Level
|
|
751
|
+
- Publish capability
|
|
752
|
+
- Email
|
|
753
|
+
|
|
754
|
+
Parameters
|
|
755
|
+
----------
|
|
756
|
+
users: Iterable[UserItem]
|
|
757
|
+
An iterable of UserItem objects to create the CSV from.
|
|
758
|
+
|
|
759
|
+
Returns
|
|
760
|
+
-------
|
|
761
|
+
bytes
|
|
762
|
+
A byte string containing the CSV data.
|
|
763
|
+
"""
|
|
764
|
+
with io.StringIO() as output:
|
|
765
|
+
writer = csv.writer(output, quoting=csv.QUOTE_MINIMAL)
|
|
766
|
+
for user in users:
|
|
767
|
+
site_role = user.site_role or "Unlicensed"
|
|
768
|
+
if site_role == "ServerAdministrator":
|
|
769
|
+
license = "Creator"
|
|
770
|
+
admin_level = "System"
|
|
771
|
+
elif site_role.startswith("SiteAdministrator"):
|
|
772
|
+
admin_level = "Site"
|
|
773
|
+
license = site_role.replace("SiteAdministrator", "")
|
|
774
|
+
else:
|
|
775
|
+
license = site_role
|
|
776
|
+
admin_level = ""
|
|
777
|
+
|
|
778
|
+
if any(x in site_role for x in ("Creator", "Admin", "Publish")):
|
|
779
|
+
publish = 1
|
|
780
|
+
else:
|
|
781
|
+
publish = 0
|
|
782
|
+
|
|
783
|
+
writer.writerow(
|
|
784
|
+
(
|
|
785
|
+
f"{user.domain_name}\\{user.name}" if user.domain_name else user.name,
|
|
786
|
+
getattr(user, "password", ""),
|
|
787
|
+
user.fullname,
|
|
788
|
+
license,
|
|
789
|
+
admin_level,
|
|
790
|
+
publish,
|
|
791
|
+
user.email,
|
|
792
|
+
)
|
|
793
|
+
)
|
|
794
|
+
output.seek(0)
|
|
795
|
+
result = output.read().encode("utf-8")
|
|
796
|
+
return result
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
def remove_users_csv(users: Iterable[UserItem]) -> bytes:
|
|
800
|
+
"""
|
|
801
|
+
Create a CSV byte string from an Iterable of UserItem objects. This function
|
|
802
|
+
only consumes the domain and name attributes of the UserItem objects. The
|
|
803
|
+
CSV will have space for the following columns, though only the first column
|
|
804
|
+
will be populated, and no header row:
|
|
805
|
+
|
|
806
|
+
- Username
|
|
807
|
+
- Password
|
|
808
|
+
- Display Name
|
|
809
|
+
- License
|
|
810
|
+
- Admin Level
|
|
811
|
+
- Publish capability
|
|
812
|
+
- Email
|
|
813
|
+
|
|
814
|
+
Parameters
|
|
815
|
+
----------
|
|
816
|
+
users: Iterable[UserItem]
|
|
817
|
+
An iterable of UserItem objects to create the CSV from.
|
|
818
|
+
|
|
819
|
+
Returns
|
|
820
|
+
-------
|
|
821
|
+
bytes
|
|
822
|
+
A byte string containing the CSV data.
|
|
823
|
+
"""
|
|
824
|
+
with io.StringIO() as output:
|
|
825
|
+
writer = csv.writer(output, quoting=csv.QUOTE_MINIMAL)
|
|
826
|
+
for user in users:
|
|
827
|
+
writer.writerow(
|
|
828
|
+
(
|
|
829
|
+
f"{user.domain_name}\\{user.name}" if user.domain_name else user.name,
|
|
830
|
+
None,
|
|
831
|
+
None,
|
|
832
|
+
None,
|
|
833
|
+
None,
|
|
834
|
+
None,
|
|
835
|
+
None,
|
|
836
|
+
)
|
|
837
|
+
)
|
|
838
|
+
output.seek(0)
|
|
839
|
+
result = output.read().encode("utf-8")
|
|
840
|
+
return result
|
|
@@ -371,6 +371,29 @@ class Views(QuerysetEndpoint[ViewItem], TaggingMixin[ViewItem]):
|
|
|
371
371
|
# Returning view item to stay consistent with datasource/view update functions
|
|
372
372
|
return view_item
|
|
373
373
|
|
|
374
|
+
@api(version="3.27")
|
|
375
|
+
def delete(self, view: ViewItem | str) -> None:
|
|
376
|
+
"""
|
|
377
|
+
Deletes a view in a workbook. If you delete the only view in a workbook,
|
|
378
|
+
the workbook is deleted. Can be used to remove hidden views when
|
|
379
|
+
republishing or migrating to a different environment.
|
|
380
|
+
|
|
381
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#delete_view
|
|
382
|
+
|
|
383
|
+
Parameters
|
|
384
|
+
----------
|
|
385
|
+
view: ViewItem | str
|
|
386
|
+
The ViewItem or the luid for the view to be deleted.
|
|
387
|
+
|
|
388
|
+
Returns
|
|
389
|
+
-------
|
|
390
|
+
None
|
|
391
|
+
"""
|
|
392
|
+
id_ = getattr(view, "id", view)
|
|
393
|
+
self.delete_request(f"{self.baseurl}/{id_}")
|
|
394
|
+
logger.info(f"View({id_}) deleted.")
|
|
395
|
+
return None
|
|
396
|
+
|
|
374
397
|
@api(version="1.0")
|
|
375
398
|
def add_tags(self, item: Union[ViewItem, str], tags: Union[Iterable[str], str]) -> set[str]:
|
|
376
399
|
"""
|
|
@@ -30,9 +30,12 @@ from tableauserverclient.models import WorkbookItem, ConnectionItem, ViewItem, P
|
|
|
30
30
|
from tableauserverclient.server import RequestFactory
|
|
31
31
|
|
|
32
32
|
from typing import (
|
|
33
|
+
Literal,
|
|
33
34
|
Optional,
|
|
34
35
|
TYPE_CHECKING,
|
|
36
|
+
TypeVar,
|
|
35
37
|
Union,
|
|
38
|
+
overload,
|
|
36
39
|
)
|
|
37
40
|
from collections.abc import Iterable, Sequence
|
|
38
41
|
|
|
@@ -325,16 +328,92 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
325
328
|
logger.info(f"Updated workbook item (ID: {workbook_item.id} & connection item {connection_item.id})")
|
|
326
329
|
return connection
|
|
327
330
|
|
|
331
|
+
# Update workbook_connections
|
|
332
|
+
@api(version="3.26")
|
|
333
|
+
def update_connections(
|
|
334
|
+
self,
|
|
335
|
+
workbook_item: WorkbookItem,
|
|
336
|
+
connection_luids: Iterable[str],
|
|
337
|
+
authentication_type: str,
|
|
338
|
+
username: Optional[str] = None,
|
|
339
|
+
password: Optional[str] = None,
|
|
340
|
+
embed_password: Optional[bool] = None,
|
|
341
|
+
) -> list[ConnectionItem]:
|
|
342
|
+
"""
|
|
343
|
+
Bulk updates one or more workbook connections by LUID, including authenticationType, username, password, and embedPassword.
|
|
344
|
+
|
|
345
|
+
Parameters
|
|
346
|
+
----------
|
|
347
|
+
workbook_item : WorkbookItem
|
|
348
|
+
The workbook item containing the connections.
|
|
349
|
+
|
|
350
|
+
connection_luids : Iterable of str
|
|
351
|
+
The connection LUIDs to update.
|
|
352
|
+
|
|
353
|
+
authentication_type : str
|
|
354
|
+
The authentication type to use (e.g., 'AD Service Principal').
|
|
355
|
+
|
|
356
|
+
username : str, optional
|
|
357
|
+
The username to set (e.g., client ID for keypair auth).
|
|
358
|
+
|
|
359
|
+
password : str, optional
|
|
360
|
+
The password or secret to set.
|
|
361
|
+
|
|
362
|
+
embed_password : bool, optional
|
|
363
|
+
Whether to embed the password.
|
|
364
|
+
|
|
365
|
+
Returns
|
|
366
|
+
-------
|
|
367
|
+
Iterable of str
|
|
368
|
+
The connection LUIDs that were updated.
|
|
369
|
+
"""
|
|
370
|
+
|
|
371
|
+
url = f"{self.baseurl}/{workbook_item.id}/connections"
|
|
372
|
+
|
|
373
|
+
request_body = RequestFactory.Workbook.update_connections_req(
|
|
374
|
+
connection_luids,
|
|
375
|
+
authentication_type,
|
|
376
|
+
username=username,
|
|
377
|
+
password=password,
|
|
378
|
+
embed_password=embed_password,
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
# Send request
|
|
382
|
+
server_response = self.put_request(url, request_body)
|
|
383
|
+
connection_items = ConnectionItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
384
|
+
updated_ids: list[str] = [conn.id for conn in connection_items]
|
|
385
|
+
|
|
386
|
+
logger.info(f"Updated connections for workbook {workbook_item.id}: {', '.join(updated_ids)}")
|
|
387
|
+
return connection_items
|
|
388
|
+
|
|
389
|
+
T = TypeVar("T", bound=FileObjectW)
|
|
390
|
+
|
|
391
|
+
@overload
|
|
392
|
+
def download(
|
|
393
|
+
self,
|
|
394
|
+
workbook_id: str,
|
|
395
|
+
filepath: T,
|
|
396
|
+
include_extract: bool = True,
|
|
397
|
+
) -> T: ...
|
|
398
|
+
|
|
399
|
+
@overload
|
|
400
|
+
def download(
|
|
401
|
+
self,
|
|
402
|
+
workbook_id: str,
|
|
403
|
+
filepath: Optional[FilePath] = None,
|
|
404
|
+
include_extract: bool = True,
|
|
405
|
+
) -> str: ...
|
|
406
|
+
|
|
328
407
|
# Download workbook contents with option of passing in filepath
|
|
329
408
|
@api(version="2.0")
|
|
330
409
|
@parameter_added_in(no_extract="2.5")
|
|
331
410
|
@parameter_added_in(include_extract="2.5")
|
|
332
411
|
def download(
|
|
333
412
|
self,
|
|
334
|
-
workbook_id
|
|
335
|
-
filepath
|
|
336
|
-
include_extract
|
|
337
|
-
)
|
|
413
|
+
workbook_id,
|
|
414
|
+
filepath=None,
|
|
415
|
+
include_extract=True,
|
|
416
|
+
):
|
|
338
417
|
"""
|
|
339
418
|
Downloads a workbook to the specified directory (optional).
|
|
340
419
|
|
|
@@ -683,6 +762,30 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
683
762
|
"""
|
|
684
763
|
return self._permissions.delete(item, capability_item)
|
|
685
764
|
|
|
765
|
+
@overload
|
|
766
|
+
def publish(
|
|
767
|
+
self,
|
|
768
|
+
workbook_item: WorkbookItem,
|
|
769
|
+
file: PathOrFileR,
|
|
770
|
+
mode: str,
|
|
771
|
+
connections: Optional[Sequence[ConnectionItem]],
|
|
772
|
+
as_job: Literal[False],
|
|
773
|
+
skip_connection_check: bool,
|
|
774
|
+
parameters=None,
|
|
775
|
+
) -> WorkbookItem: ...
|
|
776
|
+
|
|
777
|
+
@overload
|
|
778
|
+
def publish(
|
|
779
|
+
self,
|
|
780
|
+
workbook_item: WorkbookItem,
|
|
781
|
+
file: PathOrFileR,
|
|
782
|
+
mode: str,
|
|
783
|
+
connections: Optional[Sequence[ConnectionItem]],
|
|
784
|
+
as_job: Literal[True],
|
|
785
|
+
skip_connection_check: bool,
|
|
786
|
+
parameters=None,
|
|
787
|
+
) -> JobItem: ...
|
|
788
|
+
|
|
686
789
|
@api(version="2.0")
|
|
687
790
|
@parameter_added_in(as_job="3.0")
|
|
688
791
|
@parameter_added_in(connections="2.8")
|
|
@@ -919,15 +1022,27 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
919
1022
|
revisions = RevisionItem.from_response(server_response.content, self.parent_srv.namespace, workbook_item)
|
|
920
1023
|
return revisions
|
|
921
1024
|
|
|
1025
|
+
T = TypeVar("T", bound=FileObjectW)
|
|
1026
|
+
|
|
1027
|
+
@overload
|
|
1028
|
+
def download_revision(
|
|
1029
|
+
self, workbook_id: str, revision_number: Optional[str], filepath: T, include_extract: bool
|
|
1030
|
+
) -> T: ...
|
|
1031
|
+
|
|
1032
|
+
@overload
|
|
1033
|
+
def download_revision(
|
|
1034
|
+
self, workbook_id: str, revision_number: Optional[str], filepath: Optional[FilePath], include_extract: bool
|
|
1035
|
+
) -> str: ...
|
|
1036
|
+
|
|
922
1037
|
# Download 1 workbook revision by revision number
|
|
923
1038
|
@api(version="2.3")
|
|
924
1039
|
def download_revision(
|
|
925
1040
|
self,
|
|
926
|
-
workbook_id
|
|
927
|
-
revision_number
|
|
928
|
-
filepath
|
|
929
|
-
include_extract
|
|
930
|
-
)
|
|
1041
|
+
workbook_id,
|
|
1042
|
+
revision_number,
|
|
1043
|
+
filepath,
|
|
1044
|
+
include_extract=True,
|
|
1045
|
+
):
|
|
931
1046
|
"""
|
|
932
1047
|
Downloads a workbook revision to the specified directory (optional).
|
|
933
1048
|
|