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

※01-31スクリプトIDが誤っていたので差し替えました｡
※01-31 13:50：クラスはライブラリで参照できないようなので、コードを保管しました。
参考：[GASのライブラリでクラス化と補完](https://qiita.com/soundTricker/items/42c114945c20624da237)
15：22 元のKintoneManagerから移行するにはどうすればいいですか？を追記

※この記事は自分自身の[ブログ](https://so.sha-box.com/2022/01/kintonemanagerv8/)で公開した内容をQiita用にしたものです｡



kintoneをGoogle Apps Script(GAS)で使用する際にお世話になる[KintoneManager](https://qiita.com/Arahabica/items/063877b0da439020d2c2)

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

元記事:

[kintone とGoogle Apps Script連携](https://qiita.com/Arahabica/items/063877b0da439020d2c2)

# 主な変更箇所

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

# コード

```jsx
'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;
  }
}
```

# 初期化

## トークン認証＋ベーシック認証なし

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

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

```jsx
//パスワード認証＋ベーシック認証なし
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);
}
```

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

```jsx
//パスワード認証＋ベーシック認証なし ゲストスペース
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);

}
```

## トークン認証＋ベーシック認証あり

```jsx
//トークン認証＋ベーシック認証あり
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);
}
```

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

```jsx
//パスワード認証＋ベーシック認証あり
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)
}
```

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

```jsx
//パスワード認証＋ベーシック認証あり＋ゲストスペース
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化したものを返してくれます。

```jsx
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を指定して検索するメソッドです。

```jsx
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をログ出力します｡

```jsx
function sampleCodegetRecordTemplateJson (){

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

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

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

1dtIBGa-2Ij4CSWl8cs4hnDPjVz43rnIrG_oT_nBTinwob2yBAW4anL_X



# ライブラリ利用時の初期化
```jsx
function sampleCodeUseLibrary (){
  //トークン認証
  const KintoneManager = new KintoneManagerV8.KintoneManagerV8(subdomain,apps)
  //パスワード認証の場合はこちら
  //const kintoneManager = new KintoneManagerV8.KintoneManagerV8(subdomain, apps, user, pass);
}
```

## GitHub

[こちらで公開しています｡](https://github.com/soichirow/kintoneManagerV8)

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

コメント欄や[Twitter](https://twitter.com/black777cat)にて

## ほしいものリスト

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

[ほしいものリストへ](https://www.amazon.jp/hz/wishlist/ls/1Z0X9ZM1D65A7?ref_=wl_share)

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

[kintone とGoogle Apps Script連携](https://qiita.com/Arahabica/items/063877b0da439020d2c2)

[GASが動かない時に見るところ](https://qiita.com/sy250f/items/7fe62873255e1b4e4c2b)



## 元のKintoneManagerから移行するにはどうすればいいですか？
※めちゃくちゃ聞かれるので追記
※完全に同じ動きをするか保証はしません。

1、元のライブラリを削除して、KintoneManagerV8（ID ： 1dtIBGa-2Ij4CSWl8cs4hnDPjVz43rnIrG_oT_nBTinwob2yBAW4anL_X ）を読み込む

2、ライブラリの名前元のライブラリの名前に合わせる。
※何も変更してないようであればKintoneManagerV8からKintoneManagerに変更する。

![2022-01-31_15h18_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238617/4b2e1ede-8ca7-1f16-9b72-ea52c35333fc.png)