【GASV8 ベーシック認証対応】KintoneManagerを改良したKintoneManagerV8を作成しました! #kintone #GAS

2022-01-31

当サイトではアフィリエイト広告を利用して商品を紹介しています。

みなさん、こんにちは!
そーちゃん(@black777cat)です。

kintoneをGoogle Apps Script(GAS)で使用する際にお世話になるKintoneManager

こちらは2015年に公開されたされた記事で、当時のGASではclassを使って書くことはできず、prototypeを使って書かれています。
今回は自分のclassへの理解を深めるために公開されているコードのファクタリングと使いやすいようにメソッドをいくつか増やしたものを作成しました。
基本的な使い方については、元のKintoneManagerと同じです。
まずはそちらの記事も読むことをオススメいたします。

元記事:
kintone とGoogle Apps Script連携

動作の確認は一通り行ってはおりますが、使用に際しましては、自己責任にてお願いいたします。

またこのコードを作成するにあたり、普段お世話になっているオンラインの学習コミュニティノンプロ研の皆様にお力を貸していただきました。
この場を借りて感謝をさせていただきます。

主な変更箇所

  • V8のアップデートで使えるようになったclassに変更
  • ベーシック認証に対応
  • メソッドの追加
  • ドキュメンテーションコメントの追加

コード

'use strict'
class KintoneManagerV8 {
  /**
   * kintoneのAPIに関するコンストラクタ
   * @param {string} subdomain - サブドメイン
   * @param {object} apps - アプリケーションの情報
   * @param {string} user (optional) -  ユーザー名 または  authentication information: base64("USER:PASS")
   * @param {string} pass (optional)  - パスワード
   * @constructor
   */
  constructor(subdomain, apps, user, pass) {
    /** @type {string} */
    this.subdomain = subdomain;
    /** @type {string} */
    this.authorization = null;
    /** @type {string} */
    this.apps = apps;

    if (arguments.length > 3) {
      this.authorization = Utilities.base64Encode(user + ":" + pass);
    } else if (arguments.length > 2) {
      // 引数が3つの場合はエンコード済みの認証情報として処理
      this.authorization = user;
    }
  }
  /**
   * kintoneにレコードを追加するメソッド
   * @param {string} appName - アプリの名前
   * @param {Array.<Array.<string>>} records - 登録するレコード <https://developer.cybozu.io/hc/ja/articles/202166160>
   * @return {HTTPResponse} response - UrlFetchAppの結果 <https://developers.google.com/apps-script/reference/url-fetch/http-response>
   */
  create(appName, records) {
    const app = this.apps[appName];
    const payload = {
      app: app.appid,
      records: records
    };
    const endpoint = `${this._getEndpoint(app.guestid)}/records.json`;
    const option = this._postOption(app, payload);
    const response = UrlFetchApp.fetch(endpoint, option);
    return response
  }
  /**
   * kintoneからレコードを検索するメソッド
   * @param {string} appName - アプリの名前
   * @param {string} query - 検索するクエリ <https://developer.cybozu.io/hc/ja/articles/202331474>
   * @return {HTTPResponse} response - UrlFetchAppの結果 <https://developers.google.com/apps-script/reference/url-fetch/http-response>
   */
  search(appName, query) {
    const q = encodeURIComponent(query);
    const app = this.apps[appName];
    const endpoint = `${this._getEndpoint(app.guestid)}/records.json?app=${app.appid}&query=${q}`;
    const option = this._getOption(app);
    const response = UrlFetchApp.fetch(endpoint, option);
    return response;
  }
  /**
   * kintoneからレコード番号を指定して検索するメソッド
   * @param {string} appName - アプリの名前
   * @param {string} recordId - レコードの番号 <https://developer.cybozu.io/hc/ja/articles/202331474>
   * @param {string} recordNofieldName(optional) - 省略可。デフォルト引数はレコード番号
   * @return {HTTPResponse} response - UrlFetchAppの結果 <https://developers.google.com/apps-script/reference/url-fetch/http-response>
   */
  searchById(app_name, recordId, recordNofieldName = "レコード番号") {
    const query = `${recordNofieldName}=${recordId}`;
    return this.search(app_name, query);
  }
  /**
   * kintoneに登録されたレコードを更新するメソッド
   * @param {string} appName - アプリの名前
   * @param {Array.<Array.<string>>} records - 登録するレコード <https://developer.cybozu.io/hc/ja/articles/201941784>
   * @return {HTTPResponse} response - UrlFetchAppの結果 <https://developers.google.com/apps-script/reference/url-fetch/http-response>
   */
  update(appName, records) {
    const app = this.apps[appName];
    const payload = {
      app: app.appid,
      records: records
    };
    const endpoint = `${this._getEndpoint(app.guestid)}/records.json`;
    const option = this._putOption(app, payload);
    const response = UrlFetchApp.fetch(endpoint, option);
    return response;
  }
  /**
   * kintoneに登録されたレコードを削除するメソッド(最大100件まで)
   * @param {string} appName - アプリの名前
   * @param {Array.string} recordIds - 削除するレコードのIDの配列 <https://developer.cybozu.io/hc/ja/articles/201941794>
   * @return {HTTPResponse} response - UrlFetchAppの結果 <https://developers.google.com/apps-script/reference/url-fetch/http-response>
   */
  destroy(appName, recordIds) {
    const app = this.apps[appName];
    const payload = {
      app: app.appid,
      ids: recordIds
    };
    const endpoint = `${this._getEndpoint(app.guestid)}/records.json`;
    const option = this._deleteOption(app, payload);
    const response = UrlFetchApp.fetch(endpoint, option);
    return response;
  }

