この記事は『CRESCO Advent Calendar 2021』5日目の記事です。

 

こんにちは、データテクノロジーセンターのn-satoです。

以前TableauOnlineまたはTableauServer上のワークブックやデータソースを管理できるツールを作りたいなと思い、Tableauが提供するREST APIを用いて実装しようとしました。
欲しい情報を取得するためにいろいろなREST APIのエンドポイントにリクエストを投げる必要があり、なかなか利用するのに苦労しました。

Metadata APIはエンドポイント1つに対し、GraphQLを使うことでエンドポイント1つでもいろいろ情報が取れるとのことです。
GraphQL自体使ったことがなかったので、どんな風に実装できるのか調査しました。

本記事ではPythonでMetadata APIでGraphQLのクエリを発行できるようになるまでを記事にしたいと思います。

やること

  • Tableau OnlineからAPI使用するためのトークンを取得する
  • Metadata APIのエンドポイントへクエリ発行を行い、結果を取得する

環境

Tableau Onlineバージョン:2021.4.0
※Tableau OnlineはDevelopper Programに登録することで利用できる環境を使用しています。
https://www.tableau.com/ja-jp/developer

Tableau OnlineからAPI使用するためのトークンを取得する

Metadata APIはREST APIと同じ認証プロセスとトークンを使用します。
以下のREST APIのリファレンスサイトを参考に実装していきます。
https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_concepts_auth.htm

The Sign In URI

この項目にサインインリクエストのURIが書いてあります。<server-name>の部分は自身の接続先に置き換えます。

http://<server-name>/api/3.13/auth/signin

Make a Sign In Request with Username and Password

ボディに含める内容が書いてあります。
JSON形式では以下の形式で作成します。
※今回はユーザー名、パスワードでのサインイン処理で進めます。

以下はリファレンスサイトの例です。

{
"credentials": {
"name": "admin",
"password": "p@ssword",
"site": {
"contentUrl": "MarketingTeam"
}
}
}

本文中にJSONで送るときはヘッダに”Content-Type” : “application/json”つけてねとあるので、以下をヘッダに含めます。

{
"accept": "application/json",
"Content-Type": "application/json"
}

Response for a Successful Sign In Operation

サインイン処理に成功したときのレスポンスの内容が書いてあります。
ヘルプの例にあるように、token部分(以下の例では12ab34cd56ef78ab90cd12ef34ab56cd)を使うことでAPIを使うことができます。

以下はリファレンスサイトの例です。

{
"credentials": {
"site": {
"id": "9a8b7c6d5-e4f3-a2b1-c0d9-e8f7a6b5c4d",
"contentUrl": ""
},
"user": {
"id": "9f9e9d9c-8b8a-8f8e-7d7c-7b7a6f6d6e6d"
},
"token": "12ab34cd56ef78ab90cd12ef34ab56cd"
}
}

実装

ここまでで一旦トークン取得するまでをPythonで作ります。

import requests
import json
def get_token(server, username, password, site):
# URLの構築(APIのバージョンが変わったら3.13の部分を変更する)
url = server + "/api/3.13/auth/signin"
# ヘッダ
headers = {
"accept": "application/json",
"content-type": "application/json"
}
# ボディ
body = {"credentials": {"name": username, "password": password, "site": {"contentUrl": site}}}
# POST
pst = requests.post(url, json=body, headers=headers)
# 結果取得
response = pst.json()
token = response["credentials"]["token"]
return token
def main():
server = <Tableau OnlineのURL>
username = <ユーザー名>
password = <パスワード>
site = <サイト名>
token = get_token(server, username, password, site)
print(token)
if __name__ == "__main__":
main()

Metadata APIのエンドポイントへクエリ発行を行い、結果を取得する

トークンを取得できるようになったら、それを利用してMetadata APIを使っていきます。

Using the Authentication Token In Subsequent Calls

トークンの渡し方が書いてあります。
ヘッダにX-Tableau-Authの値で含めて渡します。

"X-Tableau-Auth" : <トークン>

JSON形式でやり取りするので以下のようになります。

{
"accept": "application/json",
"Content-Type": "application/json",
"X-Tableau-Auth": <トークン>
}

