BluemixのCloud Functions（OpenWhisk）で、Cloudant更新をトリガーとしてアクションを起動する

温度センサーシミュレータのデータをIoT Platform経由でCloudantのsensorというDBに保管し、温度の値が40度を超えていた時にはalertというDBにも保管するという処理を、BluemixのサーバーレスであるCloud Functions（OpenWhisk）を使って構成します（以下、OpenWhiskと呼びます）。
構成概略はこんな感じです。sensor DBの更新処理はNode-REDで実装し、温度に応じてalertにも保管する処理をOpenWhiskのトリガーとアクションで実装します。
<img width="805" alt="Screen Shot 2017-09-07 at 1.52.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/c641df10-2e31-3d67-7bb2-671c7086b3f0.png">

手順は次のとおりです。

1. Cloudantを更新するNode-REDフローを作成する
2. CloudantにFilterを作成する
3. Cloud FunctionsのためのCLI環境を準備する
4. Cloud Functionsのアクション、バインディング、トリガー、ルールを作成する
5. センサーの温度を上昇させてalert DBのデータが増加することを確認する

-------

## 1. Cloudantを更新するNode-REDフローを作成する

フローの完成形を以下に示します。フローデータも添付しておきます。
<img width="1280" alt="Screen Shot 2017-09-07 at 2.14.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/54e8acdb-88dc-a9b8-ff87-cdb3176ee2bd.png">

```json
[{"id":"4c042e90.7d6608","type":"ibmiot in","z":"f5969270.b73f","authentication":"quickstart","apiKey":"","inputType":"evt","deviceId":"b5f57846c141","applicationId":"","deviceType":"+","eventType":"+","commandType":"","format":"json","name":"IBM IoT","service":"quickstart","allDevices":"","allApplications":"","allDeviceTypes":true,"allEvents":true,"allCommands":"","allFormats":"","qos":0,"x":90,"y":40,"wires":[["1e65aa7e.46adbe"]]},{"id":"5c421e96.e77248","type":"cloudant out","z":"f5969270.b73f","name":"","cloudant":"","database":"sensor","service":"nr-bmx01-cloudantNoSQLDB","payonly":true,"operation":"insert","x":450,"y":120,"wires":[]},{"id":"1e65aa7e.46adbe","type":"delay","z":"f5969270.b73f","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"5","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"x":260,"y":80,"wires":[["5c421e96.e77248"]]}]
```

各ノードの設定方法を説明しておきます。
1-1. IBM IoT入力ノード
<img width="1280" alt="Screen Shot 2017-09-07 at 2.26.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/3deab832-b154-c4df-d7c6-ed595e427b18.png">
温度センサーシミュレータ（https://quickstart.internetofthings.ibmcloud.com/iotsensor/） の右上のセンサーIDの値（ここではac4b21be7d61）を、IBM IoT入力ノードの「Device id」にコピペします。AuthenticationはQuickstart（デフォルト）です。他もデフォルトのままでOKです。設定したら右上の完了をクリックします。

1-2. Delayノード
<img width="946" alt="Screen Shot 2017-09-07 at 2.29.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/eeccd0c1-2d33-2555-aa5d-63dfe87a3f75.png">
中央のノードはdelayノードです。料金プランがLite（無料）のCloudantはスループットが1件／秒以下に制限されています。温度センサーシミュレータのデータ発生頻度が1秒なので、これにCloud Functionsの動作が加わると1件／秒を超えるため、温度センサーのデータをdelayノードで間引きます。上図の設定ではスループットを5秒に1件とし、間で発生したデータは削除することにしています。

1-3. Cloudantノード
<img width="710" alt="Screen Shot 2017-09-07 at 2.36.14.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/46cf6d67-37f1-dd29-7906-4f1a6ad56abd.png">
Service名はデフォルトでOKです。Node-REDインスタンス作成時に一緒に作成されていたCloudantを使用します。Database名はsensorとしておきます。Only store msg.payload object? のチェックボックスにはチェックを入れておきます。

各ノードの設定が終了したら右上のデプロイをクリックして、フローの完成です。
<img width="946" alt="Screen Shot 2017-09-07 at 2.39.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/0813a4d3-26ed-1cef-81a0-eb5743f18dee.png">

Cloudantのダッシュボードを見ると、sensorというDBができて、文書件数が増加していることが確認できます。
<img width="946" alt="Screen Shot 2017-09-07 at 5.58.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/34b62b54-fe2d-b7c3-bb6e-416bd875bbcd.png">

動作が確認できたら、一旦はCloudantをフローから外しておいて下さい（無駄なデータ蓄積を防ぐため）。
<img width="947" alt="Screen Shot 2017-09-07 at 2.40.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/bb11ac29-d982-8924-1b0a-dae81d0d9fb7.png">

