TerraformとecspressoでAmazon ECSを構築してAWS AppConfigの機能フラグを活用する
yassan
デロイト トーマツ ウェブサービス株式会社(DWS)公式ブログ
こんにちは、関口です。
最近はUber EatsとAmazonフレッシュを多用して以前にもまして引きこもり気味です。
今回は現状所属しているプロジェクトのキャッチアップ的意味合いを込めて、
ポジティブなワードを返してくれる自動LineBotを
AWS SAMとDynamoDBで試してみました。
まずは雛形のプロジェクトを作成します。
$ sam init --runtime go1.x --name positive-line-bot
$ cd positive-line-bot
//依存モジュール管理
$ go mod init line-positive-bot
Lineと連携する前に、
DynamoDBから自動返信されるワードを取得する部分を実装します。
一覧をDynamoDBのテーブルから取得してきて、
そのうち1つをランダムに抽出しています。
type Positive struct {
ID int `json:"ID"`
Name string `json:"Name"`
}
func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
endpoint := os.Getenv("DYNAMODB_ENDPOINT")
tableName := os.Getenv("DYNAMODB_TABLE_NAME")
sess := session.Must(session.NewSession())
config := aws.NewConfig().WithRegion("ap-northeast-1")
if len(endpoint) > 0 {
config = config.WithEndpoint(endpoint)
}
db := dynamodb.New(sess, config)
result, err := db.Scan(&dynamodb.ScanInput{
TableName: aws.String(tableName),
ConsistentRead: aws.Bool(true),
ReturnConsumedCapacity: aws.String("NONE"),
})
if err != nil {
return events.APIGatewayProxyResponse{}, err
}
var positives []Positive
err = dynamodbattribute.UnmarshalListOfMaps(result.Items, &positives)
if err != nil {
fmt.Println(err)
return events.APIGatewayProxyResponse{}, err
}
var words []string
for _, positive := range positives {
words = append(words, positive.Name)
}
rand.Seed(time.Now().UnixNano())
i := rand.Intn(len(words))
word := words[i]
return events.APIGatewayProxyResponse{
Body: word,
StatusCode: 200,
}, nil
}
func main() {
lambda.Start(handler)
}
template.yml
Resources:
PositiveLineBotFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: positive-line-bot/
Handler: positive-line-bot
Runtime: go1.x
Tracing: Active
Policies:
- arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess
Events:
CatchAll:
Type: Api
Properties:
Path: /positive
Method: GET
Environment:
Variables:
DYNAMODB_ENDPOINT: ''
DYNAMODB_TABLE_NAME: 'PositiveLineBotTable'
PositiveLineBotTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: 'PositiveLineBotTable'
AttributeDefinitions:
- AttributeName: 'ID'
AttributeType: 'N'
KeySchema:
- AttributeName: 'ID'
KeyType: 'HASH'
ProvisionedThroughput:
ReadCapacityUnits: '2'
WriteCapacityUnits: '2'
うまくできているかどうかローカルで確認します。
docker-compose.yml
version: '3'
services:
dynamodb:
image: amazon/dynamodb-local
container_name: dynamodb
ports:
- 8000:8000
ローカルテストで用いるテーブル定義および、テストデータを作成します。
test/positive-line-bot_table.json
{
"AttributeDefinitions": [
{
"AttributeName": "Id",
"AttributeType": "N"
}
],
"TableName": "PositiveLineBotTable",
"KeySchema": [
{
"AttributeName": "Id",
"KeyType": "HASH"
}
],
"ProvisionedThroughput": {
"ReadCapacityUnits": 2,
"WriteCapacityUnits": 2
}
}
test/positive-line-bot_table_data.json
{
"PositiveLineBotTable": [
{
"PutRequest": {
"Item": {
"ID": {"N": "1"},
"Name": {"S": "test"}
}
}
},
{
"PutRequest": {
"Item": {
"ID": {"N": "2"},
"Name": {"S": "testtest"}
}
}
}
]
}
ローカルテスト用の環境変数を設定します
{
"PositiveLineBotFunction": {
"DYNAMODB_ENDPOINT": "http://{ポート番号}:8000",
"DYNAMODB_TABLE_NAME": "PositiveLineBotTable"
}
}
AWS CLI のコマンドを実行してデータを反映した上で
実行すると、テストデータの値が1つランダムで返されます
$ docker-compose up -d
$ aws dynamodb create-table --cli-input-json file://test/positive-line-bot_table.json --endpoint-url http://127.0.0.1:8000
$ aws dynamodb batch-write-item --request-items file://test/positive-line-bot_table_data.json --endpoint-url http://127.0.0.1:8000
$ aws dynamodb scan --table-name PositiveLineBotTable --endpoint-url http://127.0.0.1:8000
$ sam local start-api --env-vars test/env.json --profile dummy
$ curl http://localhost:3000/positive
{"id":1,"name":"testtest"}
Messaging APIを利用するにはなどを参考にLine側の設定行います。
設定時、ChannelSecretとアクセストークン(ロングターム)をメモします。
Lineに入力された値が渡ってくるように、コードとtemplate.ymlを修正します。
func UnmarshalLineRequest(data []byte) (LineRequest, error) {
var r LineRequest
err := json.Unmarshal(data, &r)
return r, err
}
type LineRequest struct {
Events []Event `json:"events"`
Destination string `json:"destination"`
}
type Event struct {
Type string `json:"type"`
ReplyToken string `json:"replyToken"`
Source Source `json:"source"`
Timestamp int64 `json:"timestamp"`
Message Message `json:"message"`
}
type Message struct {
Type string `json:"type"`
ID string `json:"id"`
Text string `json:"text"`
}
type Source struct {
UserID string `json:"userId"`
Type string `json:"type"`
}
func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
myLineRequest, err := UnmarshalLineRequest([]byte(request.Body))
if err != nil {
log.Fatal(err)
}
bot, err := linebot.New(
"ChannelSecret",
"アクセストークン(ロングターム)")
if err != nil {
log.Fatal(err)
}
// 中略
var tmpReplyMessage string
tmpReplyMessage = word
if _, err = bot.ReplyMessage(myLineRequest.Events[0].ReplyToken, linebot.NewTextMessage(tmpReplyMessage)).Do(); err != nil {
log.Fatal(err)
}
return events.APIGatewayProxyResponse{
Body: word,
StatusCode: 200,
}, nil
}
template.yml
Method: POST
最後にデプロイをして実際に確認してみます。
$ sam validate --profile my_profile --debug
$ sam build --template template.yaml --profile my_profile
$ sam deploy --guided --profile my_profile
作成されたdynamoDBテーブルにデータを入れた状態で
試しに自身の携帯から確認したところ無事自動返答がされました。