ここからMetadata APIの内容になっていきます。

以下のリファレンスサイトを参考に進めます。
https://help.tableau.com/current/api/metadata_api/en-us/docs/meta_api_start.html

URI

エンドポイントは以下となります。<server-name>の部分は自身の接続先に置き換えます。

http://<server-name>/api/metadata/graphql

Query body

クエリの例があります。

query <query-name>{
<object> (<arguments>){
<attribute>
<attribute>{
<attribute>
}
}
}

Explore the Metadata API schema using GraphiQL

ブラウザツールを使ってクエリをテストする環境のことが書かれています。
いろいろ書いてありますが、ログインした端末から以下のURLをブラウザで表示させればツールが使えるとのことです。

http://<server-name>/metadata/graphiql/

・・・クエリ言語名はGraphQLだけどこっちはGraphiQLなんですね。GUIの名前でしょうか。

アクセスすると以下のような画面が表示されます。左側にクエリを書いて実行します。左側にリファレンスがあるのでそれを参考に組み立てられそうです。

とりあえず”WB01″という名前のワークブックの情報を取るクエリを作りました。

リクエストボディの作成

作ったクエリをリクエストボディに含めていきます。
リファレンスサイトでは見つけられませんでしたが、一般的に以下となります(JSON形式の場合)。

{ "query": <クエリ本文>}

今回は以下のように作成しました。

"query":
'''
query getworkbook{
workbooks(filter: {name: "WB01"}) {
id
name
projectName
createdAt
updatedAt
sheets {
name
}
}
}
'''

Query response

レスポンスの構造が書いてあります。
“data”の項目をひとまず取得すれば大丈夫そうです。

以下はリファレンスサイトの例です。

{
"data": {
"databases": [
{
"name": "adventureworks",
"tables": [
{
"name": "AWBuildVersion"
}
]
},
(省略)
]
}
}

実装

ここまでの内容をPythonで作成します。

import requests
import json
# トークンの取得
def get_token(server, username, password, site):
# URLの構築(APIのバージョンが変わったら3.13の部分を変更する)
url = server + "/api/3.13/auth/signin"
# ヘッダ
headers = {
"accept": "application/json",
"content-type": "application/json"
}
# ボディ
body = {"credentials": {"name": username,
"password": password, "site": {"contentUrl": site}}}
# POST
pst = requests.post(url, json=body, headers=headers)
# 結果取得
response = pst.json()
token = response["credentials"]["token"]
return token
# MetaData APIの実行
def executeMetaDataAPI(server, token):
# URLの構築
url = server + "/api/metadata/graphql"
# ヘッダ
headers = {
"accept": "application/json",
"content-type": "application/json",
"X-tableau-auth": token
}
# ボディ(クエリ)
body = {
"query":
'''
query getworkbook{
workbooks(filter: {name: "WB01"}) {
id
name
projectName
createdAt
updatedAt
sheets {
name
}
}
}
'''
}
# POST
pst = requests.post(url, json=body, headers=headers)
response = pst.json()
# 結果取得
data = response["data"]
print(data)
def main():
server = <Tableau OnlineのURL>
username = <ユーザー名>
password = <パスワード>
site = <サイト名>
token = get_token(server, username, password, site)
data = executeMetaDataAPI(server, token)
if __name__ == "__main__":
main()

実行結果

{
'workbooks': [
{
'id': 'xxxxxx',
'name': 'WB01',
'projectName': 'Test01',
'createdAt': '2021-05-30T12: 20: 23Z',
'updatedAt': '2021-05-30T12: 20: 23Z',
'sheets': [
{
'name': 'シート 1'
}
]
}
]
}

Metadata APIを使って情報取得できるようになりました。

まとめ

今回GraphQLの書き方がわかっていない部分が多かったので静的なクエリでの実装を行いましたが、
クエリ内に変数を組み込んで動的なクエリに発展させていくこともできそうでした。

使い勝手としてはエンドポイントが1つなのは嬉しいですが、GraphiQLでの記述が使いこなせるかといった具合になりそうです。

最後までお読みいただきありがとうございました。