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

# はじめに

インターネットの[GAS(Google Apps Script)](https://developers.google.com/apps-script)のコードをコピペして動かなくて何時間も悩んだので、次回の時の備忘録としてまとめておきます。
GASに限ることでも無いですけど。

## 参考サイト

素材として何も無いと分かりにくいので、[cybozu developer network](https://developer.cybozu.io/hc/ja)の記事をお借りして、Googleフォームに入力してkintoneにレコードとして登録する処理を参考に見ていきます。

* [Google フォームとkintoneを連携してみよう！](https://developer.cybozu.io/hc/ja/articles/115003856483-Google)

# GASはどのように動作するのか
イメージです。
![GAS.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/b7738384-c47a-2f27-9a1b-b5d6feea9361.png)

## 参考サイトの動き

ユーザーがフォーム入力して送信ボタン押下 → フォームに設定したトリガーが起動 → 
トリガーに設定したスクリプトが実行される → スクリプトでフォーム内容が処理される → スクリプトからリクエストが送信される → 成功するとレスポンスがスクリプトに返る。外部のDB(kintone)にデータが登録される

# 動かない時に見るところ

## Googleアカウント

Googleフォームとそれに結びつける [Google Apps Script] は同一の [Googleアカウント]でログインして作成する必要があります。

複数のアカウントを利用している場合は合わせること。

## トリガーの設定

<img width="579" alt="スクリーンショット 2019-11-02 9.11.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/b3bd3b05-1444-a144-17d5-6b8fe28589c2.png">

[実行する関数を選択] にプロジェクト内の関数で最初に実行される関数名を選択します。
ここを間違えると実行されません。

## デバッグログ

GASのデバッグログの出力はプロジェクトを表示した状態で、フォーム実行直後にメニューから表示→ログで確認します。

<img width="480" alt="スクリーンショット 2019-11-02 10.37.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/b5b5bc79-1404-ca98-ef24-dc7e4d8783df.png">

<img width="723" alt="スクリーンショット 2019-10-31 22.46.57のコピー.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/22b08679-d986-1c42-4464-4bbb4dab9073.png">

## デバッグ出力

APIのLoggerクラスを利用します。

* [Class Logger](https://developers.google.com/apps-script/reference/base/logger)

```javascript
function gasTest(){
  var str = 'ログの表示'
  Logger.log("ログ=> %s", str);
}
// [19-11-02 10:49:44:699 JST] ログ=> ログの表示
```

## 問題の切り分け

GASのAPIとそうで無いものを切り分けて考えます。

参考のコードでは下記はGASのAPIの話しです。

#### Google Apps API

* [Logger](https://developers.google.com/apps-script/reference/base/logger#log(Object)) ログ出力用
* [response(Google Forms events form-submit)](https://developers.google.com/apps-script/guides/triggers/events#form-submit_1) フォーム送信時のレスポンス
* [Utilities](https://developers.google.com/apps-script/reference/utilities) 文字コード変換など
* [Event Objects](https://developers.google.com/apps-script/guides/triggers/events) トリガーイベント

# e.response.getItemResponses() 

APIを一つだけ説明します。

## e ?

eって何よ？ですが、[ここ](https://developer.cybozu.io/hc/ja/articles/115003856483-Google)のコンテキストでは Googleフォームの話しなので、ここでの e はフォーム送信時のレスポンスになります。

## フォーム送信時のレスポンス

フォーム送信時に設定したトリガーで実行されてフォームの情報をGASの関数で受け取るのですが、その受け取った情報が
[Google Forms events](https://developers.google.com/apps-script/guides/triggers/events#google_forms_events) の [Form submit](https://developers.google.com/apps-script/guides/triggers/events#form-submit_1) になります。

つまり、[e]の中身はこれ

> | Form submit  (installable) | |
|:--|:--|
| authMode | A value from the ScriptApp.AuthMode enum. |
|          | FULL |
| response | A [FormResponse object](https://developers.google.com/apps-script/reference/forms/form-response), representing the user's response to the form as a whole. | |
| |  FormResponse |
| source |  A Form object, representing the Google Forms file to which the script is bound.  |
| | Form |
| triggerUid | ID of trigger that produced this event. |
| | 4034124084959907503 |

それなので、ここからフォームの中身を取得するには e.response で フォームレスポンスオブジェクトを取得して、フォームレスポンスオフジェクトのメソッド [getItemResponses()](https://developers.google.com/apps-script/reference/forms/form-response#getitemresponses) で フォームに含まれる全てのアイテム（フォームのこと）のオブジェクトを配列で取得するという算段です。

取得したアイテムはオブジェクトなので、さらにアイテムにセットされた値を取り出すには、アイテムに対して [getResponse()](https://developers.google.com/apps-script/reference/forms/item-response.html#getresponse) して取得します。
深すぎる...



## デバッグの仕掛けどころ

下記の２箇所。

1. フォームを処理した最後の `return records` の records の中身
2. 外部DBにデータを送信した戻り値の中身

特に2.の中で、エラーメッセージが返っていたらそれを確認することで対応できるのではと思います。

こんな感じでログに書き込めばよろしいかと。

```javascript
    Logger.log('response=> "%s"', response);
```

2.で具体的に仕掛ける場所は、Step10の39行目の下になります。

## 参考

エラーだとこんな感じで返ってきてました。

```
[19-11-01 07:44:55:634 JST] response=> "{"code":"CB_VA01","id":"0HxuEQ8by3JPwVQgOy1Q","message":"入力内容が正しくありません。","errors":{"records[0].attend.value":{"messages":["\"はい、参加します\"は選択肢にありません。"]},"records[0].Email.value":{"messages":["URLの形式が正しくありません。「http://」または「https://」で始まる必要があります。"]}}}"
[19-11-01 07:44:55:635 JST] Response code is "400.0"
```

# ライブラリを使っているとき

修正版が配布されている場合がありますので、最新版を試して見ましょう。

下記[KintoneManager](https://github.com/Arahabica/KintoneManager)はバージョン5が最新です。(2019-10-31現在)
<img width="620" alt="スクリーンショット 2019-11-04 8.48.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/4463c9bd-b86f-5847-0748-47386015a52b.png">

---

# KintoneManagerでBasic認証を使う

KintoneManagerをBasic認証に対応させるパッチを書きました。

```diff
diff --git a/KintoneManager.gs b/KintoneManager.gs
index 8dfd784..bda5c7e 100644
--- a/KintoneManager.gs
+++ b/KintoneManager.gs
@@ -1,8 +1,9 @@
 var KintoneManager = (function() {
   "use strict";
   /**
-   * user, passが指定されれば、パスワード認証
+   * auth: {user: 'user', pass: 'pass'}が指定されれば、パスワード認証
    * 指定されなければ、APIトークン認証
+   * auth: {basic: {user: 'user', pass: 'pass'}}が指定されれば、Basic認証
    * appsは以下の形式
    * {
    *    // アプリケーション名はkintoneのデータに依存せず、GAS内のコードで取り扱う専用
@@ -23,16 +24,18 @@ var KintoneManager = (function() {
    * @param {string} pass (optional) password
    * @constructor
    */
-  function KintoneManager(subdomain, apps, user, pass) {
+  function KintoneManager(subdomain, apps, auth) {
     this.subdomain = subdomain;
     this.authorization = null;
     this.apps = apps;

-    if (arguments.length > 3) {
-      this.authorization = Utilities.base64Encode(user + ":" + pass);
-    } else if (arguments.length > 2) {
-      // 引数が3つの場合はエンコード済みの認証情報として処理
-      this.authorization = user;
+    if (arguments.length === 3) {
+      if (auth.user && auth.pass) {
+        this.authorization = Utilities.base64Encode(auth.user + ":" + auth.pass);
+      }
+      if (auth.basic) {
+        this.basic = auth.basic;
+      }
     }
   }
   /**
@@ -197,15 +200,21 @@ var KintoneManager = (function() {
    * @private
    */
   KintoneManager.prototype._authorizationHeader = function(app) {
+    Logger.log('app=> "%s"', app);
+    var auth = {};
     if (this.authorization) {
       // Password authentication
-      return { "X-Cybozu-Authorization": this.authorization };
+      auth["X-Cybozu-Authorization"] = this.authorization;
     } else if (app.token) {
       // API token authentication
-      return { "X-Cybozu-API-Token": app.token };
+      auth["X-Cybozu-API-Token"] = app.token;
     } else {
       throw new Error("Authentication Failed");
     }
+    if (this.basic) {
+      auth["Authorization"] = "Basic " + Utilities.base64Encode(this.basic["user"] + ":" + this.basic["pass"]);
+    }
+    return auth;
   };
   return KintoneManager;
 })();
```

## 利用方法

1. [KintoneManagerのGitHubリポジトリ](https://github.com/Arahabica/KintoneManager)から
git clone または、ダウンロードしてソースファイルを取得します。
2. 上記パッチを当てます。
3. GASのプロジェクトにKintoneManager.gsとしてファイルを作成します。
4. プログラムから呼び出します。

## プログラムからの呼び出しについて

パッチを当てたプログラムを利用する方法です。

* ライブラリを削除します。（スクリプトエディタからリソース→ライブラリ→削除）
* ライブラリの呼び出し箇所を修正（下記参照）

### APIトークン認証＋Basic認証

```javascript
    var appid = 'アプリID';
    var token = 'アプリAPIトークン';
    var apps = {
      YOUR_APPLICATION1: {appid: appid, name: "kintone Meetup 参加者", token: token}
    };
    // 認証情報を設定
    var basicUser = 'Basic認証 ユーザーID';
    var basicPass = 'Basic認証 パスワード';
    var auth = {
      basic: {
        user: basicUser,
        pass: basicPass
      }
    };

    var manager = new KintoneManager(subdomain, apps, auth);

```

### パスワード認証＋Basic認証

```javascript
    var appid = 'アプリID';
    var apps = {
      YOUR_APPLICATION1: {appid: appid, name: "kintone Meetup 参加者"}
    };
    // 認証情報を設定
    var user = 'ログイン認証時のユーザーID';
    var pass = 'ログイン認証時のパスワード';
    var basicUser = 'Basic認証 ユーザーID';
    var basicPass = 'Basic認証 パスワード';
    var auth = {
      user: user,
      pass: pass,
      basic: {
        user: basicUser,
        pass: basicPass
      }
    };

    var manager = new KintoneManager(subdomain, apps, auth);

```

---
# Tips

## 固定値をコードの外部で管理する

<img width="554" alt="スクリーンショット 2019-11-02 9.38.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/4d4442f3-bdd2-3f18-dc23-da0a94f53fa4.png">

プロジェクトプロパティで固定値を保存して、スクリプト中に取り出して利用することができます。

* [Properties Service](https://developers.google.com/apps-script/reference/properties)


# あとがき

コピペして行けると思ったのですが、エラーで悩みました。
当たり前ですが、kintone側のフィールドの設定と全く同じにしないとデータが入らない。
kintoneのフィールドで[リンク](https://jp.cybozu.help/k/ja/user/app_settings/form/form_parts/link.html) という便利フィールドがあるのですが、ここフォームを設置した後は入力値の種類を確認出来ないし、変更も出来ないのですね。
Googleフォーム側はメールアドレス渡してるのに、kintone側はWebサイトのアドレスにしてあって、随分長い時間悩みました。

[kintone ヘルプより引用](https://jp.cybozu.help/k/ja/user/app_settings/form/form_parts/link.html)
>入力値の種類
・Webサイトのアドレス
　Webサイトのアドレスを入力する欄を設置できます。
・電話番号
　電話番号を入力する欄を設置できます。
・メールアドレス
　メールアドレスを入力する欄を設置できます。

---

# 関連リンク

## Google Apps Script関連

* [Google Apps Script](https://developers.google.com/apps-script)
* [Apps Script – Google Apps Script](https://www.google.com/script/start/)
* [Tutorials](https://developers.google.com/apps-script/articles/tutorials)
* [Basic JavaScript features](https://developers.google.com/apps-script/guides/services#basic_javascript_features)

## API

* [Utilities Service](https://developers.google.com/apps-script/reference/utilities) 文字コード変換など
* [Class Logger](https://developers.google.com/apps-script/reference/base/logger) ログ出力
* [response(Google Forms events form-submit)](https://developers.google.com/apps-script/guides/triggers/events#form-submit_1) フォーム送信時のレスポンス
* [Utilities](https://developers.google.com/apps-script/reference/utilities) 文字コード変換など
* [Event Objects](https://developers.google.com/apps-script/guides/triggers/events) トリガーイベント
* [Properties Service](https://developers.google.com/apps-script/reference/properties)

## ライブラリ

* [KintoneManager](https://github.com/Arahabica/KintoneManager)

---