Elixir on AWS LambdaからDynamoDBへアクセス

AWS LambdaのCustom RuntimeでElixirを動かし、DynamoDBのGET/PUTを試してみます。

## 環境
- Windows 10 Pro 64bit
- VSCode 1.38.1
- Docker Desktop for Windows 2.1.0.4
- Docker Engine 19.03.4

## 事前準備
[こちら](https://qiita.com/ansoposKAZ/items/d1a1924fc34affbd30b5)で作った環境をベースに作業を進めます。

## 参考資料
[alertlogic社のサンプルコード](https://github.com/alertlogic/erllambda_elixir_example/blob/master/lib/erllambda_elixir_example.ex)をベースに都度試しながら進めます。

## 環境変数の読込
AWS Lambdaの環境変数に設定したキーに対する値をログに出力します。

- `lib/lambda_ex.ex`
	- 以下の通り変更を加え、`MIX_ENV=prod mix erllambda.release`を実行しリリース用モジュールをリビルド。出来上がったzipファイルをAWS Lambdaへ反映
		- ログへの環境変数の内容出力を追加

```diff
defmodule LambdaEx do

  def handle(event, context) do
    :erllambda.message("event: ~p", [event])
    :erllambda.message("context: ~p", [context])
+    :erllambda.message("envName: ~p", [System.get_env("ENV_NAME")])

    {:ok, response({:ok, [[{"result", "OK"}]]})}
  end

  defp items_to_json(items) do
    items
    |> Enum.map(&:maps.from_list/1)
    |> :jiffy.encode
  end

  defp response({:ok, response}) do
    %{
      statusCode: "200",
      body: items_to_json(response),
      headers: %{
        "Content-Type": "application/json"
      }
    }
  end

  defp response({:error, response}) do
    %{
      statusCode: "400",
      body: response,
      headers: %{
        "Content-Type": "application/json"
      }
    }
  end
end
```
- AWS Lambdaの環境変数に値を設定
	- `ENV_NAME` : `hoge`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48472/7a84cee7-86fa-8ed7-e45c-95648248f01d.png)

- 実行結果
	- CloudWatch Logsでログに`envName: hoge`が出力されていることを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48472/9f83256f-242c-9496-ed0d-64e5fdfe9ab9.png)

## DynamoDBからGET
- テスト用のテーブルを作成
	- テーブル名：`TEST_ITEMS`
	- プライマリキー：`code`(string)
	- 設定：デフォルト

- テスト用のレコードを作成
	- `{"code": "AAA", "name": "サンプル１", "type": "xxx"}`
	- `{"code": "BBB", "name": "サンプル２", "type": "yyy"}`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48472/79c16ed8-cba9-aa62-f5d5-3be62ae5f945.png)

- `lib/lambda_ex.ex`
	- 以下の通り変更を加え、モジュールリビルド＆AWS Lambdaへ反映
		- 環境変数からテーブル名取得を追加
		- テーブルからの全件取得を追加
		- テーブルから取得した値をレスポンスに追加

```diff
defmodule LambdaEx do

  def handle(event, context) do
    :erllambda.message("event: ~p", [event])
    :erllambda.message("context: ~p", [context])
+    # :erllambda.message("envName: ~p", [System.get_env("ENV_NAME")])

+    table_name = System.get_env("TABLE_NAME")
    
+    result = scan_all(table_name)

+    # {:ok, response({:ok, [[{"result", "OK"}]]})}
+    {:ok, response(result)}
  end

+  defp scan_all(table_name) do
+    :erlcloud_ddb_util.scan_all(table_name)
+  end
  
  defp items_to_json(items) do
    items
    |> Enum.map(&:maps.from_list/1)
    |> :jiffy.encode
  end

  defp response({:ok, response}) do
    %{
      statusCode: "200",
      body: items_to_json(response),
      headers: %{
        "Content-Type": "application/json"
      }
    }
  end

  defp response({:error, response}) do
    %{
      statusCode: "400",
      body: response,
      headers: %{
        "Content-Type": "application/json"
      }
    }
  end
end

```

- AWS Lambdaの環境変数に値を設定
	- `TABLE_NAME` : `TEST_ITEMS`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48472/85dc31eb-b7ba-6a34-c71c-7840a7c53a2a.png)

- 実行結果
	- 実行詳細のbodyにDynamoDBの値が出力されていることを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48472/628aa341-ae54-9d2c-8631-a2c1cd01edd9.png)

## DynamoDBへPUT

- `lib/lambda_ex.ex`
	- 以下の通り変更を加え、モジュールリビルド＆AWS Lambdaへ反映
		- AWS LambdaのeventパラメータをそのままDynamoDBへPUTする処理を追加

```diff
defmodule LambdaEx do

  def handle(event, context) do
    :erllambda.message("event: ~p", [event])
    :erllambda.message("context: ~p", [context])
    # :erllambda.message("envName: ~p", [System.get_env("ENV_NAME")])

    table_name = System.get_env("TABLE_NAME")

+    put_item(event, table_name)

    result = scan_all(table_name)

    # {:ok, response({:ok, [[{"result", "OK"}]]})}
    {:ok, response(result)}
  end

  defp scan_all(table_name) do
    :erlcloud_ddb_util.scan_all(table_name)
  end

+  defp put_item(item, table_name) do
+    :erlcloud_ddb2.put_item(table_name, :maps.to_list(item))
+  end

  defp items_to_json(items) do
    items
    |> Enum.map(&:maps.from_list/1)
    |> :jiffy.encode
  end

  defp response({:ok, response}) do
    %{
      statusCode: "200",
      body: items_to_json(response),
      headers: %{
        "Content-Type": "application/json"
      }
    }
  end

  defp response({:error, response}) do
    %{
      statusCode: "400",
      body: response,
      headers: %{
        "Content-Type": "application/json"
      }
    }
  end
end

```

- テストイベントのペイロードにDynamoDBへ登録する値を設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48472/f31ec64d-7397-862e-f368-1942896363c1.png)

- 実行結果
	- 実行詳細のbodyに新規追加したレコードの値が出力されていることを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48472/8f6f3ac3-8ff8-6e1a-3df7-53fc2e07e91f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48472/dde8e457-1956-91ad-d888-bff463ba2de5.png)


## 所感
[erlcloud](https://github.com/gleber/erlcloud)を使うことで簡単にAWSのリソースへアクセスすることができました。また、Docker + VSCodeのリモートアクセス機能を使うことでビルド～反映が簡単になりました。あとは[localstack](https://github.com/localstack/localstack)である程度動作確認してから反映できればより良いと思います。