kintone plugin-uploader の暫定エラー対応

kintone のプラグイン管理画面の構成が変わって、plugin-uploader でエラーが発生したので、とりあえず暫定対策したメモ書き

※[plugin-uploader](https://github.com/kintone/js-sdk/tree/master/packages/plugin-uploader)　で、本対策済みのようです。


# 概要

※plugin-uploader のバージョンアップで、本対策されます。
```
npm install @kintone/plugin-uploader@latest
or
npm install -g @kintone/plugin-uploader@latest
```

以下はメモ書きで、古い情報です。

2024/05/12 の kintone アップデートで、プラグイン管理画面の構成が変わって、plugin-uploader でエラーが発生。
※「エラーが発生しました kintone管理者権限のあるユーザーで実行してください」
クラス名等がかわっているので、とりあえず暫定対応した。

kintone 画面刷新の一環で、プラグイン管理画面が変わったと思われます。
プラグイン管理画面のクラス名は、自動生成されたものだと思われるので、今後のアップデートのタイミングで変更される可能性がある。
本対策としては、プラグインアップロードの REST API 対応が必要ですね。

```error.log
field-format  [master ≡] > npm run upload-prod

> field-format@0.1.0 upload-prod
> kintone-plugin-uploader dist/field-format-plugin8.zip --watch --waiting-dialog-ms 3000

Open https://xxxxxxx.cybozu.com/login?saml=off
Trying to log in...
Navigate to https://xxxxxxx.cybozu.com/k/admin/system/plugin/
エラーが発生しました kintone管理者権限のあるユーザーで実行してください
```

- プラグイン管理画面
![2024-05-12_18h46_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/5277dce7-317f-b7ab-3e42-bfeafa6c7777.png)

- プラグインアップロードダイアログ
![2024-05-12_18h46_53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/ca9bc77f-0d2b-5fbc-21bc-6ee3812c0b3f.png)

# plugin-uploader 暫定対応

plugin-uploader がインストールされているパスを探して、エラー部分を修正


C:\Program Files (x86)\Nodist\bin\node_modules\@kintone\plugin-uploader\dist\index.js

```.index.js
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.run = void 0;
const chalk_1 = __importDefault(require("chalk"));
const fs_1 = __importDefault(require("fs"));
const puppeteer_1 = __importDefault(require("puppeteer"));
const messages_1 = require("./messages");
const TIMEOUT_MS = 10000;
const UPLOAD_TIMEOUT_MS = 60000;
const launchBrowser = (proxy, ignoreDefaultArgs) => {
    const args = proxy ? [`--proxy-server=${proxy}`] : [];
    return puppeteer_1.default.launch({ args, ignoreDefaultArgs, headless: "new" });
};
const readyForUpload = (browser, baseUrl, userName, password, lang, basicAuth) => __awaiter(void 0, void 0, void 0, function* () {
    const m = (0, messages_1.getBoundMessage)(lang);
    const page = yield browser.newPage();
    const loginUrl = `${baseUrl}/login?saml=off`;
    if (basicAuth) {
        yield page.authenticate(basicAuth);
    }
    console.log(`Open ${loginUrl}`);
    yield page.goto(loginUrl);
    try {
        yield page.waitForSelector(".form-username-slash", { timeout: TIMEOUT_MS });
    }
    catch (e) {
        throw chalk_1.default.red(m("Error_cannotOpenLogin"));
    }
    console.log("Trying to log in...");
    yield page.type(".form-username-slash > input.form-text", userName);
    yield page.type(".form-password-slash > input.form-text", password);
    yield page.click(".login-button");
    try {
        yield page.waitForNavigation({
            timeout: TIMEOUT_MS,
            waitUntil: "domcontentloaded",
        });
    }
    catch (e) {
        throw chalk_1.default.red(m("Error_failedLogin"));
    }
    const pluginUrl = `${baseUrl}/k/admin/system/plugin/`;
    console.log(`Navigate to ${pluginUrl}`);
    yield page.goto(pluginUrl);
    try {
        yield page.waitForSelector("#root button.sc-bZHSRq.fHDQvG", {
            timeout: TIMEOUT_MS,
        });
    }
    catch (e) {
        throw chalk_1.default.red(m("Error_adminPrivilege"));
    }
    return page;
});
const upload = (page, pluginPath, lang) => __awaiter(void 0, void 0, void 0, function* () {
    const m = (0, messages_1.getBoundMessage)(lang);
    console.log(`Trying to upload ${pluginPath}`);

    yield page.evaluate(() => {
        const button1 = document.querySelector('#root button.sc-bZHSRq.fHDQvG');
        if (button1) {
            button1.click();
        }
        else {
            throw new Error('button.sc-bZHSRq.fHDQvG is not found');
        }
    });

    const file = yield page.$('input[type="file"]');
    if (file == null) {
        throw new Error('input[type="file"] is not found');
    }
    yield file.uploadFile(pluginPath);
    // HACK: `page.click` does not work as expected, so we use `page.evaluate` instead.
    // ref: https://github.com/puppeteer/puppeteer/pull/7097#issuecomment-850348366
    yield page.evaluate(() => {
        const button = document.querySelector('button.sc-ifyrAs.RhNPH');
        if (button) {
            button.click();
        }
        else {
            throw new Error('button.sc-ifyrAs.RhNPH is not found');
        }
    });
    yield page.waitForSelector(".sc-hABBmJ.fnsoHW", {
        hidden: true,
        timeout: UPLOAD_TIMEOUT_MS,
    });
    console.log(`${pluginPath} ${m("Uploaded")}`);
});
const run = (baseUrl, userName, password, pluginPath, options) => __awaiter(void 0, void 0, void 0, function* () {
    let browser = yield launchBrowser(options.proxyServer, options.puppeteerIgnoreDefaultArgs);
    let page;
    const { lang, basicAuth } = options;
    const m = (0, messages_1.getBoundMessage)(lang);
    try {
        page = yield readyForUpload(browser, baseUrl, userName, password, lang, basicAuth);
        yield upload(page, pluginPath, lang);
        if (options.watch) {
            let uploading = false;
            fs_1.default.watch(pluginPath, () => __awaiter(void 0, void 0, void 0, function* () {
                if (uploading) {
                    return;
                }
                try {
                    uploading = true;
                    yield upload(page, pluginPath, lang);
                }
                catch (e) {
                    console.log(e);
                    console.log(m("Error_retry"));
                    yield browser.close();
                    browser = yield launchBrowser(options.proxyServer);
                    page = yield readyForUpload(browser, baseUrl, userName, password, lang, basicAuth);
                    yield upload(page, pluginPath, lang);
                }
                finally {
                    uploading = false;
                }
            }));
        }
        else {
            yield browser.close();
        }
    }
    catch (e) {
        console.error(m("Error"), e);
        yield browser.close();
    }
});
exports.run = run;
//# sourceMappingURL=index.js.map
```