【React】Webアプリケーションの関連付け

React axiosとAWS Lambdaを用いて、webページからwebアプリを実行する方法を紹介する。

## フロントエンド(React)の作成
1. axiosをインストールする
    ```
    npm install axios --save
    ```
1. axiosをimportして、webアプリのURLへPOSTする
    ```react
    import React from 'react'
    import axios from 'axios'

    const Breadcrumbs = () => {
      const [responseData, setResponseData] = React.useState(null)
      const handleSubmission = async (event) => {
        try {
          await axios
            .post(
              'https://*****************************',
              event,
              {
                headers: {
                  'Content-type': 'text/plain',
                },
              },
            )
            .then((res) => {
              setResponseData(res.data)
            })
        } catch (err) {
          console.error(err)
          alert('[ERROR] POSTに失敗しました。')
        }
      }
    }
    ```
1. ```'https://*****************************'```の部分を以下で作成するwebアプリのURLに置き換える。

## バックエンド(Lambda関数)の作成

### lambdaへPythonコードをアップロードする
1. 下記のPythonコードを作成する
    CORS(Cross-Origin Resource Sharing)で、どのWebサイトにもOriginを越えるアクセスを許可するため、返却するヘッダーにAccess-Control-Allow-OriginとAccess-Control-Allow-Headersを加えることに注意する。
```diff_python:lambda_function.py
import os
import sys
import time
import json
import base64
import io
import csv
import boto3
import cgi
from requests_toolbelt.multipart import decoder

def lambda_handler(event, context):
    if 'content-type' in event['headers'].keys():
        body = event["body"].encode('utf-8')
        content_type = event['headers']['content-type']
        c_type, c_data = cgi.parse_header(event['headers']['content-type'])
    elif 'Content-Type' in event['headers'].keys():
        body = event["body"].encode('utf-8')
        content_type = event['headers']['Content-Type']
        c_type, c_data = cgi.parse_header(event['headers']['Content-Type'])
    else:
        raise RuntimeError('content-type or Content-Type not found')

    data = json.loads(body)
    
    return {
        'statusCode': 200,
        'headers': {
+            "Access-Control-Allow-Origin": "*",
+            "Access-Control-Allow-Headers": "Content-Type",
        },
        'body': json.dumps('正常にアップロードされました！')
    }
```
1. 同じフォルダで下記コマンドを実行して、ローカルにPython moduleをインストールする
  ```
  pip install -t . --platform manylinux2014_x86_64 --implementation cp --python 3.9 --only-binary=:all: --upgrade requests-toolbelt
  ```
1. zipで圧縮する
1. 「コード」->「アップロード元」->「.zipファイル」で圧縮したzipファイルを選択して、アップロードする

### LambdaにS3とDynamoDBのフルアクセス権限を付与する
1. 「設定」＞「アクセス権限」＞実行ロールのロール名のリンクをクリックする
1. 「許可」＞「許可を追加」＞「ポリシーをアタッチ」をクリックする
1. AmazonS3FullAccessとAmazonDynamoDBFullAccessにチェックを入れ、「ポリシーをアタッチ」をクリックする

### タイムアウトとメモリの設定を変更する
1. 「設定」->「一般設定」からメモリを500MBとタイムアウトを15分に変更する

### APIgatewayでwebAPIを作成する
1. トリガーを作成をクリックして、ソースを選択からAPIGatewayをクリックする
1. Create New APIを選択する
1. REST APIを選択する
1. 追加を押す
1. APIGatewayで作成したAPIの名前をクリックする
1. 「アクション」->「メゾッドの作成」->でPOSTを選択する
1. 作成したPOSTをクリックし、統合レスポンスをクリックする
1. マッピングテンプレートをクリックして、マッピングテンプレートの追加を押す
1. ```application/json;charset=UTF-8```を追記して、右のチェックを押す
1. 右側のテンプレートの欄に、```$input.json('$')```を記入する
   https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
1. 保存を押す

### 非同期APIにする場合
1. [Resources] (リソース) で [POST] を選択し、[Integration Request] (統合リクエスト) を選択する
1. [HTTP Headers] (HTTP ヘッダー) を展開し、[Add header] (ヘッダーを追加) を選択する
1. [Name] (名前) に X-Amz-Invocation-Type、 [Mapped from] (マッピング元) に'Event'と入力する
1. REST API を再デプロイする
1. 以上

非同期APIではLambdaで受け取るeventの形式が異なり、データサイズが同期が6MBに対して、248KBしかないたため、注意。
あと、なぜかCORSをわざわざAPI gatewayで有効化しないといけない。

## 補足
Lmabdaのファイルの開き方はいくつかある
パターン1
```python
import csv
import io
import requests

def lambda_handler(event, context):
    csv_file = event['body']
    data = io.StringIO(csv_file)
    reader = csv.DictReader(data)
    for row in reader:
        print(row['column_1'], row['column_2'], row['column_3'], ...)
    return {
        'statusCode': 200,
        'body': 'CSV file processed successfully'
    }
```

パターン2
```react
import io

def lambda_handler(event, context):
    file_data = event['body']
    decoded_file = file_data.decode('utf-8')
    lines = decoded_file.split('\n')
    for line in lines:
        print(line)
    return {
        'statusCode': 200,
        'body': 'File processed successfully'
    }
```

## 参考記事

https://dev.classmethod.jp/articles/react-api-gateway-lambda-dynamodb-viewcount/

https://dev.classmethod.jp/articles/apigateway-lambda-proxy-cors/