【AWS认证备考】零成本在线实践学习AWS入门(API Gateway, Lambda, DynamoDB)【适合初学者】

章节索引

    • どんな記事?

 

    • 何を作るのか

 

    • 環境

 

    • LocalStack環境を保存する方法

LocalStackを起動してみよう!

AWS CLIでLambdaが実行できるか確認する
AWS CLI Localをインストールする

サーバーレスアーキテクチャで翻訳Web APIを構築する

みんなの自動翻訳API呼び出すLambdaを作成する(Node.js)
Nodeをインストールする
Lambda関数の作成、デプロイしてテスト
API Gatewayの構築とLambdaとの連携
DynamoDBの構築
LambdaとDynamoDBを連携
API Gateway, Lambda, 翻訳API, DynamoDBが連携されているか確認する

終わり

什么样的文章?

在云计算领域,亚马逊网络服务(AWS)被广泛使用。然而,为了获得与AWS相关的资质,通常需要一些教育材料和培训的费用。对于初学者来说,这样的费用可能成为障碍。

在成为工程师之前,我私下进行了AWS的实践学习,但我担心会不会不小心产生费用(我在某个学习网站的AWS实践课程问答页面上看到一个问题,问:“我产生了20万的费用,我该怎么办?”让我感到很震惊)。

因此,本文将重点介绍初学者可以免费便捷地学习AWS认证考试的实践学习方法。

我们将使用一种名为LocalStack的工具,在本地环境中模拟AWS服务,来进行实践。通过模拟AWS服务,LocalStack可以进行与实际的AWS环境相类似的操作和测试。这篇文章详细介绍了LocalStack 2.0的发布和功能改进。

希望能为初学者们在AWS认证备考上提供帮助,助他们提升技能!

你要做什么?

AWS Hands-on for Beginnersより引用

我知道CLI的操作可能会让人感到困难,但是我会尽量在解释的过程中使用图解来帮助理解!

环境

我使用的开发环境是WSL2 Ubuntu 20.04 LTS。由于有关WSL1无法启动Docker的报道,我建议将其升级至WSL2。(因为我最初尝试在WSL1上启动,遇到了相当大的困难)

在这篇文章中(关于在WSL上运行Docker时遇到的问题),似乎即使是在WSL1也可以运行。。。

以下是环境的详细信息。

    • Docker 23.0.4:Docker Desktop for Windowsは使わずUbuntuに直接インストールしています。

 

    • docker-compose v1.29.2

 

    • Python 3.8.10

 

    • pip 23.1.1

 

    Visual Studio Code

请注意:文章撰写时所提到的环境版本可能与最新的LocalStack使用条件不同,请及时查阅最新的LocalStack安装页面以获取准确信息。

保存LocalStack环境的方式

由于LocalStack在容器中运行,因此当停止容器时,所有数据都会消失。因此,LocalStack V2引入了Community Cloud Pods功能,可以使用它来进行快照。请务必使用该功能。参考:LocalStack Community Cloud Pods

操作步骤

    1. 本地堆栈 CLI 安装

 

    1. 参考:本地堆栈 CLI 安装

使用 localstack pod save file://<保存路径, 文件名> 命令保存快照

使用 localstack pod load file://<保存路径, 文件名> 命令加载快照

现在让我们启动LocalStack吧!

首先,我們將安裝並嘗試啟動LocalStack。雖然有很多安裝方法可選,但這次我們將使用docker-compose來啟動。

另外,我们还可以在启动LocalStack后使用localhost:4566/health来检查各种服务的状态。

# 作業用ディレクトリ作成後に作業ディレクトリに移動
Ubuntu@dev01:~$ mkdir ./workplace && cd $_
# GithubからLocalStackをクローン
Ubuntu@dev01:~/workplace$ git clone https://github.com/localstack/localstack.git
Ubuntu@dev01:~/workplace$ ls
localstack
Ubuntu@dev01:~/workplace$ cd localstack/
# docker composeでLocalStack起動
Ubuntu@dev01:~/workplace/localstack$ sudo docker-compose up -d
Ubuntu@dev01:~/workplace/localstack$ sudo docker ps -a
CONTAINER ID   IMAGE                   COMMAND                  CREATED         STATUS                   PORTS                                                                    NAMES
843a54664031   localstack/localstack   "docker-entrypoint.sh"   3 minutes ago   Up 3 minutes (healthy)   127.0.0.1:4510-4559->4510-4559/tcp, 127.0.0.1:4566->4566/tcp, 5678/tcp   localstack_main
# jsonを整形するツールjqインストール
Ubuntu@dev01:~/workplace/localstack$ sudo apt install jq
# LocalStackの利用可能なサービス一覧を確認する
Ubuntu@dev01:~/workplace/localstack$ curl localhost:4566/health | jq
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   876  100   876    0     0   171k      0 --:--:-- --:--:-- --:--:--  171k
{
  "services": {
    "acm": "available",
    "apigateway": "available",
    "cloudformation": "available",
    "cloudwatch": "available",
    "config": "available",
    "dynamodb": "available",
    "dynamodbstreams": "available",
    "ec2": "available",
    "es": "available",
    "events": "available",
    "firehose": "available",
    "iam": "available",
    "kinesis": "available",
    "kms": "available",
    "lambda": "available",
    "logs": "available",
    "opensearch": "available",
    "redshift": "available",
    "resource-groups": "available",
    "resourcegroupstaggingapi": "available",
    "route53": "available",
    "route53resolver": "available",
    "s3": "available",
    "s3control": "available",
    "secretsmanager": "available",
    "ses": "available",
    "sns": "available",
    "sqs": "available",
    "ssm": "available",
    "stepfunctions": "available",
    "sts": "available",
    "support": "available",
    "swf": "available",
    "transcribe": "available"
  },
  "version": "2.0.3.dev"
}

请使用AWS CLI来验证是否可以执行Lambda函数。

当使用AWS CLI时,由于根据选择的服务和区域自动确定端点URL,因此如果要使用LocalStack,需要使用–endpoint-url进行指定。

后来安装AWS CLI Local并使用awslcoal命令。如果使用awslocal命令,可以省略指定端点和配置文件,所以可以跳过安装AWS CLI。