## 2. CloudantにFilterを作成する

「温度が40度を超えたら」という判断は、CloudantのFilterを使って実装します。OpenWhiskのトリガーはCloudantの更新をきっかけにして呼ばれ、トリガーはFilterを利用してアクションを呼び出すかどうかを判断する、という仕組みです。
Filterの実体は以下のJSON文書です。よく見ると「40以下ならfalse,そうでなければtrue」という分岐ロジックが入っているのがわかります。

```json
{
  "_id": "_design/hightemp",
  "filters": {
    "over40": "function(doc, req){if (parseInt(doc.d.temp) <= 40){return false;} return true;}"
  }
}
```

これをCloudantのsensor DBのDesign Documentsに登録します。CloudantダッシュボードのDB一覧から、「sensor」をクリックしてsendor DB内の文書一覧画面に入ります。
<img width="947" alt="Screen Shot 2017-09-07 at 6.01.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/cbea490f-8b90-42f5-5268-795855af6237.png">

Design Documents横の「+」をクリックし、プルダウンメニューから「New Doc」を選択します。
<img width="948" alt="Screen Shot 2017-09-07 at 6.02.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/c3367d44-176b-aeb2-0162-636d768503b1.png">

ここにフィルターを記述します。初期値として{ "_id":"xxxxx..."}という値が入っていますが必要ありません。上記のフィルターのJSON文書をそのままコピペして上書きして下さい。
<img width="946" alt="Screen Shot 2017-09-07 at 6.04.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/b9aae1b1-34da-16f0-e002-f8e9994eeeed.png">

コピペしたらCreate Documentをクリックして保管します。
<img width="948" alt="Screen Shot 2017-09-07 at 6.06.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/48a3bda1-ad1c-3db4-c48e-dbbc69f81b0b.png">

Design Documentsの下に「hightemp」という項目が現れるはずです。これでフィルターは完成です。
<img width="945" alt="Screen Shot 2017-09-07 at 6.07.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/a217e56c-dff5-56ae-c199-5c4fe4c04e10.png">

Cloudantのポータルを開いたついでに、温度が40度超の時の保管先DBのalertも作成しておきます。
Databasesの画面で「Create Database」をクリックします。
<img width="943" alt="Screen Shot 2017-09-07 at 8.22.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/fddef82b-df8b-8f14-5b20-8fe50ee82dd9.png">

データベース名としてalertと入力し、Createをクリックします。
<img width="945" alt="Screen Shot 2017-09-07 at 8.23.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/19c8c0ce-c48d-bb4c-9d1c-5e5e81596759.png">

alert DBが作成されました。
<img width="946" alt="Screen Shot 2017-09-07 at 8.24.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/266b46fd-07e5-6a7e-e02d-c877dfbb0637.png">





## 3. Cloud Functions（OpenWhisk）のためのCLI環境を準備する

OpenWhiskのトリガーやアクションの作成の操作は、CLI（コマンドライン・インターフェース）で実行することにします。BluemixにはOpenWhisk管理用のGUIも用意されているのですが、今回は触れません。

CLIインストール、セットアップの手順はBluemixのオンライン・マニュアルの記述に従います。
BluemixのCLIのプラグインとしてOpenWhiskのCLIが提供されているので、BluemixのCLIをインストールしていない場合は、まずそちらからインストールする手順から始めて下さい。
https://console.bluemix.net/docs/cli/reference/bluemix_cli/index.html#getting-started

続いてOpenWhiskのCLIをインストールします。手順は以下のリンク先を参考にして下さい。
https://console.bluemix.net/docs/openwhisk/bluemix_cli.html

OpenWhisk用プラグインをインストールするコマンドをいかに転記しておきます。

```bash
bx plugin install cloud-functions -r Bluemix
```

続いてBluemixにログインし、環境をセットアップします。
まずはログインします。以下の実行例を参考にして下さい。Email>には各自のBluemixアカウント登録したメールアドレス（=BluemixのユーザーID）を入力して下さい。
<img width="1155" alt="Screen Shot 2017-09-07 at 6.31.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/cff09fc1-f20f-492d-32aa-33f19bca5641.png">

ログインしたら、次に「組織名」「スペース名」を登録します。これらはBluemixポータルから最初にログインした際に設定した値です。まだBluemixポータルにログインしたことがない場合は、先にポータルにログインして自分の「組織名」「スペース名」を設定してからこの手順を実行して下さい。以下に例を示します。引数「-o」の後ろが組織名、「-s」の後ろがスペース名です。
<img width="1158" alt="Screen Shot 2017-09-07 at 6.35.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/2a469ad6-9efe-e6bf-0848-e18668161554.png">

