js-sdkを使ってアプリ&レコードコピー

js-sdkというSDKが登場しました。 以前からあるkintone-js-sdkとは別物になります。 今回はjs-sdkを使った、アプリとレコードコピーのサンプルを紹介します。 以前紹介したkintone-js-sdk版で対応していなかった一部設定値(カスタマイズ設定など)のコピーにも対応しました。

コード

const{KintoneRestAPIClient}=require('@kintone/rest-api-client');constclient=newKintoneRestAPIClient({baseUrl:'https://\*\*\*\*.cybozu.com',auth:{username:'\*\*\*\*',password:'\*\*\*\*'},// basicAuth: {username: '\*\*\*\*', password: '\*\*\*\*'} // ベーシック認証が必要な場合のみ});constreduce=require('awaity/reduce').default;constoriginAppId= **** ;constaddAllRecords=({app,records})=\>{constlimit=100;returnPromise.all(records.reduce((recordsBlocks,record)=\>{if(recordsBlocks[recordsBlocks.length-1].length===limit){recordsBlocks.push([record]);}else{recordsBlocks[recordsBlocks.length-1].push(record);}returnrecordsBlocks;},[[]]).map(recordsBlock=\>(client.record.addRecords({app:app,records:recordsBlock}))));};constcopyFile=asyncfile=\>((awaitclient.file.uploadFile({file:{name:file.name,data:awaitclient.file.downloadFile({fileKey:file.fileKey})}})).fileKey);constformatProperties=properties=\>(Object.fromEntries(Object.entries(properties).filter(([fieldCode])=\>(!['レコード番号','$id','$revision','作成者','更新者','作成日時','更新日時','カテゴリー','作業者','ステータス'].includes(fieldCode)))));constformatRecords=records=\>(Promise.all(records.map(record=\>(reduce(Object.entries(record).filter(([fieldCode])=\>(!['レコード番号','$id','$revision','作成者','更新者','作成日時','更新日時'].includes(fieldCode))),async(record,[fieldCode,field])=\>{record[fieldCode]=field;if(field.type==='FILE'){record[fieldCode].value=awaitPromise.all(field.value.map(asyncfile=\>({...file,fileKey:awaitcopyFile(file)})));}elseif(field.type==='SUBTABLE'){record[fieldCode].value=awaitPromise.all(field.value.map(asyncrow=\>({...row,value:awaitreduce(Object.entries(row.value),async(rowValue,[fieldCode,field])=\>{rowValue[fieldCode]=field;if(field.type==='FILE'){rowValue[fieldCode].value=awaitPromise.all(field.value.map(asyncfile=\>({...file,fileKey:awaitcopyFile(file)})));}returnrowValue;},{})})));}returnrecord;},{})))));constformatAppCustomizeFiles=appCustomizeFiles=\>(Promise.all(appCustomizeFiles.map(asyncappCustomizeFile=\>((appCustomizeFile.type!=='FILE')?appCustomizeFile:{type:'FILE',file:{fileKey:awaitcopyFile(appCustomizeFile.file)}}))));(async()=\>{constoriginApp=awaitPromise.all([client.app.getApp({id:originAppId}).then(app=\>({app})),client.app.getAppSettings({app:originAppId}).then(appSettings=\>({appSettings})),client.app.getFormFields({app:originAppId}).then(formFields=\>({formFields})),client.app.getFormLayout({app:originAppId}).then(formLayout=\>({formLayout})),client.app.getViews({app:originAppId}).then(views=\>({views})),client.app.getAppAcl({app:originAppId}).then(appAcl=\>({appAcl})),client.app.getFieldAcl({app:originAppId}).then(fieldAcl=\>({fieldAcl})),client.app.getRecordAcl({app:originAppId}).then(recordAcl=\>({recordAcl})),client.app.getAppCustomize({app:originAppId}).then(appCustomize=\>({appCustomize})),client.app.getProcessManagement({app:originAppId}).then(processManagement=\>({processManagement})),client.record.getAllRecordsWithCursor({app:originAppId,query:'order by $id asc'}).then(records=\>({records}))]).then(responses=\>{returnresponses.reduce((originApp,response)=\>{return{...originApp,[Object.keys(response)[0]]:Object.values(response)[0]};},{});});constcopyAppName=`${originApp.app.name}\_copy\_${new Date()}`;constcopyAppId=awaitclient.app.addApp({name:copyAppName}).then(response=\>response.app);awaitclient.app.updateAppSettings({app:copyAppId,description:originApp.appSettings.description,icon:originApp.appSettings.icon,theme:originApp.appSettings.theme});awaitclient.app.addFormFields({app:copyAppId,properties:formatProperties(originApp.formFields.properties)});awaitclient.app.updateFormLayout({app:copyAppId,layout:originApp.formLayout.layout});awaitclient.app.updateViews({app:copyAppId,views:originApp.views.views});awaitclient.app.updateAppAcl({app:copyAppId,rights:originApp.appAcl.rights});awaitclient.app.updateFieldAcl({app:copyAppId,rights:originApp.fieldAcl.rights});awaitclient.app.updateRecordAcl({app:copyAppId,rights:originApp.recordAcl.rights});awaitclient.app.updateAppCustomize({app:copyAppId,scope:originApp.appCustomize.scope,desktop:{js:awaitformatAppCustomizeFiles(originApp.appCustomize.desktop.js),css:awaitformatAppCustomizeFiles(originApp.appCustomize.desktop.css)},mobile:{js:awaitformatAppCustomizeFiles(originApp.appCustomize.mobile.js),css:awaitformatAppCustomizeFiles(originApp.appCustomize.mobile.css)},});awaitclient.app.updateProcessManagement({app:copyAppId,enable:originApp.processManagement.enable,states:originApp.processManagement.states,actions:originApp.processManagement.actions,});awaitclient.app.deployApp({apps:[{app:copyAppId}]});awaitnewPromise(resolve=\>{consttimer=setInterval(async()=\>{conststatus=awaitclient.app.getDeployStatus({apps:[copyAppId]}).then(response=\>response.apps[0].status);if(status==='SUCCESS'){clearInterval(timer);resolve();}},1000);});awaitaddAllRecords({app:copyAppId,records:awaitformatRecords(originApp.records)});console.log(`「${originApp.app.name}(appId:${originAppId})」を「${copyAppName}(appId:${copyAppId})」へコピーしました。`);})();

・Node.js v12以上で動作します。(Object.fromEntries()を利用しているため。)
・「@kintone/js-sdk」及び「awaity」はnpm等でインストールして下さい。
・add、update処理に関してはエラーを避けるために直列処理としております。(参考)

kintone-js-sdkに対するjs-sdkの違い

・APIを利用するまでの記述が少ない。
・対応しているAPIの種類が多い。
・ファイル操作時にローカルのストレージを介さない。

サンプルのご紹介や「kintone-js-sdk」との違いの説明ありがとうございます。

「kintone Utility Library for JavaScript」→「kintone-js-sdk」→「js-sdk」

と移り変わりが早いですね。