2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

sam-cliを使ってKotlinでlambdaを書いた話

Last updated at Posted at 2019-02-28

概要

最近流行りのsam-cliを使って、Kotlinでlambdaを書いてみました。
今回書いたのは簡単なGETとPOSTの処理になります。
完成物はこちら

環境

  • Kotlin1.3.20
  • Docker for Mac
  • SAM-CLI

開発

開発環境構築

sam-cliのインストール〜パッケージ作成

  1. brew tap aws/tap
  2. brew install aws-sam-cli
  3. sam init --runtime java8

mavenにKotlinの依存関係等の追記

  1. dependenciesに以下のライブラリを追加します。
    • kotlin-stdlib-jdk8
    • kotlin-test-junit
  2. pluginに以下のプラグインを追記します。
    • maven-shade-plugin
    • kotlin-maven-plugin
    • maven-compiler-plugin
  3. sourceDirectory, testSourceDirectoryのpathをsrc/main/kotlin, src/test/kotlinに変更する。

完成品はこちら

javaのソースをKotlinに変換

こちらはintellijを使っていれば、自動でjavaからKotlinに変更することができます。

最初の動作確認

$ mvn package
$ sam local start-api

これでlocal環境でlambdaが立ち上がり、localhostでアクセスできるようになるのでまずは動くことを確認します。
最初は localhost:3000/helloHello World になるはずです。

POST用のlambdaの作成

今回は新たにリクエストボディに人の名前を詰めると、レスポンスにそれを含めてHello Worldを返すAPIを作成します。

受け取るRequestのclassの作成

今回はbodyに以下のようなJsonを渡します。


{
    "firstName": "太郎",
    "lastName": "山田"
}

次にリクエストを受け取るためのクラスを作成します。
ターミナルにて、以下のコマンドを実行するとlambdaが受け取るeventのjsonが取得できるので、それに沿ってクラスを作ります。

$ sam local generate-event apigateway aws-proxy --method POST

{
  "body": "eyJ0ZXN0IjoiYm9keSJ9",
  "resource": "/{proxy+}",
  "path": "/path/to/resource",
  "httpMethod": "POST",
  "isBase64Encoded": true,
  "queryStringParameters": {
    "foo": "bar"
  },
  "pathParameters": {
    "proxy": "/path/to/resource"
  },
  "stageVariables": {
    "baz": "qux"
  },
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Encoding": "gzip, deflate, sdch",
    "Accept-Language": "en-US,en;q=0.8",
    "Cache-Control": "max-age=0",
    "CloudFront-Forwarded-Proto": "https",
    "CloudFront-Is-Desktop-Viewer": "true",
    "CloudFront-Is-Mobile-Viewer": "false",
    "CloudFront-Is-SmartTV-Viewer": "false",
    "CloudFront-Is-Tablet-Viewer": "false",
    "CloudFront-Viewer-Country": "US",
    "Host": "1234567890.execute-api.us-east-1.amazonaws.com",
    "Upgrade-Insecure-Requests": "1",
    "User-Agent": "Custom User Agent String",
    "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
    "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
    "X-Forwarded-For": "127.0.0.1, 127.0.0.2",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https"
  },
  "requestContext": {
    "accountId": "123456789012",
    "resourceId": "123456",
    "stage": "prod",
    "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
    "requestTime": "09/Apr/2015:12:34:56 +0000",
    "requestTimeEpoch": 1428582896000,
    "identity": {
      "cognitoIdentityPoolId": null,
      "accountId": null,
      "cognitoIdentityId": null,
      "caller": null,
      "accessKey": null,
      "sourceIp": "127.0.0.1",
      "cognitoAuthenticationType": null,
      "cognitoAuthenticationProvider": null,
      "userArn": null,
      "userAgent": "Custom User Agent String",
      "user": null
    },
    "path": "/prod/path/to/resource",
    "resourcePath": "/{proxy+}",
    "httpMethod": "POST",
    "apiId": "1234567890",
    "protocol": "HTTP/1.1"
  }
}

続いてこのJsonに対応するクラスを作っていきます。

class Request {
    lateinit var body: String |
}

data class Person(val firstName: String, val lastName: String)

Requestクラスの方は、 RequestHandler を継承したクラスでlambdaの関数を作成する場合、空のコンストラクタがないとパースできないので、今回は通常のclassにしています。
また、リクエストボディに関しては、Jsonではなく文字列で受け取るので、RequestクラスではString型で定義します。
今回はbody以外使わないので、classにはbody以外持たせていません。

lambdaの関数の実装

まずはJsonをパースするために jackson-module-kotlin をpom.xmlのdependencyに追記します。
続いていよいよ関数を実装していきます。
基本的な流れは自動生成されるgetのものと同様ですが、最初にリクエストボディをパースします。
実際のコードは以下の通りです。


class PostApp : RequestHandler<Request, GatewayResponse> {

    override fun handleRequest(input: Request, context: Context?): GatewayResponse {
        val headers = HashMap<String, String>()
        headers["Content-Type"] = "application/json"
        headers["X-Custom-Header"] = "application/json"

        val mapper = jacksonObjectMapper()
        val person = mapper.readValue<Person>(input.body)
        return try {
            val pageContents = this.getPageContents("https://checkip.amazonaws.com")
            val output = String.format("{ \"message\": \"hello %s\", \"location\": \"%s\" }", person.lastName, pageContents)
            GatewayResponse(output, headers, 200)
        } catch (e: IOException) {
            GatewayResponse("{}", headers, 500)
        }
    }

    @Throws(IOException::class)
    private fun getPageContents(address: String): String {
        val url = URL(address)
        BufferedReader(InputStreamReader(url.openStream())).use { br -> return br.lines().collect(Collectors.joining(System.lineSeparator())) }
    }
}

動作確認

$ mvn package
$ sam local start-api

これらを実行し、今回自分で作成したlambdaのエンドポイントを叩いてみます。

$ curl -X POST -d '{"firstName": "山田", lastName: "太郎"}' https://127.0.0.1:3000

これで期待したレスポンスが返ってくれば成功です!

最後に

「javaでかけるんだからKotlinでもかけるっしょ」の精神で書いてみました。
ただ書いてみて思うのは、あえてKotlinで書く必要性がそこまでなかったのではないだろうか。。。というものでした。
個人の趣味とか、会社で使うlambdaの一つをお試しで、とかなら良いかもしれません。

以上でsam-cliを使ってKotlinでlambdaを書いた話は終わりになります。
ありがとうございました。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?