【kintone】 APIトークンの自動生成

## まえがき

レコードやアプリの操作は、[kintone REST API](https://developer.cybozu.io/hc/ja/articles/201941754)でほぼほぼ実装できます。
しかし、通知やAPIトークンといった一部設定値については、kintone REST APIでサポートされておりません。
cybozu developer networkの[ナレッジノート記事](https://developer.cybozu.io/hc/ja/community/posts/360054477771)で紹介した[Puppeteer](https://www.npmjs.com/package/puppeteer)を使うと、そんなかゆい所に手が届いたりします。
今回は、APIトークンを自動生成する例を紹介します。

## デモ

Puppeteerを使って、APIトークンを自動生成します。
![demo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528077/0ec3ae0b-1886-b55e-bed0-842b6962dbdc.gif)
生成したAPIトークンが利用可能か、[kintone コマンドラインツール](https://developer.cybozu.io/hc/ja/articles/202708214)を用いて検証しています。
オプションの「-d」にドメイン名、「-a」にアプリID、「-t」にAPIトークンを指定しています。

## コード

Puppeteer、[readline-sync](https://www.npmjs.com/package/readline-sync)を利用しています。 npm等を用いてインストールしてください。

・sample.js (実行コード)

```JavaScript
(async () => {
  const readline = require('readline-sync'); //readline-syncの読み込み
  const puppeteer = require('puppeteer'); //puppeteerの読み込み

  //設定値
  const domain = '****.cybozu.com'; //kintoneのドメイン
  const basicUser = false; //Basic認証のユーザー名(設定していない場合はfalse)
  const basicPassword = false; //Basic認証のユーザー名(設定していない場合はfalse)
  const user = '****'; //kintoneのログインユーザー名
  const password = '****'; //kintoneのログインパスワード

  const appId = readline.questionInt('App ID: '); //アプリのID
  const accessRights = []; //追加するAPIトークンに与えるアクセス権
  accessRights[0] = readline.keyInYN('Record view right: ');
  accessRights[1] = readline.keyInYN('Record add right: ');
  accessRights[2] = readline.keyInYN('Record edit right: ');
  accessRights[3] = readline.keyInYN('Record delete right: ');
  accessRights[4] = readline.keyInYN('App edit right: ');

  const browser = await puppeteer.launch(); //ブラウザ起動
  const page = await browser.newPage();
  if(basicUser && basicPassword){ //Basic認証(設定している場合)
    await page.setExtraHTTPHeaders({
      Authorization: `Basic ${new Buffer.from(`${basicUser}:${basicPassword}`).toString('base64')}`
    });
  }
  await page.goto(`https://${domain}/k/admin/app/apitoken?app=${appId}`); //ログインページへ遷移
  await page.type('input[name="username"]', user); //kintoneのユーザー名入力
  await page.type('input[name="password"]', password); //kintoneのログインパスワード入力
  await (await page.$('.login-button')).click(); //「ログイン」ボタンをクリック

  await page.waitForNavigation({waitUntil: "domcontentloaded"}); //APIトークンの設定ページへの遷移を待機
  await page.waitFor('.gaia-admin-app-apitoken-add'); //「生成する」ボタンの描画を待機
  await (await page.$('.gaia-admin-app-apitoken-add')).click(); //「生成する」ボタンをクリック
  const apitoken = await page.evaluate((accessRights) => (
    new Promise(resolve => {
      new MutationObserver(() => { //APIトークンの生成を待機
        const targetRow = document.getElementsByClassName('gaia-admin-app-apitoken-row')[0];
        accessRights.forEach((accessRight, index) => {
          targetRow.childNodes[1].getElementsByTagName('input')[index].checked = accessRight; //APIトークンのアクセス権の変更
        });
        resolve(targetRow.childNodes[0].innerText); //APIトークンの取得
      }).observe(document.getElementsByClassName('gaia-admin-app-apitoken-table-body')[0], {childList: true, subtree: true});
    })
  ), accessRights);
  await (await page.$('.button-submit-cybozu')).click(); //「保存」ボタンをクリック

  await page.waitForNavigation({waitUntil: "domcontentloaded"}); //アプリの設定ページへの遷移を待機
  await page.waitFor('.gaia-admin-app-deploy-button'); //「アプリを更新」ボタンの描画を待機
  await (await page.$('.gaia-admin-app-deploy-button')).click(); //「アプリを更新」ボタンをクリック
  await page.waitFor('.gaia-argoui-dialog-buttons-default-tutorial'); //「OK」ボタンの描画を待機
  await (await page.$('.gaia-argoui-dialog-buttons-default-tutorial')).click(); //「OK」ボタンをクリック

  browser.close(); //ブラウザ停止

  console.log(apitoken); //コマンドラインにAPIトークンを出力
})();
```

kintoneの仕様変更により、正しく動作しなくなる可能性があります。
予めご了承ください。