概要
最近流行りのsam-cliを使って、Kotlinでlambdaを書いてみました。
今回書いたのは簡単なGETとPOSTの処理になります。
完成物はこちら
環境
- Kotlin1.3.20
- Docker for Mac
- SAM-CLI
開発
開発環境構築
sam-cliのインストール〜パッケージ作成
- brew tap aws/tap
- brew install aws-sam-cli
- sam init --runtime java8
mavenにKotlinの依存関係等の追記
- dependenciesに以下のライブラリを追加します。
- kotlin-stdlib-jdk8
- kotlin-test-junit
- pluginに以下のプラグインを追記します。
- maven-shade-plugin
- kotlin-maven-plugin
- maven-compiler-plugin
- 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/hello
で Hello 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を書いた話は終わりになります。
ありがとうございました。