The Tableau REST API has very convenient endpoints for retrieving a Workbook or View Preview Image. Unfortunately, there are many conditions where Tableau Server itself doesn’t generate anything other than the gray “User Filtered View” thumbnail. This post describes a process for using the full Image (not preview) REST API endpoints to generate attractive thumbnails images.
One of the conditions that results in a gray generic thumbnails is any workbook that is connected to a Published Data Source that has Row Level Security set up through Data Source Filters.
The basic workflow is:
- Request all of the Views from a Workbook (or the Default View if you only need thumbnails at the Workbook level) for one particular user, who you know has enough data access to result in a good looking thumbnail
- Call the actual Image endpoints (instead of the Thumbnail / Preview endpoints) for every View you need
- Reduce the images to thumbnail size
- Cache the thumbnails for efficient retrieval by your embedded web application
Setting up Security Rules to Allow for a Good Looking Thumbnail
When you have Row Level Security implemented in a workbook, the actual rendered viz that each user sees can be different from anyone else’s view. Once RLS is implemented as a Data Source Filter, it can even be difficult for someone to work on the workbook if they aren’t granted access in the RLS entitlements tables.
The easiest way to work around this is to create a few rules that “opt-out” people in certain groups from the RLS calculations. A Tableau Calculated Field might look like:
IF ISMEMBEROF('Admins') THEN 1 ELSEIF ISMEMBEROF('Server Automation') THEN 1 ELSEIF [Username] = USERNAME() THEN 1 ELSE 0 END
This would allow you to use any username in the “Server Automation” Tableau Server Group to generate thumbnails using ALL of the data.
What if I can’t show ANY of the data in the real database
Sometimes you want to generate a thumbnail, but security rules mean that NO ONE should see anything except their own data. There are a few patterns you can follow:
- Generate unique thumbnails for each person: This is definitely possible, but requires a large amount of processing time and storage. It is the equivalent of sending an e-mail out for every view the user has access to, and storing them somewhere.
- Have some “Dummy Data” in the Production Database: which only a “Server Automation” user would have access to. In this case, the Calculated Field in Tableau would filter down to this specific “Dummy Data” when someone is in the ‘Server Automation’ Group, rather than just returning 1 for every row like the example above
- Generate Thumbnails from a Different Database of “Dummy Data”: There’s no reason you have to show thumbnails generated from the exact workbook, since you’re having to go to all this work already. You could publish a second workbook, have it connected to your test database with dummy data, then generate all the thumbnails from that workbook. Cache them and then display the thumbnails put link to the Production workbook
Out of these, #3 requires more setup but probably is the most efficient in the long run.
Requesting the Images
The endpoint to use for actually requesting the images is Query View Image . To use Query View Image, you need to have the View IDs, which can be acquired through several different endpoints:
- Query Views on Site
- A combination of Query Workbooks on Site and Query Views For Workbook
If you are just generating one set of thumbnails for everyone to see, then you just need to log into the REST API as the “thumbnail generator” user (remember, this should be a user who belongs to a group specified in the RLS calculations to see enough data for a representative view to be generated).
Multi-threading your image requests
Most examples we show of REST API commands are sequential, since they are often either adding or updating, and we want that process to complete before we move on to other actions. Image requests actually benefit from being run in parallel, because they are long running requests and they are independent of one-another.
The best practice I’ve found is to create separate threads (multi-threading) to run each image request, which allows any further actions in your program to keep running while the image requests are being processed. You can see the multi-threading in Python in the example at the end, but any language you implement in should be able to do the same.
Resizing the Images Down to Thumbnails
The Query View Image endpoint will return the image at the size of it comes back automatically from the Tableau Server. In most cases, this will be larger than whatever thumbnail size you have chosen to use in your display (for example, our demo system uses a height of 350 pixels for all displayed thumbnails). However, make sure to check before resizing, as certain views (particularly single sheets of something like a pie chart) can actually be smaller than you expect and don’t need to be resized down.
Again, depending on the environment you are implementing in, you’ll use whatever image resizing capabilities you have available that can scale down a PNG image, typically in an imaging package. In the Python example, I used the Pillow and resizeimage packages to give the capabilities necessary.
Caching and Retrieving the Images
Every View on Tableau Server has a “Locally Unique Identifier” (LUID), which, as the name suggests, uniquely identifies that particular view on that particular site. The LUID is always contained in an id property of any XML element in a response from the REST API. If you are generating your thumbnails directly from the Site that will be used in your embedded system, then you can just save or cache your files with the LUID as the identifier, then serve them up when requested.
If you are generating thumbnails from a Test site, instead of the Production workbooks, you’ll need to come up with a naming convention that will let you reference the matching workbooks on each site. I would suggest a combination of the Project Name and Workbook Name as a unique identifier in this case.