ThingWorx から外部サービスを REST で呼ぶ

#はじめに

**対象読者**
この記事は、下記の知識・スキルを持っている人を対象としています。

- ThingWorx 開発の基礎値基礎
- JavaScript の基礎知識

**対象バージョン**

この記事は、ThingWorx 8.x で有効です。おそらく 6.5 以降で共通の仕組みを使っているので古いバージョンでも動くはずですが、Composer のスクリーンショットを 8.3 以降の New Composer で取得していますので、あらかじめご承知おきを。

#ThingWorxと外部サービス接続

ThingWorxから外部のサービスへ接続してデータを取得する際、よく使われるプロトコルが JDBC と REST です。JDBC 経由でデータベースからデータを取得したり、REST API を備える外部サービスへ接続したりします。この記事では、REST API を備える外部サービスへ ThingWorx から接続する方法と簡単なサンプルを紹介します。

#ThingWorx マッシュアップで郵便番号検索をしてみる

では早速 ThingWorx から外部サービスへ接続する具体的な方法を見ていきましょう。今回は、[IW3 Project](http://project.iw3.org/)が提供している[ZIP SEARCH API SERVICE JIS X0404 対応版](http://project.iw3.org/zip_search_x0401/)を利用して、ThingWorx マッシュアップに「郵便番号から住所を検索する」機能を追加してみます。完成形は下図のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119047/6dea0b7f-e6d5-0b0a-f58d-2c38552c61a4.png)

#データシェイプを作る

まず最初にデータシェイプを作ります。なぜデータシェイプが必要かというと、外部サービスから戻される JSON の構造を ThingWorx のマッシュアップビルダーに教えるためです。マッシュアップビルダーで外部サービスから戻された JSON の内部データを他のウィジェットのプロパティにバインドするには、JSON のデータを INFOTABLE へと変換する必要があります。（その方が使い勝手が良いのです）。INFOTABLE には各列のデータ型を指定するデータシェイプが必要ですので、最初にそれを作ります。

ZIP SEARCH API SERVICE のドキュメントをみて、かつ Postman で実際に REST API を叩いてみた限り、このサービスは次の形式の JSON を返すようです。

``` output.json
{
    "state":"14",
    "stateName":"神奈川県",
    "city":"鎌倉市",
    "street":"西御門"
}
```

下図は Postman での REST API を呼び出した様子。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119047/260acc5c-d22b-2bb4-f2e9-b6f9f3b85ec7.png)

今回はこの JSON の構造をそのまま使いましょう。

ThingWorx のマッシュアップビルダーで、新しいデータシェイプを作ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119047/7d0ba5ca-9d0d-a44a-d7be-6ec2d749b7de.png)

ここではデータシェイプの名前を "PostalCode"としました。"state" プロパティを Integer で、その他の "stateName", "city", "street" の各プロパティは String 型で作成しています。

プロパティを追加し終えたら、忘れずに "Save" ボタンをクリックしておくようにしましょう。



#Thing を作る

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119047/3e3a4969-3e26-70db-eb1b-81589ad6d991.png)

外部サービスへ接続する Thing を新しくひとつ作ります。ここでは名前を PostalCodeSearch としました。"Base Thing Template" に GenericThing を選択します。その他は特に設定の必要はありません。（プロジェクトやタグの設定はお好みで）。

一旦、緑色の "Save" ボタンをクリックして、ThingWorx サーバー側に Thing を作成させます。その後、"Services" タブへ移動します。

”Service" タブで新しいサービスを追加します。黒い "Add" ボタンをクリックします。

新しいサービスを定義する画面が表示されますので、まずサービスの名前を決めます。ここでは "SearchAddress" としました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119047/822c0311-be5c-4001-a012-5fd0182a5458.png)

つぎに、SearchAddress サービスが受け取るパラメタを指定します。郵便番号を受け取るので、頭3桁と下4桁を受け取れるように、Number 型の入力パラメタ upperCode と lowerCode を定義します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119047/6731d166-64f9-0c54-eb12-c997bc66f051.png)

さらに、SearchAddress サービスが戻すデータを "Output" に指定します。JSON を戻してもいいのですが、今後の使い勝手を考えて今回は INFOTABLE を選択し、データ型として先ほど作成した "PostalCode" データシェイプを指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119047/0b893f8b-c0ce-a115-df27-b49107492d60.png)

