diff --git a/tableauserverclient/models/user_item.py b/tableauserverclient/models/user_item.py index 0ba1e8eb2..04171d28d 100644 --- a/tableauserverclient/models/user_item.py +++ b/tableauserverclient/models/user_item.py @@ -32,7 +32,16 @@ class UserItem: Parameters ---------- name: str - The name of the user. + The username used to authenticate the user, NOT the person's display + name (that is ``fullname``). The required format depends on the site's + authentication scheme: + + - Tableau Cloud: the sign-in email address (e.g. ``user@example.com``). + - Local authentication (on-prem Tableau Server): any unique username + for the site (e.g. ``jsmith``). + - Active Directory: the fully-qualified AD username, typically + ``SAMAccountName@FullyQualifiedDomain`` (e.g. + ``jsmith@corp.example.com``), or the User Principal Name (UPN). site_role: str The role of the user on the site. diff --git a/tableauserverclient/server/endpoint/projects_endpoint.py b/tableauserverclient/server/endpoint/projects_endpoint.py index edc6ef670..e0b529bc0 100644 --- a/tableauserverclient/server/endpoint/projects_endpoint.py +++ b/tableauserverclient/server/endpoint/projects_endpoint.py @@ -293,6 +293,32 @@ def update_permissions(self, item: ProjectItem, rules: list[PermissionsRule]) -> ------- list[PermissionsRule] Returns the updated list of permissions rules. + + Examples + -------- + Assign a group as Project Leader on a project. ``ProjectLeader`` is + the capability that grants the group the ability to publish and + manage content in the project as if they were the project owner. + + >>> import tableauserverclient as TSC + >>> server = TSC.Server('https://SERVERURL') + >>> # Login to the server + + >>> project = next(p for p in TSC.Pager(server.projects) if p.name == 'Marketing') + >>> group = next(g for g in TSC.Pager(server.groups) if g.name == 'Marketing Leads') + + >>> rule = TSC.PermissionsRule( + ... grantee=group, + ... capabilities={ + ... TSC.Permission.Capability.ProjectLeader: TSC.Permission.Mode.Allow, + ... }, + ... ) + >>> server.projects.update_permissions(project, [rule]) + + Note: ``update_permissions`` REPLACES the full permissions list on the + project. Any existing rules not included in the call will be removed. + To preserve existing rules, call ``populate_permissions(project)`` + first, then modify ``project.permissions`` and pass the full list. """ return self._permissions.update(item, rules) diff --git a/tableauserverclient/server/endpoint/users_endpoint.py b/tableauserverclient/server/endpoint/users_endpoint.py index 48c77da66..5e4b285eb 100644 --- a/tableauserverclient/server/endpoint/users_endpoint.py +++ b/tableauserverclient/server/endpoint/users_endpoint.py @@ -254,11 +254,26 @@ def add(self, user_item: UserItem) -> UserItem: To add a new user to the site you need to first create a new user_item (from UserItem class). When you create a new user, you specify the name of the user and their site role. For Tableau Cloud, you also specify - the auth_setting attribute in your request. When you add user to - Tableau Cloud, the name of the user must be the email address that is - used to sign in to Tableau Cloud. After you add a user, Tableau Cloud - sends the user an email invitation. The user can click the link in the - invitation to sign in and update their full name and password. + the auth_setting attribute in your request. After you add a user, Tableau + Cloud sends the user an email invitation. The user can click the link in + the invitation to sign in and update their full name and password. + + The value of ``user_item.name`` is the username the server uses to + authenticate the user, NOT the person's display name. Its required + format depends on the site's authentication scheme: + + - Tableau Cloud: the user's email address (e.g. ``user@example.com``), + which is also what they sign in with. + - Local authentication (on-prem Tableau Server): any username unique to + the site (e.g. ``jsmith``). + - Active Directory: the fully-qualified AD username, either + ``SAMAccountName@FullyQualifiedDomain`` (e.g. + ``jsmith@corp.example.com``) or the User Principal Name (UPN) if AD + is configured to use UPNs. A bare ``SAMAccountName`` typically will + not resolve. + + Set the person's display name via ``user_item.fullname`` — it is a + separate attribute. Parameters ---------- @@ -333,9 +348,16 @@ def add(self, user_item: UserItem) -> UserItem: >>> server = TSC.Server('https://SERVERURL') >>> # Login to the server - >>> new_user = TSC.UserItem(name='new_user', site_role=TSC.UserItem.Role.Unlicensed) + >>> # Tableau Cloud: name must be the sign-in email address + >>> new_user = TSC.UserItem(name='jsmith@example.com', site_role=TSC.UserItem.Role.Explorer) + >>> new_user.auth_setting = TSC.UserItem.Auth.TableauIDWithMFA + >>> new_user.fullname = 'Jane Smith' >>> new_user = server.users.add(new_user) + >>> # Active Directory-backed Tableau Server: name is SAMAccountName@Domain + >>> ad_user = TSC.UserItem(name='jsmith@corp.example.com', site_role=TSC.UserItem.Role.Viewer) + >>> ad_user = server.users.add(ad_user) + """ url = self.baseurl logger.info(f"Add user {user_item.name}") diff --git a/tableauserverclient/server/filter.py b/tableauserverclient/server/filter.py index 2e64361ba..542d89af5 100644 --- a/tableauserverclient/server/filter.py +++ b/tableauserverclient/server/filter.py @@ -5,6 +5,46 @@ class Filter: + """Represents a single Tableau REST API filter clause. + + A `Filter` is one entry in a `?filter=` query parameter and serializes as + ``::``. Use one filter per attribute, and pass + multiple filters via `RequestOptions.filter.add(...)` if you need to AND + conditions. + + Special characters in filter values + ---------------------------------- + The REST API's filter grammar treats several characters as delimiters. The + server does NOT support escaping them, so a value containing any of these + characters cannot be matched exactly with the `Equals` operator: + + - ``,`` — separates values in an ``in`` list. + - ``&`` — separates filter clauses. + - ``:`` — separates field/operator/value. + - ``[`` and ``]`` — bracket an ``in`` value list. + + Workaround for names containing these characters: use the ``Equals`` + operator with an asterisk substituted for the special character. Asterisk + behaves as a wildcard, so for example filtering a workbook named + ``T(L-F,SZ&V-MY) - PC`` can be found via ``name="T(L-F*SZ*V-MY) - PC"``. + Post-filter the result client-side to disambiguate if multiple names could + match. + + Parameters + ---------- + field : str + The field to filter on (e.g. ``RequestOptions.Field.Name``). + + operator : str + The operator to apply (e.g. ``RequestOptions.Operator.Equals``, + ``In``, ``GreaterThan``). + + value : str | int | bool | datetime | list + The value to compare against. Lists require operator ``In``. Datetimes + must be timezone-aware and serialize as ISO-8601 UTC. Bools serialize + as lowercase ``true``/``false``. + """ + def __init__(self, field, operator, value): self.field = field self.operator = operator diff --git a/tableauserverclient/server/query.py b/tableauserverclient/server/query.py index eb84248b5..3c5895fe5 100644 --- a/tableauserverclient/server/query.py +++ b/tableauserverclient/server/query.py @@ -186,6 +186,18 @@ def page_size(self: Self) -> int: return self._pagination_item.page_size or self.request_options.pagesize def filter(self: Self, *invalid, page_size: int | None = None, **kwargs) -> Self: + """Add filter clauses to the queryset. + + Each keyword argument becomes one filter. Shorthand suffixes select + the operator (e.g. ``name__gt="A"`` -> operator ``GreaterThan``); a + bare keyword uses ``Equals``. See ``docs/filter-sort.md`` for the + supported suffixes. + + Special-character caveat: values containing ``,``, ``&``, ``:``, ``[`` + or ``]`` cannot be matched exactly with ``Equals``; the REST filter + grammar treats them as delimiters and the server does not support + escaping. See ``Filter`` for details and the wildcard workaround. + """ if invalid: raise RuntimeError("Only accepts keyword arguments.") for kwarg_key, value in kwargs.items():