# zip, unzipをインストール
Ubuntu@dev01:~$ sudo apt install zip unzip 

# AWS CLIインストーラーをダウンロード
Ubuntu@dev01:~$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
Ubuntu@dev01:~$ unzip awscliv2.zip
# AWS CLIをインストール
Ubuntu@dev01:~$ sudo ./aws/install

# AWS Credentialsを設定
Ubuntu@dev01:~$ aws configure --profile=localstack
AWS Access Key ID [None]:None
AWS Secret Access Key [None]:None
Default region name [None]: ap-notheast-1
Default output format [None]: json

请按照这个页面的说明,创建Lambda函数文件并压缩为zip文件。

# Lambdaで実行するファイルを作成する作業ディレクトリ作成して、移動
Ubuntu@dev01:~$ mkdir -p ./workplace/demo/ && cd $_
# 関数を作成する
Ubuntu@dev01:~/workplace/demo$ vi index.js
exports.handler = async function(event, context) {
          console.log("ENVIRONMENT VARIABLES\n" + JSON.stringify(process.env, null, 2))
          console.log("EVENT\n" + JSON.stringify(event, null, 2))
          return context.logStreamName
}

# 関数ファイルをデプロイパッケージにする
Ubuntu@dev01:~/workplace/demo$ zip function.zip index.js

接下来,使用lambda create-function命令将zip文件部署到Lambda。
有关lambda create-function命令的详细信息,请参阅此页面。

在LocalStack V1中,您可以像r1那样指定任意字符串,但在LocalStack V2中,角色的指定为arn:aws:iam::<12位数字>:role/<任意字符串>。不检查指定角色的存在与否,所以可以是任意值。请参考此页面以了解更多详细信息。
–handler选项指定为–handler <文件名>.<入口点函数名>。
–根据这个页面上的指示进行了runtime的设定。
# 作成したデプロイパッケージを使ってLambda関数を作成する
Ubuntu@dev01:~/workplace/demo$ aws --endpoint-url=http://localhost:4566 lambda create-function --function-name demo-function --zip-file fileb://function.zip --handler index.handler --runtime nodejs18.x --role arn:aws:iam::123456789012:role/lambda-demo1 --profile=localstack
{
    "FunctionName": "demo-function",
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:000000000000:function:demo-function",
    "Runtime": "nodejs18.x",
    "Role": "arn:aws:iam::123456789012:role/lambda-demo1",
    "Handler": "index.handler",
    "CodeSize": 325,
    "Description": "",
    "Timeout": 3,
    "MemorySize": 128,
    "LastModified": "2023-04-23T05:57:03.340517+0000",
    "CodeSha256": "c1MYsVaMfs4+EDf4IRHEVTtsD0X6CnwxI7OE2oqwN98=",
    "Version": "$LATEST",
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "a330e7cb-045e-488d-ac15-fec69a4abac2",
    "State": "Pending",
    "StateReason": "The function is being created.",
    "StateReasonCode": "Creating",
    "PackageType": "Zip",
    "Architectures": [
        "x86_64"
    ],
    "EphemeralStorage": {
        "Size": 512
    },
    "SnapStart": {
        "ApplyOn": "None",
        "OptimizationStatus": "Off"
    },
    "RuntimeVersionConfig": {
        "RuntimeVersionArn": "arn:aws:lambda:ap-northeast-1::runtime:8eeff65f6809a3ce81507fe733fe09b835899b99481ba22fd75b5a7338290ec1"
    }
}

在LocalStack中,由于Lambda是在容器中管理的,所以在首次调用Lambda时可能需要花费时间来下载映像。

# Lambdaを実行する
Ubuntu@dev01:~/workplace/demo$ aws --endpoint-url=http://localhost:4566 lambda invoke --function-name demo-function outfile --profile localstack
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}

安装AWS CLI Local

使用AWS CLI需要指定端点和配置文件,但使用LocalStack AWS CLI可以省略这些步骤,因此我们会进行安装。
LocalStack AWS CLI- Github

# pip経由でインストールするため、まずpipインストール
Ubuntu@dev01:~/workplace/localstack$ sudo apt install python3-pip
Ubuntu@dev01:~/workplace/localstack$ pip3 install awscli-local

# awslocalコマンドが使用可能か確認
Ubuntu@dev01:~/workplace/localstack$ awslocal --version
aws-cli/2.11.15 Python/3.11.3 Linux/5.15.90.1-microsoft-standard-WSL2 exe/x86_64.ubuntu.20 prompt/off

从现在开始,我们将使用AWS CLI Local(awslcoal)进行操作。

使用无服务器架构构建翻译Web API。

AWS Hands-on for Beginnersより引用
    • 手順

翻訳API呼び出すLambdaを作成する。
みんなの自動翻訳API呼び出すLambdaを作成する(Node.js)
Nodeをインストールする
Lambda関数の作成、デプロイしてテスト
API Gatewayの構築
API Gatewayの構築

创建调用DeppL API的Lambda函数

由于信用卡注册出现频繁错误,我感到灰心丧气。
我进行了一番调查,发现大家的自动翻译工具似乎非常易于使用,所以我决定尝试一下。

创建一个调用大家的自动翻译API的Lambda函数(使用Node.js)

aws-serverless-arch01.png

在Node.js中创建Lambda函数。如果想要在Lambda中使用外部模块如requests,就需要创建Lambda层。

哪怕是、、、

在LocalStack中,Lambda层是付费支持的,因此本次我们仅使用Node.js的标准模块进行创建!
参考:LocalStack Lambda层

安装Node

请根据这个页面(Node.js 在Linux的Windows子系统(WSL2)上安装)进行安装。
:::note warn
请注意,目前通过Ubuntu的apt-get命令安装的Node.js版本已经过期,请注意。
安装Node.js在Linux的Windows子系统(WSL2)上。