つづいて、いよいよ JavaScript のコーディング部分です。まず最初に、先ほど定義した入力パラメタを受け取るコードを書きます。コードエディタに次のように JavaScript のコードを書きます。

```code.js
var zip_main = ('000' + upperCode).slice(-3);
var zip_sub = ('0000' + lowerCode).slice(-4);
```
コードエディタでは下図のように表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119047/4be84670-4692-fb37-dc34-c382389b87bb.png)

これ、少しわかりづらいのですが、入力パラメタが Number 型なので、たとえばユーザーが "0001" を入力しても、数値の 1 が渡されてしまいます。しかしこれだと郵便番号としては不十分です。郵便番号は "0001" でなければなりません。そこで、受け取った数値の桁数を強制的に合わせ、先頭に 0 をパディングするためにこうしたコードになっています。

次に、"Snippets" の "Resources" グループで "ContentLoaderFunctions" を探して、"GetJSON" サービスの右側にある黒い矢印をクリックします。そうすると、コードエディタにコードの断片（スニペット）が挿入されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119047/7e988bb4-8114-bda8-81e6-50c9e1e644ab.png)

自動で挿入されたコードの断片を編集します。

```code.js
var params = {
	proxyScheme: undefined /* STRING */,
	headers: undefined /* JSON */,
	ignoreSSLErrors: undefined /* BOOLEAN */,
	useNTLM: undefined /* BOOLEAN */,
	workstation: undefined /* STRING */,
	useProxy: undefined /* BOOLEAN */,
	withCookies: undefined /* BOOLEAN */,
	proxyHost: undefined /* STRING */,
	//
	// 次の行を編集する
	// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓
	url: "http://api.thni.net/jzip/X0401/JSON/" + zip_main + "/" + zip_sub + ".js" /* STRING */,
	timeout: undefined /* NUMBER */,
	proxyPort: undefined /* INTEGER */,
	password: undefined /* STRING */,
	domain: undefined /* STRING */,
	username: undefined /* STRING */
};

// result: JSON
// 次の行の return を json に書き換える
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓
var json = Resources["ContentLoaderFunctions"].GetJSON(params);
```
変更点は、"url" に外部サービスの URL をセットすることと、戻された JSON を受け取る変数を result ではなく json という名前に変えているところです。ThingWorx はサービスの最後で result を評価した結果をサービスの出力として扱います。外部サービス呼び出しで戻ってきた JSON をそのまま戻すわけではないので（INFOTABLE を返す）、ここでは result ではなく json という別の変数に格納しているんですね。

それでは受け取った JSON 形式のデータを INFOTABLE へと変換していきましょう。

コードエディタ上でカーソルが最終行にあることを確認して、"Snippets" の "Resources" グループから "InfoTable Functions" を探し、"CreateInfoTableFromDataShape" の右側にある黒い矢印をクリックします。そうするとコードエディタに JavaScript のコード断片が挿入されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119047/b6a250f3-2b99-10ed-e05e-ded388aaf4be.png)

挿入される JavaScript のコードを次のように書き換えます。

```code.js
// params という変数の名前が衝突するので、params2 に変更
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓
var params2 = {
	infoTableName: undefined /* STRING */,
	// dataShapeName に先の手順で作った PostalCode を指定。
	// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓
	dataShapeName: "PostalCode" /* DATASHAPENAME */
};


// result: INFOTABLE
// こちらも忘れずに params という変数の名前を、params2 に変更
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓
var result = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape(params2);
```
これで SearchAddress サービスが返す INFOTABLE オブジェクトを作成できました。しかし、まだ箱を作っただけで肝心のデータをセットしていません。

つづいて、コードエディタのカーソルが最終行にあることを確認して、"Snippets" の "Code Snippets" から "Infotable" を辿り、"Create infotable entry from..." というサービスの右側にある矢印をクリックします。"Create infotable entry from datashape" という画面が立ち上がりますので、セレクトボックスから "PostalCode" を選択して "Insert Code Snippet" をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119047/5e7a3159-36cf-c0ff-d031-98739c2d237c.png)

挿入された JavaScript コードの断片を編集します。下記のようになれば大丈夫です。

