新規投稿
フォローする

kintone JS SDKを使ってアプリ&レコードコピー

kintone JS SDKを使って、指定したアプリとそのレコードを一括でコピーするコードを書いてみました。

コード

const kintone = require('@kintone/kintone-js-sdk');
const fs = require('fs');
const reduce = require('awaity/reduce').default;
const auth = new kintone.Auth();
auth.setBasicAuth({ //Basic認証情報(必要な場合のみ)
  username: '****',
  password: '****'
});
auth.setPasswordAuth({ //kintoneログインユーザー情報(「kintoneのシステム管理権限」、「コピー元アプリのアプリ・レコード・フィールドの閲覧権限」を持ったユーザー)
  username: '****',
  password: '****'
});
const connection = new kintone.Connection({
  domain: '****.cybozu.com', //kintoneのドメイン
  auth: auth
});
const originAppId = ****; //コピーするアプリのID

const appApi = new kintone.App({connection});
const recordApi = new kintone.Record({connection});
const fileApi = new kintone.File({connection});
const copyFile = async file => {
  await fileApi.download({
    fileKey: file.fileKey,
    outPutFilePath: file.name
  });
  const fileUploadResponse = await fileApi.upload({filePath: file.name});
  fs.unlink(file.name, () => {});
  return fileUploadResponse.fileKey;
};
const formatProperties = properties => (
  Object.fromEntries(Object.entries(properties).filter(([fieldCode]) => (
    ![
      'レコード番号',
      '$id',
      '$revision',
      '作成者',
      '更新者',
      '作成日時',
      '更新日時',
      'カテゴリー',
      '作業者',
      'ステータス'
    ].includes(fieldCode)
  )))
);
const formatRecords = async records => await(
  Promise.all(records.map(async record => await(
    reduce(Object.entries(record).filter(([fieldCode]) => (
      ![
        'レコード番号',
        '$id',
        '$revision',
        '作成者',
        '更新者',
        '作成日時',
        '更新日時'
      ].includes(fieldCode)
    )), async(record, [fieldCode, field]) => {
      record[fieldCode] = field;
      if(field.type === 'FILE'){
        record[fieldCode].value = await Promise.all(field.value.map(async file => await({
          ...file,
          fileKey: await copyFile(file)
        })));
      }else if(field.type === 'SUBTABLE'){
        record[fieldCode].value = await Promise.all(field.value.map(async row => await({
          ...row,
          value: await reduce(Object.entries(row.value), async(rowValue, [fieldCode, field]) => {
            rowValue[fieldCode] = field;
            if(field.type === 'FILE'){
              rowValue[fieldCode].value = await Promise.all(field.value.map(async file => await({
                ...file,
                fileKey: await copyFile(file)
              })));
            }
            return rowValue;
          }, {})
        })));
      }
      return record;
    }, {})
  )))
);

(async()=>{
  const originApp = await Promise.all([
    appApi.getApp({id: originAppId}).then(app => ({app})),
    appApi.getGeneralSettings({app: originAppId}).then(generalSettings => ({generalSettings})),
    appApi.getFormFields({app: originAppId}).then(formFields => ({formFields})),
    appApi.getFormLayout({app: originAppId}).then(formLayout => ({formLayout})),
    appApi.getViews({app: originAppId}).then(views => ({views})),
    recordApi.getAllRecordsByCursor({
      app: originAppId,
      query: 'order by $id asc'
    }).then(records => ({records}))
  ]).then(responses => {
    return responses.reduce((originApp, response) => {
      return {
        ...originApp,
        [Object.keys(response)[0]]: Object.values(response)[0]
      };
    }, {});
  });
  const copyAppName = `${originApp.app.name}_copy_${new Date()}`; //コピー先アプリ名
  const copyAppId = await appApi.addPreviewApp({name: copyAppName}).then(response => response.app);
  await appApi.updateGeneralSettings({
    app: copyAppId,
    description: originApp.generalSettings.description,
    icon: originApp.generalSettings.icon,
    theme: originApp.generalSettings.theme
  });
  await appApi.addFormFields({
    app: copyAppId,
    fields: formatProperties(originApp.formFields.properties)
  });
  await appApi.updateFormLayout({
    app: copyAppId,
    layout: originApp.formLayout.layout
  });
  if(!originApp.views.views){
    await appApi.updateViews({
      app: copyAppId,
      views: originApp.views.views
    });
  }
  await appApi.deployAppSettings({apps: [{app: copyAppId}]});
  await new Promise(resolve => {
    const timer = setInterval(async() => {
      const status = await appApi.getAppDeployStatus({apps: [copyAppId]}).then(response => response.apps[0].status);
      if(status === 'SUCCESS'){
        clearInterval(timer);
        resolve();
      }
    }, 1000);
  });
  await recordApi.addAllRecords({
    app: copyAppId,
    records: await formatRecords(originApp.records.records)
  });
  console.log(`${originApp.app.name}(appId:${originAppId})」を「${copyAppName}(appId:${copyAppId})」へコピーしました。`);
})();

・Node.js v12以上で動作します。(Object.fromEntries()を利用しているため。)
・「@kintone/kintone-js-sdk」及び「awaity」はnpm等でインストールして下さい。
・add、update処理に関してはエラーを避けるために直列処理としております。(参考)
・kintone REST API及びkintone JS SDKで対応していないアプリ設定値はコピーできません。
過去記事でPHP版も紹介しています。PHP版では、kintone REST APIで対応しているアプリ設定値は全てコピーできます。

1

0件のコメント

ログインしてコメントを残してください。