kintone APIを使ってスペースを有効活用する（サーバーレスで）

[kintone Advent Calendar 2015](http://qiita.com/advent-calendar/2015/kintone)の2日目です。
今回はkintoneのスペースを活用するためのプログラムを作ってみたいと思います。

$\color{red}{\rm ※訂正}$
　「Process exited before completing request」エラーが出力される件で、出力されないようにソースを修正しました。
　[yamaryu0508](http://qiita.com/yamaryu0508)さん、ご指摘ありがとうございましたm(_ _)m

# kintoneのスペースを有効活用しよう
kintoneにはデータを蓄積する**アプリ**だけで無く、ダッシュボードや掲示板として使える**スペース**が存在します。
スペースにはアプリやグラフを貼り付ける事ができ、アプリに蓄積されたデータを見える化するためにとても便利に使えます。

[スペース](https://kintone.cybozu.com/jp/feature/space.html)
![acba049f-3fca-3350-8293-80e1b7d40d18.png](https://qiita-image-store.s3.amazonaws.com/0/30557/74ba8310-5bfd-a3e9-dbde-83a345324582.png)

上のキャプチャにもあるように「今季売上◯◯円突破！」とか「今季実績◯◯円！」のように、アプリに蓄積した数値データを集計して表示したいケースがありますが、手作業でメンテナンスしている人も多いのではないでしょうか。
今回はkintone APIを使って、自動化してみたいと思います。

# 想定するアプリ
販売実績が蓄積された簡単なアプリを想定しています。
販売実績アプリに蓄積された実績金額を集計し、「今季の実績は◯◯円です！」とスペースに表示します。

販売実績アプリのイメージです。
![FireShot Screen Capture #096 - '販売実績 - サイボウズ株式会社 - レコードの詳細' - cy-asaga_cybozu_com_k_505_show#record=1&l_view=5264865&l_q&l_sort_0=f5264846&l_order_0=DESC.png](https://qiita-image-store.s3.amazonaws.com/0/30557/f361744e-a5f2-c41d-37e3-1cf48f156b16.png)

会社名や型番など入力項目がいくつもありますが、これから作るプログラムに関係がある点は以下の通りです。

- 「販売金額」のフィールドに集計したい金額が保存されている
- 「販売金額」のフィールドコードはsalesamountとする
- 「販売年月」のフィールドコードはsaledateとする

後でアプリIDが必要になるので控えておいてください。
アプリIDはkintoneアプリをブラウザで閲覧した際のURLで確認でき、以下の例だとアプリIDは123です。
http://_SUBDOMAIN_.cybozu.com/k/**123**/

実績金額を表示するためのスペースも用意しておきます。今回はシングルスレッドのスペースを使う想定です。
![FireShot Screen Capture #098 - '営業部スペース' - cy-asaga_cybozu_com_k_#_space_4_thread_5.png](https://qiita-image-store.s3.amazonaws.com/0/30557/285e402e-e8d7-b691-90da-7c3f035d4063.png)

後ほどスレッドIDが必要になるので控えておいてください。
スレッドIDはkintoneスペースをブラウザで閲覧した際のURLで確認でき、以下の例だとスペースIDは5です。
https://_SUBDOMAIN_.cybozu.com/k/#/space/4/thread/**5**

# プログラムの流れ
プログラムの流れは以下のようになります。
1. [レコードの取得API](https://cybozudev.zendesk.com/hc/ja/articles/202331474)を使い、販売実績アプリから実績金額を取得します。
2. 取得した実績金額を合算し、合計金額を計算します。
3. [スペースの本文の更新API](https://cybozudev.zendesk.com/hc/ja/articles/201941884)を使い、スペースの本文に合計金額を記載します。

上記の処理を実現するプログラムを作り、タスクスケジューラやcronで定期的に実行する事でスペースの更新を自動化する事ができます。
通常はプログラムを実行するためにサーバーを用意する必要がありますが、今回はサーバーレスを謳ってみたのでAWS Lambdaを使って実現したいと思います。
AWS Lambdaについての詳細は[公式サイト](https://aws.amazon.com/jp/lambda/)をご覧下さい。

# Lets's Coding!
今回の処理をLambdaで実現するには、JavaScript（Node.js）でコーディングしたプログラム一式をZip化してLambdaにアップロードする必要があります。
サーバーレスを謳ってみた（しつこい）ので、開発もクラウド環境を使ってみます。
今回は無料プランがある[Koding](https://koding.com/)を使ってみました。（[Cloud9](https://c9.io/)でも同じ事が実現できます）
![FireShot Screen Capture #093 - 'Koding I Say goodbye to your localhost and code in the cloud_' - koding_com.png](https://qiita-image-store.s3.amazonaws.com/0/30557/258235a1-39c8-95f3-8288-862dfcf93d3a.png)

KodingはGithub、Google、Facebook、Twitterのアカウントでサインアップできるので手軽に使えます。
![FireShot Screen Capture #092 - 'Koding I Say goodbye to your localhost and code in the cloud_' - koding_com_Login.png](https://qiita-image-store.s3.amazonaws.com/0/30557/36f1ed28-ba48-194e-a0af-749e54704d79.png)

ログインすると自分用のVM環境が割り当てられます。
![ef0ca93c-77ea-5a3f-b5c3-32177a2fbeef.png](https://qiita-image-store.s3.amazonaws.com/0/30557/1ac39920-8931-7317-830b-9664a5bf5d31.png)

以下のコードをindex.jsとして保存します。

```lang:index.js
var aws = require('aws-sdk');
var request = require('request');

/* kintone用のパラメータ（変更が必要） */
var DOMAIN = 'subdomain.cybozu.com'; //kintone環境のドメイン
var APP_ID = 1;   //実績アプリのアプリID
var SPACE_ID = 1;   //スペースのID
var LOGIN_NAME = 'Administrator';
var PASSWORD = 'secret';

/* 変更は不要 */
var BASE_URL = "https://" + DOMAIN + '/k/v1/';
var AUTH_VALUE = new Buffer(LOGIN_NAME + ':' + PASSWORD).toString('base64');
var headers = {'X-Cybozu-Authorization': AUTH_VALUE};

exports.handler = function(event, context) {

    //--------------------------------------------------
    //販売実績アプリから実績金額を取得
    //--------------------------------------------------
    //販売実績アプリからレコードを取得する際のリクエストパラメーター
    var body_getsalesamount = {
        app: APP_ID,
        query: 'saledate=THIS_MONTH()',
        fields: "salesamount"
    }
    //販売実績アプリからレコードを取得する際のヘッダー
    var options_getsalesamount = {
        url: BASE_URL + 'records.json',
        method: 'GET',
        headers: headers,
        'Content-Type': 'application/json',
        json: body_getsalesamount
    }
    //レコードを取得
    request(options_getsalesamount, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            //取得した結果から販売実績を取得し、レコードの数だけ合算する
            var records = body.records;
            if(records.length === 0) return;
            var total = 0;
            for(var i=0;i<records.length;i++){
                var salesamount = parseInt(records[i].salesamount.value);
                total = total + salesamount;
            }
            console.log(total);
            
            //--------------------------------------------------
            //スペース本文を更新
            //--------------------------------------------------
            //スペース本文を更新する際のリクエストパラメーター
            body_updatespace = {
                id: SPACE_ID,
                body : '<font size="6">今期実績：<font color="red">￥' + total + '</font></font>'
            }
            //スペース本文を更新する際のヘッダー
            options_updatespace = {
                url: BASE_URL + 'space/thread.json',
                method: 'PUT',
                headers: headers,
                'Content-Type': 'application/json',
                json: body_updatespace
            }
            //スペース本文を更新
            request(options_updatespace, function(error, response, body){
                if (!error && response.statusCode == 200) {
                    console.log(body);
                    context.succeed(body);
                } else {
                    console.log('error: '+ response.statusCode);
                    context.fail('error: '+ response.statusCode);
                }
            });
        }
    })

};
```

以下の点は環境に合わせて適宜変更して下さい。

| 変数名 | 設定する値 |
|:-----------|:------------|
|DOMAIN|kintone環境のドメイン|
|APP_ID|販売実績アプリのアプリID|
|SPACE_ID|実績金額を表示するスペースのスペースID|
|LOGIN_NAME|APIを実行するkintoneユーザーのログイン名|
|PASSWORD|APIを実行するkintoneユーザーのパスワード|

Node.jsのrequestモジュールを使うため、モジュールのインストールを行います。
Koding上でTerminalを開き、以下のコマンドを実行すれば完了です。
```npm install request```

LambdaにインポートするZip形式にするため、Terminalで以下のコマンドを実行します。
※コマンドが完了した直後は、space.zipと/home/_Username_/space.zipの2つのファイルがディレクトリツリー上に表示されていますが、ブラウザをリロードすればspace.zipだけが表示されるようになります。
```zip -r space.zip index.js node_modules/```

space.zipをダウンロードするには、ファイルをWebディレクトリに移動してから以下のURLにアクセスします。
http://_Username_.koding.io/space.zip

# Lambdaの設定
AWSのコンソールにログインし、「Lambda > Functions」の「Create a Lambda function」をクリックします。
![8958978a-deb6-c244-c0c6-52c51c7c197c.png](https://qiita-image-store.s3.amazonaws.com/0/30557/37e161a0-1cc6-fadc-f99f-af6863ccf9a8.png)

**Step 1: Select blueprint**
「Skip」を選択します。

**Step 2: Configure function**

+ Configure function
    + NameとDescriptionは適当に入力、Runtimeは「Node.js」を選択します。
+ Lambda function code
    + Code entry typeは「Upload a .ZIP file」を選択し、KondingからダウンロードしたZipファイルを指定します。
+ Lambda function handler and role
    + Handlerはデフォルト（index.handler）のままで
    + Roleは「Basic execution role」を選択し、IAM ロールを新規に作成するか、既に作成済みのロールを選択します

Zipの読み込みが完了したらコンソール上でテストを実行してみます。
![b42ab465-b21c-8230-8905-a9ee59e9418a.png](https://qiita-image-store.s3.amazonaws.com/0/30557/b9ca9d1d-9c1b-3dd9-a74f-f880b24fbc2a.png)

~~「Process exited before completing request」と表示されていますが、今回の場合は問題ありません。~~

スペースを確認して、実績金額が表示されていたら成功です！
![FireShot Screen Capture #105 - '営業部スペース' - cy-asaga_cybozu_com_k_#_space_4_thread_5.png](https://qiita-image-store.s3.amazonaws.com/0/30557/24e2c818-788d-1d97-7846-8890f0f26e31.png)

最後にLambdaファンクションが定期的に実行されるように設定を行います。
「Event sources」タブを選択し、 「Add event source」をクリックします。
![da31ce0c-e444-e3ae-3510-86491b1a5cb5.png](https://qiita-image-store.s3.amazonaws.com/0/30557/c96d8bf6-00c0-556d-21a9-f2215c3937a1.png)

「Add event source」のポップアップが表示されたら以下のように設定します。

+ Event source type
    + Scehduled Eventを選択します。
+ Name
    + 任意の情報を入力します。
+ Description
    + 任意の情報を入力します。
+ Schedule expression
    + 定期実行したい時間間隔を選びます。

![FireShot Screen Capture #103 - 'AWS Lambda' - ap-northeast-1_console_aws_amazon_com_lambda_home_region=ap-northeast-1#_functions_effectiveuseofkintonespace_tab=eventSources.png](https://qiita-image-store.s3.amazonaws.com/0/30557/6eeedf78-203c-3a51-fd6f-089b35c9c750.png)

これでLambdaがkintoneのスペースを自動で更新してくれるようになります！

**注意事項**

- スペースが更新されない場合、Lambdaがタイムアウトしている可能性があります。（CloudWatchに「Task timed out after 3.00 seconds」と出力されます）その場合は、「Lambda > Functions」の「Configuration」タブを選択し、「Advanced settings」のTimeout値を延ばして下さい。
- 現状のスクリプトではレコードが100件を超えたレコードは取得されません。

# まとめ
今回はスペースを活用する例を取り上げてみました。
kintone APIを使うと他にもいろいろな事が実現できます。
APIの仕様は[cybozu.com developer network](https://cybozudev.zendesk.com/hc/ja)に情報をまとめていますので、興味を持って頂いた方は是非ご覧下さい！
1年間無料で利用できる開発者ライセンスも発行しております。