GoogleAppsScriptを使ってGoogleFormsのWeb APIをつくる

## 今回のおはなし

アンケート機能は自分で実装したくないけど、
GoogleFormsの画面はアンケート回答画面に使いたくないから、
GoogleFormsのWeb APIをGoogleAppsScriptで作成して、
WebAPI経由でGoogleFormsにアクセスできるようにしたおはなし

## アンケート機能作るのつらい問題

アプリケーション内でアンケートを実装したい場合はみなさんどのように実装しているでしょうか？
既存のライブラリやgemを使用したり、フルスクラッチで要件にあったものを実装する等あると思います。
ですがアンケート機能というのは実装するとなると難しいものです。(ライブラリやgemを使う場合も含めて)

どんな点で難しいかというと
アンケートの質問はラジオボタンやチェックボックス、テキストなどの形式に対応する必要があるが質問や回答データはどのような形式でどのように紐付けて保存するか？
という点です。

アンケート機能とは質問の形式、数がその時々で異なるせいで
データの置き場をRDBMSにするとカラム定義が難しく、
KVSにすると管理が難しかったりと
集計することも考えるとつらい実装になることが多いのではないでしょうか。

GoogleFormsやSurveyMonkey等を使用すれば上記の難しい点は全てサービス側が肩代わりしてくれるため、「アンケートサービスを使用しよう！」で終われればいいのですが、

+ 回答するには各サービスが用意している回答ページへ遷移しないと回答できない(回答ページからは遷移元ページへは戻れない)
+ 回答画面のデザインはいじれず、表示されるページデザインは各サービスの色が強い

等という点でアプリケーションからはあまり使用したくありません。

### 各サービスのフォーム例
#### GoogleForms
<img width="425" alt="無題のフォーム.png" src="https://qiita-image-store.s3.amazonaws.com/0/21017/c1399c60-e8f1-619d-605b-da9685f5abe8.png">
#### SurveyMonkey
<img width="425" alt="商品に対する好み_Survey.png" src="https://qiita-image-store.s3.amazonaws.com/0/21017/5c3fbc9c-156b-7899-28c3-fd3913774df9.png">

アンケート機能の実装はGoogleFormsへ任せて、UIはアプリケーション側で実装するというのが理想的ですがそのようには出来ないでしょうか。。  

というわけで今回は
「GoogleAppsScriptを使用してGoogleFormsのWeb APIを作成する」お話です。

## GoogleFormsのWeb APIをGoogleAppsScriptで作成する

### 1. フォームの作成

GoogleFormsでフォームを作成しましょう！

### 2. GoogleAppsScriptの作成

次にGoogleFormのAPIとして動くGoogleAppsScriptを作成します。
GoogleAppsScriptのコードエディタ画面へは下記のようにGoogleFormsの回答結果一覧が表示されるGoogleSpreadSheetから遷移が可能です。

GoogleFormsのWeb APIとして動くGoogleAppsScriptは下記のようなコードです。  

```javascript
function doGet(e) {
  var form = FormApp.openById(e.parameter.form_id);
  var items = form.getItems();

  var result = {form_id: form.getId(), items: []};
  for (var i = 0; i < items.length; i++) {
    var item = items[i];
    var itemId = item.getId();
    var itemTitle = item.getTitle();
    var itemType = String(item.getType());

    switch (itemType) {
      case 'TEXT':
        var item = item.asTextItem();
        break;
      case 'MULTIPLE_CHOICE':
        var item = item.asMultipleChoiceItem();
        break;
      case 'LIST':
        var item = item.asListItem();
        break;
      case 'CHECKBOX':
        var item = item.asCheckboxItem();
        break;
    }

    var itemChoices = [];
    if (itemType !== 'TEXT') {
      var choices = item.getChoices();
      for (var j = 0; j < choices.length; j++) {
        itemChoices[j] = {value: choices[j].getValue()};
      }
    }

    result.items[i] = {id: itemId, title: itemTitle, type: itemType, choices: itemChoices};
  }

  return ContentService.createTextOutput(JSON.stringify(result)).setMimeType(ContentService.MimeType.JSON);
}

function doPost(e) {
  var responseObject = getResponseObject(e.parameters);
  var form = FormApp.openById(responseObject.formId);

  var itemResponses = [];
  for (var i = 0; i < responseObject.responseItems.length; i++) {
    var responseItemObject = responseObject.responseItems[i];
    var item = form.getItemById(responseItemObject.responseItemId);
    var itemType = String(item.getType());

    switch (itemType) {
      case 'TEXT':
        item = item.asTextItem();
        break;
      case 'MULTIPLE_CHOICE':
        item = item.asMultipleChoiceItem();
        break;
      case 'LIST':
        item = item.asListItem();
        break;
      case 'CHECKBOX':
        item = item.asCheckboxItem();
        break;
    }

    itemResponses.push(item.createResponse(responseItemObject.responseItemValues));
  }

  var formResponse = form.createResponse();
  for (var i = 0; i < itemResponses.length; i++) {
    formResponse = formResponse.withItemResponse(itemResponses[i]);
  }
  formResponse = formResponse.submit();

  return ContentService.createTextOutput(JSON.stringify({form_id: formResponse.getId()})).setMimeType(ContentService.MimeType.JSON);
}

function getResponseObject(paramObject) {
  var result = {formId: '', responseItems: []};
  result.formId = paramObject.form_id[0];

  Object.keys(paramObject).forEach(function (key) {
    var matchs = (/([\w$]*)\[([\w$]*)\]/).exec(key);

    if (matchs !== null && matchs[1] === 'response_items') {
      var responseItemObject = {responseItemId: matchs[2], responseItemValues: paramObject[key]};
      result.responseItems.push(responseItemObject);
    }

  });

  return result;
}
```
実装されているコードについて順に説明していきます。

