適当おじさんの適当ブログ

技術のことやゲーム開発のことやゲームのことなど自由に雑多に書き連ねます

kintone プラグイン開発について調べたメモ

はじめてkintoneのプラグイン開発をすることになって色々調べたときのメモです。 そもそもプラグインってなんやねんというところから、開発を便利にするためのツールと各種APIについて調べました。

参考(公式ドキュメント)

プラグインとカスタマイズの違い

kintoneに機能を追加する方法は「プラグイン」と「カスタマイズ」の2種類があるのですが、これらをどう使い分ければ良いのかわかりませんでした。ただ、調べれば調べるほど 「微修正の場合のみカスタマイズで、それ以外はすべてプラグイン」 で良いと感じました。

プラグインとして開発するメリットは以下のような感じだと思います。

相互関係しているファイルがわかりやすい

設定画面用のHTML/JavaScript/CSSとカスタマイズ用のJavaScript/CSSを1つのzipファイルにまとめたものがプラグインです。カスタマイズの場合、JavaScript, CSSが独立しているため、互いに依存しているのかどうか一見判別できません。

更新の手間が少し省ける

プラグインは、「kintoneにインストールし、インストールされているプラグインを各アプリで有効化する」という構造になっています。そのため、プラグインを一回更新すれば有効化されているすべてのアプリのプラグインも最新になります。カスタマイズの場合は、アプリごとに細かく有効化/無効化を制御したりできません。また、カスタマイズ用のファイルが複数ある場合、その分アップロードが必要になってしまいます。

プラグイン専用の設定画面を作成できる

kintoneの管理画面にプラグインの設定画面を生やすことができます。設定画面では任意の値を設定・保存でき、他の箇所でそれらの値を使うことができます。設定値を使えば、アプリ/開発環境ごとにコードを変更せずに挙動を変えられるので、そういった振る舞いが要求される場合もプラグインを使うのが良さそうです。

外部APIに安全にリクエストを送信できる

プラグインは専用の関数を使うことで、安全に外部APIにリクエスト送信できます。JavaScriptカスタマイズの場合は、APIリクエストに必要なクレデンシャルをコードに埋め込む必要があり、ソースコードやネットワークからクレデンシャルを参照できる状態になってしまいます。外部APIへのリクエスト送信が必要な場合は、選択の余地はなくプラグイン一択だと思います。

コマンドラインツール

kintoneはプラグイン・カスタマイズを開発しやくするための便利なツールを提供してくれています。プラグイン開発に役立つツールは以下の3つのようです。

詳細な使い方は各ツールのREADME.mdに書かれているので割愛しますが、create-pluginでプラグインのひな型の作成、plugin-packerでパッケージング、plugin-uploaderでkintoneへのアップロード、といったように開発の最初から最後まで役に立ちます。これらを使わなくても開発できますが、特に理由がなければ使ったほうが幸せになれると思います。コマンド操作できるようになるのでCI/CDする際にも便利です。

上記3つのツールを含むコマンドラインツールは js-sdk という1つのリポジトリにまとめられています。webpackやTypeScriptのツールも提供されており、これらもとても簡単に使うことができました。これらについてはあまり深くさわれていないので、この記事では触れていません。

コマンドを実行してみる

@kintone/create-plugin コマンドを実行すると、対話形式でプラグインに必要なディレクトリ・ファイルを作成できます。

#プラグインのインストール
> npm install -g @kintone/create-plugin
# test という名前のプラグイン作成
> create-plugin test
<省略>
? @kintone/plugin-uploaderを使いますか? (Y/n) Y

途中の質問で Y を入れれば、plugin-packer plugin-uploader を含むディレクトリが作成されます。create-plugin を使わずにプラグインの構造を手動作成しても良いですが、コマンド実行すればサンプルのJavaScript, CSS, html が作成されるので初めてプラグインを開発する人は使っておくのが無難そうです。

plugin-packer plugin-uploader の2つはいずれもプラグイン開発する上でマストだと思います。

JavaScript API の詳細

kintone JavaScript API を使うことで、kintoneの振る舞いを変更したり特定のイベントをトリガーにした処理を実現できます。すべてのAPIは、kintone JavaScript API(イベント)一覧 に記載されていますので全貌&詳細はそちらを参照してください。この記事では、初見で混乱した「リクエストの送信」にポイントを絞って記載しています。

kintone REST APIの実行

プラグインから kintone REST API にリクエストを送信したい場合に kintone.api を使うようです。ドキュメントでは、kintone REST APIでない他サービスのAPIを「外部API」と区別しており、外部APIの実行には別の関数を用います。

