Tableau Webデータコネクタ でいくつかハマったからHello Worldまでまとめ


## はしがき
最近、Tableauを社内で盛り上げよう！というミッションを(自分の中で)背負っています。
データは武器だ、素敵に扱えれば無限大に強くなるぞ！！(そして楽しい！)というのを味わう環境にいたので、BIツールとかワクワクしますね。
前職はD3やらHighchartsやらを使っていたので、「スクラップ&ビルドが出来てこそ、データ分析はその真価を得る・・！」と信じてやみません。
そこで、社内での啓蒙を押し進めていくにあたって、「今まで色々に散らばっていたデータソースを1つにまとめる」事が急務でした。そして、それらもまとめて「気軽に、高速にスクラップ&ビルド」をしてもらおうと。
そのための武器が、 Webデータコネクタ(Web Data Connector)だったわけです。

この記事では、

1. Webデータコネクタってどんなもの・・？
2. 実装サンプルの紹介
3. 実際にどうやって開発を進めるの・・？
4. 参考になりそうなリンク

といったトピックを扱いたいと思います

## Webデータコネクタ? 

公式サイトのページより引用。
[Tableau Web データコネクタ | Tableau Software](http://www.tableau.com/ja-jp/web-data-connector)

```
Web データコネクタで、Web 上のあらゆるデータに Tableau を接続できるようになり、データをこれまで以上に幅広く活用できるようになります。 独自の Web データコネクタを作成しましょう。JSON、XML、 HTML 形式でデータを公開しているサイトであればどのサイトからもデータを読み込むことができるようになります。
```

私は、これをみて、「おお！スゴイ！あらゆるものに勝てる！！」と思ったのでした〜

### どうやって動いてるの？

とはいえ、コレが非常に複雑だったら扱えないわけで、「(技術的に)どういう仕組・・？どんなインターフェイスになっているの・・？」というのが気になります。
結論から言うと、「用意されているJavsScriptのSDKを媒介にして、Tableauに送信できる」ようになっています。つまり、必要なのはJavaScriptです。

「いかにシンプルであるか」を示すために、最終的なコードをここで一旦示します

```html
<html>
<head>
<script src="https://connectors.tableau.com/libs/tableauwdc-1.1.1.js" type="text/javascript"></script>
<script type="text/javascript">
(function() {
    var myConnector = tableau.makeConnector();
    myConnector.getColumnHeaders = function() {
        var fieldNames = ['id', 'fruit', 'color'];
        var fieldTypes = ['int', 'string', 'string'];
        tableau.headersCallback(fieldNames, fieldTypes);
    };
    myConnector.getTableData = function(lastRecordToken) {
        dataToReturn = [
            [1, 'Apples', 'red'],
            [2, 'Oranges', 'orange'],
            [3, 'Bananas', 'yellow']
        ];
        tableau.dataCallback(dataToReturn, false, false);
    };

    myConnector.init = function() {
        tableau.initCallback();
        if (tableau.phase == tableau.phaseEnum.interactivePhase) {
            tableau.submit();
        }
    };
    tableau.registerConnector(myConnector);
})();
</script>
</head>
<body>
</body>
</html>
```

これを `localhost:8087` で実行して接続した時のイメージがこちらです

![Hellow Wodld動作サンプル](https://qiita-image-store.s3.amazonaws.com/0/941/4172a053-f4fc-38a7-aaf6-64606793a36f.gif "qiita-wdc-hello_world.gif")

## 実装内容
最低限やることは5つです。

1. connectorの初期化
2. 列情報(ヘッダー)の定義
3. 行情報(データ)の読み出し
4. Tableauへの情報送信
5. connectorの登録

これらを、公式ドキュメント等を通してTableauオブジェクトと称されているオブジェクトを介して行っていきます。
まずTableauのJS SDKが必要になるのですが、これはホストされているサーバーから直接読み込ませることが可能です

```html
<script src="https://connectors.tableau.com/libs/tableauwdc-1.1.1.js" type="text/javascript"></script>
```

### 1. connectorの初期化(インスタンスの生成)
これは説明を省略・・そのまんまですね^^

```js
var myConnector = tableau.makeConnector();
```
このインスタンスに対して各種設定(列情報の取り出し方、など)のアタッチしていきます
[API: makeConnector()](https://onlinehelp.tableau.com/current/api/wdc/en-us/help.htm#WDC/wdc_ref.htm#tableau_functions_makeConnector)

### 2. 列情報(ヘッダー)の定義
`myConnector`に対して、 `getColumnHeaders()`の中身を定義していきます。

```js
myConnector.getColumnHeaders = function() {
    var fieldNames = ['id', 'fruit', 'color'];
    var fieldTypes = ['int', 'string', 'string'];
    tableau.headersCallback(fieldNames, fieldTypes);
};
```

この内部で、 `tableau.headersCallback()`を呼ぶようにして下さい。
「`getColumnHeaders()`がdocumentから呼ばれて」「`tableau.headersCallback()`がtableauに情報を伝える」という感じです。
`headersCallback()`は、「第1引数に列名のリスト」「第2引数にそれぞれの列(順一致)の型を列挙したリスト」を渡すという感じです。
型には、`bool・date・datetime・float・int・string`が利用可能とのことです

[API: getColumnHeaders()](https://onlinehelp.tableau.com/current/api/wdc/en-us/help.htm#WDC/wdc_ref.htm#connector_getColumnHeaders)
[API: headersCallback](https://onlinehelp.tableau.com/current/api/wdc/en-us/help.htm#WDC/wdc_ref.htm#tableau_functions_headersCallback)

### 3. 行情報(データ)の読み出し
これもフィールド定義と同じように、「呼び出されるメソッドが決まっていて」「その内部からTableauにデータを送る」という処理になります。

```js
myConnector.getTableData = function(lastRecordToken) {
    dataToReturn = [
        [1, 'Apples', 'red'],
        [2, 'Oranges', 'orange'],
        [3, 'Bananas', 'yellow']
    ];
    tableau.dataCallback(dataToReturn, false, false);
};
```
`tableau.dataCallback()`の第1引数が実際のデータです。第2,3引数はページングに関連した内容で、今回はこちらの説明は省略します。
データに関しては、図中のように「列の宣言順と同じ順番で(各行の)データを入れる」、もしくは「列の名前と同じキーを持つオブジェクト」でも入力可能です

```js
[
	{id: 1, fruit: 'Apples', color: 'red'},
	{id: 2, fruit: 'Oranges', color: 'orange'},
	{id: 3, fruit: 'Other'}  // 特定のフィールドを省略、という事も可能
];
```

[API: getTableData()](https://onlinehelp.tableau.com/current/api/wdc/en-us/help.htm#WDC/wdc_ref.htm#connector_getTableData)
[API: dataCallback()](https://onlinehelp.tableau.com/current/api/wdc/en-us/help.htm#WDC/wdc_ref.htm#tableau_functions_dataCallback)


### 4. Tableauへの情報送信

`tableau.submit()`を呼ぶと、情報の送信が行われます。
もう少し具体的に言うと、`submit()`を読んでTableau側に「操作フェーズ(Intaractive Phase)や認証フェーズ(authentication phase)」が完了して、データの抽出フェーズに移行できる状態になったことを伝えます。
これにより、(ポップアップされたウィンドウが閉じて)Tableau本体側のデータ読み込みに切り替わるのです。
下記のコードでは、「インタラクティブフェーズを終了していたら、`submit()` をコールしない」という処理のために条件式を加えています。

```js
myConnector.init = function() {
    tableau.initCallback();
    if (tableau.phase == tableau.phaseEnum.interactivePhase) {
        tableau.submit();
    }
};

```

[API: init()](https://onlinehelp.tableau.com/current/api/wdc/en-us/help.htm#WDC/wdc_ref.htm#connector_init)

今回の例では、インタラクティブな操作なしに勝手にデータを読みこませるという処理になるので、`init()` の中で `submit()`まで呼んでしまっています。
実際には、フォーム等を操作してボタンをクリックした時に初めてデータの取得を〜というケースが多くなるかなと。その場合は、`init()`の内側にはおかずDOMイベントのコールバックに仕掛けるといった実装になるかと思います。

#### フェーズについて
Webデータコネクタを操作するにあたって、基本的に「画面を操作する」「データを抽出する」という2つの段階をもっています(※フローによって、「認証方法の確認をする」が挟まれます)。 これらの後に、「抽出したデータがTableauに反映される」という状態の遷移をたどるという格好です。

[API: phase](https://onlinehelp.tableau.com/current/api/wdc/en-us/help.htm#WDC/wdc_ref.htm#tableau_properties_phase)

これにより、「いまは条件指定などの操作をしている？」「前設定が完了して、データを集めている？？」といった状態を判別が可能になります。
以下の様なphaseが管理できるようになります。

* tableau.phaseEnum.interactivePhase.
* tableau.phaseEnum.gatherDataPhase.
* tableau.phaseEnum.authPhase.

「パラメータ等のデータを入力するときの処理」「Tableauに実際に返す時の処理」を簡単に切り分けられますね。
また、phaseについては次の記事も参考です。
[What Happens at Run Time (Phases of a Web Data Connector)](https://onlinehelp.tableau.com/current/api/wdc/en-us/help.htm#WDC/wdc_phases.htm)

特に注意すべきは「interactivePhase」から「getherDataPhase」に移る際に、「全く別のクライアントである」ように振る舞われることです。
(当初、これを把握しておらずセッションを維持しつつサーバーとのやりとりをしようとしてめっちゃハマりました。。。)

```
This is important; the two phases of the web data connector interaction happen in entirely separate browser contexts, so there is no other mechanism (like global variables or cookies) that your code can use to persist and pass information between the phases.
```

この辺りは、 `tableau.connectionData` を上手く活用するか、SPAっぽくしてページ遷移を発生させないようにする・・といったことになるかと思います。

### 5. connectorの登録
最後に、諸々の設定を加えて `myConnector`を登録して完了です。

```js
tableau.registerConnector(myConnector);
```

[API: registerConnector()](https://onlinehelp.tableau.com/current/api/wdc/en-us/help.htm#WDC/wdc_ref.htm#tableau_functions_registerConnector)

## 実際の開発フロー
これで実装はできました。
あとはTableauに直接食わせて利用を開始するだけ・・・なのですが、実際の開発についてはいちいちTableauを介していては手間もかかるし、何よりエラー情報等が読み取れずデバッグができません。
かといって(Tableauでない)Webブラウザ上から直接よんでみたところで、実行も不能です。
そこで利用するのが、webdataconnectorのSimulatorです。
https://github.com/tableau/webdataconnector


この中に `start_server_mac.sh` `start_server_windows.bat`というプログラムがありますので、これを実行してみてください。スタンドアロンなサーバーとして機能し、tableauオブジェクトを扱って読み取ったデータを表として出力する・・というところまでやってくれます。

```sh
$ sh start_server_mac.sh
Serving HTTP on 0.0.0.0 port 8888 ...
```

ブラウザから http://localhost:8888/Simulator/ を開き、 _WDC URL_ に検証したいWebデータコネクタのURLを入力します。その上で「Run Intaractive Phase」ボタンをクリックすると、Webデータコネクタに接続してIntractive Phaseが実行されます。

![qiita-wdc-simulator.gif](https://qiita-image-store.s3.amazonaws.com/0/941/89300ee1-af1d-2aff-784d-413b07a569c3.gif "qiita-wdc-simulator.gif")

また、 `tableau.log()` を用いることで、(Simulator経由でブラウザのDeveloperツールに)ログを送出することも可能です。

```js
myConnector.init = function() {
	  tableau.connectionName = 'Qiita用サンプルソース';
	  tableau.initCallback();
	  tableau.log('initの時でも')
	  if(tableau.phase == tableau.phaseEnum.interactivePhase || tableau.phase == table    au.phaseEnum.authPhase) {
		  tableau.submit();
	  tableau.log('submitと同じタイミングでもログが吐ける');
	 }
};
```

![qiita-wdc-simulator-logging.gif](https://qiita-image-store.s3.amazonaws.com/0/941/6a7e77e4-5fa9-8cb4-2e58-c7b321bc7d95.gif "qiita-wdc-simulator-logging.gif")



以上で、TableauでWebデータコネクタを実装するにあたっての流れは完了となります。
 
## 参考になりそうなリンク
最後に、「基本的な情報がある」「ハマった時に助けられた」という観点から参考になりそうな(参考にした)ページのリンクを貼っておきます。

* [Web データ コネクターの基本チュートリアル](https://onlinehelp.tableau.com/current/api/wdc/ja-jp/help.htm#WDC/wdc_tutorial_basic.htm)
	* 公式のチュートリアル。最初に読みました
* [tableau/webdataconnector](https://github.com/tableau/webdataconnector)
	* Tableauの公式レポジトリで、様々なExampleも含まれています
* [Tableau 9.1 新機能: Tableau Web Data Connectorを使ってGoogleスプレッドシートへの接続を試してみた ｜ Developers.IO](http://dev.classmethod.jp/business/business-analytics/connecting-to-google-spread-sheet-from-tableau-by-using-web-data-connector/)
* [Web Data Connector API Reference](https://onlinehelp.tableau.com/current/api/wdc/en-us/help.htm#WDC/wdc_ref.htm%3FTocPath%3D_____9)
	* 本文中になんども触れていますが、API リファレンスです。
* [Debugging Tips & Tricks - YouTube](https://www.youtube.com/watch?v=RIVIh-btZh4&list=PL_qx68DwhYA8E80s9JOnQzzPkX7sWK-pl&index=5)
	* 公式の動画です
* [[Tableau] Web データコネクタ (WDC) 機能を使ってみた ｜ Developers.IO](http://dev.classmethod.jp/tableau/how-to-tableau-wdc/)
	* 実際に何か動かしてみよう！と思った時に1番参考にさせていただきました！