#### 2.1 GoogleFormsのアンケート内容を取得するAPIの作成

GoogleFormsのアンケート内容を取得するコードは下記の部分です。

```javascript
function doGet(e) {
  var form = FormApp.openById(e.parameter.form_id);
  var items = form.getItems();

  var result = {form_id: form.getId(), items: []};
  for (var i = 0; i < items.length; i++) {
    var item = items[i];
    var itemId = item.getId();
    var itemTitle = item.getTitle();
    var itemType = String(item.getType());

    switch (itemType) {
      case 'TEXT':
        var item = item.asTextItem();
        break;
      case 'MULTIPLE_CHOICE':
        var item = item.asMultipleChoiceItem();
        break;
      case 'LIST':
        var item = item.asListItem();
        break;
      case 'CHECKBOX':
        var item = item.asCheckboxItem();
        break;
    }

    var itemChoices = [];
    if (itemType !== 'TEXT') {
      var choices = item.getChoices();
      for (var j = 0; j < choices.length; j++) {
        itemChoices[j] = {value: choices[j].getValue()};
      }
    }

    result.items[i] = {id: itemId, title: itemTitle, type: itemType, choices: itemChoices};
  }

  return ContentService.createTextOutput(JSON.stringify(result)).setMimeType(ContentService.MimeType.JSON);
}
```

##### doGetメソッドについて

doGetメソッドとはGoogleAppsScript内で使用できるHTML Serviceの中で定義されている関数です。
(参照： [HTML Service: Create and Serve HTML](https://developers.google.com/apps-script/guides/html/))  

doGetメソッドはGoogleAppsScriptに与えられたエンドポイントにGETメソッドでアクセスした場合に実行されるメソッドです。
(参照: [Serve HTML as a web app](https://developers.google.com/apps-script/guides/html/#serve_html_as_a_web_app))

##### FormAppクラスについて

FormAppクラスとはGoogleAppsScript内で使用できる[Forms Service](https://developers.google.com/apps-script/reference/forms/)の中で定義されているクラスです。
(参照： [Class FormApp](https://developers.google.com/apps-script/reference/forms/form-app))

このクラスを使用するとGoogleFormsの状態を取得したり、操作が行えます。

```javascript
var form = FormApp.openById("GoogleFormsのID");
```
というふうに指定するとIDで指定したGoogleFormsをオブジェクトとして取得できます。
取得したオブジェクトに対し各種操作を行い、最終的には

```json
{
  "form_id": "1WtY8YsGAyxAMKFGXk6xbr587_2pqMtuTL8NsZzWmw8I", // 対象のGoogleFormsID
  "items": [
    {
      "id": 204147741, // 対象のGoogleForms内に定義されている質問ID
      "title": "朝起きる時間は何時ですか？",
      "type": "MULTIPLE_CHOICE", // 質問の種類
      "choices": [
        {
          "value": "8:00"
        },
        {
          "value": "9:00"
        },
        {
          "value": "10:00"
        }
      ]
    }
  ]
}
```
といったフォーム内容をJSON形式で返しています。

![request.gif](https://qiita-image-store.s3.amazonaws.com/0/21017/39cd1f1b-95a7-acb1-4b4b-af6f4cfe5e5e.gif)


↓ すみません。。間に合わなかったので12/8までにはアップデートします。。。 mm

#### 2.2 GoogleFormsへアンケート回答結果を登録するAPIの作成

### 3. GoogleAppsScriptの公開方法

### 4. アンケート結果の集計方法

## まとめ