  /**
   * kintoneにレコードを追加するメソッド
   * 戻り値はresponseのJSONを変換したオブジェクト
   * @param {string} appName - アプリの名前
   * @param {Array.<Array.<string>>} records - 登録するレコード <https://developer.cybozu.io/hc/ja/articles/202166160>
   * @return {Object} Object - 変換後のObject
   */
  getCreateResultObject(appName, records) {
    const response = this.create(appName, records)
    return this.getAsObject(response)
  }
  /**
   * kintoneからレコードを検索するメソッド
   * 戻り値はresponseのJSONを変換したオブジェクト
   * @param {string} appName - アプリの名前 
   * @param {string} query - 検索するクエリ <https://developer.cybozu.io/hc/ja/articles/202331474>
   * @return {Object} Object - 変換後のObject
   */
  getSearchResultObject(appName, query) {
    const response = this.search(appName, query)
    return this.getAsObject(response)
  }
  /**
   * kintoneからレコード番号を指定して検索するメソッド
   * 戻り値はresponseのJSONを変換したオブジェクト
   * @param {string} appName - アプリの名前
   * @param {string} recordId - レコードID <https://developer.cybozu.io/hc/ja/articles/202331474>
   * @param {string} recordNofieldName - 省略可。デフォルト引数は「レコード番号」
   * @return {Object} Object - 変換後のObject
   */
  getSearchByIdResultObject(app_name, recordId, recordNofieldName = "レコード番号") {
    const response = this.searchById(app_name, recordId, recordNofieldName)
    return this.getAsObject(response)
  }
  /**
   * kintoneに登録されたレコードを更新するメソッド
   * 戻り値はresponseのJSONを変換したオブジェクト
   * @param {string} appName - アプリの名前
   * @param {Array.<Array.<string>>} records - 登録するレコード <https://developer.cybozu.io/hc/ja/articles/201941784>
   * @return {Object} Object - 変換後のObject
   */
  getUpdateResultObject(appName, records) {
    const response = this.update(appName, records)
    return this.getAsObject(response)
  }
  /**
   * kintoneに登録されたレコードを削除するメソッド(最大100件まで)
   * 戻り値はresponseのJSONを変換したオブジェクト(成功した場合は{})
   * @param {string} appName - アプリの名前
   * @param {Array.string} recordIds - 削除するレコードのIDの配列 <https://developer.cybozu.io/hc/ja/articles/201941794>
   * @return {Object} Object - 変換後のObject
   */
  getDestroyResultObject(appName, recordIds) {
    const response = this.destroy(appName, recordIds)
    return this.getAsObject(response)
  }
  /**
   * UrlFetchApp を利用して取得したresponseをオブジェクト化して返す関数
   * @param {string} response - 
   * @return {Object} - Object 変換後のObject
   */
  getAsObject(response) {
    const json = response.getContentText();
    const responceCode = response.getResponseCode();
    if(responceCode === 200){
      const object = JSON.parse(json);
      return object;
    }else if(responceCode === 401){
      throw new Error("認証エラーです。");
    }else{
      throw new Error(json);
    }

  }
  /**
   * 登録されているレコードを1件取得して、レコードを登録するためのテンプレートとなるJSONをログ出力します。
   */
  getRecordTemplateJson(appName) {
    const searchresponse = this.getSearchResultObject(appName, "")
    const record = searchresponse.records[[0]];

    delete record['レコード番号'];
    delete record['更新者'];
    delete record['作成者'];
    delete record['$revision'];
    delete record['更新日時'];
    delete record['作成日時'];
    delete record['$id'];

    for (const [_, value] of Object.entries(record)) {
      delete value.type;
    }
    console.log(record);
    return
  }

