I got out ahead of the visual changes in Tableau Server 9.2, only to miss the fact that there are major changes in the REST API, enough to necessitate a new major version number. Of course, these broke the tableau_rest_api library in multiple ways. I’ve updated it to handle all of the changes, and I’ll list them out in this post. But please update to the latest library version, 2.0.0 (or above) either from PyPi or GitHub!
Project Permissions + Default Permissions
The biggest change to note is that Project Permissions have changed in Tableau Server 9.2, so even if you use the older version of the REST API (still valid by the way), you can’t assign permissions to a project the same way!
Put simply, in 9.1 and before, a Project could have ALL the permissions itself, and then data sources and workbooks inherited those specific permissions that applied. In 9.2, a Project only has three permissions itself (ProjectLeader, Read (view), and Write), but can be set with Default Permissions that will apply to a Data Source or a Workbook.
Adding default permissions uses an extension of the syntax and GranteeCapabilities XML already used for permissions. The URIs end up looking like
I’ve implemented it in tableau_rest_api by
add_default_permissions_to_project_by_gcap_obj_list(project_luid, obj_type, obj_luid_s, gcap_obj_list)
which eventually calls the existing permissions setting mechanisms, but limited to workbooks and datasources, since projects only have defaults for what they contain (the project itself has its own permissions). There is an equivalent method to remove default permissions
Along with the new concept of default permissions for projects, in 9.2 you can also Lock the permissions of content in a project so that they always match the Project’s default permissions. This is all done through an additional attribute in the XML for creating or updating a project
where the possible values are LockedToProject or ManagedByOwner (the default). To this end, there is now an optional “locked_permissions=False” parameter added to the create_project and update_project methods. If you set this parameter to True, then the project will have locked permissions. There are two additional quick methods to affect existing projects:
PublishedContent classes – Project, Workbook, Datasource
After thinking about how best to handle the complexity of the permissions model, and the differences between the versions of the REST API, I decided to create some classes that could be a local representation of the projects, workbooks and data sources that are published to server. This solves two issues:
- There is no “update” functionality for permissions capabilities — if you want to submit changes, you must first delete out those permissions. By having objects that keep the state locally, you can implement an “update” which sends the minimal amount of calls to update the permissions.
- In API version 2.1, Projects have default permissions for Workbooks and Datasources. The Project class thus actually includes a Workbook and a Datasource object, referenced as Project.workbook_default or Project.datasource_default. Because they are all descendant classes of the same PublishedContent class, you use the same method to update the default permissions objects as you would a stand-alone Workbook or Datasource object.
The algorithm used when sending an update is:
- For the given user or group to be updated, see if there are any existing permissions for that user or group
- If the existing permissions match exactly, do not make any changes (Otherwise, you’d have to delete out every permission only to reset it exactly as it was before)
- If the permissions do not match exactly, delete all of the existing permissions for that user or group (and only those that are set, therefore saving wasted deletion calls)
- Set the new permissions for that user or group
All of the PublishedContent objects take a LUID and a TableauRestApi object so that they can make their update calls (represented in the following code by ‘t’). The following code shows the set_permissions_by_gcap_obj method, which is all that is necessary for an add or an update for any of the object types. You still have to create a GranteeCapabilities object to represent the actual permission capabilities — it has been greatly expanded though to give useful functionality like matching the roles that exist in the Tableau Server UI (all of which have been updated to match 9.2, where there are different role names for projects, workbooks and data sources).
sandbox_proj_luid = t.query_project_luid_by_name(u"Sandbox") sandbox_proj = Project(sandbox_proj_luid, t) sandbox_proj.lock_permissions() sandbox_proj.unlock_permissions() team_group_luid = t.query_group_luid_by_name(u"Team Member") team_gcap_obj = GranteeCapabilities(u"group", team_group_luid, content_type=u"project") team_gcap_obj.set_capabilities_to_match_role(u"Publisher") sandbox_proj.set_permissions_by_gcap_obj(team_gcap_obj) team_gcap_obj = GranteeCapabilities(u"group", team_group_luid, content_type=u"workbook") team_gcap_obj.set_capabilities_to_match_role(u"Interactor") sandbox_proj.workbook_default.set_permissions_by_gcap_obj(team_gcap_obj)
API Version Number
As I mentioned earlier, there’s a new API version number (2.1 instead of 2.0) for Tableau 9.2. To use this version of the API, all your URIs need to have 2.1 where they previously had 2.0. In tableau_rest_api 1.6.0 and above, this is the default. I’ve added a parameter when you initialize the TableauRestApi class if you need to specify a previous version number (like the following:) NOTE: Later versions of the library detect version capabilities automatically and this is not necessary:
TableauRestApi(server, username, password, site_content_url="", tableau_server_version="9.1")
The most noticeable change that I’ve seen with API 2.1 is that the namespace has changed from tableausoftware.com/api to tableau.com/api . This required some code changes when reading the XML responses, but that may be specific to lxml and Python, so I don’t know how much it would affect any other implementation.