AWSサーバレスアーキテクチャで文章解析サービスを構築する　その３

# はじめに

本記事はAWSで提供されているサーバレスサービスを使用し、文章の感情分析および翻訳を行うサービスを構築するハンズオンです。
全３記事の構成となっており、本記事は第３回となります。

- [AWSサーバレスアーキテクチャで文章解析サービスを構築する　その１](https://qiita.com/kenryo/private/a47eaf8195e30f6a8182)
- [AWSサーバレスアーキテクチャで文章解析サービスを構築する　その２](https://qiita.com/kenryo/private/c9f0fe7b2f6bdf765412)
- AWSサーバレスアーキテクチャで文章解析サービスを構築する　その３

構築するアーキテクチャ、使用するサービスなどについては[第１回目の記事](https://qiita.com/kenryo/private/a47eaf8195e30f6a8182)を参照ください。

:::note warn
注意①
本資料内スクショのAWS管理コンソールのデザインは資料作成当時のものです。
デザインは今後変更される可能性があり、スクショと差異がある場合がありますのでご了承ください。
:::

:::note warn
注意②
今までのハンズオン（[前提条件・知識](#前提条件・知識)に記載）で実施した手順は基本省略して記載するのでご了承ください。
複雑な手順等は例外として記載する場合があります。
:::

:::note warn
注意③
ハンズオンで作成したリソースについてはハンズオン終了後、各自削除をお願いします。
:::

## ゴール

- Amazon API Gateway（以降、API Gatewayと略）の概要が理解できている
- Amazon DynamoDB（以降、DynamoDBと略）の概要が理解できている
- REST APIの概要が理解できている
- リレーショナルデータベースとNoSQLデータベースの違いが理解できている
- API GatewayでREST APIを作成し、実行できる
- ステートマシンからDynamoDBにデータを格納する処理を作成できる

## 前提条件・知識

- [前回ハンズオン](https://qiita.com/kenryo/private/c9f0fe7b2f6bdf765412)を実施していること

## 使用サービス紹介

本ハンズオンで触るAWSサービスを説明します。
その他サービスについては以降その都度説明いたします。

### Amazon API Gateway

Amazon API Gatewayとは、AWSが提供する、`API`の作成および管理を行うことができる`フルマネージドサービス`です。
作成したAPIはAmazon S3や`AWS Lambda`などのAWSサービスと簡単に統合することができ、バックエンドとして利用することができます。
また、HTTPSリクエストを受け付けるAWS以外のサービスにもリクエストを転送することができます。
名前の通りAPIのゲートウェイ（入り口）となるサービスです。
`2015年7月`に一般公開されました。

以下の図は[外部から AWS のバックエンドサービス利用を実現する仕組みをグラレコで解説](https://aws.amazon.com/jp/builders-flash/202004/awsgeek-api-gateway/?awsf.filter-name=*all)より

![yougo_apigateway.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/05932e6c-2dc5-13ce-2e6f-523bf2fa4622.png)

#### 用語

|用語|意味|
|---|---|
|API|Application Programming Interfaceの略。主にソフトウェアやプログラム同士を定められたルールに従ってつなぐもののことを指す|
|Web API|HTTPなどのWeb技術によってやりとりするAPI|
|REST API|REST（Representational State Transfer）の原則に従って作成されたWeb API。この設計原則は現在主流であるが、[GraphQL](https://www.sbbit.jp/article/cont1/93872)を使用したAPIが注目を集めている|
|マネージドサービス|サービスで使用するサーバの管理（セキュリティ、可用性、障害対応など）の一部を委託できるサービス|
|フルマネージドサービス|マネージドサービスで提供している内容に加え、より細かく管理業務を委託することができるサービス|
|サーバレスサービス|サーバの設置や管理不要（サーバを意識しない）で利用できるサービス|
|AWS Lambda|サーバの設置や管理不要でコードを実行できるサーバレスサービス（コンピューティングサービス）|

#### REST APIの４原則について

REST APIとは先ほど説明した通り、REST（Representational State Transfer）の原則に従って作成されたWeb APIを指します。
この原則は大きく4つに集約されます。

- 原則1：ステートレス
  - 過去行ったHTTP通信による状態を記憶しておく必要がありません。
  - HTTP通信のリクエストのメッセージに全て必要な情報が含まれており、各HTTP通信は独立、分離しています。
- 原則2：全ての情報（リソース）は汎用的な構文で一意に識別できる
  - URIによってリソースの場所を文字列で表現できます。
    - そのためURIのパス部分が名詞になる場合が多いです。例を以下に示します。
      - http://localhost:8080/testapp/users
      - http://localhost:8080/testapp/animal/dogs
- 原則3：リソースを操作する命令の体系が予め定義・共有されている
  - HTTPのGETやPOSTなどのメソッドで操作方法を表現します。例を以下に示します。
    - `POST /user HTTP/1.1`だとシステムにユーザを登録するようなAPI呼び出しを表します。
    - `GET /animal/dogs HTTP/1.1`だと犬の一覧情報を取得するようなAPI呼び出しを表します。
  - このように、`GET`は`取得`、`POST`は`登録`など操作が定められています。
- 原則4：接続性
  - やりとりされる情報内部に、別の情報やその情報の別の状態へのリンクを含めることができます。
  - イメージ図は[こちらの記事](https://ramble.impl.co.jp/2886/#toc9)から拝借しました。
![Connectabilityについて-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/6087249d-abec-9d5e-128b-94d5e3c3b34b.png)

### Amazon DynamoDB

Amazon DynamoDBとは、AWSが提供する、`フルマネージド`・`サーバレス`なキーバリュー型`NoSQLデータベースサービス`です。
DynamoDBはAmazon.comの現CTO兼副社長のWerner Vogels氏が開発した`Dynamo`という技術を採用しています。
この`Dynamo`はショッピングシーズンの大量のトラフィックに対してスケーラビリティと可用性を併せ持ったデータベースが必要、というAmazonからの要望から生まれました。
1桁ミリ秒のパフォーマンス、最大99.999%の高可用性を実現しています。
`2012年1月`に一般公開されました。

https://aws.amazon.com/jp/blogs/news/happy-birthday-dynamodb/

https://cloud.watch.impress.co.jp/docs/interview/541656.html

以下の図は[高速で柔軟な NoSQL データベースサービス。Amazon DynamoDB をグラレコで解説](https://aws.amazon.com/jp/builders-flash/202206/awsgeek-dynamodb/?awsf.filter-name=*all)より

![yougo_dynamodb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/fe80a819-7fbf-970d-1b65-777954c9c8f9.png)

#### 用語

|用語|意味|
|---|---|
|リレーショナルデータベース|関連があるデータを表形式で管理するデータベース。関係モデルという理論が基礎となっている。データベース内のデータ操作は主にSQLを使用し行われる|
|NoSQLデータベース|リレーショナルデータベース以外のデータベースを指す。年々増加するデータ量、さまざまなデータ形式、サイズに対して柔軟に対応するため誕生した|

#### 概念

DynamoDBの以下の概念についてAWS公式資料[DynamoDB の基礎と設計 / DynamoDB Design Practice](https://speakerdeck.com/_kensh/dynamodb-design-practice)を参考に説明します。

- キーバリュー型
- プライマリーキー
- Secondary Index

まず、`キーバリュー型`についてです。
DynamoDBはNoSQLデータベースであり、その中でも`キーバリュー型`の分類にあたります。
`キーバリュー型`はデータを`キー`と`バリュー（値）`の1対1の単純な構造で保持します。
このような単純な構造であるため、データの取得がしやすく、高速であるという特徴があります。

![スクリーンショット 2023-08-23 22.29.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/3aee532c-1f27-b649-65f5-34bc0d75b00f.png)

次に、`プライマリーキー`についてです。

`プライマリーキー`はデータを一意に識別する項目を指します。
`プライマリーキー`には`Partition Key`と`Sort Key`の2種類があります。
`Partition Key`はリレーショナルデータベースでいう、主キーに当たります。
`Sort Key`は`Partition Key`と組み合わせることでデータを一意にする項目です。
`Partition Key`と`Sort Key`の組み合わせはリレーショナルデータベースでいう、複合主キーに当たります。

`Partition Key`と`Sort Key`を条件にすれば、SQLのWHERE句のようにクエリ（検索）することができるようになります。
しかし、それ以外の項目は条件として指定することができません。
指定できるようにするためには項目を`Secondary Index`として指定する必要があります。

![スクリーンショット 2023-08-23 22.31.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/2e38f989-c11c-8899-898e-1aba87d812bf.png)

最後に、`Secondary Index`についてです。

`Secondary Index`は`Local Secondary Index(LSI)`と`Global Secondary Index(GSI)`の2種類があります。
`LSI`は`Sort Key`の代わりとなるKeyを指定することができます。ただし、`Partition Key`は同じです。
`GSI`は`Partition Key`の代わりとなるKeyを指定することができます。テーブルにもう一つ検索可能な`Partition Key`(+ `Sort Key`も指定可能)を追加するイメージです。ただ、`GSI`とした項目の属性は一意でなくても構いません。

以下に例を示します。
PKは`Partition Key`、SKは`Sort Key`、GSI-PKは`GSI`の`Partition Key`、GSI-SKは`GSI`の`Sort Key`を表します。

|Name(PK)|Color(SK)|Season(GSI-SK)|Kind(LSI)|Country(GSI-PK)|
|---|---|---|---|---|
|Apple|Red|Winter|Fruit|Japan|
|Apple|Green|Winter|Fruit|NZ|
|Orange|Orange|Summer|Fruit|USA|
|Tomato|Red|Winter|Vegetable|Japan|

以上のテーブル設定で検索条件に指定可能な項目は以下になります。

- Name(PK)
- Name(PK) + Color(SK)
- Name(PK) + Kind(LSI)
- Country(GSI-PK)
- Country(GSI-PK) + Season(GSI-SK)

![スクリーンショット 2023-08-23 22.36.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/bc0c664d-a525-3f07-74da-cf632734110a.png)

#### AWSが提供するデータベースサービス

AWSが提供しているデータベースサービスは`リレーショナルデータベースサービス`、`NoSQLデータベースサービス`の大きく分けて２種類あります。
また、ユースケースごとにさまざまなサービスを提供しています。

- リレーショナルデータベースサービス
  - [Amazon RDS](https://aws.amazon.com/jp/rds/)
  - [Amazon Aurora](https://aws.amazon.com/jp/rds/aurora/)
  - [Amazon Redshift](https://aws.amazon.com/jp/redshift/)

- NoSQLデータベースサービス
  - キーバリュー
    - [Amazon DynamoDB](https://aws.amazon.com/jp/dynamodb/)
  - インメモリ
    - [Amazon ElastiCache](https://aws.amazon.com/jp/elasticache/)
    - [Amazon MemoryDB for Redis](https://aws.amazon.com/jp/memorydb/)
    - [Amazon DynamoDB Accelerator (DAX)](https://aws.amazon.com/jp/dynamodb/dax/)
  - ドキュメント
    - [Amazon DocumentDB (MongoDB 互換)](https://aws.amazon.com/jp/documentdb/)
  - グラフ
    - [Amazon Neptune](https://aws.amazon.com/jp/neptune/)
  - 検索
    - [Amazon OpenSearch Service](https://docs.aws.amazon.com/ja_jp/opensearch-service/latest/developerguide/what-is.html)
  - 時系列
    - [Amazon Timestream](https://aws.amazon.com/jp/timestream/)
  - キーバリュー分散型
    - [Amazon Keyspaces](https://aws.amazon.com/jp/keyspaces/)
  - 台帳
    - [Amazon Quantum Ledger Database（QLDB）](https://aws.amazon.com/jp/qldb/)

## 今回ハンズオンで作成する構成

今回は赤枠で囲まれた部分をハンズオンで作成します。
処理の流れとしては以下になります。（[前回記事で記載した流れ](https://qiita.com/kenryo/private/c9f0fe7b2f6bdf765412)は省略しています）

- Amazon API Gatewayで用意したREST APIを実行し、Amazon S3に日本語文章が記載されたテキストファイルをアップロードする
- EventBridgeで起動したステートマシンは以下の処理を行う
  - Amazon Translate、Amazon Comprehendの結果をAmazon DynamoDBに登録する

![第３回やるところの図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/5b47b985-55ae-e93d-8e27-29f0d17611ed.png)

## ハンズオン

### S3バケットにファイルをアップロードするREST APIをAPI Gatewayに定義する

#### IAMロールにポリシーを追加

S3バケットにファイルをアップロードするためには、API Gatewayにアップロードするための権限が必要です。
Step Functionsと同様にIAMロールに必要な権限を付与します。
必要な権限は[s3:PutObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html)です。
実はこの権限は、[第１回目「IAMロールに権限（IAMポリシー）を追加」](https://qiita.com/kenryo/private/a47eaf8195e30f6a8182#iam%E3%83%AD%E3%83%BC%E3%83%AB%E3%81%AB%E6%A8%A9%E9%99%90iam%E3%83%9D%E3%83%AA%E3%82%B7%E3%83%BC%E3%82%92%E8%BF%BD%E5%8A%A0)でIAMロールに付与した`AmazonS3FullAccess`ポリシーにすでに含まれています。
実際に当該権限が付与されているか確認します。

画面上部の検索窓に`iam`と入力し、表示された`IAM`をクリックしてください。

![EB-03-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/e783a7b8-4111-53f7-ac74-c9f061709f52.png)

左側または中央にある`ロール`をクリックしてください。

![EB-03-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/a9f9a3b4-a410-db3e-cdd7-e2b3ef4afd45.png)

一覧の検索窓にユーザ名を入力し、当該IAMロールを選択します。

![EB-03-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/25b3da8c-7d06-fb84-9b31-12ecd78090ec.png)

`許可`タブを選択し、`許可ポリシー`一覧に表示されている`AmazonS3FullAccess`リンクをクリックしてください。

![スクリーンショット 2023-08-21 21.55.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/9099a0bb-9b5b-35a2-8821-a59aaa14516a.png)

`AmazonS3FullAccess`ポリシーの詳細画面に遷移します。
`許可`タブを選択し、`このポリシーで定義されている許可`一覧の`サービス`蘭に表示されている`S3`リンクをクリックしてください。

![スクリーンショット 2023-08-21 21.57.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/0e087e89-f5c7-6e1d-efd6-2f0d4b3673d3.png)

再び`許可`タブを選択し、検索窓に`putobject`と入力してください。
`このポリシーで定義されている許可`一覧の`書き込み`セクションの`アクション`蘭に`PutObject`が表示されていることを確認してください。

![screencapture-us-east-1-console-aws-amazon-iamv2-home-2023-08-21-22_00_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/12d1d289-cc25-ab7b-ae05-dc7fc91c7e39.png)

#### IAMロールに信頼関係の追加

API GatewayにIAMロールの権限の使用を許可するため、信頼ポリシーを追加します。
IAMロールの詳細画面に戻り、`信頼関係`タブを選択し、`信頼ポリシーを編集`をクリックしてください。

![EB-03-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/2afd46fc-cca7-2e5f-5f53-6e40c4fd775b.png)

続いて、信頼ポリシーを追加します。既存の`Statement`オブジェクトに以下のJsonオブジェクトを追加し、`ポリシーを更新`ボタンを押下してください。

```json
{
    "Effect": "Allow",
    "Principal": {
        "Service": "apigateway.amazonaws.com"
    },
    "Action": "sts:AssumeRole"
}
```

![スクリーンショット 2023-08-21 22.36.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/9f5a5808-cc49-d051-ee60-3060cb7592eb.png)

#### APIの作成

画面上部の検索窓に`api`と入力し、表示された`API Gateway`をクリックしてください。

![スクリーンショット 2023-08-21 22.37.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/8fd58a04-6997-22b0-cc6e-8bd52a8f3843.png)

作成するAPIの種類を選択します。
今回は`REST API`を作成します。（プライベートではない）`REST API`の`構築`ボタンをクリックしてください。

![スクリーンショット 2023-08-21 22.40.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/ade5be18-4280-2bbf-1836-2be436e7b8e6.png)

APIのプロトコルや名前などを入力します。
`プロトコルを選択する`は`REST`になっていることを確認してください。
`新しいAPIの作成`は`新しいAPI`を選択してください。
つづいて`名前と説明`の入力です。
`API名`は`<ユーザ名>API`としてください。
`説明`は入力不要です。
`エンドポイントタイプ`は`リージョン`を選択してください。他に`エッジ最適化`、`プライベート`が選択できます。
細かい説明は省略しますが、`リージョン`はリージョンにデプロイされるパブリックなAPI、`エッジ最適化`は高パフォーマンスなAPI、`プライベート`はパブリックからアクセスできないAPIという感じです。

入力し終わったら、画面右下`APIの作成`ボタンをクリックしてください。

![スクリーンショット 2023-08-21 22.42.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/bc4b189c-4bfc-fdca-0e93-3837552600ac.png)

#### リソースの作成

フォルダを表すリソースを作成します。
左メニュー`リソース`が選択されていることを確認し、`アクション`プルダウンを選択、`リソースの作成`をクリックしてください。

![スクリーンショット 2023-08-21 22.53.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/441facd5-0e2e-9143-dd79-4bd59658fe7a.png)

リソース情報を入力します。
`リソース名`に`bucket`と入力してください。
`リソースパス`は`{bucket}`と入力してください。
リソースパスを中括弧で指定することでその部分は可変項目になります。
可変項目にしたい場合、この後の`URLパスパラメータ`の設定も必要です。
入力し終わったら、画面右下`リソースの作成`ボタンをクリックしてください。

![スクリーンショット 2023-08-22 1.14.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/74b0d7ee-eac9-ce3f-5871-16baec4faccf.png)

作成が終わるとリソースに`/{bucket}`が追加されていることを確認し、クリックしてください。
そして、再度`アクション`プルダウンを選択、`リソースの作成`をクリックしてください。

![スクリーンショット 2023-08-22 1.16.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/7380a08a-4515-a702-0c6e-a858b4751de0.png)

`bucket`リソース配下のリソース情報を入力します。
`リソース名`に`folder`と入力してください。
`リソースパス`は`{folder}`と入力してください。
入力し終わったら、画面右下`リソースの作成`ボタンをクリックしてください。

![スクリーンショット 2023-08-22 1.17.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/e9c10287-c6d3-fb23-b78b-0eeb5214ab20.png)

作成が終わるとリソースに`/{folder}`が追加されていることを確認し、クリックしてください。
そして、再度`アクション`プルダウンを選択、`リソースの作成`をクリックしてください。

![スクリーンショット 2023-08-22 1.18.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/83b2b885-0c16-e8ae-c2db-41932cfe5b21.png)

`folder`リソース配下のリソース情報を入力します。
`リソース名`に`object`と入力してください。
`リソースパス`は`{object}`と入力してください。
入力し終わったら、画面右下`リソースの作成`ボタンをクリックしてください。

![スクリーンショット 2023-08-22 1.21.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/149cd1f6-4477-179a-61c3-c7ac24c819b2.png)

#### メソッドの作成

リソースへの操作を表すメソッドを作成します。

`object`リソースの作成が終わるとリソースに`/{bucket}/{folder}/{object}`が追加されていることを確認し、クリックしてください。
そして、`アクション`プルダウンを選択、`メソッドの作成`をクリックしてください。

![スクリーンショット 2023-08-22 1.22.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/db89e6bb-cc77-e8ff-0ffa-c806a29c9711.png)

`/{bucket}/{folder}/{object}`リソース配下にメソッドを選択するプルダウンが表示されます。
`PUT`を選択し、チェックマークをクリックしてください。

![スクリーンショット 2023-08-22 1.23.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/47d902c7-5940-5851-b422-4930f40245eb.png)

PUTメソッドの情報を入力します。
`統合タイプ`は`AWSサービス`を選択してください。
`AWSリージョン`は`ap-northeast-1`を選択してください。
`AWSサービス`は`Simple Storage Service (S3)`を選択してください。
`AWSサブドメイン`は空で構いません。
`HTTPメソッド`は`PUT`を選択してください。
`アクションの種類`は`パス上書きの使用`を選択してください。
`パス上書き（省略可能）`は`{bucket}/{folder}/{object}`と入力してください。
※パスの上書きを使用すると、API Gatewayはリクエストを[Amazon S3のREST API](https://docs.aws.amazon.com/AmazonS3/latest/userguide/RESTAPI.html)に対応するリクエストに変換して転送してくれます。
`実行ロール`は[IAMロールにポリシーを追加](#iamロールにポリシーを追加)で扱ったIAMロールのARNを入力してください。
`コンテンツの処理`は`パススルー`を選択してください。
入力し終わったら、画面右下`保存`ボタンをクリックしてください。

![スクリーンショット 2023-08-22 1.25.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/848b05cc-132f-3cc8-33ed-d54a43e91aa4.png)

#### パラメータマッピング

リクエスト情報を加工し、バックエンドのAWSサービスに渡したり、AWSサービスから受け取った返却値を加工し、APIのレスポンスとして返却する設定を行います。

`/{bucket}/{folder}/{object}`リソースの`PUT`メソッドを選択した状態で、`統合リクエスト`リンクをクリックしてください。

![スクリーンショット 2023-08-22 1.26.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/db9f2cc4-8797-6c93-793e-e6b7ffb3d30a.png)

`URLパスパラメータ`を選択し、`パスの追加`をクリックしてください。
この設定は[リソースの作成](#リソースの作成)で指定したパスパラメータを有効にするため必要です。

![スクリーンショット 2023-08-22 0.02.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/964de735-0d6c-b618-a294-13b5d1f50717.png)

`名前`に`bucket`と入力してください。
[メソッドの作成](#メソッドの作成)の`パス上書き（省略可能）`で`{bucket}/{folder}/{object}`を指定しました。
この`{bucket}`の値と`名前`に指定する値を同じにすることでリクエスト時に指定したパスの値が自動的にマッピングされるようになります。
例えば、`パス上書き（省略可能）`が`{hoge}/{folder}/{object}`である場合、名前も`hoge`にしなければ、パスの値がマッピングされず、`パス上書き`で指定した値そのままでリクエストされます。

`マッピング元`に`method.request.path.bucket`と入力し、チェックマークをクリックしてください。
末尾部分`bucket`は[リソースの作成](#メソッドの作成)で指定した`リソースパス(中括弧は除く)`と一致させる必要があります。

![スクリーンショット 2023-08-22 1.28.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/44e79b31-f5e3-6621-be05-5158fbe00a20.png)

再度`パスの追加`をクリックしてください。
`名前`に`folder`と入力してください。
`マッピング元`に`method.request.path.folder`と入力し、チェックマークをクリックしてください。

![スクリーンショット 2023-08-22 1.29.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/9de2abec-ba7a-31f8-fca6-4136df6e08f8.png)

再度`パスの追加`をクリックしてください。
`名前`に`object`と入力してください。
`マッピング元`に`method.request.path.object`と入力し、チェックマークをクリックしてください。

![スクリーンショット 2023-08-22 1.30.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/5c91aa41-88e4-7b30-c852-5de8297b9112.png)

#### APIのデプロイ

作成したAPIを実際呼べるようにデプロイを行います。
左メニュー`リソース`を選択し、`アクション`プルダウンを選択、`APIのデプロイ`をクリックしてください。

![スクリーンショット 2023-08-22 1.31.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/80696645-54a1-17f1-38cd-613a388445af.png)

デプロイに関する情報を入力します。
`デプロイされるステージ`は`[新しいステージ]`を選択してください。
`ステージ名`は`v1`と入力してください。
入力し終わったら、右下`デプロイ`ボタンをクリックしてください。

![スクリーンショット 2023-08-22 0.30.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/51b1139c-e3a6-0a8f-bb3f-d6280ba9b235.png)

#### APIファイルアップロード確認

APIは`CloudShell`で`curl`コマンドを叩いて実行します。
`CloudShell`はブラウザベースのコマンドラインです。Amazon Linuxベースであり、手軽にShellを実行させることができます。
`curl`コマンドはさまざまなプロトコル（HTTPなど）で通信を行うことができるコマンドです。

画面上部の右側にCloudShellのアイコンがあるのでクリックしてください。

![スクリーンショット 2023-08-22 1.32.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/3c2de1c8-cac3-5835-c72e-947090bad119.png)

CloudShellの右上の`アクション`プルダウンをクリックしてください。
`ファイルのアップロード`をクリックし、翻訳、感情分析対象のテキストファイルを選択してアップロードしてください。

![スクリーンショット 2023-08-22 1.34.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/c0f2e74f-09b0-b456-0e35-e9a8b7c26738.png)

`ls`コマンドでアップロードしたファイルが表示されることを確認してください。

![スクリーンショット 2023-08-22 1.34.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/acb3e8e8-a21c-a6ce-dbdf-1d6da54904b6.png)

ファイルをアップロードするAPIを実行するcurlコマンドは以下です。CloudShellで実行してください。
`APIID`はAPI GatewayのAPI一覧から確認してください。

```text
curl -i --location --request PUT 'https://<APIID>.execute-api.ap-northeast-1.amazonaws.com/v1/<S3バケット名>/uploads/<アップロードしたファイル名>' --data '@<アップロードしたファイル名>'
```

実行例は以下になります。`HTTP/2 200`が表示されたらAPIの実行に成功です。

```text
[cloudshell-user@ip-10-6-53-91 ~]$ curl -i --location --request PUT 'https://<APIID>.execute-api.ap-northeast-1.amazonaws.com/v1/<S3バケット名>/uploads/<アップロードしたファイル名>' --data '@<アップロードしたファイル名>'
HTTP/2 200 
date: Mon, 21 Aug 2023 16:10:06 GMT
content-type: application/json
content-length: 0
x-amzn-requestid: xxxxxxxxxxxxxxxxx
x-amz-apigw-id: xxxxxxxxxxxxxxxxx
x-amzn-trace-id: xxxxxxxxxxxxxxxxx
```

S3バケットの`uploads`フォルダに遷移し、ファイルがアップロードされていることを確認してください。

![スクリーンショット 2023-08-22 1.37.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/39e7ed31-a6ee-9d0f-8e60-938785fe09f0.png)

### 英訳結果、感情分析結果をDynamoDBに格納する

#### テーブルを設計する

天下り的ですが、今回は以下の項目をテーブルに格納させたいと思います。

```text
実施日時(ExecutionDateTime)(PK)、翻訳後テキスト(TranslatedText)、センチメント(Sentiment)(GSI-PK)
```

`実施日時`は`Partition Key`とし、フォーマットは`YYYY-MM-DDThh:mm:ss.fffZ`とします。
`翻訳後テキスト`は出力文字列をそのまま格納します。
`センチメント`は検索項目としたいという想定で`GSI`にします。出力文字列をそのまま格納します。

今回はテーブルがシンプルであるため、設計は楽でしたが、複雑な場合、ER図作成などデータモデリングをしていく必要があります。
詳細は以下の記事を参照ください。

https://qiita.com/_kensh/items/2351096e6c3bf431ff6f

#### テーブルを作成する

設計に従ってDynamoDBのテーブルを作成します。

画面上部にある検索窓に`dynamo`と入力し、検索結果に`DynamoDB`が表示されるので、クリックしてください。

![スクリーンショット 2023-08-24 20.40.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/b5d686b9-eb53-a593-eb53-348d29d2203e.png)

左メニュー`テーブル`を選択するとテーブル一覧が表示されます。
一覧右上の`テーブルの作成`ボタンをクリックしてください。

![スクリーンショット 2023-08-24 20.41.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/5e74c7d6-1528-3c8e-947d-f9a81df231b6.png)

作成するテーブルに関する情報を入力します。

`テーブルの詳細`セクションの入力です。
`テーブル名`は`<ユーザー名>Table`にしてください。
`パーティションキー`は`ExecutionDateTime`と入力し、`文字列`を選択してください。
`ソートキー - オプション`は未入力で構いません。

![スクリーンショット 2023-08-24 20.47.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/17badb6a-e608-426c-1180-16dcd768b54c.png)

`テーブル設定`セクションの入力です。
`テーブル設定`は`設定をカスタマイズ`を選択してください。

![スクリーンショット 2023-08-24 20.48.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/3d046514-4fa2-ddf5-fd77-3f860a37f83d.png)

`テーブルクラス`セクションの入力です。
`テーブルクラス`は`DynamoDB標準`を選択してください。

![スクリーンショット 2023-08-24 20.48.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/9d129a67-03b1-0b5f-2e3d-b865b25235d9.png)

`読み込み/書き込みキャパシティーの設定`セクションの入力です。
`キャパシティーモード`は`プロビジョンド`を選択してください。
`読み込みキャパシティー`の`Auto Scaling`は`オフ`にしてください。
`読み込みキャパシティー`の`プロビジョンドキャパシティーユニット`は`1`にしてください。
`書き込みキャパシティー`の`Auto Scaling`は`オフ`にしてください。
`書き込みキャパシティー`の`プロビジョンドキャパシティーユニット`は`1`にしてください。

![スクリーンショット 2023-08-24 20.49.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/191bf17d-33df-675a-a222-77b1d5eb29da.png)

`セカンダリインデックス`セクションの入力です。
今回`センチメント`をグローバルインデックスに指定するため、`グローバルインデックスの作成`ボタンをクリックしてください。
なお、`ローカルインデックスの作成`はテーブル作成時のみ実施可能です。必要な場合忘れずに追加してください。（GSIはいつでも追加可能です）

![スクリーンショット 2023-08-24 20.55.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/27c78162-c838-721d-b119-3abe49719865.png)

`パーティションキー`に`Sentiment`と入力し、`文字列`を選択してください。
`ソートキー - オプション`は未入力で構いません。
`インデックス名`は自動的に入力される`Sentiment-index`であることを確認してください。
`属性の射影`は`All`を選択してください。
この値はセカンダリインデックスでの検索結果で何を返却するかを指定します。指定項目は`KEYS_ONLY`、`INCLUDE`、`ALL`の3種類です。
`KEYS_ONLY`は文字通り、キーの値のみ返却する設定です。
`INCLUDE`はキーの他指定した項目を返却する設定です。
`ALL`は検索された行を全て返却する設定です。
入力し終わったら、`インデックスの作成`ボタンをクリックしてください。

![スクリーンショット 2023-08-24 20.58.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/b294081f-e830-615a-2293-0ae4161e3020.png)

それ以降の値はデフォルト値で構いません。
画面最下部右下の`テーブルの作成`ボタンをクリックしてください。
テーブル一覧に遷移します。テーブルの`状態`が`アクティブ`になれば作成完了です。

![スクリーンショット 2023-08-24 21.02.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/e7073d16-948d-48c5-7506-1982108a93ee.png)

さて、作成するテーブルに格納したい情報は以下でした。
しかし、テーブル作成時に`翻訳後テキスト`について何も指定はせず、`実施日時(PK)`と`センチメント(GSI-PK)`のみ入力しました。
リレーショナルデータベースに慣れていると`翻訳後テキスト`はテーブルに格納できないのではないかと感じます。本当でしょうか？

```text
実施日時(ExecutionDateTime)(PK)、翻訳後テキスト(TranslatedText)、センチメント(Sentiment)(GSI-PK)
```

DynamoDBはキーバリュー型のNoSQLでありスキーマレスです。リレーショナルデータベースとは異なり、格納することができます。
以下の通り、`プライマリーキー`以外は何を入力しても構わないのです。

![スクリーンショット 2023-08-24 21.07.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/15ef1624-5a2e-4f43-a521-bb55a3ec71fa.png)

#### 結果を格納する

テーブルができたのでStep Functionsのステートマシンから必要なデータを格納するステートを追加しましょう。
[前回ハンズオン](https://qiita.com/kenryo/private/c9f0fe7b2f6bdf765412#step-functions%E3%82%B9%E3%83%86%E3%83%BC%E3%83%88%E3%83%9E%E3%82%B7%E3%83%B3%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B)で作成したステートマシンの`Workflow Studio`に移動してください。

次に、左メニューで`アクション`タブを選択し、検索窓に`dynamo`と入力してください。
検索結果の`PutItem`ステートを`Parallel`の箱の下にそれぞれドラッグ&ドロップしてください。

![スクリーンショット 2023-08-24 22.10.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/ebb49d12-38f5-54af-916f-1245f59521c7.png)

`Amazon Translate`の翻訳結果を格納する処理を追加します。
`Amazon Translate`のステートを選択してください。
`APIパラメータ`のJsonにどのテーブルのどのカラムにどのような値をINSERTするか定義します。
Jsonの構文は以下の通りです。
`【データ型】`は文字列や数値を表します。`S`は文字列、`N`は数値を表します。その他は以下を参照ください。

https://uyaroom.com/dynamodb-symbol/

```json
{
  "TableName": "【テーブル名】",
  "Item": {
    "【カラム名】": {
      "【データ型】": "【値】"
    }
  }
}
```

格納するデータの元となるParallelステート後のJsonを以下に示します。

```json
[
  {
    "SourceLanguageCode": "ja",
    "TargetLanguageCode": "en",
    "TranslatedText": "Good morning!"
  },
  {
    "Sentiment": "POSITIVE",
    "SentimentScore": {
      "Mixed": 0.000012929639,
      "Negative": 0.00003391724,
      "Neutral": 0.00018313748,
      "Positive": 0.99977
    }
  }
]
```

`翻訳後テキスト(TranslatedText)`、`センチメント(Sentiment)(GSI-PK)`は上記Jsonから取得できそうですが、`実施日時(ExecutionDateTime)(PK)`はどのように取得するのでしょうか？
方法は色々ありますが、今回はステートマシンの[Contextオブジェクト](https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/input-output-contextobject.html)から取得します。
Contextオブジェクトはステートマシン起動時に生成されるオブジェクトでステートマシンのメタ情報が含まれます。
この情報には`Execution.StartTime`という、ステートマシンの起動時間(UTC)が含まれているのでこれを格納することにします。
Contextオブジェクトへのアクセスは`$$`ですることができます。
以下が今回APIパラメータに指定する値です。入力してください。

```json
{
  "TableName": "【テーブル名】",
  "Item": {
    "ExecutionDateTime": {
      "S.$": "$$.Execution.StartTime"
    },
    "TranslatedText": {
      "S.$": "$.[0].TranslatedText"
    },
    "Sentiment": {
      "S.$": "$.[1].Sentiment"
    }
  }
}
```

入力が終わったら、ステートマシンの状態を保存してください。

#### IAMロールにポリシーを追加

このままではステートマシンにDynamoDBを操作する権限がないため実行に失敗します。
ステートマシンに付与しているIAMロールに必要な権限を付与します。

画面上部の検索窓に`iam`と入力し、表示された`IAM`をクリックしてください。

![EB-03-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/e783a7b8-4111-53f7-ac74-c9f061709f52.png)

左側または中央にある`ロール`をクリックしてください。

![EB-03-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/a9f9a3b4-a410-db3e-cdd7-e2b3ef4afd45.png)

一覧の検索窓にユーザ名を入力し、当該IAMロールを選択します。

![EB-03-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/25b3da8c-7d06-fb84-9b31-12ecd78090ec.png)

`許可`タブを選択すると`許可ポリシー`が表示されます。  
その右上の`許可を追加`プルダウンを選択し、`ポリシーのアタッチ`をクリックしてください。

![sf-type-30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/bf5ae3f7-bbe7-5add-812f-ed69b1c2770c.png)

検索窓に`dynamo`と入力し、Enterしてください。  
ポリシー`AmazonDynamoDBFullAccess`が表示されるのでチェックし、右下の`許可を追加`をクリックしてください。

![スクリーンショット 2023-08-24 22.36.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/d70cd200-d9b1-b0d6-ee84-0361a44de65e.png)

`許可ポリシー`の一覧に`AmazonDynamoDBFullAccess`が表示されたら追加完了です。

![スクリーンショット 2023-08-24 22.36.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/d9c4ac68-a0bb-c51b-ff2a-3dc121d7f056.png)

#### 処理確認

今まで作成したものが期待通り動くか確認します。
最初にAPI Gatewayで作成したファイルアップロードAPIの実行です。
[APIファイルアップロード確認](#apiファイルアップロード確認)と同様にCloudShellでcurlコマンドを実行し、S3にファイルをアップロードしてください。

次に、ステートマシンの実行の確認です。
作成したステートマシンの直近の実行結果を確認し、全てのステートが成功していることを確認してください。

![スクリーンショット 2023-08-24 22.38.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/bb839da8-ed88-f306-4921-b29c1c5533a3.png)

最後に、DynamoDBにデータが格納されているかの確認です。
DynamoDBのダッシュボードに遷移し、左メニュー`項目を探索`をクリックしてください。
`テーブル`で自分が作成したテーブルを選択し、期待通りのデータが表示されていることを確認してください。

![スクリーンショット 2023-08-24 22.39.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/6b4124cd-6050-2cb3-e0a5-35ddf0789bca.png)

## 参考

- https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/integrating-api-with-aws-services-s3.html
- https://tenshoku-careerchange.jp/column/3737/
- https://dev.classmethod.jp/articles/aws-release-durable-redis-amazon-memorydb-for-redis/
- https://tech.nri-net.com/entry/aws_history_and_chronology_all
- https://xtech.nikkei.com/it/article/NEWS/20120119/378871/