REST API

Web Services / REST APIs as a Data Source in Tableau: A PostgreSQL/Python solution

The trend toward wrapping together data and security filtering into RESTful web services has only increased in the past few years, and for a lot of good reasons. If the REST API is optimized to return very quickly based on a set of filtering parameters, the performance alone can justify the architecture. However, Tableau is planning to do more with the result set than simply display it directly — Tableau is ready to do deep analysis and discover new insights. Because of this, Tableau’s VizQL engine needs something it can query in many different ways as the basis for a data source connection.

How can we bridge the gap between a JSON (or XML, if anyone still does that) object response and a relational query engine?

What if I told you THIS was all that was necessary on the Tableau Desktop side to build a data source that acts as a “live” connection to a web service?:

Accessing the PL/Python Function in Tableau

Custom SQL in Tableau

 

Connect to the PostgreSQL database in Tableau Desktop using whatever credentials you created for an end user.

Then on the Data Connection screen, you’ll find on New Custom SQL on the left:

If you drag that out, then the dialog to define the Custom SQL query appears:

Custom SQL dialog

As you can see, the query is just SELECT * FROM function_name( arguments ), with whatever parameters you want to attach. You can create new parameters right from this dialog box, then put them in place. Make sure to give sensible defaults, and probably most should default to an empty string so that someone can only access if they have the right tokens / etc.

How is this possible?

There’s a whole lot of setup on the PostgreSQL side, but the payoff is the simplicity of the setup in Tableau.

The solution is based on the architecture originally laid out here, but I’ve put together a Dockerfile which builds out a PostgreSQL database ready for your custom set of functions which will dynamically call a RESTful Web Service based on Tableau’s parameters.

(more…)

tableau_tools 5.0 : Python 3 for 2020 (and so much more!)

Just in time for 2020, tableau_tools has gone a thorough upgrade to bring it into the Python 3 era. While the 4 series of tableau_tools was Python 3 compatible, tableau_tools 5.0 and beyond are Python 3 native, dropping support for 2.7 entirely. It also drops support for any version of Tableau before 10.3, which matches Tableau’s official support policy at this point.

In the process, the source has also been completely refactored for anyone who wants to join in and help with the project or just is tracking down a bug or strange behavior. It’s far easier now to find where everything is implemented, understand the logic of it, and make suggestions or changes.

Update to Python 3.6 or better, and then use PIP to install the latest version from PyPi. For all the good details, read more…

(more…)

Exposing Limited Amounts of Tableau REST API Functionality to Users via tableau_tools

As of 2019.2, the Tableau Server REST API only allows logging in for a REST API session using a combination of username and password. This means there is no effective way to directly start a REST API session using a SSO mechanism (SAML, JWT, etc.)  Even if you were able to, you might still want to restrict the user to only do certain actions (for example, enabling Querying methods but not Updates or Deletes).

The best practice for working around this is to wrap the Tableau REST API in another REST API service of your own design. Then within that wrapper, use a Server or Site Administrator level account to log in to the Tableau Server REST API. In this article, we’ll discuss how to achieve this using tableau_tools, with both a simple and a more complex but efficient design pattern.

(more…)

Keeping Web Edit Content Private

Tableau’s behavior for saving content when using Web Edit follows these rules:

  1. If you are the Content Owner, you can Save or Save As
  2. If you are not the Content Owner, you can Save As

Save As is only allowed to Projects where you (or the groups you belong to) have a Save permission set to “Allow”.

Since a newly Saved Workbook will take the Default Permissions of the Project it saves into, if other people also have permissions for that same Project, they will also be able to access that content. This leads to several different strategies for controlling the privacy of content created through Save As.

Possible solutions:

  • A Project Per Team / Group
  • A Project Per User
  • A REST API script that “fixes” Permissions
  • Publishing a New Copy rather than Save As

(more…)

Replicating Workbooks with Published Data Sources

If you were ever wondering why there is both a REST API and a Document API produced by Tableau, or why we at this blog put out tableau_tools implementing both of those functionalities (and more!), this use case will illustrate it clearly.

The desired action: Specify a workbook on one Tableau Server site to be downloaded and published to a different Tableau Server site (we’ll call this “replicating over”).

Why it is complicated: Best practice with Tableau Workbooks is to Publish their Data Sources separately, to aid in managing the metadata and to provide for unbreakable Row Level Security, among other great reasons. This means we need to download any Published Data Sources that the Workbook is connected to, and publish them over to the new site as well. Simple enough, right?

