プログラマーのメモ書き

伊勢在住のプログラマーが気になることを気ままにメモったブログです

kintone カスタマイズ設定を Live Server に戻す

こちらの記事でまとめたように、 kintone のアプリに対してカスタマイズファイルをアップロードできます。

ですが、自分の場合だと、普段は Live Server 設定で VSCode 上でデバッグして、ある程度開発が終わったら、一度カスタマイズファイルとしてアップロードして、動作確認&本番環境への反映、となるので、カスタマイズファイルアップロード後に不具合があると Live Server 設定で再度デバッグ等を行うことになります。

ということで、複数アプリのカスタマイズファイル設定を Live Server を使う設定に戻すための js を作ってみました。

という記事だったんですが、最終的な結論としては、 kintone の customize-uploader が対応してくれていたので、以下の作業は不要になりました。まあ、せっかくなので、残しておきますが、 customize-uploader を使った方法はこちらで紹介しておきます。

作成

アプリの設定を変更するのは kintone REST API client を利用します。

PS D:\work\tmp\kintone_tutorial\revert_live_server> npm install --save-dev @kintone/rest-api-client

up to date, audited 301 packages in 2s

71 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
PS D:\work\tmp\kintone_tutorial\revert_live_server> 

js ファイルの主な処理は、次のようになります。

  • 最初に app.getDeployStatus でアプリのステータスを取得します
  • ステータスが正しく取得されて、それらがすべて SUCCESS になっていることを確認します
  • 確認出来たら、app.updateAppCustomize でアプリのカスタマイズ設定を Live Server のURLに変更します。
  • 最後に、 app.deployApp を呼び出して、変更を反映します。

こういう流れになった理由については、後述してある補足もご覧ください。

最終的に作成した js ファイルはこんな感じです(こちらに書いたように ESModule にしています)。

#!/bin/bash/env node
"use strict";

import process from "node:process";
import {parseArgs} from "node:util";
import {KintoneRestAPIClient} from "@kintone/rest-api-client";
import {DEV_ENV_APP_ID} from "../src/lib/const/AppIdConfig.js";

const DEV_ENV_URL = "https://kintoneのドメイン名.cybozu.com";
const WEBPACK_DIST_URL = "https://localhost:5500/dist/";

// webpack.config.js の entry のキーに対応したファイル名を記述
const APP_JS_DEV = {
    sample_app: "kintone-revert-test.js",
};
function usage() {
    console.log("Usage: node  kintone_revert_localserver.js  [-h]  username  password");
    console.log("    -h:  display help");
    console.log("    username: kintone login username");
    console.log("    password: password for username");
}
function parseCliArgs() {
    const options = {
        help: {
            type: "boolean",
            short: "h",
        },
    };
    const {values, positionals} = parseArgs({
        options: options,
        allowPositionals: true,
    });
    // console.log(values);
    // console.log(positionals);
    if (values.help) {
        usage();
        return false;
    }
    if (positionals.length !== 2) {
        console.warn("invalid parameters");
        usage();
        return false;
    }
    return positionals;
}
/**
 * js カスタマイズ設定を、ローカルサーバーに戻す
 * @param {*} client
 */
async function revertCustomizeToLocalServer(client) {
    Object.entries(APP_JS_DEV).forEach(async (elm) => {
        const param = {
            app: DEV_ENV_APP_ID[elm[0]],
            desktop: {
                js: [
                    {
                        type: "URL",
                        url: WEBPACK_DIST_URL + elm[1],
                    },
                ],
            },
        };
        try {
            await client.app.updateAppCustomize(param);
        } catch (error) {
            console.error("failed to revert js customize to localserver, param:" + JSON.stringify(param));
            console.error(error);
        }
    });
}
/**
 * アプリの更新状況を取得
 * @param {*} client
 */
async function getDeployStatuses(client) {
    const app_ids = Object.keys(APP_JS_DEV).map((elm) => {
        return DEV_ENV_APP_ID[elm];
    });
    const param = {
        apps: app_ids,
    };

    try {
        const status = await client.app.getDeployStatus(param);
        return status;
    } catch (error) {
        console.error("failed to get app deploy status, param:" + JSON.stringify(param));
        console.error(error);
    }
    return undefined;
}
/**
 * 変更したアプリ設定を更新
 *
 * 変更した全てのアプリを同時に指定して、APIを呼び出す
 * @param {*} client
 */
async function updateAppSettings(client) {
    const app_ids = Object.keys(APP_JS_DEV).map((elm) => {
        return {
            app: DEV_ENV_APP_ID[elm],
        };
    });
    const param = {
        apps: app_ids,
        revert: false,
    };

    try {
        await client.app.deployApp(param);
    } catch (error) {
        console.error("failed to update app settings, param:" + JSON.stringify(param));
        console.error(error);
    }
}

/** ///////////////////////////// */
// 以下、メイン処理

const parsed = parseCliArgs();
if (!parsed) {
    process.exit(1);
}

const client = new KintoneRestAPIClient({
    baseUrl: DEV_ENV_URL,
    auth: {
        username: parsed[0],
        password: parsed[1],
    },
});