  /**
   * GETする時のオプションを作成するサブメソッド
   * @param {Object} app - app
   * @return {Object} option - option
   * @private
   */
  _getOption(app) {
    const option = {
      method: "get",
      headers: this._authorizationHeader(app),
      muteHttpExceptions: true
    };
    return option;
  }

  /**
   * POSTする時のオプションを作成するサブメソッド
   * @param {Object} app - app
   * @param {Object} payload - payload
   * @return {Object} option - option
   * @private
   */
  _postOption(app, payload) {
    const option = {
      method: "post",
      contentType: "application/json",
      headers: this._authorizationHeader(app),
      muteHttpExceptions: true,
      payload: JSON.stringify(payload)
    };
    return option;
  }

  /**
   * putする時のオプションを作成するサブメソッド
   * @param {Object} app - app
   * @param {Object} payload - payload
   * @return {Object} option - option
   * @private
   */
  _putOption(app, payload) {
    const option = {
      method: "put",
      contentType: "application/json",
      headers: this._authorizationHeader(app),
      muteHttpExceptions: true,
      payload: JSON.stringify(payload)
    };
    return option;
  }

  /**
   * deleteする時のオプションを作成するサブメソッド
   * @param {Object} app - app
   * @param {Object} payload - payload
   * @return {Object} option - option
   * @private
   */
  _deleteOption(app, payload) {
    const option = {
      method: "DELETE",
      contentType: "application/json",
      headers: this._authorizationHeader(app),
      muteHttpExceptions: true,
      payload: JSON.stringify(payload)
    };
    return option;
  }

  /**
   * kintoneのエンドポイントを作成するサブメソッド
   * @param {string} guestid (optional)  - ゲストID
   * @return {string} endpoint - エンドポイント
   * @private
   */
  _getEndpoint(guestid) {
    let endpoint = `https://${this.subdomain}.cybozu.com`
    if (guestid == null) {
      endpoint += "/k/v1";
      return endpoint
    } else {
      endpoint = `${endpoint}/k/guest/${guestid}/v1`
      return endpoint
    }
  }
  /**
   * kintoneの認証情報を作成するサブメソッド
   * @param {string} app - アプリの名前
   * @return {Object}
   * @private
   */
  _authorizationHeader(app) {
    let auth = {};
    if (this.authorization) {
      // Password authentication
      auth["X-Cybozu-Authorization"] = this.authorization;
    } else if (app.token) {
      // API token authentication
      auth["X-Cybozu-API-Token"] = app.token;
    } else {
      throw new Error("kintone APIを呼ぶための認証情報がありません。");
    }
    //ベーシック認証
    if(app.basicUserName){
      auth["Authorization"] = "Basic " + Utilities.base64Encode(app.basicUserName + ":" + app.basicPass);
    }
    return auth;
  }
}

初期化

トークン認証+ベーシック認証なし

//トークン認証+ベーシック認証なし
function authSampleCode1(){
  const subdomain = "YOUR_COMPANY";
  const apps ={
    YOUR_APPLICATION1: { appid: 1, name: "アプリ1", token: "YOUR_API_TOKEN1" }
  };
  const kintoneManagerV8 = new KintoneManagerV8(subdomain, apps);
}

パスワード認証+ベーシック認証なし

//パスワード認証+ベーシック認証なし
function authSampleCode2(){
const subdomain = "YOUR_COMPANY";
const apps ={
YOUR_APPLICATION2: { appid: 2, name: "アプリ2"},
};
const user = "YOUR_KINTONE_USER_NAME";
const pass = "YOUR_KINTONE_PASSWORD";
const kintoneManagerV8 = new KintoneManagerV8(subdomain, apps, user, pass);
}

パスワード認証+ベーシック認証なし+ゲストスペース