After a lot of research and testing, the following steps are required to accomplish this correctly:

  1. Download all of the workbooks you are interested in using the REST API
    1. Makes sure to do this one Project at a time, because Workbooks can have the same name if they are in different Projects
  2. Open up each of the workbook files to look at which published data sources (use tableau_tools.tableau_documents)
    1. Scan through all of the datasource elements in the Workbook XML.
    2. Check to see if each datasource is a published data sources
    3. If a published data source is found, find the contentUrl referenced within
  3. Query all Data Sources using the REST API. Search for any Data Source whose  contentURL attribute matches one of those from the workbooks
  4. Download the matching data sources using the REST API
  5. Publish the data sources across to the new Site
    1. You will need to provide the credentials for any data source at publish time, since there is no way to securely retrieve them from the originating site
  6. Once published, retrieve the details from the new Data Source on the new site, including the new contentUrl property
  7. Reopen the workbook file, then change the Site and Data Source cotentUrls to match the the newly published Data Sources on the destination site
  8. Publish the workbook using the REST API

Luckily, all of this is possible using tableau_tools, and there is a sample script available now showing how to do it.

(more…)

Publishing Extracts from a Template Data Source using tableau_tools

With the release of tableau_tools 4.0.0 and Tableau Server 10.5, most of the pieces are in place in the library and in the product itself to allow for an efficient path for publishing unique extracts for different customers all from a single original data source (or workbook) template.

The basics steps of the technique are:

  1. Create a template live connection to a database table, Custom SQL or a Stored Procedure in Tableau Desktop. This does not need to be the final table/custom SQL or Stored Proc; you can use a test or QA data source and switch it programmatically to the final source
    1. Optional: Set up your the appropriate filtering for a single customer / user / etc. — whatever the main filtering field will be. You can instead add this later programmatically.
  2. Save that file (TDS or TWB)
  3. Use the tableau_tools.tableau_documents sub-module to programmatically add any additional filters or modify the filters / parameters you set
  4. Use tableau_tools to alter the actual table / SP / Custom SQL to the final version of that customer
  5. Add an extract to that data source in tableau_tools. This will use the Extract API / SDK to generate an empty extract with the bare minimum of requirements to allow it to publish and refresh
  6. Save the new file. It will be saved as a TWBX or TDSX, based on the input file type
  7. Publish the file to Tableau Server
  8. Send an Extract Refresh command to Tableau Server using the REST API (using the tableau_tools.tableau_rest_api sub-module).
  9. Extract will refresh based on the information in the TDS and be filled out with information just for the specified customer/user/whatever you filtered

(more…)

Republishing Extracts from One Site (or Server) to Another with tableau_tools

Imagine you have a Data Source (in a workbook or outside of one) which is an extract, refreshing on a schedule. But that same data could be used on a different site, or a different server. There are lots of reasons to have logical partitions that basically need a copy of data, particularly related to security. You might have an internal server that connects to data sources allowing the refresh, but want to push that content to a server that eventually connects to the public Internet.

The REST API allow for this fairly easily — you simply download the first workbook, then republish to a different site with the Save Credentials options set to “False”. No credentials means the extract can’t update, but that’s exactly the idea behind this exercise — you want no way to access the database.

Note: The Tableau workbook and data source files will still contain some information about the original live data source that the extract was created from, but no passwords (no credentials are passed to the second site/server). If you need complete lock-down security, I can try and explore how much you can blank out of the XML while still publishing successfully.

This is very easily accomplished via tableau_tools:


# -*- coding: utf-8 -*-

from tableau_tools.tableau_rest_api import *
from tableau_tools import *
import time

o_server = u'http://'
o_username = u''
o_password = u''
o_site_content_url = u''

logger = Logger(u'move.log')

d_server = u'http://'
d_username = u''
d_password = u''
d_site_content_url = u''

t = TableauRestApiConnection26(o_server, o_username, o_password, o_site_content_url)
t.signin()
t.enable_logging(logger)
downloaded_filename = u'File Name'
wb_name_on_server = u'WB Name on Server'
proj_name = u'Default'
t.download_workbook(wb_name_on_server, downloaded_filename, proj_name_or_luid=proj_name)

d = TableauRestApiConnection26(d_server, d_username, d_password, d_site_content_url)
d.signin()
d.enable_logging(logger)
proj = d.query_project(u'Default')
d.publish_workbook(u'{}.twbx'.format(downloaded_filename), wb_name_on_server, proj, save_credentials=False, overwrite=True)