```code.js
// PostalCode entry object
var newEntry = new Object();

// 外部サービスから戻された JSON の各フィールドを INFOTABLE の各フィールドに代入する
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓
newEntry.stateName = json.stateName; // STRING
newEntry.city = json.city; // STRING
newEntry.street = json.street; // STRING
newEntry.state = json.state; // INTEGER

// 返却用の INFOTABLE に上記のオブジェクト "newEntry" を追加する
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓
result.AddRow(newEntry);
```

これでサービスの準備は完了です。忘れずに "Save" をクリックして、編集をサーバー側で確定させてください。
コードの全体は下記になります。

```code.js
var zip_main = ('000' + upperCode).slice(-3);
var zip_sub = ('0000' + lowerCode).slice(-4);

var params = {
	proxyScheme: undefined /* STRING */,
	headers: undefined /* JSON */,
	ignoreSSLErrors: undefined /* BOOLEAN */,
	useNTLM: undefined /* BOOLEAN */,
	workstation: undefined /* STRING */,
	useProxy: undefined /* BOOLEAN */,
	withCookies: undefined /* BOOLEAN */,
	proxyHost: undefined /* STRING */,
	url: "http://api.thni.net/jzip/X0401/JSON/" + zip_main + "/" + zip_sub + ".js" /* STRING */,
	timeout: undefined /* NUMBER */,
	proxyPort: undefined /* INTEGER */,
	password: undefined /* STRING */,
	domain: undefined /* STRING */,
	username: undefined /* STRING */
};

// result: JSON
var json = Resources["ContentLoaderFunctions"].GetJSON(params);


var params2 = {
	infoTableName: undefined /* STRING */,
	dataShapeName: "PostalCode" /* DATASHAPENAME */
};

// result: INFOTABLE
var result = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape(params2);

// PostalCode entry object
var newEntry = new Object();
newEntry.stateName = json.stateName; // STRING
newEntry.city = json.city; // STRING
newEntry.street = json.street; // STRING
newEntry.state = json.state; // INTEGER

result.AddRow(newEntry);
```

#作ったサービスをテストする

コードエディタの下にある "Execute" タブの中から、作ったサービスのテスト実行ができます。
入力パラメタ "upperCode" と "lowerCode" に適当な値をセットし、"Execute Service" もしくは "Save and Execute" をクリックすると、サービスが実行されます。結果は右側の "Output" に出力されます。下図は、upperCode に 248、LowerCode に 0004 をセットしてテスト実行した際の様子です。神奈川県の県番号は 14なんですね...

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119047/a833efac-3660-2b1b-49a4-db06f0568414.png)

#マッシュアップを作る

サービスの準備が整いましたので、マッシュアップを作ってみましょう。今回は名前を "PostalCodeSearch" とし、下図のようなウィジェット配置にしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119047/72a6a60b-5b1e-82b2-cb42-c4237f5791b0.png)

"Data" タブで、先ほど作った PostalCodeSearch" Thing の "SearchAddress" サービスを呼びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119047/7dcffb9a-1f7f-752c-dc0d-8bda4f34c8d9.png)

ウィジェットとサービスの関係は次の通り。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119047/2dd47f54-be5e-88ef-d570-803a4aab5cf7.png)

ボタンのクリックで PostalCodeSearch の SearchAddress サービスが呼ばれます。その際、二つの Numeric Entry ウィジェットの値が、入力パラメタとして渡されます。SearchAddress サービスは INFOTABLE を戻してくるので、その中から stateName、city、そして street フィールドの値を、それぞれ Label ウィジェットに渡していますね。

無事マッシュアップも作れたようなので、"Save" ボタンでサーバー側上で編集を確定して、"View Mashup" をクリックしてマッシュアップを実行してみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119047/6dea0b7f-e6d5-0b0a-f58d-2c38552c61a4.png)

頭3桁と下4桁を入力して "検索" ボタンをクリックすると...

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119047/188719ab-8061-bb69-ae95-350e207ed88d.png)

無事動きました。

#終わりに

本記事では、ThingWorx の ContentLoader 機能を使って、外部サービスに REST で接続する方法を説明しました。

今回は外部サービスに REST で接続して GET メソッドで情報を取得しましたが、ConentLoaderFunctions には GET 用のサービス ``GetJSON`` 以外にも　``PostJSON`` や ``PutJSON`` も準備されています。また JSON だけではなく、XML 用の各メソッドも用意されているので、いろいりなサービスに対して CRUD 処理を実行できますね。