// アプリのデプロイ状況をチェック
console.log("deploy status, before update:");
const status = await getDeployStatuses(client);
console.log(JSON.stringify(status));

if (!status) {
    console.warn("status cannot get. so update process stopped.");
    process.exit(1);
}

if (
    status.apps.some((elm) => {
        return elm.status !== "SUCCESS";
    })
) {
    console.warn("some app is not deploied. so update process stopped.");
    process.exit(1);
}

console.log("start process to revert to localserver's URL");
// カスタマイズファイルの設定を localserver に戻す
await revertCustomizeToLocalServer(client);

// アプリの変更を反映
console.log("update app settings");
await updateAppSettings(client);
console.log("deploy status, after update:");
console.log(JSON.stringify(await getDeployStatuses(client)));

console.log("finish process to revert");

補足

実は上記の処理において、 getDeployStatus を呼ばずに、 deployApp を適用すると

KintoneRestAPIError: [520] [GAIA_DA02] データベースのロックに失敗したため、変更を保存できませんでした。時間をおいて再度お試しください。 (mlTC5KYXuKy0DOpUvvxs)

といったメッセージが出てきます。

でも、一度 getDeployStatus を実行後に、 deployApp を実行すると、問題なく完了します。 getDeployStatus を呼び出すタイミングは、updateAppCustomize 実行前でも後でも変わらず、大丈夫でした。

ドキュメントのどこを見ても、 deployApp の前に getDeployStatus を実行する必要がある、なんて記述はないようですが、何かあるんでしょうかね?

とりあえず実行できないと困るので、上記のような処理にしています。

試してみる

上記を作成したら、試してみます。

src/lib/const に AppIdConfig.js というファイルを用意して、

/**
 * アプリケーションID を定義するモジュール
 */

// 開発環境
const APP_ID_DEV = {
    sample_app: nnn,
};

// ステージング環境
const APP_ID_STAGE = {
    sample_app: nnn,
};

// 本番環境
const APP_ID_PROD = {
    sample_app: nnn,
};

/**
 * 複数環境におけるアプリ ID のオブジェクト
 */
export const environments = {
    "開発環境.cybozu.com": [APP_ID_DEV],
    "本番環境.cybozu.com": [APP_ID_STAGE, APP_ID_PROD],
};

/**
 * 開発環境のアプリ ID
 *
 * 開発環境には1つしかスペースがない前提
 */
export const DEV_ENV_APP_ID = environments["開発環境.cybozu.com"][0];

こんな感じに、アプリ名(sample_app)とそのアプリIDを対応させておきます。このようなファイルを利用している理由は、もともとこのファイルは、カスタマイズ時に環境ごとにアプリIDを切り替えるために定義していたものになります(こちらの記事で触れています)。

これを利用して、アプリIDを特定するということを行っています。

実行するには、

PS D:\work\tmp\kintone_tutorial\revert_live_server> node .\bin\kintone_revert_localserver.js ユーザー名 パスワード
deploy status, before update:
{"apps":[{"app":"nnn","status":"SUCCESS"}]}
start process to revert to localserver's URL
update app settings
deploy status, after update:
{"apps":[{"app":"nnn","status":"PROCESSING"}]}
finish process to revert
PS D:\work\tmp\kintone_tutorial\revert_live_server>

のように呼び出せばできます。

customize-uploader

さあ、これで Live Server に簡単に戻せるな、と思っていたところ、ふと気がついたのですが、 customize-uploader の js ファイルとして URL を指定すれば、切り替えられるんじゃないか?という疑問が出てきました。

早速、試してみます。

dest/customize-manifest-revert.json としてURLを記載しておきます。

{
    "app": "nnn",
    "scope": "ALL",
    "desktop": {
        "js": ["https://localhost:5500/dist/kintone-revert-test.js"],
        "css": []
    },
    "mobile": {
        "js": [],
        "css": []
    }
}

これをコマンドラインで指定します。

PS D:\work\tmp\kintone_tutorial\revert_live_server> npx kintone-customize-uploader --base-url https://kintoneのドメイン名.cybozu.com --username ユーザー名 --password パスワード dest/customize-manifest-revert.json
カスタマイズのアップロードを開始します
JavaScript/CSS ファイルをアップロードしました!
JavaScript/CSS カスタマイズの設定を変更しました!
運用環境への反映の完了を待っています...
運用環境への反映の完了を待っています...
運用環境への反映の完了を待っています...
運用環境に反映しました!
PS D:\work\tmp\kintone_tutorial\revert_live_server> 

あ、できてしまいました・・・。kintone 側のアプリの設定を見ても、ちゃんと切り替わっています。

ということで、上記のスクリプトは無駄になってしまいましたが、一応残しておきますね。

まとめ

上記のように customize-uploader で Live Server に戻せるのに気が付いた後、 customize-uploader のリポジトリをみたら、ちゃんとサンプルに URL を含んだものが載ってました。

ドキュメントはちゃんと確認しないといけませんね。