Ubuntu@dev01:~/workplace/localstack$ cd ../
Ubuntu@dev01:~/workplace/$ mkdir ./translate-function && cd $_
Ubuntu@dev01:~/workplace/translate-function$ mkdir nodejs && cd $_
Ubuntu@dev01:~/workplace/translate-function/nodejs$ curl -o- [https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh](https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh) | bash
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
100 15916  100 15916    0     0  46133      0 --:--:-- --:--:-- --:--:-- 46267
=> Downloading nvm from git to '/home/Ubuntu/.nvm'
=> Cloning into '/home/Ubuntu/.nvm'...
remote: Enumerating objects: 359, done.
remote: Counting objects: 100% (359/359), done.
remote: Compressing objects: 100% (305/305), done.
remote: Total 359 (delta 40), reused 168 (delta 28), pack-reused 0
Receiving objects: 100% (359/359), 219.46 KiB | 2.44 MiB/s, done.
Resolving deltas: 100% (40/40), done.

- (HEAD detached at FETCH_HEAD)
master
=> Compressing and cleaning up git repository

=> Appending nvm source string to /home/Ubuntu/.bashrc
=> Appending bash_completion source string to /home/Ubuntu/.bashrc
/usr/bin/env: ‘bash\r’: No such file or directory
=> Close and reopen your terminal to start using nvm or run the following to use it now:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

Ubuntu@dev01:~/workplace/translate-function/nodejs$ export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
 -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ nvm install --lts
