自前フォームをGoogle フォームとS3を利用してサーバーレスに構築する

この記事は、[Gaiax Group Advent Calendar 2018](https://qiita.com/advent-calendar/2018/gaiax-group) の14日目の記事です。

## はじめに
Gaiaxでは年に2回社員全員が集まり会社の状況や方針について確認し、各事業の成果などを共有する社員総会というものがあります。
今回の社員総会では、「Gaiax Most Valuable Story」と称して、一緒に目標を達成した仲間や、お世話になった人に感謝の手紙を書くという取り組みを行うことになりました。
この一連のシステム開発について書いて見ようと思います。

実現したいことは以下のような感じです。

- それっぽいフォームで手紙を書くことができる
- 宛名、宛先メールアドレス、差出人名、差出人メールアドレス、メッセージが入力できる
- フォーム送信時
  - 内容を保存する
  - 宛先へ手紙が書かれたことを通知（本文は後日送信)
- 社員総会時に手紙の内容をメールで送信

今回はこの要件のうち、フォーム送信時までを次の仕組みで実現してみます。
(社員総会時に手紙の内容をメールで送信はまたのちほど）

- それっぽい自前フォームの送信先をGoogle フォームにする
- 送信をトリガーにして、Google App Scriptで宛先へ手紙が書かれたことを通知する
- 自前のフォームはS3で静的ホスティングする

## Google フォームの作成
まずは、Google フォームを作成します。こんな感じで、名前とメールアドレスは記述式、メッセージは段落で作成しました。全てのデータが必要なのですが、Google フォームで必須チェックにかかってもそれをキャッチできないので、ここでは必須項目の設定をせずフロント側で対応します。
<img width="798" alt="スクリーンショット 2018-12-14 23.20.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/42222/a04d5dfc-47a9-c9ae-fe11-3d93700d051a.png">

この時フォームの送信からリンクを取得し、回答画面で`form`タグにある`action`と各入力項目の`name`をメモしておきます。(末尾を変更しています)

- action
<img width="1139" alt="スクリーンショット 2018-12-14 23.30.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/42222/dd99eb4e-2fbf-fffb-7fd9-4d0b28accac2.png">

- name
<img width="1138" alt="スクリーンショット 2018-12-14 23.33.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/42222/990e8986-1aa1-9b4a-2b7b-4acec8b0adf6.png">

inspecterで要素を選ぶと見つけやすいです。

## 自前のフォームの作成
続いて自前のフォームを作成します。こんな感じに普通のフォームを作成、先ほどメモした`action`と各入力項目の`name`を設定します。
これで自前フォームからのPOSTをGoogle フォームで受け取ることができるようになりました。

```html:form.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>感謝の手紙</title>
    <link rel="stylesheet" type="text/css" href="./assets/css/style.css">
  </head>
  <body>
    <div id="formWrapper">
      <form action="https://docs.google.com/forms/u/1/d/e/1FAIpQLSd2zHuravWpQfdS0fwUiaPD6r2l8TQN-yzArPDfXXXXXXXXXX/formResponse">
        <div class="letter">
          <!-- 宛先と宛名 -->
          <div class="toEmail">
            <input id="toEmail" type="email" name="entry.214573XXXX" placeholder="宛先メールアドレスを入力" required/>
          </div>
          <div class="toName">
            <input id="toName" type="text" name="entry.119565XXXX" placeholder="宛名を入力" required/>
          </div>

          ~~ 省略 ~~

        </div>
      </form>
    </div>
  </body>
</html>
```
バリデーションについては厳密なバリデーションは必要ないため、`required`を利用してフロント側で対応しました。こちらも紆余曲折ありましたが、また別の機会で。

## S3での静的ホスティング
今回は、ページ数も少なく動的な要素もないためAmazon AWSのS3を利用して静的ホスティングで配信してみます。
まずはAWSのコンソールからS3バケットを作成します。バケット名を決めて、次へ進みます。
<img width="1104" alt="スクリーンショット 2018-12-15 0.25.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/42222/112759c8-9469-cd92-751b-2f321e72f59c.png">

オプションの設定はなにも変更せず進み、アクセス許可の設定で「このバケットのパブリックバケットポリシーを管理する」内にある2つの項目のチェックを外します。（後ほどバケットポリシーを追加したら、`新規のパブリックバケットポリシーをブロックする (推奨)`はチェックを戻します。）
これでバケットが作成できました。
<img width="1107" alt="スクリーンショット 2018-12-15 0.40.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/42222/01d36854-4b84-8c44-38fe-749458193fcb.png">

続いてバケットポリシーで、匿名ユーザーへの読み取り専用アクセス許可の付与をします。`examplebucket`は作成したバケット名を設定します。

```json:バケットポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::examplebucket/*"
        }
    ]
}
```

これで、S3でのバケット公開設定が完了しました。
先ほど作成した自前フォームのファイルをアップロードし、ファイルの概要内にあるリンクをクリックするとフォームが閲覧できると思います。

## Google Apps Script(GAS)でのメール通知
さて次は、フォームでメッセージが送信された際に宛先メールアドレスに対して手紙が書かれたことを通知したいと思います。メッセージの内容はここではあえて記載せず、社員総会後にまとめて送信する形です。

GASをこんな感じに書きます。通知メールは雰囲気をだしたかったので、HTMLメールにし、HTMLは外部ファイルから読み込んでいます。
受け取った宛先メールアドレスを`address`に入れ`to`に設定します。また、返信されても困るので、`noreply`オプションをつけています。

```javascript:notify.js
function submitForm(e){
  FormApp.getActiveForm();
  
  var itemResponses = e.response.getItemResponses();
  var address = itemResponses[0].getResponse();
  var html = HtmlService.createHtmlOutputFromFile("message").getContent();

  MailApp.sendEmail({
    to: address,
    subject: 'Gaiax Most Valuable Story からのお知らせ',
    body: '感謝の手紙\n\n誰かがあなたに感謝の手紙を送ったようです。\n送られた手紙は来年の社員総会後に届きます。\n今年１年を振り返り、あなたも誰かに感謝の手紙を書いてみませんか？\n\nGaiax MVS運営', 
    htmlBody: html,
    noReply: true
  });
}
```
よくある例には`FormApp.getActiveForm();`の記載はないのですが、これを書いて権限の承認をしないと、メール送信ができません。他の部分が動かなくてもいいので、これを書いたらスクリプトエディタで実行し、権限の承認をしておきます。


```html:message.html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <style type="text/css">
    p,h1 {text-align:center}

    a {
      text-align: center;
      display: block;
      margin: 50px auto;
      width: 200px;
    }

    .content {
      margin: 10px auto;
      width: 350px;
    }
    </style>
  </head>
  <body>
    <h1>感謝の手紙</h1>
    <div class="content">
      <p>誰かがあなたに感謝の手紙を送ったようです。</p>
      <p>送られた手紙は1月の社員総会後に届きます。</p>
      <p>今年1年を振り返り、あなたも誰かに感謝の手紙を書いてみませんか？</p>
      <a href="https://s3-ap-northeast-1.amazonaws.com/XXXXXXXX/index.html" class="square_btn" style="text-align: center;display: block;margin: 50px auto;width: 200px;padding: 0.3em 1em;text-decoration: none;background-color: #FFA700;color: #FFF;border: solid 2px #FFA700;border-radius: 3px;transition: 0.4s">手紙を書く</a>
    </div>
  </body>
</html>
```

簡単なCSSでしたので、ファイル内に記載しています。Gmailで受信テストをしていたのですが、Gmailでは一部のstyle要素が落とされてしまうためインラインで指定をしてたりします。

続いてトリガーの設定です。リソースを`フォームから`に設定し、イベントの種類を`フォーム送信時`に設定します。
<img width="731" alt="スクリーンショット 2018-12-15 1.12.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/42222/586b6e8e-6073-136c-2166-83c31327f916.png">

これで、手紙送信時に宛先へメールでの通知が自動送信されるようになりました。

そしてこんな感じのできあがりになりました。自分で言うのもあれですが、なかなかそれっぽいフォームになったと思います :clap:
[![Image from Gyazo](https://i.gyazo.com/0b008c3193b5e81b0a1f55679c05275a.gif)](https://gyazo.com/0b008c3193b5e81b0a1f55679c05275a)

<img width="644" alt="スクリーンショット 2018-12-15 1.30.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/42222/ecfa104b-217f-4de3-4247-4ae08add128b.png">

## まとめと感想
長くなってしまいましたが以上となります。
普段あまり実装をしないので、なかなか時間がかかってしまいましたが、たのしく構築することができました。間違いなどあれば、ご指摘していただけると嬉しいです。
今回書ききれなかったことは、また別の記事にでもしようと思います。ここまで読んでいただき、ありがとうございました。

それではよいクリスマスをお過ごしください。
:christmas_tree: Happy Merry X'mas!! :tada: