AWS AmplifyでLambdaを使うwith Go

Amplifyはちょくちょく使う機会があるのですが、Amplifyを介したLambdaってほぼ使った事ないぞ、と思ったので使ってみようと思い立ちました。
そしてLambdaでGoが使えるけど、ちゃんと使った事なかった気がするので使ってみたいなとついでに。

# Functionが使えるらしい
FUNCTIONS - Overview
https://docs.amplify.aws/cli/function

## 環境

- win10 Home(surface go) + WSL2 + ubuntu18.04
- node v14.1.0
- npm 6.14.5
- go version go1.10.4 linux/amd64

# 使ってみる

## 前置き

vueを使う設定にてamplifyをsetupしておきます。
詳しくはこのあたりから。
https://docs.amplify.aws/start/getting-started/installation/q/integration/vue

``` bash
$ vue create myamplifyproject
$ cd myamplifyproject
$ npm install
$ amplify init
$ npm install aws-amplify @aws-amplify/ui-vue
$ vi src/main.js
$ npm run serve
```

![amplify.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308709/4b5b4656-4699-69f5-1528-66164167c0e0.png)


## add function

```bash
$ amplify add function
? Select which capability you want to add: Lambda function (serverless function)
? Provide a friendly name for your resource to be used as a label for this category in the project: myamplifyproject3e7c
0b2b
? Provide the AWS Lambda function name: myamplifyproject3e7c0b2b
? Choose the runtime that you want to use: Go
go executable was not found in PATH, make sure it's available. It can be installed from https://golang.org/doc/install
Only one template found - using Hello World by default.
? Do you want to access other resources in this project from your Lambda function? No
? Do you want to invoke this function on a recurring schedule? No
? Do you want to configure Lambda layers for this function? No
? Do you want to edit the local lambda function now? Yes
Please edit the file in your editor: /mnt/c/Users/user/myamplifyproject/amplify/backend/function/myamplifyproject3e7c0b2b/src/main.go
? Press enter to continue
Successfully added resource myamplifyproject3e7c0b2b locally.

Next steps:
Check out sample function code generated in <project-dir>/amplify/backend/function/myamplifyproject3e7c0b2b/src
"amplify function build" builds all of your functions currently in the project
"amplify mock function <functionName>" runs your function locally
"amplify push" builds all of your local backend resources and provisions them in the cloud
"amplify publish" builds all of your local backend and front-end resources (if you added hosting category) and provisions them in the cloud
```

そうすると `main.go` ってファイルができました。

```go:main.go
package main

import (
  "fmt"
  "context"
  "github.com/aws/aws-lambda-go/lambda"
)

type MyEvent struct {
  Name string `json:"name"`
}

func HandleRequest(ctx context.Context, name MyEvent) (string, error) {
  return fmt.Sprintf("Hello %s!", name.Name ), nil
}

func main() {
  lambda.Start(HandleRequest)
}
```

`github.com/aws/aws-lambda-go/lambda` をGETしておきましょう。

```bash
$ go get github.com/aws/aws-lambda-go/lambda
```

## 試しに実行

```bash
$ amplify function build
? Are you sure you want to continue building the resources? Yes
✔ All resources are built.
$ amplify mock function myamplifyproject3e7c0b2b
? Provide the path to the event JSON object relative to /mnt/c/Users/masra/myamplifyproject/amplify/backend/function/mya
mplifyproject3e7c0b2b src/event.json
Starting execution...
Local invoker binary was not found, building it...
Launching Lambda process, port: 8000
Result:
Hello Amplify!
Finished execution.
```

`amplify function build` でbuildした後、`amplify mock function [functionname]` でローカル実行できるみたいです。
なんかsamみたいな仕組み使ってそうだなー。

## deploy

おなじみの`amplify push` & `amplify publish`でdeployできます。cli使ってる人は`git経由ですかね`
その前に`hosting add`もしておきますね。お試しなのでS3に直deployします。

```bash
$ amplify hosting add
? Select the plugin module to execute Amazon CloudFront and S3
? Select the environment setup: DEV (S3 only with HTTP)
? hosting bucket name myamplifyproject-20210102225615-hostingbucket
? index doc for the website index.html
? error doc for the website index.html

You can now publish your app using the following command:
Command: amplify publish
```

```bash
$ amplify push
| Category | Resource name            | Operation | Provider plugin   |
| -------- | ------------------------ | --------- | ----------------- |
| Function | myamplifyproject3e7c0b2b | Create    | awscloudformation |
| Hosting  | S3AndCloudFront          | Create    | awscloudformation |
? Are you sure you want to continue? Yes
$ amplify publish
✔ Successfully pulled backend environment test from the cloud.

Current Environment: test

| Category | Resource name            | Operation | Provider plugin   |
| -------- | ------------------------ | --------- | ----------------- |
| Function | myamplifyproject3e7c0b2b | Create    | awscloudformation |
| Hosting  | S3AndCloudFront          | Create    | awscloudformation |
? Are you sure you want to continue? Yes
⠋ Updating resources in the cloud. This may take a few minutes...

...

✔ All resources are updated in the cloud

Hosting endpoint: http://myamplifyproject-20210102233234-hostingbucket-test.s3-website-ap-northeast-1.amazonaws.com


> myamplifyproject@0.1.0 build /mnt/c/Users/masra/myamplifyproject
> vue-cli-service build


⠋  Building for production...

...

frontend build command exited with code 0
Publish started for S3AndCloudFront
✔ Uploaded files successfully.
Your app is published successfully.
http://myamplifyproject-20210102233234-hostingbucket-test.s3-website-ap-northeast-1.amazonaws.com
```

・・とまあdeployできました。
vueのdefault画面は表示できてます。

functionはというと・・こんな感じで作成されてました。

![amplify3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308709/fd19b091-6d34-b893-9c67-201490634294.png)

はて？api gatewayも何もないけどどうやって実行するのだろう？
AppSyncの裏で使ったり、Scheduleから起動する方法しか書かれてないなー。
webapi的な使い方は想定されてないのだろうか。

## amplify add apiしてみる。

REST APIとして使えば使えるのでは？と思ったので試してみます。


```bash
$ amplify add api
? Please select from one of the below mentioned services: REST
? Provide a friendly name for your resource to be used as a label for this category in the project: api090983e4
? Provide a path (e.g., /book/{isbn}): /items
? Choose a Lambda source Use a Lambda function already added in the current Amplify project
? Choose the Lambda function to invoke by this path myamplifyproject3e7c0b2b
? Restrict API access Yes
? Who should have access? Authenticated and Guest users
? What kind of access do you want for Authenticated users? create, read, update, delete
? What kind of access do you want for Guest users? create, read, update, delete
Successfully added auth resource locally.
? Do you want to add another path? No
Successfully added resource api090983e4 locally

Some next steps:
"amplify push" will build all your local backend resources and provision it in the cloud
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
```

そして`amplify push`します。

```bash
$ amplify push
✔ Successfully pulled backend environment test from the cloud.

Current Environment: test

| Category | Resource name            | Operation | Provider plugin   |
| -------- | ------------------------ | --------- | ----------------- |
| Auth     | cognito557e6fef          | Create    | awscloudformation |
| Api      | api090983e4              | Create    | awscloudformation |
| Function | myamplifyproject3e7c0b2b | No Change | awscloudformation |
| Hosting  | S3AndCloudFront          | No Change | awscloudformation |
? Are you sure you want to continue? Yes
⠼ Updating resources in the cloud. This may take a few minutes...

...

REST API endpoint: https://uyyhhs6ca8.execute-api.ap-northeast-1.amazonaws.com/test

```

![amplify4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308709/2cda9c4e-94d1-730a-f891-d310b25e3dce.png)

お。でけた。

しかし、`{"message":"Missing Authentication Token"}`と言われます。
なんか`Auth`のリソースも勝手につくられちゃってますしね。Cognitoの認証通ってないと実行できない感じなんでしょう。Amplifyから使うのであればそれでただしい。

ただ、今回はその辺はすっとばしてとりあえず実行だけしたい。
`Do you want to access other resources in this project from your Lambda function?` って所で`No`にしちゃったのが要因な気がするので今度は`Yes`にしてみます。

## Add funtion

```bash
$ amplify function add
? Select which capability you want to add: Lambda function (serverless function)
? Provide a friendly name for your resource to be used as a label for this category in the project: myamplifyproject2451
e9a2
? Provide the AWS Lambda function name: myamplifyproject2451e9a2
? Choose the runtime that you want to use: Go
Only one template found - using Hello World by default.
? Do you want to access other resources in this project from your Lambda function? Yes
? Select the category

You can access the following resource attributes as environment variables from your Lambda function

? Do you want to invoke this function on a recurring schedule? No
? Do you want to configure Lambda layers for this function? No
? Do you want to edit the local lambda function now? Yes
Please edit the file in your editor: /mnt/c/Users/user/myamplifyproject/amplify/backend/function/myamplifyproject2451e9a2/src/main.go
? Press enter to continue
Successfully added resource myamplifyproject2451e9a2 locally.

Next steps:
Check out sample function code generated in <project-dir>/amplify/backend/function/myamplifyproject2451e9a2/src
"amplify function build" builds all of your functions currently in the project
"amplify mock function <functionName>" runs your function locally
"amplify push" builds all of your local backend resources and provisions them in the cloud
"amplify publish" builds all of your local backend and front-end resources (if you added hosting category) and provisions them in the cloud
```

```bash
$ $ amplify add api
? Please select from one of the below mentioned services: REST
? Provide a friendly name for your resource to be used as a label for this category in the project: api62457bd3
? Provide a path (e.g., /book/{isbn}): /hage
? Choose a Lambda source Use a Lambda function already added in the current Amplify project
? Choose the Lambda function to invoke by this path myamplifyproject2451e9a2
? Restrict API access Yes
? Who should have access? Authenticated and Guest users
? What kind of access do you want for Authenticated users? create, read, update, delete
? What kind of access do you want for Guest users? create, read, update, delete
? Do you want to add another path? No
Successfully added resource api62457bd3 locally

Some next steps:
"amplify push" will build all your local backend resources and provision it in the cloud
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
```

```bash
$ amplify push
✔ Successfully pulled backend environment test from the cloud.

Current Environment: test

| Category | Resource name            | Operation | Provider plugin   |
| -------- | ------------------------ | --------- | ----------------- |
| Api      | api7dcd31c5              | Create    | awscloudformation |
| Auth     | cognito557e6fef          | Create    | awscloudformation |
| Api      | api090983e4              | Create    | awscloudformation |
| Function | myamplifyproject3e7c0b2b | No Change | awscloudformation |
| Function | myamplifyproject2451e9a2 | Create    | awscloudformation |
| Hosting  | S3AndCloudFront          | No Change | awscloudformation |
? Are you sure you want to continue? Yes
⠼ Updating resources in the cloud. This may take a few minutes...

...

REST API endpoint: https://xdewrmoiva.execute-api.ap-northeast-1.amazonaws.com/test
```

ん～・・
でもダメっすねー。IAM認証がついてるのでToken必須か。

![amplify5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308709/aa1eac5c-280e-0dab-bee7-c8cf8c652050.png)

https://github.com/aws-amplify/amplify-cli/blob/fad6377bd384862ca4429cb1a83eee90efd62b58/packages/amplify-category-api/src/provider-utils/awscloudformation/service-walkthroughs/apigw-walkthrough.ts#L246
https://github.com/aws-amplify/amplify-cli/blob/master/packages/amplify-category-api/resources/awscloudformation/cloudformation-templates/apigw-cloudformation-template-default.json.ejs#L248

このへんとかみてもIAM認証必須っぽいかな。authでもunauthでも。
まあ、本来はAmplifyの認証を介して使うものだからそれでいいんでしょうけどね。

# しかたないのでVueを介してAPIを叩いてみる。

なんで、ちゃんとAPI叩くようにしてみます。
authはadd apiの時に入っちゃったぽいので、vueを少し書き換えるだけ。
HelloWorld.vueを再利用してます。
なお、Amplifyの認証は行いません。Guestユーザーで叩いてる想定。それでもAmplifyのライブラリを使うとAPIリクエストの際にTokenが乗ってくるので叩ける・・はず。

```src/components/HelloWorld.vue
<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <button @click="getHelloWorld">API.post</button>
  </div>
</template>

<script>
import { API } from 'aws-amplify';

export default {
  name: 'HelloWorld',
  data: function() {
    return {
      msg: ""
    }
  },
  methods: {
    getHelloWorld() {
      API.post('api5afc9d6e', '/hage', {
         response: true,
         body: {
           text: "Amplify"
         },
         headers: {}
      }).then(result => {
        console.log(result.data)
        this.msg = result.data.text
      }).catch(err => {
        console.log(err)
      })
    }
  }
}
</script>
```

で、そのままAPI叩くとCORSでエラーになったのでResponse HeaderにAccess-Allow-Originしてやらないといけないかも。
GO+Lambdaのあたりはこちらを参考にさせていただきます。
https://qiita.com/coil_msp123/items/b1751dbe74ada7fdd5a1

で、main.goはこういう感じに。
やっつけですみません。

```go:main.go
package main

import (
  "fmt"
  "log"
  "encoding/json"
  "github.com/aws/aws-lambda-go/lambda"
  "github.com/aws/aws-lambda-go/events"
)

func HandleRequest(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
  reqBody := request.Body
  req := make(map[string]string)
  json.Unmarshal([]byte(reqBody), &req)

  req["text"] = fmt.Sprintf("Hello, %s!", req["text"])
  bytes, _ := json.Marshal(req)
  log.Print(string(bytes))

  return events.APIGatewayProxyResponse{
    Body:  string(bytes),
    StatusCode: 200,
    Headers: map[string]string{
        "Content-Type": "application/json",
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Headers": "withcredentials,origin,Accept,Authorization,Content-Type",
    },
  }, nil
}

func main() {
  lambda.Start(HandleRequest)
}
```

`go get github.com/aws/aws-lambda-go/events`も忘れずに。
で、`amplify function build`と`amplify function update`も実行しておきます。
さらには`amplify push`も。

でもろもろ終わったら、`npm run serve`しつつ、`locahost:8080`を開きまして、、

![amplify7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308709/9999c5b9-566f-0ed9-0bec-a4c481e2167f.png)

いけましたね。

# 最後に

```bash
$ amplify delete
```

はわすれません。エンドポイント名とかむき出しにしてましたし、ちゃんと消しておく。