CloudFront経由でDynamoDBのデータを更新する

### 別サイト
に書いていたのだけどサ終してしまったので移行した記事その4
2019/03/09のもの

## サーバレスでDynamoDBにデータを入れる
EC2インスタンスを立てたら負けってどこかに書いてあったのでなしでどうにかやってみる


### DynamoDBにテーブルを作成
なんでもいいのでIDとアドレスを入れるやつを適当に作る
テーブル名はregisterにする

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48480/f48c66ea-2d2a-b4fc-bd4d-7aee916c1a92.jpeg" width="70%">


### LambdaからDynamoDBを操作するIAMを作成
Lambdaで使うためのIAMを作る
名前は
lambda-dynamodb-execution-role
とでもして権限を付与。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48480/c8e4b340-c264-d375-0ab1-a763dbc065ca.jpeg" width="70%">

### Lambdaを作成
テーブルに対して検索と投入をするLambdaを作成
ブランクで作成し、Node.js 8.10を選択
名前は適当にregisterInsertFuncとregisterSelectFuncとでもする
実行ロールは先程作成したやつを設定

```
## insert
var aws = require('aws-sdk');
var dynamo = new aws.DynamoDB.DocumentClient({region: 'ap-northeast-1'});

exports.handler = (event, context, callback) => {
    console.log("event:", event);
    dynamo.put({
        "TableName": "register",
        "Item": {
            "event_id": event.event_id,
            "address": event.address
        }
    }, function( err, data ) {
        console.log("dynamo_err:", err);
        context.done(null, data);
    });
};
```

```
## select
var aws = require('aws-sdk');
var dynamo = new aws.DynamoDB.DocumentClient({region: 'ap-northeast-1'});

exports.handler = function (event, context) {

    var params = {
        TableName : 'register',
        KeyConditionExpression: "event_id = :event_id and address = :address",
        ExpressionAttributeValues: {
            ":event_id": event.event_id,
            ":address": event.address
        }
    };

    dynamo.query(params, function(err, data) {
        console.log("dynamo_data:", data);
        console.log("dynamo_err:", err);
        context.done(null, data);
    });
};
```

テスト用のパラメータは下記

```
{
  "event_id": "101",
  "address": "hogehuga@hoge.jp"
}
```

### API Gatewayの作成
Lambdaを呼び出すためのAPI Gatewayを作成する
/eventリソースを作成してメソッドの作成でGET,POSTを作成する（PUTもあるが気にしない）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48480/b3711a23-44ae-7d85-71df-d34f6991bcc3.jpeg" width="20%">


- メソッドリクエストにパラメータ
- 統合リクエストにマッピングテンプレート
- メソッドレスポンスのコンテンツタイプをapplication/json;charset=UTF-8
をそれぞれ設定
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48480/441ca5c9-4e94-8fa3-0ac2-6c0112bd9972.jpeg" width="80%">


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48480/5e1acde6-5e91-8dca-8bc2-30a462b135b5.jpeg" width="80%">

```
{
    "event_id": "$input.params('event_id')",
    "address": "$input.params('address')"
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48480/49b94f13-963d-8201-dbf9-6b1ec45bf692.jpeg" width="40%">

/eventリソースに対してアクションからCORSの有効化をする
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48480/6bbdf7aa-0021-d223-b38f-8e7a4ecd511f.jpeg" width="20%">

ここまで作業できたらAPIのデプロイを行って反映

### 一旦確認
ここまできたらAPI GatewayのURLが生成されるのでcurl等で確認ができる

```bash
$ curl -X GET https://[API GatewayのURL]\?event_id\=102\&address\=hogehoge
{"Items":[],"Count":0,"ScannedCount":0}
$ curl -X POST https://[API GatewayのURL]\?event_id\=102\&address\=hogehogehoge
{}
```

### S3にコンテンツ配置
S3からAPI Gatewayを呼び出すためにhtmlを置く
event-registerというバケットを適当につくってトップにindex.htmlを配置
API Gatewayからライブラリをダウンロードして配置ってのもあったけどjQueryだけでいけたのでそっちにしてみた
PUTメソッドはなんかうまく動かなかったので諦め。。。（PUTメソッドがあったのはそのため）

```html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>API test</title>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
</head>
<body>
<input type="text" id="addId" value="102" />
<input type="text" id="addAddress" value="hogehogehoge" />
<button id="add">追加</button>
<br>
<input type="text" id="searchId" value="102" />
<input type="text" id="searchAddress" value="hogehogehoge" />
<button id="search">検索</button>

<script type="text/javascript">
$('#add').click(function(){
  var addId = $('#addId').val()
  var addAddress = $('#addAddress').val()
  $.ajax({
        url: "https://[API GatewayのURL]?event_id=" + addId + "&address=" + addAddress,
        type: 'POST',
        crossDomain: true,
        dataType: 'json',
        contentType: "application/json",
        success: function (data) {
            console.info(data);
        }
    });
});
$('#search').click(function(){
  var searchId = $('#searchId').val()
  var searchAddress = $('#searchAddress').val()
  $.ajax({
        url: "https://[API GatewayのURL]?event_id=" + searchId + "&address=" + searchAddress,
        type: 'GET',
        crossDomain: true,
        dataType: 'json',
        contentType: "application/json",
        success: function (data) {
            console.info(data);
        }
    });
});
</script>
</body>
</html>
```
こんな感じの画面が出てボタンを押してエラーがでなければOK
![c032f3500c9b956b9c859e1f803d943f.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48480/fbb8949d-8fb6-32ba-bdc1-1cd95be64899.jpeg)


### CloudFront経由でS3へアクセス
CreateDistributionでS3のevent-registerを指定

- Restrict Bucket AccessをYes
- Origin Access IdentityをCreate a New Identity
- Grant Read Permissions on BucketをYes

に設定して作成
S3のバケットポリシーが更新されているのを確認してアクセスコントロールからEveryOneの権限をoffにする
これでCloudFront経由からのみS3にアクセスできるようになった
https://[CloudFrontで作成したURL]/index.html
で先程と同じ画面が出れば成功

### 感想
実際書いてみるとそんなに作業はないけど結構ハマった。。。
特にPUTメソッドのときだけSameオリジンポリシー制約にハマって結局わからなかった。。。
これが作成できると何ができるのって考えると
例えば事前登録サイトとかぐらいならhtmlさえちゃんと作ればだいたい作れそうだなーと思った
そうなるとインスタンス立ててサーバエンジニアが色々やることもなくてハッピーかな