カスタマイズファイルの利用状況を管理する

著者名:花塚 愛子( サイボウズ株式会社 (External link)

目次

はじめに

みなさんは、kintoneにカスタマイズを加えていますか?
kintoneは、業務に合わせて柔軟にカスタマイズできます。
しかし、カスタマイズを行う際「どこにどんな変更を加えたか把握できなくなる」「引き継ぎがうまくできない」などの問題を生じることがあります。
これらの問題を防ぐためには、適切な管理が欠かせません。

そこで、この記事では、カスタマイズファイルの利用状況を管理する方法を紹介します。
後半では、ワイドコース限定のAPIを活用する方法についても解説します。

information

このTipsの後半では、ワイドコースでのみ利用できるAPIを使います。
ワイドコースとは (External link)

想定読者

  • kintoneシステム管理者
  • 情報システム部門

カスタマイズファイルの管理が必要な理由

kintoneは、JavaScriptやCSSファイルを設定画面からアップロードすることでカスタマイズが可能です。
簡単にアップロードできて、即座にカスタマイズが反映されるので忘れがちですが、このとき、担当者がアップロードファイルを適切に管理しないと、さまざまなリスクが想定されます。
まずは、カスタマイズファイルの管理が必要になる主な理由を説明します。

過去の状態の復元

何か問題が発生した場合など、過去のバージョンに切り戻したいといったことはよくあります。
kintoneには、バージョン管理機能はないため、自身で過去のカスタマイズファイルも保管しておくと安心です。
すぐに過去のファイルを参照できる状態にしておくと、スムーズな復元が可能になります。

再利用性の向上

他のアプリでも同じような処理を入れたいといったケースはよくあります。
そんなときに、管理がされていないと、無駄な工数がかかったり、ファイルの散在につながります。
カスタマイズファイルを適切に管理しておくことで、再利用性の向上が見込めます。

保守性の向上

ファイルをアップロードした時はカスタマイズの内容を理解していますが、期間があくと忘れがちです。
また、カスタマイズ担当者の入れ替えにより引き継ぎが発生する可能性もあります。
そんなとき、カスタマイズしたアプリがどこにあるか、どのように動作するかを明確にしておくことで、将来的なメンテナンスが容易になります。
概要や変更箇所のコメントを残すことで、カスタマイズの内容を誰もが理解しやすくする管理が重要です。

GitHubでカスタマイズファイルを管理する方法

GitHubは、ソフトウェア開発プロジェクトを管理するためのクラウドベースのプラットフォームで、主にソースコードのバージョン管理のために使用されます。
kintoneのカスタマイズファイルも、GitHubを利用して管理することは有効な管理方法の1つでしょう。

information

GitHubからkintoneに直接アップロードする機能はないため、GitHubでファイル管理をしつつ、kintoneに手動でファイルをアップロードする必要があります。

使い慣れている人にとっては、とても便利なGitHubですが、GitHubは主にソフトウェア開発者向けに設計されており、専門的な技術や操作が必要になります。
次のような理由から、導入のハードルが少し高い部分もあります。

  • バージョン管理の概念(ブランチ、コミット、マージなど)を理解する必要がある。
  • コマンドライン操作を覚える必要がある。

そこで、ここでは、kintoneでカスタマイズファイルを管理する方法を一例として紹介したいと思います。

kintoneでカスタマイズファイルを管理する方法

GitHubのような他のサービスを使わずに、kintoneでカスタマイズファイルを管理する方法を紹介します。
今回実現したいことは、次のとおりです。

  • kintoneに適用されているファイルをすべて一ヵ所のアプリで管理する。
  • どのアプリにどのようなカスタマイズが適用されているかわかる。
  • カスタマイズファイルの変更時に、プログラム変更箇所やコメントを残せることで、誰が見ても過去の履歴を追える。

アプリの構成

カスタマイズファイルを管理するために、以下のフィールドをもつアプリを作成します。

フィールド名 フィールドタイプ フィールドコード 備考
アプリID 文字列(1行) app_id
アプリ名 文字列(1行) app_name
カスタマイズの概要 文字列(複数行) summary
【デスクトップ】JSカスタマイズ履歴 テーブル desktop_table
日時 日時 desktop_datetime 【デスクトップ】JSカスタマイズ履歴テーブルに追加する
変更箇所/コメント 文字列(複数行) desktop_comment 【デスクトップ】JSカスタマイズ履歴テーブルに追加する
添付ファイル 添付ファイル desktop_file 【デスクトップ】JSカスタマイズ履歴テーブルに追加する
【モバイル】JSカスタマイズ履歴 テーブル mobile_table
日時 日時 mobile_datetime 【モバイル】JSカスタマイズ履歴テーブルに追加する
変更箇所/コメント 文字列(複数行) mobile_comment 【モバイル】JSカスタマイズ履歴テーブルに追加する
添付ファイル 添付ファイル mobile_file 【モバイル】JSカスタマイズ履歴テーブルに追加する

運用イメージ

アプリの運用イメージとしては、次のような流れです。

  1. カスタマイズを適用するアプリが生じたら、レコードを登録する。
  2. アプリの情報をフィールドに登録する。
    • アプリID
    • アプリ名
    • カスタマイズの概要
  3. カスタマイズファイルをテーブルに登録し、過去のファイルの履歴も残す。
    • 変更日時
    • 任意のコメント
    • カスタマイズファイル

上記のイメージでアプリを運用すると、次のようにカスタマイズが適用されたアプリの一覧をレコード一覧画面で確認できます。

そして、レコード詳細画面をみると、アプリのカスタマイズに関する情報を確認できます。

このように、カスタマイズの概要やコメント履歴を残しておくと、状態の復元、再利用性、保守性の向上につながります。

JavaScript / CSSカスタマイズ設定画面では、現在適用されている最新のカスタマイズファイルしか保持していません。
問題が起きた際、過去のカスタマイズファイルに戻す可能性があるので、アプリ上に過去のファイルも保管しておきます。
過去のファイルをどこまで保管しておくかは、「1年に1回棚卸しをする」などのルールを決めてチームに合った方法で運用するとよいでしょう。

tips
補足

ファイルの管理は、GitHubなどのバージョン管理ツールを利用し、kintoneではそのリンクを登録しておくのもひとつの方法です。
こうすると、ファイルを手動で管理アプリにアップロードする手間は省けます。
バージョン管理ができるので、過去のファイル管理も不要になり、棚卸しなどのルール決めも不要になりますね。

ワイドコース限定APIを使って、カスタマイズの利用状況も管理する

ここまで紹介した方法をAPIを使って一部自動化できます。
さらに、アプリのAPIリクエスト数もAPIで取得し、アプリの利用状況も把握する例を紹介します。

information

ワイドコースでのみ利用できるAPIを使います。
ワイドコースとは (External link)

実現したいことは、次のとおりです。

  • カスタマイズが適用されているアプリを自動でレコード登録する。
  • 次のデータを自動でフィールドに登録/更新する。
    • アプリID
    • アプリ名
    • アプリのAPIリクエスト数
    • アプリのJavaScript / CSSカスタマイズ設定画面へのリンク

今回は例として、レコード一覧画面に設置した「更新ボタン」をクリックしたら、APIから最新情報を取得して情報を更新したいと思います。

フィールドの追加

先ほど作った アプリの構成に次のフィールドを追加します。

フィールド名 フィールドタイプ フィールドコード 備考
1日のAPIリクエスト数 数値 api_request_count
JavaScript / CSSカスタマイズ設定画面へのリンク リンク setting_link

サンプルコード

レコード一覧画面に設置したボタンをクリックすると、最新情報をAPIから取得してくるJavaScriptのサンプルコードです。
コードをコピーして、任意のファイル名で保存し、アプリ設定画面から、ファイルをアップロードします。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
(() => {

  // 使用状況を取得するアプリの数(最大値は100)
  const APP_GET_LIMIT = 10;

  // 一覧画面上部にボタンを作成
  const createButton = () => {
    // すでにボタンが配置されていたら処理は行わない
    if (document.getElementById('menu_button') !== null) {
      return null;
    }
    // ボタンを作成
    const button = document.createElement('button');
    button.id = 'menu_button';
    button.textContent = '更新ボタン';
    return button;
  };

  // カスタマイズの設定画面へのリンク文字列を生成
  const createSettingLink = appId => {
    const url = new URL(`${location.origin}/k/admin/app/customize`);
    url.searchParams.set('app', appId);
    return url.toString();
  };

  // カスタマイズされているアプリの情報を取得
  const getAppsStatistics = async () => {
    // アプリの使用状況を取得
    const {apps} = await kintone.api(
      kintone.api.url('/k/v1/apps/statistics.json'),
      'GET',
      {limit: APP_GET_LIMIT}
    );

    return apps
      // カスタマイズされているアプリ情報のみを抽出
      .filter(({customized}) => customized === true)
      // オブジェクトを作成する
      .map(({id: appId, name, dailyRequestCount}) => ({
        appId,
        name,
        dailyRequestCount
      }));
  };

  // アプリの使用状況を更新する
  const updateAppsStatistics = async () => {
    const customizedApps = await getAppsStatistics();

    // カスタマイズされているアプリがない場合は処理を終了する
    if (customizedApps.length === 0) {
      return {};
    }

    // 既にアプリに登録されているレコード一覧を取得
    const {records} = await kintone.api(
      kintone.api.url('/k/v1/records'),
      'GET',
      {app: kintone.app.getId()}
    );

    // 登録・更新用の records を作成する
    const recordsForRequest = customizedApps.reduce(
      ({post, put}, {appId, name, dailyRequestCount}) => {
        // アプリ内でapp_idが一致するレコードを検索
        const existingRecord = records.find(
          record => record.app_id.value === appId
        );
        // 登録・更新する record オブジェクトを作成
        const record = {
          app_id: {value: appId},
          app_name: {value: name},
          api_request_count: {value: dailyRequestCount},
          setting_link: {value: createSettingLink(appId)}
        };
        // 既存レコードの有無によって、登録・更新用の records として振り分け
        if (existingRecord) {
          return {
            post: [...post],
            put: [...put, {id: existingRecord.$id.value, record}]
          };
        }
        return {post: [...post, record], put: [...put]};
      },
      {post: [], put: []}
    );

    // レコードの登録・更新を行う
    const result = await Object.entries(recordsForRequest).reduce(
      async (promiseChain, [method, upsertRecords]) => {
        const responses = await promiseChain;
        const response = await kintone.api(
          kintone.api.url('/k/v1/records'),
          method.toUpperCase(),
          {app: kintone.app.getId(), upsertRecords}
        );
        return [...responses, [method, response]];
      },
      Promise.resolve([])
    );

    return Object.fromEntries(result);
  };

  // レコード一覧の表示後イベント
  kintone.events.on('app.record.index.show', (event) => {

    // ボタンがない場合はボタンを作成する
    const menuButton = createButton();

    // ボタンがすでに存在していた場合は、この後の処理は行わない
    if (!menuButton) return event;

    // ボタンクリック時の処理を追加する
    menuButton.onclick = async () => {
      try {
        await updateAppsStatistics();
        // 画面を更新する
        location.reload();
      } catch (e) {
        console.error(e);
        alert('更新処理でエラーが発生しました。');
      }
    };

    // レコード一覧のメニューの右側の要素を取得し、ボタンを配置
    const headerMenuSpace = kintone.app.getHeaderMenuSpaceElement();
    headerMenuSpace.appendChild(menuButton);
    return event;
  });

})();
コードの解説

ワイドコース限定APIが使用されているのはこの部分です。
アプリの使用状況を取得するのAPIを使用してアプリの使用状況の一覧を取得しています。

28
29
30
31
32
33
// アプリの使用状況を取得
const {apps} = await kintone.api(
  kintone.api.url('/k/v1/apps/statistics.json'),
  'GET',
  {limit: APP_GET_LIMIT}
);

取得したアプリの使用状況の一覧から、今回はカスタマイズされているアプリに絞り込み、アプリID、アプリ名、APIリクエスト数を抽出しています。

35
36
37
38
39
40
41
42
43
return apps
  // カスタマイズされているアプリ情報のみを抽出
  .filter(({customized}) => customized === true)
  // オブジェクトを作成する
  .map(({id: appId, name, dailyRequestCount}) => ({
    appId,
    name,
    dailyRequestCount
  }));

このとき、APIの仕様上、カスタマイズだけでなくプラグインが適用されているアプリも含まれます。

tips
補足

今回は、わかりやすくレコード一覧のボタン押下時に処理が走りますが、AWSのLambdaなどを利用して処理を定期実行させるのもよいでしょう。
定期実行のやり方については、 定期実行でデータの同期を実現するスマートな方法 その2〜AWS Lambda編〜などを参考にしてください。

動作確認

先ほどのカスタマイズを適用したうえで、アプリの一覧画面を開きます。
画面上部に表示されている「更新ボタン」をクリックすると、次のようにカスタマイズが適用されているアプリの情報がレコード一覧に自動で表示されます。

「更新ボタン」をクリックするたびに、すでに登録されているアプリについても、1日あたりのAPIリクエスト数やアプリ名が最新情報に更新されます。

レコード詳細画面を見ると、ワイドコースAPIで自動取得したアプリのカスタマイズに関する情報を確認できます。

アプリのJavaScript / CSSカスタマイズ設定画面へのリンクを追加したのは、そこから各アプリの設定画面に直接遷移できると、現在適用されているカスタマイズをすぐに確認できて便利なためです。

APIを使用しない時と比べると、カスタマイズの概要とファイルのフィールドのみを入力すればいい形にできました。

おわりに

今回は、カスタマイズファイルの利用状況をkintoneで管理する方法を紹介しました。
方法はあくまで一例で、実際にはそれぞれの環境に合った管理方法を選ぶことが重要です。
この記事が自社に合った効率的な運用管理体制を整えるためのヒントになれば幸いです。