From a84bcfd2b533a184ee5fbead4df03bd9fa20f91e Mon Sep 17 00:00:00 2001 From: Jordan Woods <13803242+jorwoods@users.noreply.github.com> Date: Wed, 2 Oct 2024 18:08:57 -0500 Subject: [PATCH 1/2] docs: docstrings for user item and endpoint --- tableauserverclient/models/user_item.py | 30 +++ .../server/endpoint/users_endpoint.py | 236 ++++++++++++++++++ 2 files changed, 266 insertions(+) diff --git a/tableauserverclient/models/user_item.py b/tableauserverclient/models/user_item.py index fb29492e4..365e44c1d 100644 --- a/tableauserverclient/models/user_item.py +++ b/tableauserverclient/models/user_item.py @@ -19,9 +19,34 @@ class UserItem: + """ + The UserItem class contains the members or attributes for the view + resources on Tableau Server. The UserItem class defines the information you + can request or query from Tableau Server. The class attributes correspond + to the attributes of a server request or response payload. + + + Parameters + ---------- + name: str + The name of the user. + + site_role: str + The role of the user on the site. + + auth_setting: str + Required attribute for Tableau Cloud. How the user autenticates to the + server. + """ + tag_name: str = "user" class Roles: + """ + The Roles class contains the possible roles for a user on Tableau + Server. + """ + Interactor = "Interactor" Publisher = "Publisher" ServerAdministrator = "ServerAdministrator" @@ -43,6 +68,11 @@ class Roles: SupportUser = "SupportUser" class Auth: + """ + The Auth class contains the possible authentication settings for a user + on Tableau Cloud. + """ + OpenID = "OpenID" SAML = "SAML" TableauIDWithMFA = "TableauIDWithMFA" diff --git a/tableauserverclient/server/endpoint/users_endpoint.py b/tableauserverclient/server/endpoint/users_endpoint.py index 793638396..164101e79 100644 --- a/tableauserverclient/server/endpoint/users_endpoint.py +++ b/tableauserverclient/server/endpoint/users_endpoint.py @@ -14,6 +14,14 @@ class Users(QuerysetEndpoint[UserItem]): + """ + The user resources for Tableau Server are defined in the UserItem class. + The class corresponds to the user resources you can access using the + Tableau Server REST API. The user methods are based upon the endpoints for + users in the REST API and operate on the UserItem class. Only server and + site administrators can access the user resources. + """ + @property def baseurl(self) -> str: return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/users" @@ -21,6 +29,31 @@ def baseurl(self) -> str: # Gets all users @api(version="2.0") def get(self, req_options: Optional[RequestOptions] = None) -> tuple[list[UserItem], PaginationItem]: + """ + Query all users on the site. Request is paginated and returns a subset of users. + By default, the request returns the first 100 users on the site. + + Parameters + ---------- + req_options : Optional[RequestOptions] + Optional request options to filter and sort the results. + + Returns + ------- + tuple[list[UserItem], PaginationItem] + Returns a tuple with a list of UserItem objects and a PaginationItem object. + + Examples + -------- + >>> import tableauserverclient as TSC + >>> tableau_auth = TSC.TableauAuth('USERNAME', 'PASSWORD') + >>> server = TSC.Server('https://SERVERURL') + + >>> with server.auth.sign_in(tableau_auth): + >>> users_page, pagination_item = server.users.get() + >>> print("\nThere are {} user on site: ".format(pagination_item.total_available)) + >>> print([user.name for user in users_page]) + """ logger.info("Querying all users on site") if req_options is None: @@ -36,6 +69,23 @@ def get(self, req_options: Optional[RequestOptions] = None) -> tuple[list[UserIt # Gets 1 user by id @api(version="2.0") def get_by_id(self, user_id: str) -> UserItem: + """ + Query a single user by ID. + + Parameters + ---------- + user_id : str + The ID of the user to query. + + Returns + ------- + UserItem + The user item that was queried. + + Examples + -------- + >>> user1 = server.users.get_by_id('9f9e9d9c-8b8a-8f8e-7d7c-7b7a6f6d6e6d') + """ if not user_id: error = "User ID undefined." raise ValueError(error) @@ -47,6 +97,47 @@ def get_by_id(self, user_id: str) -> UserItem: # Update user @api(version="2.0") def update(self, user_item: UserItem, password: Optional[str] = None) -> UserItem: + """ + Modifies information about the specified user. + + If Tableau Server is configured to use local authentication, you can + update the user's name, email address, password, or site role. + + If Tableau Server is configured to use Active Directory + authentication, you can change the user's display name (full name), + email address, and site role. However, if you synchronize the user with + Active Directory, the display name and email address will be + overwritten with the information that's in Active Directory. + + For Tableau Cloud, you can update the site role for a user, but you + cannot update or change a user's password, user name (email address), + or full name. + + Parameters + ---------- + user_item : UserItem + The user item to update. + + password : Optional[str] + The new password for the user. + + Returns + ------- + UserItem + The user item that was updated. + + Raises + ------ + MissingRequiredFieldError + If the user item is missing an ID. + + Examples + -------- + >>> user = server.users.get_by_id('9f9e9d9c-8b8a-8f8e-7d7c-7b7a6f6d6e6d') + >>> user.fullname = 'New Full Name' + >>> updated_user = server.users.update(user) + + """ if not user_item.id: error = "User item missing ID." raise MissingRequiredFieldError(error) @@ -61,6 +152,31 @@ def update(self, user_item: UserItem, password: Optional[str] = None) -> UserIte # Delete 1 user by id @api(version="2.0") def remove(self, user_id: str, map_assets_to: Optional[str] = None) -> None: + """ + Removes a user from the site. You can also specify a user to map the + assets to when you remove the user. + + Parameters + ---------- + user_id : str + The ID of the user to remove. + + map_assets_to : Optional[str] + The ID of the user to map the assets to when you remove the user. + + Returns + ------- + None + + Raises + ------ + ValueError + If the user ID is not specified. + + Examples + -------- + >>> server.users.remove('9f9e9d9c-8b8a-8f8e-7d7c-7b7a6f6d6e6d') + """ if not user_id: error = "User ID undefined." raise ValueError(error) @@ -73,6 +189,39 @@ def remove(self, user_id: str, map_assets_to: Optional[str] = None) -> None: # Add new user to site @api(version="2.0") def add(self, user_item: UserItem) -> UserItem: + """ + Adds the user to the site. + + 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. + + Parameters + ---------- + user_item : UserItem + The user item to add to the site. + + Returns + ------- + UserItem + The user item that was added to the site with attributes from the + site populated. + + Examples + -------- + >>> import tableauserverclient as TSC + >>> server = TSC.Server('https://SERVERURL') + >>> # Login to the server + + >>> new_user = TSC.UserItem(name='new_user', site_role=TSC.UserItem.Role.Unlicensed) + >>> new_user = server.users.add(new_user) + + """ url = self.baseurl logger.info(f"Add user {user_item.name}") add_req = RequestFactory.User.add_req(user_item) @@ -122,6 +271,42 @@ def create_from_file(self, filepath: str) -> tuple[list[UserItem], list[tuple[Us # Get workbooks for user @api(version="2.0") def populate_workbooks(self, user_item: UserItem, req_options: Optional[RequestOptions] = None) -> None: + """ + Returns information about the workbooks that the specified user owns + and has Read (view) permissions for. + + This method retrieves the workbook information for the specified user. + The REST API is designed to return only the information you ask for + explicitly. When you query for all the users, the workbook information + for each user is not included. Use this method to retrieve information + about the workbooks that the user owns or has Read (view) permissions. + The method adds the list of workbooks to the user item object + (user_item.workbooks). + + Parameters + ---------- + user_item : UserItem + The user item to populate workbooks for. + + req_options : Optional[RequestOptions] + Optional request options to filter and sort the results. + + Returns + ------- + None + + Raises + ------ + MissingRequiredFieldError + If the user item is missing an ID. + + Examples + -------- + >>> user = server.users.get_by_id('9f9e9d9c-8b8a-8f8e-7d7c-7b7a6f6d6e6d') + >>> server.users.populate_workbooks(user) + >>> for wb in user.workbooks: + >>> print(wb.name) + """ if not user_item.id: error = "User item missing ID." raise MissingRequiredFieldError(error) @@ -142,11 +327,62 @@ def _get_wbs_for_user( return workbook_item, pagination_item def populate_favorites(self, user_item: UserItem) -> None: + """ + Populate the favorites for the user. + + Parameters + ---------- + user_item : UserItem + The user item to populate favorites for. + + Returns + ------- + None + + Examples + -------- + >>> import tableauserverclient as TSC + >>> server = TSC.Server('https://SERVERURL') + >>> # Login to the server + + >>> user = server.users.get_by_id('9f9e9d9c-8b8a-8f8e-7d7c-7b7a6f6d6e6d') + >>> server.users.populate_favorites(user) + >>> for obj_type, items in user.favorites.items(): + >>> print(f"Favorites for {obj_type}:") + >>> for item in items: + >>> print(item.name) + """ self.parent_srv.favorites.get(user_item) # Get groups for user @api(version="3.7") def populate_groups(self, user_item: UserItem, req_options: Optional[RequestOptions] = None) -> None: + """ + Populate the groups for the user. + + Parameters + ---------- + user_item : UserItem + The user item to populate groups for. + + req_options : Optional[RequestOptions] + Optional request options to filter and sort the results. + + Returns + ------- + None + + Raises + ------ + MissingRequiredFieldError + If the user item is missing an ID. + + Examples + -------- + >>> server.users.populate_groups(user) + >>> for group in user.groups: + >>> print(group.name) + """ if not user_item.id: error = "User item missing ID." raise MissingRequiredFieldError(error) From a50b213916d2d23f70607c1ff24d8c289691f17e Mon Sep 17 00:00:00 2001 From: Jordan Woods <13803242+jorwoods@users.noreply.github.com> Date: Fri, 4 Oct 2024 07:57:19 -0500 Subject: [PATCH 2/2] docs: add serverresponseerror details --- .../server/endpoint/users_endpoint.py | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/tableauserverclient/server/endpoint/users_endpoint.py b/tableauserverclient/server/endpoint/users_endpoint.py index 164101e79..d81907ae9 100644 --- a/tableauserverclient/server/endpoint/users_endpoint.py +++ b/tableauserverclient/server/endpoint/users_endpoint.py @@ -43,6 +43,35 @@ def get(self, req_options: Optional[RequestOptions] = None) -> tuple[list[UserIt tuple[list[UserItem], PaginationItem] Returns a tuple with a list of UserItem objects and a PaginationItem object. + Raises + ------ + ServerResponseError + code: 400006 + summary: Invalid page number + detail: The page number is not an integer, is less than one, or is + greater than the final page number for users at the requested + page size. + + ServerResponseError + code: 400007 + summary: Invalid page size + detail: The page size parameter is not an integer, is less than one. + + ServerResponseError + code: 403014 + summary: Page size limit exceeded + detail: The specified page size is larger than the maximum page size + + ServerResponseError + code: 404000 + summary: Site not found + detail: The site ID in the URI doesn't correspond to an existing site. + + ServerResponseError + code: 405000 + summary: Invalid request method + detail: Request type was not GET. + Examples -------- >>> import tableauserverclient as TSC @@ -82,6 +111,32 @@ def get_by_id(self, user_id: str) -> UserItem: UserItem The user item that was queried. + Raises + ------ + ValueError + If the user ID is not specified. + + ServerResponseError + code: 404000 + summary: Site not found + detail: The site ID in the URI doesn't correspond to an existing site. + + ServerResponseError + code: 403133 + summary: Query user permissions forbidden + detail: The user does not have permissions to query user information + for other users + + ServerResponseError + code: 404002 + summary: User not found + detail: The user ID in the URI doesn't correspond to an existing user. + + ServerResponseError + code: 405000 + summary: Invalid request method + detail: Request type was not GET. + Examples -------- >>> user1 = server.users.get_by_id('9f9e9d9c-8b8a-8f8e-7d7c-7b7a6f6d6e6d') @@ -212,6 +267,62 @@ def add(self, user_item: UserItem) -> UserItem: The user item that was added to the site with attributes from the site populated. + Raises + ------ + ValueError + If the user item is missing a name + + ValueError + If the user item is missing a site role + + ServerResponseError + code: 400000 + summary: Bad Request + detail: The content of the request body is missing or incomplete, or + contains malformed XML. + + ServerResponseError + code: 400003 + summary: Bad Request + detail: The user authentication setting ServerDefault is not + supported for you site. Try again using TableauIDWithMFA instead. + + ServerResponseError + code: 400013 + summary: Invalid site role + detail: The value of the siteRole attribute must be Explorer, + ExplorerCanPublish, SiteAdministratorCreator, + SiteAdministratorExplorer, Unlicensed, or Viewer. + + ServerResponseError + code: 404000 + summary: Site not found + detail: The site ID in the URI doesn't correspond to an existing site. + + ServerResponseError + code: 404002 + summary: User not found + detail: The server is configured to use Active Directory for + authentication, and the username specified in the request body + doesn't match an existing user in Active Directory. + + ServerResponseError + code: 405000 + summary: Invalid request method + detail: Request type was not POST. + + ServerResponseError + code: 409000 + summary: User conflict + detail: The specified user already exists on the site. + + ServerResponseError + code: 409005 + summary: Guest user conflict + detail: The Tableau Server API doesn't allow adding a user with the + guest role to a site. + + Examples -------- >>> import tableauserverclient as TSC