環境設定の最後に、OpenWhiskの動作確認をしておきます。動作確認用のアクションがありますので、CLIでそのアクションを呼び出します。{"message":"hello"}が返ってきたらOKです。
<img width="1155" alt="Screen Shot 2017-09-07 at 6.39.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/c7fbeead-d0d6-909e-1ac0-13fac9b63491.png">


## 4. Cloud Functionsのアクション、バインディング、トリガー、ルールを作成する

4-1. アクションの作成
OpenWhiskで実行するプログラムはアクションとして実装します。今回は以下のJavaScriptコードをアクションとして使用します。

```js:alert01.js
// Cloudant更新のトリガーを受けて、更新データを取得する

var Cloudant = require('cloudant');
var user="4217f8ad-efc9-4545-9442-8096cad73a00-bluemix";
var pass="2d388131558dfeebe0ef6eee82c80f6de9196c78cfe97858a084a987e9da81b7";
var host="4217f8ad-efc9-4545-9442-8096cad73a00-bluemix.cloudant.com";
var indb="sensor";
var outdb="alert";

function main(params) {

var cloudantUrl = "https://" + user + ":" + pass + "@" + host;
var cloudant = Cloudant({url:cloudantUrl});

var idb = cloudant.db.use(indb);
var odb = cloudant.db.use(outdb);

return new Promise(function(resolve, reject) {

	idb.get(params.id, {revs_info:true}, function(err, ibody, header) {
	    if (err) {
		reject("### Error:" + err);
	        }
	     else {
		    odb.insert({"d":ibody.d,"original_id":ibody._id}, function(err, obody, header) {
		    if (err) {
		        reject('??? Error = ' + err.message);
		        }
		    else {
		        resolve( obody );
			}
		    });
	        }
	});
   });
}
```

コードはPC上にalert01.jsという名前で保管して下さい。名前は任意ですが、アクションを登録するコマンドでファイル名を指定する際に間違えないようにして下さい。

このコードの最初の方にあるuser,pass,hostの値はユーザー毎に異なるので、自分の値で修正して下さい。これらの値はBluemixのダッシュボードから確認できます。
まずhttps://bluemix.net でBluemixのダッシュボードを開き、サービスの一覧からCloudantのサービスインスタンスを選択（クリック）します。
<img width="944" alt="Screen Shot 2017-09-07 at 7.57.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/ed98045d-b609-40aa-3073-fbf25f65973f.png">

Cloudantの管理画面が開いたら、左列タブを「サービス資格情報」に切り替え、「資格情報の表示」をクリックして展開します。表示されたJSON文書の中に"username","password","host"の値が含まれています。これらの値を使用します。
<img width="945" alt="Screen Shot 2017-09-07 at 7.59.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/e069e979-bde1-1ae6-86c2-cfa94e579764.png">

コードの準備ができたらアクションを登録します。以下のコマンドを実行します。
コマンドの引数のうち、alert01がアクション名、alert01.jsがアクションのコードのファイル名（先程PCに保管したもの）です。

```bash
bx wsk action create alert01 alert01.js
```

4-2. アクションの動作確認
トリガーからアクションを呼び出す際に、JSON文書形式のイベントがトリガーからアクションに渡されます。Cloudantの更新のトリガーの場合は次のような書式になっています。

```json
{
   "id": "6ca436c44074c4c2aa6a40c9a188b348",
   "seq": "2-g1AAAAL9aJyV-GJCaEuqx4-BktQkYp_dmIfC",
   "changes": [
       {
           "rev": "2-da3f80848a480379486fb4a2ad98fa16"
       }
   ]
}
```

ここでidはCloudantの文書IDで、実際の文書内では"_id"という名前で存在しています。以下にsensor DBの文書の例を示します。
<img width="946" alt="Screen Shot 2017-09-07 at 8.59.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/6328a542-452d-82b4-c85d-4945d1ba3020.png">

上記のイベントのidを実在の文書のIDで置き換えてアクションに渡してやることで、アクションの動作確認ができます。
アクションは、受け取ったIDでsensor DBを検索してヒットした文書を取得し、その文書をalert DBに保管するという仕組みになっています。テストがうまく行けば、先程作成した空のalert DBに文書がひとつ追加されるはずです。

まずは上記のイベントのJSON文書を、idの値を実在のIDで置き換えてPCに保管します。ファイル名はevent01.jsonと命名しておきます。

ファイルが準備できたら、次のコマンドでアクションを呼び出します。成功したら"ok":true を含むJSON文書がターミナルに返ってきます。

```bash
bx wsk action invoke --result alert01 --param-file event01.json
```

