kintone と EDITROOM の機能連携の実例解説： 自作プラグインを利用した連携２―設定画面 

# 目次

- [目次](#目次)
- [はじめに](#はじめに)
- [作業](#作業)
  - [（再掲）全体のユースケース](#再掲全体のユースケース)
    - [本記事中の作業範囲](#本記事中の作業範囲)
  - [開発](#開発)
    - [1. (opt.)ちょっとした手間の短縮](#1-optちょっとした手間の短縮)
    - [2. 設定値を再定義](#2-設定値を再定義)
    - [3. アップロード・動作確認](#3-アップロード動作確認)
      - [(opt.) 手動でアップロードする方法](#opt-手動でアップロードする方法)
    - [4. REST API発火のための準備](#4-rest-api発火のための準備)
    - [5. 動作確認](#5-動作確認)
- [所感](#所感)
- [おわりに](#おわりに)

# はじめに

アップデイティットの毛利です。

前回に引き続き、弊社の「[EDITROOM](https://editroom.updateit.co.jp/)」と kintone の自作プラグインを繋いで動作させるために作業を進めていきます。

前回はこちら：   

https://qiita.com/update_it_inc/items/febc9e7d4bbadfb8ed32

# 作業

## （再掲）全体のユースケース

次の一連の動作を動かせるよう進めています。

1.  該当アプリから、レコードを指定して文書生成を発火する
2.  自作プラグイン上で、EDITROOM API の発火に必要なパラメータを取り出す。
3.  自作プラグイン上から、 EDITROOM に文書作成 API を発火する。

### 本記事中の作業範囲

-   設定値・設定画面の修正。
-   desktop.js の修正と API 発火処理.

## 開発

### 1. (opt.)ちょっとした手間の短縮

今回はデモ・サンプルで、特に public 公開する予定もないため、コマンド上にユーザー／パスワードを直接置いてしまいましょう。

```JSON
{
  ...(中略)
  "scripts": {
    ...(中略)
    "upload": "kintone-plugin-uploader --base-url https://[SUBDOMAIN].cybozu.com --username [USER_NAME] --password [PASSWORD] dist/plugin.zip --watch --waiting-dialog-ms 3000"
  },
  ...(中略)
}
```

このように、コマンドライン引数として指定することで、コマンドを叩くたびに発生していた対話操作が不要となります。  
もちろん、ソースコード上に作業者の ID とパスワードが丸見えになるため、誤って git や svn などに投下すると終わります。慎重な対応が必要です。（本番開発環境では、ちゃんと環境変数ファイルを駆使するなりで切り分けるべきです。）

### 2. 設定値を再定義

前回、初期状態にあった全ての設定値を消し去りましたので、改めて定義しなおします。

今回使用する設定値は、最低限必要になる以下の２つを用意します。

-   GENERATE API URL (api_url)
-   API KEY (api_key)

API 発火のために必要なパラメータは他にもあるのですが、決め打ちにするため割愛します。

まずは、 manifest.json の修正をします。  
requrired_params に今回追加する２つの定義を書き加えるついでに、jQuery への依存を辞めてしまいましょう。

```JSON:manifest.json
{
  ...(中略)
  "desktop": {
    "js": [
      "js/desktop.js"
    ],
    ...(中略)
  },
  ...(中略)
  "config": {
    ...(中略)
    "js": [
      "js/config.js"
    ],
    ...(中略)
    "required_params": [
      "api_url",
      "api_key"
    ]
  },
  ...(中略)
}
```

次に、html を修正します。  
うすうす気づいていると思いますが、この html ファイル、一般的な html にある HEAD タグと BODY タグがないです。  

これは、kintone 側で制御するために最低限必要な記述しか要求されていないためで、不用意に足さないようにしてください。  
もし、外部のリソースを呼び出したい場合などは、manifest.json の中に利用したい js と css を記述する必要があります。(今回は、特にいじりません。)

```html:config.html
<section class="settings">
  <h2 class="settings-heading">Settings</h2>
  <form class="js-submit-settings">
    <p class="kintoneplugin-row">
      <label for="message">
        REST API URL:
        <input id="api_url" type="text" class="js-text-message kintoneplugin-input-text">
      </label>
    </p>
    <p class="kintoneplugin-row">
      <label for="message">
        REST API KEY:
        <input id="api_key" type="text" class="js-text-message kintoneplugin-input-text">
      </label>
    </p>
    <p class="kintoneplugin-row">
      <button class="kintoneplugin-button-dialog-ok">Save</button>
    </p>
  </form>
</section>
```

元々あった記述をベースに、このように記述しました。id で Element を取れるようにしてあります。

最後に、js を修正します。  
さきほど、jQuery を辞めてしまったので、jQuery 関数は一切使えません。
――が、昨今のブラウザ事情は大きく進化しているので、よほど過去のブラウザで動かしていない限りは、なんとかなります。

```JavaScript:config.js
(function (PLUGIN_ID) {
  'use strict';
  var elem_submit_list = document.getElementsByClassName('js-submit-settings');
  var elem_submit = elem_submit_list.item(0);
  var elem_api_url = document.getElementById('api_url');
  var elem_api_key = document.getElementById('api_key');
  if (!elem_submit || !elem_api_url || !elem_api_key) {
    throw new Error('Required elements do not exist.');
  }
  //
  var config = kintone.plugin.app.getConfig(PLUGIN_ID);
  if (config.api_url) elem_api_url.value = config.api_url;
  if (config.api_key) elem_api_key.value = config.api_key;
  //
  elem_submit.addEventListener('submit', function (e) {
    e.preventDefault();
    //
    kintone.plugin.app.setConfig(
      {
        api_url: elem_api_url.value,
        api_key: elem_api_key.value,
      },
      function () {
        alert('The plug-in settings have been saved. Please update the app!');
        window.location.href = '../../flow?app=' + kintone.app.getId();
      }
    );
  });
})(kintone.$PLUGIN_ID);
```

先の通り、利用ユーザーが最新のブラウザ事情であれば、上記のような非 jQuery なコードでも問題なく動きます。  
(古い FireFox から派生している Pale Moon でさえも正しく動作したので、まず問題ないでしょう。)

### 3. アップロード・動作確認

先ほどの修正を含んだ状態でプラグインをアップロードします。  
まだ、修正があるので前回の方法で行います。

> npm run start

アップロードが完了したら、前回と同様にアプリからプラグイン一覧を見てみましょう。  
おそらく、以下の画像のようにエラー文が表示されるようになっているはずです。

![pic_00.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953427/79652fb0-2133-be34-e376-6c91a10bf868.jpeg)

設定画面を開くとさきほど修正した画面が表示されるようになりました！

![pic_01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953427/e74b75ab-ce68-bc04-c8f2-ef95c0e81de2.jpeg)

さっそく、値を入力して Save ボタンを押します。  
以下の状態になれば成功です。（確認のため、この後、再度この画面に戻ったところ、値が復元されていました。）

![pic_02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953427/ca069ce2-4a65-e52a-5dee-6dd34684d864.jpeg)

#### (opt.) 手動でアップロードする方法

もし、サーバー障害などで CLI 上からのログインがうまく動かない場合、次の方法でアップロードできます。  

1.  ブラウザで普通にログインする。
2.  「kintone システム管理」⇒「その他」⇒「プラグイン」を開く
3.  「読み込む」を押下し、dist/plugin.zip を選択してアップロードする。

![pic_03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953427/abf60604-2440-4e25-b380-54aa8cf136e4.jpeg)

![pic_04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953427/84bf5b4d-6ca8-0107-a347-8ca682d02d8d.jpeg)

### 4. REST API発火のための準備

設定画面で、投げ先の URL を設定できるようになりましたので、次は実際に API を発火する側の修正を進めます。  
前回「Hello kintone plugin！」の 1 行だけ表示されたところを確認した箇所です。

まず、 desktop.js を開きます。  
ここで重要な要素として次のコードが登場します。

```JavaScript:desktop.js
kintone.events.on('app.record.index.show', function () {...}
```

これは、kintone 側で発生するイベントに自作の関数を紐づける時に使用する kintone 専用の関数です。  
イベント名は、開く画面に応じて異なるため、呼び出したい箇所を正確に記述する必要があります。

そして、次の行にある以下の行。

```JavaScript:desktop.js
kintone.app.getHeaderSpaceElement()
```

これは、前回「Hello kintone plugin！」が表示された領域に相当する Element を取り出す関数です。  
画面中のどこに自作コンポーネントを埋め込むかの基準になります。

それ以降は、 DOM 操作の一般的な関数が並んでいます。

今回は、API 発火までをゴールとしていますので、一番シンプルなボタンと発火時の関数のみを記述します。  
また、API 発火に必要なパラメータは、今回決め打ちにしています。

```JavaScript:desktop.js
(function (PLUGIN_ID) {
  'use strict';

  kintone.events.on('app.record.index.show', function () {
    var spaceElement = kintone.app.getHeaderSpaceElement();
    if (spaceElement === null) {
      throw new Error('The header element is unavailable on this page');
    }
    //
    var fragment = document.createDocumentFragment();
    var elem_p = document.createElement('p');
    elem_p.classList.add('kintoneplugin-row');
    var elem_button = document.createElement('button');
    elem_button.innerHTML = 'REST API FIRE';
    elem_button.classList.add('kintoneplugin-button-dialog-ok');
    elem_p.appendChild(elem_button);
    fragment.appendChild(elem_p);
    spaceElement.appendChild(fragment);
    //
    var config = kintone.plugin.app.getConfig(PLUGIN_ID);
    if (!config.api_url || !config.api_key) {
      elem_button.innerHTML = 'REST API FIRE(DISABLED)';
      elem_button.disabled = true;
      return;
    }
    elem_button.addEventListener('click', function (e) {
      e.preventDefault();
      elem_button.disabled = true;

      kintone.proxy(
        config.api_url,
        'POST',
        {
          Accept: 'application/json',
          'X-EDITROOM-API-KEY': config.api_key,
          'Content-Type': 'application/json; charset=UTF-8',
        },
        JSON.stringify(
          { docflow_name: 'docflow0001', export_type: 'html' },
          null,
          0
        ),
        function (body, status, headers) {
          // success
          console.log(status, JSON.parse(body), headers);
          alert('sent.');
          elem_button.disabled = false;
        },
        function (error) {
          // error
          console.log(error); // proxy APIのレスポンスボディ(文字列)を表示
          alert('send error.');
        }
      );
    });
  });
})(kintone.$PLUGIN_ID);
```

基本的な処理としては、一般的な実装と変わりないですが、fetch 関数等々の代わりに kintone.proxy() を使用するなど、通常のコードとはやや異なる点はあります。  
kintone 独自関数については、リファレンスが存在するのでそちらを参照してください。

ちなみに、ユーザー通知のためにあえて alert() を使用していますが、テキスト Element を追加挿入するだけでも問題ないです。  
ちなみに、ちなみに、 kintone.proxy() と kintone.plugin.app.proxy() が存在します。引数の数が違うので注意してください。

### 5. 動作確認

![pic_05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953427/f7e7dbe1-4f73-86ae-071e-c2be924463d3.jpeg)

実際に送ることができました。  
確認用として、コンソールにもレスポンスを吐き出すにしていますので、そちらでも status = 200 を確認できました。

お疲れ様でした。

# 所感

ここまで作業を進められれば、kintone プラグイン開発の基本的な部分を把握できるようになっているでしょう。  

レコードの値など kintone 側で管理している値は全て kintone 独自関数を経由して取得できます。（逆に言えば、API にない値は基本的には取れない。）
例えば、API のパラメータに与えるレコード名を設定値で決めて、自作関数の中で POST する JSON を組み立てることもできますし、  
逆に、最短で動作させるために、特定のアプリ専用として全ての値をガチガチにハードコーディングすることもやろうと思えばできます。

どういった方針が望ましいかは時と場合によりますが、長期的に見た場合はメンテナンス性を高く保てるほうが望ましいでしょう。  
次回は、「メンテナンス性を上げる」点において、素の JavaScript をなるべく触らない方法を模索したいと思います。

# おわりに

再度の紹介となりますが、先日、弊社から「[EDITROOM](https://editroom.updateit.co.jp/)」という BtoB 向けの文書作成クラウドサービスをリリースしました。  
特に、定期的に文書を作る人的コストや作業にかかる拘束時間の長さを改善するのに、このサービスが大きな一助になると自負しております。

もし、ご興味がありましたら、トライアルをご利用いただければ幸いです。

# 次回

https://qiita.com/update_it_inc/items/11fd9996973266629701