API Gateway を使って Amazon SESでメールを送信する

## はじめに
[AWS Lambda と API GatewayでRails使わずに済んだ話 // Speaker Deck](https://speakerdeck.com/ryonext/aws-lambda-to-api-gatewayderailsshi-wazuniji-ndahua) をスライド公開したら意外とはてブ付いてたので、手順をQiitaに書いといた方が活用されやすいかなと思ったので書きます。

## ステップ
以下の手順で構築していくことになります。

1. AWS Lambda に実行したいソースを書く
2. AWS API Gatewayの設定をする
3. 実際に呼び出す


## 1. Lambdaに実行したいソースを書く

今回は実際に私が利用したPythonでの実装例を書きます。フォームで入力を受け付け、運営側には入力された内容のメールを、ユーザには自動応答のお礼メールを送る、という内容です。

* 新規作成 > microservice-http-endpoint-pythonを使用
* 既存のコードはAmazon DynamoDBに入れるコードになっているのを以下のように修正

```
import boto3
import json

ADMIN_EMAIL = "hoge@example.com"

def send_email(to, reply, subject, body):
    client = boto3.client('ses', region_name='us-east-1')

    response = client.send_email(
        Source=ADMIN_EMAIL,
        Destination={
            'ToAddresses': [
                to,
            ]
        },
        Message={
            'Subject': {
                'Data': subject,
            },
            'Body': {
                'Text': {
                    'Data': body,
                },
            }
        },
        ReplyToAddresses=[
            reply,
        ],
        ReturnPath=ADMIN_EMAIL
    )

def send_mail_from_user(email, message):
    send_email(ADMIN_EMAIL, email, "From user", message)

def send_auto_reply_mail(email):
    send_email(email, ADMIN_EMAIL, "自動返信の件名", "自動返信の内容")

def lambda_handler(event, context):
    email = event["email"]
    message = event["message"]
    send_mail_from_user(email, message)
    send_auto_reply_mail(email)
```

lambda_handlerがリクエストを受け付け、そこのeventにrequest parameterが入っています。このparameterを取得して、それぞれ運営側とユーザにメールを送っています。SESを利用してメールを送るのは、pythonであればboto3を使えば簡単にできます。

### IAMの設定
LambdaからSESを呼べるようにするために、以下のようにIAMの設定が必要です。


```
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    },
   {
      "Effect": "Allow",
      "Action": [
        "ses:*"
      ],
      "Resource": "*"
    }
  ]
}
```

## 2. AWS API Gatewayの設定をする

作成したLambdaのページからendpointが追加可能です。

![409be366-2e4e-3938-9301-0c115f8cf36e.png](https://qiita-image-store.s3.amazonaws.com/0/5551/2e788525-9d61-b2b8-9765-d7a4c3b51668.png "409be366-2e4e-3938-9301-0c115f8cf36e.png")

Securityの設定を行う必要があります。

* AWS IAM
* Open
* Open with access key

があります。

IAMは特定のEC2インスタンスから呼ばれる用途のAPIに使うもの。Open with access keyは、キーを埋め込んだ特定のクライアントから呼べるものなのでスマホクライアントアプリ向けだと思われます。

Javascriptから呼ぶ場合はOpenまたはOpen with access keyにするしかないかな、と思います。(access keyにしても見える場所に置く必要があるのであまり意味ありませんが。）

### API Gateway側での設定画面

設定画面はこのようになっておりModelを追加したりもできますが、今回の範囲では特に必要ないです。

![e2fa6198-4765-51bb-6b2e-b3f2c9824e39.png](https://qiita-image-store.s3.amazonaws.com/0/5551/179a1824-8543-0a9f-5fde-1c1e4f33aa5b.png "e2fa6198-4765-51bb-6b2e-b3f2c9824e39.png")

## 3. 実際に呼び出す

作成されたAPIのendpointに、設定されたセキュリティ要件が満たされた状態でアクセスするだけでOKです。

一点、Javascriptから呼び出す場合、異なるドメインのAPIを呼べないというクロスオリジン問題があります。これの対策として、OPTIONSリソースを追加して、クロスオリジンを許可する設定をする必要があります。この対応としてはこちらに詳しく書かれています。 

[Amazon API Gateway をクロスオリジンで呼び出す (CORS) ｜ Developers.IO](http://dev.classmethod.jp/cloud/aws/amazon-api-gateway-cors/)

### ハマったポイント

クロスオリジン対応でOPTIONSの設定項目にPOSTと同じURLを入れたところ、たまにリクエストに成功し（体感1~2割程度）、高確率でクロスオリジンエラーが出るという現象が起きました。
OPTIONSの選択肢に、Mock Endpointというものがあるので、それを選ぶのが無難だと思います。