Cloudantも見てみましょう。空だったalert DBに文書がひとつできているはずです。以下は文書を開いた画面です。"original_id"のところに、event01.json内で指定したIDが入っています。
<img width="946" alt="Screen Shot 2017-09-07 at 9.10.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/1223d225-914e-4f46-7c10-2e4b2f35d71c.png">


4-3. バインディングの作成
OpenWhiskのトリガーがCloudantにアクセスするために、まずCloudantのバインディングを作成します。コマンドは次のとおりです。

```bash
bx wsk package bind /whisk.system/cloudant Cloudant -p username 4217f8ad-efc9-4545-9442-8096cad73a00-bluemix -p password 2d388131558dfeebe0ef6eee82c80f6de9196c78cfe97858a084a987e9da81b7 -p host 4217f8ad-efc9-4545-9442-8096cad73a00-bluemix.cloudant.com
```
ここで-p username, -p password, -p hostの各引数の値は、アクションのコードの中で指定したuser,pass,hostの値と同じものを指定します。ユーザーによって異なりますので、コマンドの値を自分の環境の値に置き換えて実行して下さい。
上記コマンドの実行後、packageの一覧を取得するコマンドを実行してCloudantが表示されたらバインディングは成功です。以下に一覧取得コマンドと実行例を示します。

```bash
bx wsk package list
```

<img width="1153" alt="Screen Shot 2017-09-07 at 21.11.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/662e6657-279b-4f65-e0f4-f4b0d903589f.png">


4-4. トリガーの作成
OpenWhiskで実行するプログラム（すなわちマイクロサービス）はアクションとして実装します。このアクション呼び出すきっかけとなる事象はトリガーとして登録します。
以下にトリガー登録コマンドのサンプルを示します。

```
bx wsk trigger create myCloudantTriggerOver40 --feed /bmx01_Dallas/Cloudant/changes --param dbname sensor --param filter "hightemp/over40"
```


各自で実行する際には以下の箇所を修正して下さい。

* myCloudantTriggerOver40 --- トリガー名です。任意の名前を指定して下さい。
* bmx01_Dallas --- 「組織名_スペース名」です。環境設定時にbx targetコマンドの引数で指定した値を指定して下さい。
* sensor --- 監視対象のCloudant DB名です。ここではこのままでOKです。
* hightemp/over40 --- フィルター名です。hightempというDesign Documentの中のover40というフィルターを使用するという定義です。ここではこのままでOKです。

4-5. ルールの作成

トリガーとアクションを結びつける定義として、ルールを作成します。以下のコマンドを実行します。
ここでmyRule01はルール名です。任意の名前を指定して下さい。
myCloudantTriggerOver40とalert01は、このルールで関連づけるトリガー名とアクション名です。トリガーおよびアクション作成時に命名した名前を指定して下さい。

```bash
bx wsk rule create myRule01 myCloudantTriggerOver40 alert01
```

以上でアクション、トリガー、ルールの作成は完了です。

作成したアクション、トリガー、ルールは、以下のコマンドで照会できます。

```bash
bx wsk list
```
実行例を示します。
<img width="1155" alt="Screen Shot 2017-09-07 at 10.11.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/dafd360e-9203-6d50-1961-c01b0428cd62.png">




## 5. センサーの温度を上昇させてalert DBのデータが増加することを確認する
以上でOpenWhiskの準備は整いましたので、Node-REDのフローに戻ってフローをもう一度つないでデプロイして下さい。デプロイが成功したらsensor DBへのデータ追加が再開します。
<img width="1280" alt="Screen Shot 2017-09-07 at 9.54.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/56ed4aab-9afa-83b8-7aaa-da770894fb0d.png">

データ追加が再開したら、センサーの温度を40度よりも高くしてみて下さい。CloudantのDatabasesの画面をリフレシュして、alertの# of Docsのカウントが上昇すれば成功です。
<img width="1280" alt="Screen Shot 2017-09-07 at 10.09.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/32083/33bbfede-1f48-4fd0-913a-113fcb22b789.png">

動作を確認したら、またNode-REDのフローを切って更新を止めておいて下さい。


以上です。

参照：
[Bluemix CLI 入門](https://console.bluemix.net/docs/cli/reference/bluemix_cli/index.html#getting-started)
[Cloud Functions CLI](https://console.bluemix.net/docs/openwhisk/openwhisk_cli.html#cloud-functions-cli)
[Cloudant パッケージの使用](https://console.bluemix.net/docs/openwhisk/openwhisk_cloudant.html#openwhisk_catalog_cloudant)
[温度センサーシミュレータ](https://quickstart.internetofthings.ibmcloud.com/iotsensor/)