Installing latest LTS version.
Downloading and installing node v18.16.0...
Downloading https://nodejs.org/dist/v18.16.0/node-v18.16.0-linux-x64.tar.xz...
############################################################################################################# 100.0%
Computing checksum with sha256sum
Checksums matched!
Now using node v18.16.0 (npm v9.5.1)
Creating default alias: default -> lts/* (-> v18.16.0)
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ node -v
v18.16.0
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ npm -v
9.5.1

创建Lambda函数并部署和测试。

通过将VScode与Ubuntu进行远程连接,可以顺利进行编码。
参考:在Windows上使用VS Code的Remote-WSL功能,访问WSL2中的Ubuntu 20.04。

# nodejs配下で作成していますが特に意図はありません
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ touch translate.js
const https = require("https");
const querystring = require("querystring");

const BASE_URL = "https://mt-auto-minhon-mlt.ucri.jgn-x.jp"; // 基底URL (https://xxx.jpまでを入力)
const API_KEY = "***"; // API key
const API_SECRET = "***"; // API secret
const LOGIN_ID = "***"; // ログインID
const API_NAME = "mt"; // API名 (https://xxx.jp/api/mt/generalNT_ja_en/ の場合は、"mt")
const API_PARAM = "generalNT_ja_en"; // API値 (https://xxx.jp/api/mt/generalNT_ja_en/ の場合は、"generalNT_ja_en")

const callTranslateApi = async (accessToken, input_text) => {
  const postData = querystring.stringify({
    access_token: accessToken,
    key: API_KEY, // API Key
    api_name: API_NAME,
    api_param: API_PARAM,
    name: LOGIN_ID, // ログインID
    type: "json", // レスポンスタイプ
    text: input_text, // 以下、APIごとのパラメータ
  });

  const options = {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      "Content-Length": postData.length,
    },
  };

  return new Promise((resolve, reject) => {
    const req = https.request(BASE_URL + "/api/", options, (res) => {
      let body = "";
      res.on("data", (chunk) => {
        body += chunk;
      });
      res.on("end", () => {
        bodyJson = JSON.parse(body);
        const output_text = bodyJson.resultset.result.text;
        resolve(output_text);
      });
    });
    req.on("error", (err) => {
      reject(err);
    });

    req.write(postData);
    req.end();
  });
};

const getAccessToken = async () => {
  const tokenData = querystring.stringify({
    grant_type: "client_credentials",
    client_id: API_KEY, // API Key
    client_secret: API_SECRET, // API secret
    urlAccessToken: BASE_URL + "/oauth2/token.php", // アクセストークン取得URI
  });

  const tokenOptions = {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      "Content-Length": tokenData.length,
    },
  };

  return new Promise((resolve, reject) => {
    const tokenReq = https.request(
      BASE_URL + "/oauth2/token.php",
      tokenOptions,
      (res) => {
        let body = "";
        res.on("data", (chunk) => {
          body += chunk;
        });
        res.on("end", () => {
          const accessToken = JSON.parse(body).access_token; // アクセストークン
          if (!accessToken) {
            console.error("Access token not found.");
            reject("Error");
          } else {
            resolve(accessToken);
          }
        });
      }
    );

    tokenReq.on("error", (err) => {
      reject(err);
    });
    tokenReq.write(tokenData);
    tokenReq.end();
  });
};

exports.handler = async (event, context) => {
  try {
    const INPUT_TEXT = "おはようございます。調子はどうですか";
    const accessToken = await getAccessToken();
    const OUTPUT_TEXT = await callTranslateApi(accessToken, INPUT_TEXT);

    console.log("INPUT_TEXT : " + INPUT_TEXT);
    console.log("OUTPUT_TEXT : " + OUTPUT_TEXT);

    const response = {
      statusCode: 200,
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        message: "success",
        OUTPUT_TEXT: OUTPUT_TEXT,
      }),
    };

    return response;
  } catch (error) {
    console.log(error.stack);
  }
};

将编写好的translate.js文件压缩成zip格式,然后部署到Lambda函数中。

Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ zip translate.zip translate.js
adding: translate.js (deflated 62%)
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal lambda create-function --function-name translateFunction01 --runtime nodejs18.x --handler translate.handler --zip-file fileb://translate.zip --role arn::iam::000000000000:role/demo
{
    "FunctionName": "translateFunction01",
    "FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:translateFunction01",
    "Runtime": "nodejs18.x",
    "Role": "arn::iam::000000000000:role/demo",
    "Handler": "translate.handler",
    "CodeSize": 1516,
    "Description": "",
    "Timeout": 3,
    "MemorySize": 128,
    "LastModified": "2023-05-14T01:40:23.596074+0000",
    "CodeSha256": "/GgVaQ6xCUWytX6IzPI9QMXYDMVG5FrgdvebCwmwcEo=",
    "Version": "$LATEST",
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "f43c33e0-bcf0-460b-bca1-9cbdcc33efa0",
    "State": "Pending",
    "StateReason": "The function is being created.",
    "StateReasonCode": "Creating",
    "PackageType": "Zip",
    "Architectures": [
        "x86_64"
    ],
    "EphemeralStorage": {
        "Size": 512
    },
    "SnapStart": {
        "ApplyOn": "None",
        "OptimizationStatus": "Off"
    },
    "RuntimeVersionConfig": {
        "RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:8eeff65f6809a3ce81507fe733fe09b835899b99481ba22fd75b5a7338290ec1"
    }
}

你可以使用lambda list-functions来确认部署的Lambda函数。参考:Lambda list-functions

Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal lambda list-functions
{
    "Functions": [
        {
            "FunctionName": "translateFunction",
            "FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:translateFunction",
            "Runtime": "nodejs18.x",
            "Role": "arn::iam::000000000000:role/demo",
            "Handler": "translate.handler",
            "CodeSize": 1516,
            "Description": "",
            "Timeout": 3,
            "MemorySize": 128,
            "LastModified": "2023-05-14T01:50:32.206828+0000",
            "CodeSha256": "/GgVaQ6xCUWytX6IzPI9QMXYDMVG5FrgdvebCwmwcEo=",
            "Version": "$LATEST",
            "TracingConfig": {
                "Mode": "PassThrough"
            },
            "RevisionId": "6b458364-8281-4d8c-89b5-c58f6513a692",
            "PackageType": "Zip",
            "Architectures": [
                "x86_64"
            ],
            "EphemeralStorage": {
                "Size": 512
            },
            "SnapStart": {
                "ApplyOn": "None",
                "OptimizationStatus": "Off"
            }
        }
    ]
}

我尝试执行部署的Lambda函数。
参考:Lambda调用

Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal lambda invoke --function-name translateFunction outputfile.txt
{
    "StatusCode": 200,
    "FunctionError": "Unhandled",
    "ExecutedVersion": "$LATEST"
}

出现了 FunctionError?‍♂️
我将检查输出的outputfile.txt文件。
参考:Node.js的AWS Lambda函数错误。

Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ cat outputfile.txt
{"errorMessage":"2023-05-14T02:25:34Z fa5edebc-c824-46f0-8283-3fc8c474c438 Task timed out after 3.00 seconds"}

由於出現超時錯誤,我們將將 Lambda 函數的執行時間設定為 60 秒並重新執行 ?(預設執行時間為 3 秒)。
您可以使用 update-function-configuration 命令進行設定。除了執行時間之外,您還可以更改內存大小,詳情請參閱以下頁面。
參考:Lambda update-function-configuration

Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal lambda update-function-configuration --function-name translateFunction --timeout 60
{
    "FunctionName": "translateFunction",
    "FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:translateFunction",
    "Runtime": "nodejs18.x",
    "Role": "arn::iam::000000000000:role/demo",
    "Handler": "translate.handler",
    "CodeSize": 1516,
    "Description": "",
    "Timeout": 60,
    "MemorySize": 128,
    "LastModified": "2023-05-14T02:32:45.737909+0000",
    "CodeSha256": "/GgVaQ6xCUWytX6IzPI9QMXYDMVG5FrgdvebCwmwcEo=",
    "Version": "$LATEST",
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "327b9f37-5db2-447d-9114-1eeb3477c11c",
    "State": "Active",
    "LastUpdateStatus": "InProgress",
    "LastUpdateStatusReason": "The function is being created.",
    "LastUpdateStatusReasonCode": "Creating",
    "PackageType": "Zip",
    "Architectures": [
        "x86_64"
    ],
    "EphemeralStorage": {
        "Size": 512
    },
    "SnapStart": {
        "ApplyOn": "None",
        "OptimizationStatus": "Off"
    },
    "RuntimeVersionConfig": {
        "RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:8eeff65f6809a3ce81507fe733fe09b835899b99481ba22fd75b5a7338290ec1"
    }
}
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal lambda invoke --function-name translateFunction   outputfile.txt
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}

由于更改了执行时间,Lambda函数能够成功执行,因此我们将继续检查输出的日志。
参考:使用命令行执行AWS Lambda并将日志输出到标准输出。

Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal lambda invoke --function-name translateFunction --log-type Tail outputfile.txt --query 'LogResult' | tr -d '"' | base64 -d
START RequestId: 37d137f7-7503-464c-aa84-87db0bc383e6 Version: $LATEST
2023-05-14T02:39:27.955Z        37d137f7-7503-464c-aa84-87db0bc383e6    INFO    INPUT_TEXT : おはようございます。調子は どうですか
2023-05-14T02:39:27.957Z        37d137f7-7503-464c-aa84-87db0bc383e6    INFO    OUTPUT_TEXT : Good morning. How are you?
END RequestId: 37d137f7-7503-464c-aa84-87db0bc383e6
REPORT RequestId: 37d137f7-7503-464c-aa84-87db0bc383e6  Duration: 4079.20 ms    Billed Duration: 4080 ms        Memory Size: 128 MB     Max Memory Used: 128 MB
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ 

搭建API网关和Lambda的协同连接。

aws-serverless-arch01.png
api-gateway.png

创建REST API

在 API Gateway 中创建的命令是 apigateway create-rest-api。在执行命令后显示的 id 是 API 的标识(请注意,不是资源的标识)。

请充分利用apigateway get-rest-apis命令,可以以列表形式查看已创建的API。

如果不指定endpoint-configuration,则默认为EDGE,在这里我们指定为REGIONAL。

创建REST API
20190514 AWS黑带在线研讨会 亚马逊API网关

Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway create-rest-api --name translate-api --endpoint-configuration types=REGIONAL
{
    "id": "hpvqgkbms0",
    "name": "translate-api",
    "createdDate": "2023-05-14T12:30:39+09:00",
    "apiKeySource": "HEADER",
    "endpointConfiguration": {
        "types": [
            "REGIONAL"
        ]
    },
    "disableExecuteApiEndpoint": false
}
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway get-rest-apis
{
    "items": [
        {
            "id": "hpvqgkbms0",
            "name": "translate-api",
            "createdDate": "2023-05-14T12:30:39+09:00",
            "apiKeySource": "HEADER",
            "endpointConfiguration": {
                "types": [
                    "REGIONAL"
                ]
            },
            "disableExecuteApiEndpoint": false
        }
    ]
}

创建测试资源

创建资源需要API ID和指定父资源ID的位置。

使用apigateway get-resources –rest-api-id 命令可以查看资源的列表。
# API IDを確認する
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway get-rest-apis
{
    "items": [
        {
            "id": "hpvqgkbms0",
            "name": "translate-api",
            "createdDate": "2023-05-14T12:30:39+09:00",
            "apiKeySource": "HEADER",
            "endpointConfiguration": {
                "types": [
                    "REGIONAL"
                ]
            },
            "disableExecuteApiEndpoint": false
        }
    ]
}

确认资源ID。在创建API之后,将会创建一个资源。新的资源将被创建在现有资源的下一级。

# リソースIDを確認する
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway get-resources --rest-api-id hpvqgkbms0
{
    "items": [
        {
            "id": "55ezw5mmr6",
            "path": "/"
        }
    ]
}

然后使用apigateway create-resource命令创建一个名为/sample的测试资源。

为了在下级目录中创建,指定了父资源ID给parent-id。

参考:apigateway 创建资源。

Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway create-resource --rest-api-id hpvqgkbms0 --parent-id 55ezw5mmr6 --path-part sample
{
    "id": "zqgeelqr0b",
    "parentId": "55ezw5mmr6",
    "pathPart": "sample",
    "path": "/sample"
}
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway get-resources --rest-api-id hpvqgkbms0
{
    "items": [
        {
            "id": "55ezw5mmr6",
            "path": "/"
        },
        {
            "id": "zqgeelqr0b",
            "parentId": "55ezw5mmr6",
            "pathPart": "sample",
            "path": "/sample"
        }
    ]
}

使用apigateway put-method命令在/sample资源中添加GET方法。
参考:API Gateway put-method

# API IDを確認する
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$  awslocal apigateway get-rest-apis
{
    "items": [
        {
            "id": "hpvqgkbms0",
            "name": "translate-api",
            "createdDate": "2023-05-14T12:30:39+09:00",
            "apiKeySource": "HEADER",
            "endpointConfiguration": {
                "types": [
                    "REGIONAL"
                ]
            },
            "disableExecuteApiEndpoint": false
        }
    ]
}
# GETメソッドの追加
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway put-method  --rest-api-id hpvqgkbms0 --resource-id zqgeelqr0b --http-method GET --authorization-type NONE
{
    "httpMethod": "GET",
    "authorizationType": "NONE",
    "apiKeyRequired": false
}

# /sampleリソースにGETメソッドが追加できたことを確認する
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$  awslocal apigateway get-resources --rest-api-id hpvqgkbms0
{
    "items": [
        {
            "id": "55ezw5mmr6",
            "path": "/"
        },
        {
            "id": "zqgeelqr0b",
            "parentId": "55ezw5mmr6",
            "pathPart": "sample",
            "path": "/sample",
            "resourceMethods": {
                "GET": {
                    "httpMethod": "GET",
                    "authorizationType": "NONE",
                    "apiKeyRequired": false,
                    "methodResponses": {}
                }
            }
        }
    ]
}

这样,您可以从客户端向/sample资源发送GET请求。然后,使用put-integration命令来定义集成请求(即从API网关发送请求到后端,并指定后端)。为了测试目的,我们暂时将–type设置为MOCK,并固定响应。
参考:
API网关put-integration命令
API集成请求的基本任务

在Windows命令提示符中,需要转义引号。更多详细信息请参见以下页面。
参考:在AWS CLI中使用引号的字符串
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway put-integration --rest-api-id hpvqgkbms0 --resource-id zqgeelqr0b --http-method GET --type MOCK --request-template '{"application/json" : "{\"report_id\": \"1\", \"report_title\": \"Hello World\"}"}'
{
    "type": "MOCK",
    "requestParameters": {},
    "requestTemplates": {
        "application/json": "{\"report_id\": \"1\", \"report_title\": \"Hello World\"}"
    },
    "cacheNamespace": "zqgeelqr0b",
    "cacheKeyParameters": []
}

在部署之前,使用apigateway test-invoke-method命令进行测试?。

Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway test-invoke-method --rest-api-id hpvqgkbms0 --resource-id zqgeelqr0b --http-method GET | jq .
{
  "status": 200,
  "body": "{\"report_id\": \"1\", \"report_title\": \"Hello World\"}",
  "headers": {
    "Host": "localhost:4566",
    "Accept-Encoding": "identity",
    "Content-Type": "application/json",
    "User-Agent": "aws-cli/2.11.15 Python/3.11.3 Linux/5.15.90.1-microsoft-standard-WSL2 exe/x86_64.ubuntu.20 prompt/off command/apigateway.test-invoke-method",
    "Accept": "application/json",
    "X-Amz-Date": "20230514T113523Z",
    "Authorization": "AWS4-HMAC-SHA256 Credential=test/20230514/us-east-1/apigateway/aws4_request, SignedHeaders=accept;content-type;host;x-amz-date, Signature=37de1d70843bf497460854f3f549fec76ee7ebf947b9d545f7b8847c70da07cc",
    "Content-Length": "2",
    "x-localstack-tgt-api": "apigateway",
    "x-moto-account-id": "000000000000",
    "X-Forwarded-For": "172.20.0.1, localhost:4566",
    "x-localstack-edge": "http://localhost:4566"
  }
}

使用apigateway create-deployment命令创建一个阶段(在此处为dev),以发布API。
参考:API create-deployment

# API IDを確認
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway get-rest-apis
{
    "items": [
        {
            "id": "hpvqgkbms0",
            "name": "translate-api",
            "createdDate": "2023-05-14T12:30:39+09:00",
            "apiKeySource": "HEADER",
            "endpointConfiguration": {
                "types": [
                    "REGIONAL"
                ]
            },
            "disableExecuteApiEndpoint": false
        }
    ]
}
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway create-deployment --rest-api-id hpvqgkbms0 --stage-name dev
{
    "id": "yxoalhr8v5",
    "createdDate": "2023-05-14T20:31:55+09:00"
}

由于部署已完成,我将调用API以确认是否返回响应。

LocalStack的API Gateway的默认端点为http://localhost:4566/restapis//<阶段名称>/_user_request_/<资源名称>。
参考:LocalStack亚马逊 API Gateway
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ curl -X GET http://localhost:4566/restapis/hpvqgkbms0/dev/_user_request_/sample
{"report_id": "1", "report_title": "Hello World"}
brows-api.png

创建翻译资源

现在我们已经完成了API测试的执行,接下来将开始创建translate资源!

Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway create-resource --rest-api-id hpvqgkbms0 --parent-id 55ezw5mmr6 --path-part translate
{
    "id": "2ebfoxwva3",
    "parentId": "55ezw5mmr6",
    "pathPart": "translate",
    "path": "/translate"
}
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway put-method --rest-api-id hpvqgkbms0 -
-resource-id 2ebfoxwva3 --http-method GET --authorization-type NONE
{
    "httpMethod": "GET",
    "authorizationType": "NONE",
    "apiKeyRequired": false
}

在API Gateway中,我们将为资源添加GET方法,并使用–request-parameters命令来接受input_text参数。

Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway put-method  --rest-api-id hpvqgkbms0
--resource-id 2ebfoxwva3 --http-method GET --authorization-type NONE  --request-parameters "{\"method.request.querystrin
g.input_text\": true}"
{
    "httpMethod": "GET",
    "authorizationType": "NONE",
    "apiKeyRequired": false,
    "requestParameters": {
        "method.request.querystring.input_text": true
    }
}

接下来,我们将进行集成请求的配置。在这里,您需要使用–type AWS_PROXY将后端集成到Lambda函数,并使用–uri指定要集成的Lambda函数的ARN。

参考:API Gateway put-integration –type

指定Lambda选项命令是–uri。值为arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions//invocations,在LocalStack中它是固定的。参考:LocalStack API Gateway –uri
# 統合したいLambda関数のARNを確認します
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$  awslocal lambda get-function --function-name translateFunction
{
    "Configuration": {
        "FunctionName": "translateFunction",
        "FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:translateFunction",
        "Runtime": "nodejs18.x",
        "Role": "arn::iam::000000000000:role/demo",
        "Handler": "translate.handler",
        "CodeSize": 1504,
        "Description": "",
        "Timeout": 60,
        "MemorySize": 128,
        "LastModified": "2023-05-14T02:39:20.431503+0000",
        "CodeSha256": "GQG2B5diM6Pe1+yZ7MkxoINkq25iX07DF1d/Co9WZpM=",
        "Version": "$LATEST",
        "TracingConfig": {
            "Mode": "PassThrough"
        },
        "RevisionId": "9d5a5a51-dcaa-49f1-8103-a0fb39964d59",
        "State": "Active",
        "LastUpdateStatus": "Successful",
        "PackageType": "Zip",
        "Architectures": [
            "x86_64"
        ],
        "EphemeralStorage": {
            "Size": 512
        },
        "SnapStart": {
            "ApplyOn": "None",
            "OptimizationStatus": "Off"
        },
        "RuntimeVersionConfig": {
            "RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:8eeff65f6809a3ce81507fe733fe09b835899b99481ba22fd75b5a7338290ec1"
        }
    },
    "Code": {
        "RepositoryType": "S3",
        "Location": "http://s3.localhost.localstack.cloud:4566/awslambda-us-east-1-tasks/snapshots/000000000000/translateFunction-e510ad6d-bc56-4bb9-bf13-b21b2ed9dcd6?AWSAccessKeyId=000000000000&Signature=aKGyEdrwShjbF6AQIuWyuu5ufTg%3D&Expires=1684073259"
    }
}
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal apigateway put-integration --rest-api-id hpvqgkbms0 --resource-id 2ebfoxwva3 --http-method GET --integration-http-method GET --type AWS_PROXY --uri arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:000000000000:function:translateFunction/invocations
{
    "type": "AWS_PROXY",
    "httpMethod": "GET",
    "uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:000000000000:function:translateFunction/invocations",
    "requestParameters": {},
    "cacheNamespace": "2ebfoxwva3",
    "cacheKeyParameters": []
}

在Lambda函数中,我将进行修改以接收参数。(https://maku.blog/p/5mv5dkt/)

const https = require("https");
const querystring = require("querystring");

const BASE_URL = "https://mt-auto-minhon-mlt.ucri.jgn-x.jp"; // 基底URL (https://xxx.jpまでを入力)
const API_KEY = "***"; // API key
const API_SECRET = "***"; // API secret
const LOGIN_ID = "***"; // ログインID
const API_NAME = "mt"; // API名 (https://xxx.jp/api/mt/generalNT_ja_en/ の場合は、"mt")
const API_PARAM = "generalNT_ja_en"; // API値 (https://xxx.jp/api/mt/generalNT_ja_en/ の場合は、"generalNT_ja_en")

const callTranslateApi = async (accessToken, input_text) => {
  const postData = querystring.stringify({
    access_token: accessToken,
    key: API_KEY, // API Key
    api_name: API_NAME,
    api_param: API_PARAM,
    name: LOGIN_ID, // ログインID
    type: "json", // レスポンスタイプ
    text: input_text, // 以下、APIごとのパラメータ
  });

  const options = {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      "Content-Length": postData.length,
    },
  };

  return new Promise((resolve, reject) => {
    const req = https.request(BASE_URL + "/api/", options, (res) => {
      let body = "";
      res.on("data", (chunk) => {
        body += chunk;
      });
      res.on("end", () => {
        bodyJson = JSON.parse(body);
        const output_text = bodyJson.resultset.result.text;
        resolve(output_text);
      });
    });
    req.on("error", (err) => {
      reject(err);
    });

    req.write(postData);
    req.end();
  });
};

const getAccessToken = async () => {
  const tokenData = querystring.stringify({
    grant_type: "client_credentials",
    client_id: API_KEY, // API Key
    client_secret: API_SECRET, // API secret
    urlAccessToken: BASE_URL + "/oauth2/token.php", // アクセストークン取得URI
  });

  const tokenOptions = {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      "Content-Length": tokenData.length,
    },
  };

  return new Promise((resolve, reject) => {
    const tokenReq = https.request(
      BASE_URL + "/oauth2/token.php",
      tokenOptions,
      (res) => {
        let body = "";
        res.on("data", (chunk) => {
          body += chunk;
        });
        res.on("end", () => {
          const accessToken = JSON.parse(body).access_token; // アクセストークン
          if (!accessToken) {
            console.error("Access token not found.");
            reject("Error");
          } else {
            resolve(accessToken);
          }
        });
      }
    );

    tokenReq.on("error", (err) => {
      reject(err);
    });
    tokenReq.write(tokenData);
    tokenReq.end();
  });
};

exports.handler = async (event, context) => {
  try {
    let message = "failed...";
    let output_text = "none";
    if (event.queryStringParameters !== null) {
      const INPUT_TEXT = event.queryStringParameters.input_text;
      const accessToken = await getAccessToken();
      output_text = await callTranslateApi(accessToken, INPUT_TEXT);
      message = "success";
      console.log("INPUT_TEXT : " + INPUT_TEXT);
    }
    console.log("output_text : " + output_text);

    const response = {
      statusCode: 200,
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        message: message,
        output_text: output_text,
      }),
    };

    return response;
  } catch (error) {
    console.log(error.stack);
  }
};

将修正后的 translate.js 压缩为 zip 文件,并使用 lambda update-function-code 命令来更新 translateFunction。
参考:Lambda update-function-code

Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ zip translate.zip translate.js
updating: translate.js (deflated 62%)
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal lambda update-function-code --function-name translateFunction --zip-file fileb://translate.zip
    "Description": "",
    "Timeout": 60,
    "MemorySize": 128,
    "LastModified": "2023-05-14T15:05:13.069049+0000",
    "CodeSha256": "HnS2h+dTY1696Pz9BVMmS+qrC4cLpG5Lls2kruAKWNQ=",
    "Version": "$LATEST",
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "6b97a5ef-763d-462a-85f3-4a3b89346e1a",
    "State": "Active",
    "LastUpdateStatus": "InProgress",
    "LastUpdateStatusReason": "The function is being created.",
    "LastUpdateStatusReasonCode": "Creating",
    "PackageType": "Zip",
    "Architectures": [
        "x86_64"
    ],
    "EphemeralStorage": {
        "Size": 512
    },
    "SnapStart": {
        "ApplyOn": "None",
        "OptimizationStatus": "Off"
    },
    "RuntimeVersionConfig": {
        "RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:8eeff65f6809a3ce81507fe733fe09b835899b99481ba22fd75b5a7338290ec1"
    }
}

image.png

構築DynamoDB

创建 DynamoDB 表格

–attribute-definitionsでは属性とその属性のタイプを指定します。下記のコードでは属性名timestampでタイプが文字列です。

–key-schemaではプライマリーを構成する属性を指定します(–attribute-definitionsで指定している中から選択します)下記のコードではtimestamp属性をパーティションキーと指定しています。
参考:DynamoDB create-table

如果不指定 billing-mode,则默认为 PROVISIONED,因此必须指定 ReadCapacityUnits 和 WriteCapacityUnits。
参考:读取/写入容量模式。
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal dynamodb create-table --table-name translate-history --attribute-definitions AttributeName=timestamp,AttributeType=S --key-schema AttributeName=timestamp,KeyType=HASH --billing-mode PAY_PER_REQUEST
{
    "TableDescription": {
        "AttributeDefinitions": [
            {
                "AttributeName": "timestamp",
                "AttributeType": "S"
            }
        ],
        "TableName": "translate-history",
        "KeySchema": [
            {
                "AttributeName": "timestamp",
                "KeyType": "HASH"
            }
        ],
        "TableStatus": "ACTIVE",
        "CreationDateTime": "2023-05-20T17:29:44.130000+09:00",
        "ProvisionedThroughput": {
            "LastIncreaseDateTime": "1970-01-01T09:00:00+09:00",
            "LastDecreaseDateTime": "1970-01-01T09:00:00+09:00",
            "NumberOfDecreasesToday": 0,
            "ReadCapacityUnits": 0,
            "WriteCapacityUnits": 0
        },
        "TableSizeBytes": 0,
        "ItemCount": 0,
        "TableArn": "arn:aws:dynamodb:us-east-1:000000000000:table/translate-history",
        "TableId": "d7231038-ef8f-4b49-a830-1d2b616b8978",
        "BillingModeSummary": {
            "BillingMode": "PAY_PER_REQUEST",
            "LastUpdateToPayPerRequestDateTime": "2023-05-20T17:29:44.130000+09:00"
        }
    }
}

我将尝试通过CLI添加项目并进行确认。

使用put-item函数来添加项,并使用scan函数来查看表中的所有项。
参考:
DynamoDB put-item
DynamoDB scan

Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal dynamodb put-item --table-name translate-history --item "{\"timestamp\":{\"S\":\"203505082144\"},\"input_text\":{\"S\":\"おはようございます\"},\"output_text\":{\"S\":\"Good morning\"}}"
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal dynamodb scan --table-name translate-history
{
    "Items": [
        {
            "input_text": {
                "S": "おはようございます"
            },
            "timestamp": {
                "S": "203505082144"
            },
            "output_text": {
                "S": "Good morning"
            }
        }
    ],
    "Count": 1,
    "ScannedCount": 1,
    "ConsumedCapacity": null
}

将Lambda和DynamoDB进行整合

aws-serverless-arch01.png

参考:
使用Node.js操作DynamoDB(适用于SDK版本3)
使用@aws-sdk/client-dynamod的PutItemCommand命令。

如果在LocalStack上使用AWS SDK,必须指定端点为http://${LOCALSTACK_HOSTNAME}:${EDGE_PORT}。在以下的代码中已经指定了DynamoDBClient的位置。
参考:LocalStack Transparent Endpoint Injection
const https = require("https");
const querystring = require("querystring");
const { DynamoDBClient, PutItemCommand } = require("@aws-sdk/client-dynamodb");

const BASE_URL = "https://mt-auto-minhon-mlt.ucri.jgn-x.jp"; // 基底URL (https://xxx.jpまでを入力)
const API_KEY = "***"; // API key
const API_SECRET = "***"; // API secret
const LOGIN_ID = "***"; // ログインID
const API_NAME = "mt"; // API名 (https://xxx.jp/api/mt/generalNT_ja_en/ の場合は、"mt")
const API_PARAM = "generalNT_ja_en"; // API値 (https://xxx.jp/api/mt/generalNT_ja_en/ の場合は、"generalNT_ja_en")

const callTranslateApi = async (accessToken, input_text) => {
  const postData = querystring.stringify({
    access_token: accessToken,
    key: API_KEY, // API Key
    api_name: API_NAME,
    api_param: API_PARAM,
    name: LOGIN_ID, // ログインID
    type: "json", // レスポンスタイプ
    text: input_text, // 以下、APIごとのパラメータ
  });

  const options = {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      "Content-Length": postData.length,
    },
  };

  return new Promise((resolve, reject) => {
    const req = https.request(BASE_URL + "/api/", options, (res) => {
      let body = "";
      res.on("data", (chunk) => {
        body += chunk;
      });
      res.on("end", () => {
        bodyJson = JSON.parse(body);
        const output_text = bodyJson.resultset.result.text;
        resolve(output_text);
      });
    });
    req.on("error", (err) => {
      reject(err);
    });

    req.write(postData);
    req.end();
  });
};

const getAccessToken = async () => {
  const tokenData = querystring.stringify({
    grant_type: "client_credentials",
    client_id: API_KEY, // API Key
    client_secret: API_SECRET, // API secret
    urlAccessToken: BASE_URL + "/oauth2/token.php", // アクセストークン取得URI
  });

  const tokenOptions = {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      "Content-Length": tokenData.length,
    },
  };

  return new Promise((resolve, reject) => {
    const tokenReq = https.request(
      BASE_URL + "/oauth2/token.php",
      tokenOptions,
      (res) => {
        let body = "";
        res.on("data", (chunk) => {
          body += chunk;
        });
        res.on("end", () => {
          const accessToken = JSON.parse(body).access_token; // アクセストークン
          if (!accessToken) {
            console.error("Access token not found.");
            reject("Error");
          } else {
            resolve(accessToken);
          }
        });
      }
    );

    tokenReq.on("error", (err) => {
      reject(err);
    });
    tokenReq.write(tokenData);
    tokenReq.end();
  });
};

const callDynamoDB = async (input_text, output_text) => {
  const dynamoConfig = {
    endpoint: `http://${process.env["LOCALSTACK_HOSTNAME"]}:${process.env["EDGE_PORT"]}`,
    region: "us-east-1",
  };
  const dynamoDBClient = new DynamoDBClient(dynamoConfig);

  const createdAt = new Date().toLocaleDateString("ja-JP", {
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
  });

  const putItemCommand = new PutItemCommand({
    TableName: "translate-history",
    Item: {
      timestamp: { S: createdAt },
      input_text: { S: input_text },
      output_text: { S: output_text },
    },
  });
  await dynamoDBClient.send(putItemCommand);
};
exports.handler = async (event, context) => {
  try {
    let message = "failed...";
    let output_text = "none";
    if (event.queryStringParameters !== null) {
      const INPUT_TEXT = event.queryStringParameters.input_text;
      const accessToken = await getAccessToken();
      output_text = await callTranslateApi(accessToken, INPUT_TEXT);
      message = "success";
      await callDynamoDB(INPUT_TEXT, output_text);
    }

    const response = {
      statusCode: 200,
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        message: message,
        output_text: output_text,
      }),
    };

    return response;
  } catch (error) {
    console.log(error.stack);
  }
};

将修正过的Lambda函数打包成zip文件并进行部署。

Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ zip translate.zip translate.js
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal lambda update-function-code --function-name translateFunction --zip-file fileb://translate.zip
{
    "FunctionName": "translateFunction",
    "FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:translateFunction",
    "Runtime": "nodejs18.x",
    "Role": "arn::iam::000000000000:role/demo",
    "Handler": "translate.handler",
    "CodeSize": 3500,
    "Description": "",
    "Timeout": 60,
    "MemorySize": 128,
    "LastModified": "2023-05-20T09:39:03.989290+0000",
    "CodeSha256": "eXu7kuOEmiXkW5T85scNKFpH9a2akXman1OxKFjxNWk=",
    "Version": "$LATEST",
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "61d78423-094c-4bc6-a242-7b55f891e4fa",
    "State": "Active",
    "LastUpdateStatus": "InProgress",
    "LastUpdateStatusReason": "The function is being created.",
    "LastUpdateStatusReasonCode": "Creating",
    "PackageType": "Zip",
    "Architectures": [
        "x86_64"
    ],
    "EphemeralStorage": {
        "Size": 512
    },
    "SnapStart": {
        "ApplyOn": "None",
        "OptimizationStatus": "Off"
    },
    "RuntimeVersionConfig": {
        "RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:8eeff65f6809a3ce81507fe733fe09b835899b99481ba22fd75b5a7338290ec1"
    }
}

确认API Gateway、Lambda、翻译API和DynamoDB是否已连接。

image.png
Ubuntu@dev01:~/workplace/translate-function/node/nodejs$ awslocal dynamodb scan --table-name translate-history
{
    "Items": [
        {
            "input_text": {
                "S": "おはようございます"
            },
            "timestamp": {
                "S": "203505082144"
            },
            "output_text": {
                "S": "Good morning"
            }
        },
        {
            "input_text": {
                "S": "これでハンズオンは完了です"
            },
            "timestamp": {
                "S": "2023/05/20"
            },
            "output_text": {
                "S": "This completes the hands-on."
            }
        }
    ],
    "Count": 2,
    "ScannedCount": 2,
    "ConsumedCapacity": null
}

我确认DynamoDB中保存了历史记录!恭喜?

结束

感谢您阅读【AWS资格准备】免费实践学习AWS入门#1【初学者专属】!本文的实践部分到此结束。
希望这对于追求AWS资格认证的人们能成为有价值的信息源?

广告
将在 10 秒后关闭
bannerAds