kintone.api はユーザーの認証情報をそのまま利用しているため、実行可能な権限もユーザーに準拠します。つまり、ユーザーが閲覧権限しか持たない場合はレコードを作成するAPIを呼び出すことはできません。以下のような「レコードの一覧表示時に、新たにレコードを追加するスクリプト」を作成すれば簡単にこの仕様を確認できます。

// レコード一覧表示時に
kintone.events.on('app.record.index.show', function(event) {
  var body = {
    'app': event.appId,
    'record': { '文字列__1行': { 'value': 'ABC' }}
  };
  // 適当なレコードを1つ追加
  kintone.api(kintone.api.url('/k/v1/record', true), 'POST', body, function(resp) {
    console.log(resp);
  }, function(resp) {
    console.log(resp);
  });
})

閲覧権限しか持たないユーザーでこの処理を実行すると、権限不足でエラーになります。開発者ツールなどで console.log の結果を見てみると、「権限がありません」エラーが出ていることが確認できます。

{code: 'xx_xxxx', id: 'xxxxxxxx', message: '権限がありません。'}
    code: "xx_xxxx"
    id: "xxxxxxxx"
    message: "権限がありません。"
    [[Prototype]]: Object

ユーザーの権限を越えた処理をさせたい場合は、「十分な権限を持つAPIトークンを発行して、外部APIとしてkintone APIにリクエストを送信する」必要がありそうです。これは後述する「外部APIの実行」と「設定値の保存」を組み合わせて実現できますが、割とレアケースな気もします。

外部APIの実行

kintoneプラグインから外部APIを実行する場合、 kintone.proxykintone.plugin.app.proxy のいずれかを使います。APIリクエスト送信時に認証情報等も一緒に送りたい場合は、kintone.plugin.app.proxy を使うことが推奨されています。その理由は、後述する「設定値の保存」の仕様が関係しています。

すごくざっくりまとめると、完全にオープンなAPIを叩くときは kintone.proxy、それ以外は kintone.plugin.app.proxy を使うのが良さそうです。多くの場合は認証情報を必要とすると思うので、後者を使うことになります。

設定値の保存

プラグイン用の設定値の保存には、setConfigsetProxyConfig のいずれかを使います。機密情報を含む外部APIのリクエスト送信に必要な値は setProxyConfig で保存し、機密情報を含まない値を保存する場合に setConfig を利用すると解釈しました。

保存した値はそれぞれ getConfiggetProxyConfig で値の取り出しが可能です。ただし、getProxyConfig はプラグイン設定画面の表示でのみ利用可能で、プラグインの設定値を表示するためだけに利用します。厳密には利用しても null が返ってくるだけです。getConfig のようにアプリ内で設定値を取得するような使い方はできません。

setConfig の値はscriptsに埋め込まれる

setConfig で保存したデータは、HTMLのscriptタグ内に cybozu.data.page['APP_PLUGIN_CONFIGS'] という変数で埋め込まれます。開発者ツール等で見れば存在が確認できると思います。当然コンソールでのアクセスも可能です。

<script>
...省略...
  cybouz.data.page['APP_PLUGIN_CONFIGS'] = {"xxxx":"xxxxxxxxx"}
...省略...
</script>

したがって、APIリクエストで使うかどうかに関わらず機密性の高い情報を保存する際は注意が必要です。先述した通り、プラグイン設定画面以外の画面で getProxyConfig は機能しないので、なんらかの措置を取るか・リスクを踏まえて setConfig を使う必要がありそうです。

setProxyConfig はオブジェクトのみ保存可能

いわゆるキーバリュー方式のデータしか保存できず、文字列を保存できません。 例えばXML形式の文字列を保存しようとすると、以下のエラーが発生してしまいます。

Uncaught Error: Usage: kintone.plugin.app.setProxyConfig(url, method, headers, data, opt_callback)

もちろんXML文字列を parseFromString などでパースしても保存できません。

kintone.plugin.app.proxy() のデータ部分にXML文字列を指定してリクエストを送信することもできますが、この場合 getConfig 経由で設定を取得することになるため機密情報を隠匿できません。つまり、JSON以外の形式でリクエストボディに機密情報を含める必要がある場合、その機密情報をセキュアに扱うことはできません。レアケースですが、古いシステムなどと連携したい場合にこのケースに該当してしまうかもしれません。

まとめ

雑多にだらだらと書き連ねましたが、プラグイン開発する際は以下のポイントに気を付けようと思いました。

  • 微修正でない限り基本的にプラグインとして開発する
  • plugin-packer plugin-uploader などの便利なCLIを利用する
  • kintone.api は、ユーザーの認証情報・権限で実行される
  • 外部APIを実行するときは setProxyConfigkintone.plugin.app.proxy のペアを使う
  • setProxyConfig はXML形式に対応していない

今回の記事では全く触れなかった Webpack や TypeScript 関連のツールを使うことがあれば、また記事にしていこうと思います。