kintone 連携の Azure Functions 開発メモ

kintone から webhook で Azure Function を呼ぶ連携処理を開発時のメモです。
VSCode を使うとローカルPCで開発・デバッグが可能で、Azure Functions 本番環境への反映もできます。
手軽に API を開発可能かつサーバー管理が不要で運用も楽です。kintone の外部連携にぴったり。
とりあえず、調べたことを書いておきます。

# 開発環境

Azure Function は、クラウド上で動作しますが VSCode で使うとローカルPC上で開発・デバッグが出来ます。
VSCode は、Mac でも動きますので、Mac でも同じように開発できると思います。

- Windows 10
- Visual Studio Code
- JavaScript, node.js
- kintone



# Azure Functions

[Azure Functions のドキュメント](https://docs.microsoft.com/ja-jp/azure/azure-functions/)

## Azure 実行環境

OS は、Windows と Linux(プレビュー) から選択できますが、Linux は、App Service プランのみで従量制は選択できません。
既定値は、 Windows です。今回はそのまま Windows を使います。
実際に動作している環境は、Azure Functions のコンソール画面から確認できます。
Windows10 ですね。

```.log
D:\home\site\wwwroot\HttpTriggerJS1> ver
Microsoft Windows [Version 10.0.14393]
```


## Azure Functions 価格

サーバーレスがはやりといっても、料金がいくらかで採用するかどうかの判断が必要です。
従量課金プランと Azure App Service プランがあり、kintone で扱うレコードなら従量課金プランの無料枠でいけそうです。ただし、関数毎ではなくアカウントの合計で 100 万回/月までです。
メモリやストレージ、帯域幅などを想定して、料金計算ツールを使えば、計算できます。


[Functions の価格](https://azure.microsoft.com/ja-jp/pricing/details/functions/)[]()
>Functions では、すべての関数に対して各月にリクエストされた実行の総数に基づいて課金されます。実行回数は、バインドによってトリガーされたイベントに応じて関数が実行されるたびにカウントされます。毎月、最初の 100 万回は無料で実行できます。

>Azure Functions 従量課金プランでは、1 秒あたりのリソースの使用量と実行回数に基づいて課金されます。従量課金プランの価格には、月間無料使用分として、月あたり 100 万回のリクエストと、400,000 GB 秒のリソース使用量が含まれています。標準の App Service プランの料金で、Functions を App Service プランの中で実行することもできます。

[帯域幅の料金詳細](https://azure.microsoft.com/ja-jp/pricing/details/bandwidth/)
[Azure Files の料金](https://azure.microsoft.com/ja-jp/pricing/details/bandwidth/)
[料金計算ツール](https://azure.microsoft.com/ja-jp/pricing/calculator/?service=functions)


# VSCode 関連

関数を作成する方法には、Azure ポータル画面、Visual Studio, Visual Studio Code 等がありますが、JavaScript を使う場合は、Visual Studio Code が便利です。

[Visual Studio Code を使用した初めての関数の作成](https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-create-first-function-vs-code)

# node.js 関連


[Node.js](https://nodejs.org/ja/)
[ドキュメント](https://nodejs.org/ja/docs/)

- [Node のバージョンとパッケージの管理](https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-reference-node#node-version-and-package-management)
>アクティブ LTS と_現在の_ Node.js のバージョン (8.11.1 と 10.6.0 を推奨)。

**ローカルPCも、バージョンを合わせて node.js 8.11.1 を使う。**

- npm : Node.jsのモジュールを管理するツール
[npm入門](https://qiita.com/maitake9116/items/7825d90c09f3e2f87dea)


- nodist: node.js のバージョン管理
最新の node.js を入れたのに、なぜか古いバージョンが使われてしまうときは、nodist を使うと便利です。
複数のバージョンがインストールされていても、nodist で切り替えられます。

[nodistでNode.jsをバージョン管理](https://qiita.com/satoyan419/items/56e0b5f35912b9374305)

# kintone 関連


[kintone REST APIの共通仕様](https://developer.cybozu.io/hc/ja/articles/201941754)


[kintoneとMicrosoft Azureを連携してみよう （Azure Functionsその1）](https://developer.cybozu.io/hc/ja/articles/214690663)
[kintoneとMicrosoft Azureを連携してみよう （Azure Functionsその2）](https://developer.cybozu.io/hc/ja/articles/217937783)
[kintoneとMicrosoft Azureを連携してみよう （Azure Functionsその3）](https://developer.cybozu.io/hc/ja/articles/217379406)


[kintone ヘルプ Webhookの設定](https://jp.cybozu.help/k/ja/user/app_settings/set_webhook.html)




# npm で使用するライブラリのインストール

VSCode のターミナルで、コマンドを実行します。

- [request-promise](https://github.com/request/request-promise)：API Request で使用


```.log
> npm install request --save-dev
> npm install request-promise --save-dev

```

# ローカルPC での開発・デバッグ

VSCode でデバッグ実行すると、ローカルPC上で HTTP が立ち上がり、API を呼び出せます。
VSCode のソースにブレークポイントを設定して、ステップ実行して変数の内容も確認できます。

例：
Http Functions:
        HttpTriggerJS1: http://localhost:7071/api/HttpTriggerJS1


<img width="1128" alt="2018-10-01_12h13_00.png" src="https://qiita-image-store.s3.amazonaws.com/0/100572/3f66e6f5-7d9a-a765-da58-9e0ba947abb5.png">


# Azure Functions 環境への反映

VSCode で deploy to Function App... を使うと、Azure Functions 環境へ反映してくれます。
npm でインストールしたライブラリも丸ごと反映してくれますので、便利です。

<img width="495" alt="2018-10-01_12h07_19.png" src="https://qiita-image-store.s3.amazonaws.com/0/100572/427657d1-fff8-aa7f-8d37-eff1f0191234.png">

# 起動時間

コンテナ起動は、やはりある程度時間がかかります。
今回は、webhook を使って、kintone からファイルをダウンロード後に微信にアップロードする処理で、kintone レコード更新から微信にファイルが表示されるまで約３０秒かかりました。
その後に同じファイルを処理すると、約３秒程度で表示されますので、起動時は３０秒近くかかっています。

即時対応が必要な場合は、プランを従量課金ではなく App Serviceプランにするとよさそうです。
あとは、予算次第ですね。

# Azure Functions のログ

context.log で、ログ出力できます。用途によって、info, warm, error 等を指定できます。
ただし、ログが格納される領域がそれほど大きくないようなので、必要最小限のログにしておいた方がよさそうです。

[context オブジェクト](https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-reference-node#context-object)
[トレース出力をコンソールに書き込む](https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-reference-node#writing-trace-output-to-the-console)

[Azure Functions のログを見てみる](https://blog.okazuki.jp/entry/2017/08/20/180152)

>```.js
module.exports = function (context, req) {
    context.log.verbose(context.invocationId, ': verbose');
    context.log.info(context.invocationId, ': info');
    context.log.warn(context.invocationId, ': warn');
    context.log.error(context.invocationId, ': error');
    context.res = {
        status: 200,
        body: "invoked"
    };
    context.done();
};
```

# いろいろと、はまったこと

node.js 関連、Azure Functions 関連ではまったことを上げておきます。

## request による kintone ファイルダウンロード

画像等のファイルを kintone REST API でダウンロードする際に、バイナリーデータとして扱う方法がわからず、ファイル書き込み等で試行錯誤した。
結局は、 request の encoding に null を指定すると、バイナリーデータとして扱えると判明。
当初は、画像データとして一時ファイルに書き込んでいた処理が不要になりました。

※[[Node]画像をバイナリデータで取得する時にはまった](https://qiita.com/tk1024/items/0590564fa7c299f3d2b7)

```.js
"encoding": null,
```

## ファイルダウンロードのファイル書き込み

今回は、不要になったファイル書き込み処理ですが、今後のためコードを残しておきます。
一時ファイルは、Azure functions で使える一時ディレクトリ TMP を使用しました。
一時ファイル使用後に、削除しました。削除エラー時は、ログだけ出力してとりあえずほっとく。

process.env は、環境変数。Azure Functions 実行時の環境変数を取得出来ます。
※[Azure Functionsでtemp fileを使う](https://qiita.com/tkykm/items/2435aeea56fccf605156)

ファイルダウンロードのファイル書き込みコード抜粋

```.js
const uuid = require('uuid-v4')
const rp = require('request-promise')
const fs = require('fs')
...
            // request ファイルダウンロード
            return rp(getFile(fileKey));
        }).then(function(resp) {
            // write tmp file
            var image_id = uuid();
            ctl.tmp_name = process.env.TMP + '\\T' + image_id + '_.tmp';
            return new Promise(function(resolve, reject) {
                let writeStream = fs.createWriteStream(ctl.tmp_name);
                writeStream.write(resp, 'binary');
                writeStream.on('finish', function() {
                    resolve(ctl.tmp_name);
                });
                writeStream.end();
            });
        }).then(function(resp) {
...

        }).then(function(resp) {
            // delete tmp file
            fs.unlink(ctl.tmp_name, function (err) {
                if (err) {
                    context.log('delete tmp file error: ', ctl.tmp_name, err);
                }
            });
```



# あとがき

Auzre Functions は、思ったより簡単に API が作成できます。
ローカルPCで開発・デバッグが簡単にできると作業効率がいいですね。
今回は、webhook での利用でしたが、タイマー処理を使うとスケジュール実行が可能です。
kintone で定期的にバッチ処理を起動する場合に、便利そうです。