//パスワード認証+ベーシック認証なし ゲストスペース
function authSampleCode3(){
  const subdomain = "YOUR_COMPANY";
  const apps ={
    YOUR_APPLICATION3: { appid: 3, guestid: 3 , name: "アプリ3"}
  };
  const user = "YOUR_KINTONE_USER_NAME";
  const pass = "YOUR_KINTONE_PASSWORD";
  const kintoneManagerV8 = new KintoneManagerV8(subdomain, apps, user, pass);

}

トークン認証+ベーシック認証あり

//トークン認証+ベーシック認証あり
function authSampleCode4(){
  const subdomain = "YOUR_COMPANY";
  const apps ={
    YOUR_APPLICATION4: { appid: 4, name: "アプリ4", token: "YOUR_API_TOKEN4", basicUserName:"YOUR_KINTONE_BASIC_USER_NAME", basicPass:"YOUR_KINTONE_BASIC_PASSWORD"}
  };
  const kintoneManagerV8 = new KintoneManagerV8(subdomain, apps);
}

パスワード認証+ベーシック認証あり

//パスワード認証+ベーシック認証あり
function authSampleCode5(){
  const subdomain = "YOUR_COMPANY";
  const apps ={
    YOUR_APPLICATION5: { appid: 5, name: "アプリ5", basicUserName:"YOUR_KINTONE_BASIC_USER_NAME", basicPass:"YOUR_KINTONE_BASIC_PASSWORD"},
  };
  const user = "YOUR_KINTONE_USER_NAME";
  const pass = "YOUR_KINTONE_PASSWORD";
  const kintoneManagerV8 = new KintoneManagerV8(subdomain, apps, user, pass)
}

パスワード認証+ベーシック認証あり+ゲストスペース

//パスワード認証+ベーシック認証あり+ゲストスペース
function authSampleCode6(){
  const subdomain = "YOUR_COMPANY";
  const apps ={
    YOUR_APPLICATION6: { appid: 6, guestid: 1 , name: "アプリ6", basicUserName:"YOUR_KINTONE_BASIC_USER_NAME", basicPass:"YOUR_KINTONE_BASIC_PASSWORD"}
  };
  const user = "YOUR_KINTONE_USER_NAME";
  const pass = "YOUR_KINTONE_PASSWORD";
  const kintoneManagerV8 = new KintoneManagerV8(subdomain, apps, user, pass);
}

追加したメソッド

get(XXXXX)ResultObject

  • getCreateResultObject
  • getSearchResultObject
  • getSearchByIdResultObject
  • getUpdateResultObject
  • getDestroyResultObject

引数は元のcreate、searchメソッドと同じですが、結果を扱いやすいようにObject化したものを返してくれます。

function sampleCodeSearch (){
  //UrlFetchAppの結果が返ってきます。
  const response = kintoneManagerV8.search("YOUR_APPLICATION1", "")
  console.log(response.getResponseCode())//200なら成功
  //UrlFetchAppの結果を変換後のObjectが返ってきます
  const responseObject =  kintoneManagerV8.getSearchResultObject("YOUR_APPLICATION1","")
  console.log(responseObject)
}

searchById getSearchByIdResultObject

レコードのIDを指定して検索するメソッドです。

function sampleCodeSearchById (){

  //UrlFetchAppの結果が返ってきます。
  const response = kintoneManagerV8.searchById("YOUR_APPLICATION1", recordId, recordNofieldName )
  console.log(response.getResponseCode())//200なら成功
  //UrlFetchAppの結果を変換後のObjectが返ってきます
  const responseObject =  kintoneManagerV8.getSearchByIdResultObject("YOUR_APPLICATION1", recordId, recordNofieldName )
  console.log(responseObject)
}

getRecordTemplateJson

登録されているレコードを1件取得して、 レコードを登録するためのテンプレートとなるJSONをログ出力します。

function sampleCodegetRecordTemplateJson (){

  kintoneManagerV8.getRecordTemplateJson("YOUR_APPLICATION1")
  //{ name: { value: 'そーちゃん' }, food: { value: 'ヨーグルト' } }
  //上記のような形でログ出力されます。
}

ライブラリのスクリプトID

スクリプトエディタのライブラリの追加から下記のIDを入れてご利用ください。

1dtIBGa-2Ij4CSWl8cs4hnDPjVz43rnIrG_oT_nBTinwob2yBAW4anL_X

GitHub

こちらで公開しています。

ご質問、ご連絡、不具合報告など

コメント欄やTwitterにて

ほしいものリスト

今後のモチベーションになります!

ほしいものリストへ

参考にさせてもらったサイト

kintone とGoogle Apps Script連携

GASが動かない時に見るところ