More Related Content Similar to [JCConf 2020] 用 Kotlin 跨入 Serverless 世代 (20) More from Shengyou Fan (20) [JCConf 2020] 用 Kotlin 跨入 Serverless 世代12. HTTP Response
—
{
"data": [
{
"id": ...,
"name": "...",
"email": "...",
"createdAt": "yyyy-MM-dd HH:mm:ss.nnnnnn",
"updatedAt": "yyyy-MM-dd HH:mm:ss.nnnnnn"
},
// ...
]
}
1. n
2. JSON
3. data
13. Data Class
—
// Data Class
data class Contact(
val id: Long?,
val name: String,
val email: String,
val createdAt: String,
val updatedAt: String
)
// JSON
{
"id": 1,
"name": "...",
"email": "...",
"createdAt": "yyyy-MM-dd HH:mm:ss.nnnnnn",
"updatedAt": "yyyy-MM-dd HH:mm:ss.nnnnnn"
}
• JSON Data Class
14. class ContactService() {
// ...
@ExperimentalTime
fun generate(amount: Int = 10): List<Contact> {
return (1..amount).map {
Contact(
// ...
)
}
}
}
Service Class
—
Service
• javafaker datetime
serilization library
•
•
16. Packages Tool Window
—
Lorem ipsum dolor sit amet, ad nam alterum copiosae facilisis, ad vis detracto vituperata, an
errem oblique discere qui. Vim noster dolorem legendos no. Cu lorem dicta docendi mei, has
an causae facilis abhorreant. Ex ornatus similique eum, et diam regione consetetur vix. Mea
id paulo vituperata, nam laoreet luptatum delicatissimi id. Fugit primis ullamcorper no qui,
nusquam accusamus temporibus sit ad.
Vis iudico commodo ne, ad eam quas legimus, esse dicant explicari has in. Eum ocurreret
interesset appellantur ei, quo cu timeam euismod laoreet. Per ne nisl justo. An vel laboramus
efficiantur deterruisset, nec ne mandamus scripserit, vix id rebum dicta dictas. Mel suscipit
periculis similique in.
17. // build.gradle
dependencies {
implementation "com.github.javafaker:javafaker:1.0.2"
}
// io.kraftsman.services.ContactService
val faker = Faker(Locale("en"))
Contact(
id = it.toLong(),
name = faker.name().fullName(),
email = faker.internet().emailAddress(),
createdAt = ...,
updatedAt = ...
)
Java Faker
—
• Java Faker
• Faker
18. kotlinx.datetime
• repository
•
// build.gradle
repositories {
maven { url "https://kotlin.bintray.com/kotlinx/" }
}
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-datetime:$ver"
}
// io.kraftsman.services.ContactService
val current = Clock.System.now()
val tz = TimeZone.of("Asia/Taipei")
Contact(
// ...
createdAt = current
.toLocalDateTime(tz).toString(),
updatedAt = (current + x.hours + y.minutes)
.toLocalDateTime(tz).toString()
)
Datetime
—
kotlinx.datetime
•
•
• (x + y )
•
19. // build.gradle
plugins {
id 'org.jetbrains.kotlin.plugin.serialization' version '1.4.10'
}
repositories {
jcenter()
}
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$ver"
}
• Gradle Plugin
Serialization (pt.1)
—
• Repository
•
20. // io.kraftsman.dtos.Contact
@Serializable
data class Contact(
// ...
)
// io.kraftsman.functions.Handler
val contacts: List<Contact> = ContactService().generate(amount)
Json.encodeToString(
mapOf("data" to contacts)
)
Serialization (pt.2)
—
• Annotation
JSON
• List Map data
• Map JSON
22. $ brew cask install google-cloud-sdk
$ gcloud auth login
$ gcloud projects list
$ gcloud config set project PROJECT_ID
/ gcloud
—
• gcloud CLI
• GCP
• GCP Project
• GCP Project
24. configurations {
invoker
}
dependencies {
implementation "...:functions-framework-api:$ver"
invoker "...:java-function-invoker:$ver"
}
tasks.register("runFunction", JavaExec) {
main = '...'
classpath(configurations.invoker)
inputs.files(..., ...)
args(
'--target', ...Property('runFunction.target') ?: '',
'--port', ...Property('runFunction.port') ?: ...
)
doFirst {
args('--classpath', ...)
}
}
Gradle
—
Cloud Function
• functions-framework-api
• java-function-invoker
Gradle Task
$ gradle runFunction
Java Invoker
25. class Handler: HttpFunction {
@Throws(IOException::class)
override fun service(req: HttpRequest, res: HttpResponse) {
}
}
Handler
—
• Class
• HttpFunction (
functions-framework-api)
• service()
• req res
26. class Handler: HttpFunction {
@Throws(IOException::class)
override fun service(req: HttpRequest, res: HttpResponse) {
val param = request.getFirstQueryParameter("amount")
.orElse("")
val amount = param.toIntOrNull() ?: 10
}
}
Query
—
• HttpRequest Query
•
27. class Handler: HttpFunction {
@Throws(IOException::class)
override fun service(req: HttpRequest, res: HttpResponse) {
val param = request.getFirstQueryParameter("amount")
.orElse("")
val amount = param.toIntOrNull() ?: 10
val contacts = ContactService().generate(amount)
val jsonString = Json.encodeToString(
mapOf("data" to contacts)
)
}
}
/
—
• Service
• JSON
28. class Handler: HttpFunction {
@Throws(IOException::class)
override fun service(req: HttpRequest, res: HttpResponse) {
val param = request.getFirstQueryParameter("amount")
.orElse("")
val amount = param.toIntOrNull() ?: 10
val contacts = ContactService().generate(amount)
val jsonString = Json.encodeToString(
mapOf("data" to contacts)
)
with(res) {
setStatusCode(HttpURLConnection.HTTP_OK)
setContentType("application/json")
writer.write(jsonString)
}
}
}
Response
—
• HTTP Status Code
• Content-Type
• JSON
30. $ gcloud functions deploy PUBLIC_PATH
--region REGION
--runtime java11
--trigger-http
--memory 256MB
--entry-point ENTRY_POINT_CLASS
--allow-unauthenticated
—
• gcloud
31. Azure Functions
—
• IntelliJ IDEA + Plugins
• JDK 8
• Azure CLI
• Azure Functions Core Tools
Repository: https://github.com/shengyou/microsoft-azure-functions-kotlin
32. $ brew update
$ brew install azure-cli
$ az login
$ az account -a SUBSCRIPTION_ID
$ brew tap azure/functions
$ brew installazure-function-core-tools@3
—
• Azure CLI
• Azure
• Azure Functions Core Tools
• Azure Toolkit for IntelliJ
36. class Handler {
@FunctionName("...")
fun run(
@HttpTrigger(
name = "...",
route = "...",
methods = [HttpMethod.GET],
authLevel = AuthorizationLevel.ANONYMOUS
)
request: HttpRequestMessage<Optional<String?>>,
context: ExecutionContext
): HttpResponseMessage {
// ...
}
}
Handler
—
• Class Method
• Annotation
• route methods
• request context
37. class Handler {
@FunctionName("...")
fun run(/* . . . */): HttpResponseMessage {
val query = request.queryParameters["amount"]
val amount = query?.toIntOrNull() ?: 10
}
}
Query
—
• request Query
•
38. class Handler {
@FunctionName("...")
fun run(/* . . . */): HttpResponseMessage {
val query = request.queryParameters["amount"]
val amount = query?.toIntOrNull() ?: 10
val contacts = ContactService().generate(amount)
val jsonString = Json.encodeToString(
mapOf("data" to contacts)
)
}
}
/
—
• Service
• JSON
39. class Handler {
@FunctionName("...")
fun run(/* . . . */): HttpResponseMessage {
val query = request.queryParameters["amount"]
val amount = query?.toIntOrNull() ?: 10
val contacts = ContactService().generate(amount)
val jsonString = Json.encodeToString(
mapOf("data" to contacts)
)
return request.createResponseBuilder(HttpStatus.OK)
.header("Content-Type", "application/json")
.body(jsonString)
.build()
}
}
Response
—
• HTTP Status Code
• Content-Type
• JSON
41. azurefunctions {
subscription = '...'
resourceGroup = '...'
appName = '...'
pricingTier = 'Consumption'
region = '...'
runtime {
os = 'linux'
}
authentication {
type = 'azure_cli'
}
}
—
• Azure Gradle
• Gradle Task
42. AWS Lambda
—
• IntelliJ IDEA + Plugin
• JDK 11
• Docker
• AWS CLI
• AWS SAM CLI
Repository: https://github.com/shengyou/amazon-lambda-kotlin
43. $ brew update
$ brew install awscli
$ aws configure
$ aws sts get-caller-identity
$ brew tap aws/tap
$ brew install aws-sam-cli
—
• AWS CLI
• AWS
• Account ID
• IAM (User & Role)
• AWS SAM CLI
• AWS Toolkit
45. plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '6.1.0'
}
dependencies {
implementation "...:aws-lambda-java-core:1.2.1"
implementation "...:aws-lambda-java-events:3.1.0"
runtimeOnly "...:aws-lambda-java-log4j2:1.2.0"
}
Gradle
—
AWS Lambda
• aws-lambda-java-core
• aws-lambda-java-events
• aws-lambda-java-log4j2
Gradle Plugin
• java
• shadow
46. class Handler : RequestHandler <
APIGatewayProxyRequestEvent,
APIGatewayProxyResponseEvent
> {
@Throws(IOException::class)
override fun handleRequest(
request: APIGatewayProxyRequestEvent,
context: Context
): APIGatewayProxyResponseEvent {
// ...
}
}
Handler
—
• Class Method
• RequestHandler ( aws-
lambda-java-core)
• Handler Method
• reqest context
47. class Handler : RequestHandler <...> {
@Throws(IOException::class)
override fun method(/* ... */): ...ResponseEvent {
val param = request.queryStringParameters["amount"]
val amount = param?.toIntOrNull() ?: 10
}
}
Query
—
• request Query
•
48. class Handler : RequestHandler <...> {
@Throws(IOException::class)
override fun method(/* ... */): ...ResponseEvent {
val param = request.queryStringParameters["amount"]
val amount = param?.toIntOrNull() ?: 10
val contacts = ContactService().generate(amount)
val jsonString = Json.encodeToString(
mapOf("data" to contacts)
)
}
}
/
—
• Service
• JSON
49. class Handler : RequestHandler <...> {
@Throws(IOException::class)
override fun method(/* ... */): ...ResponseEvent {
val param = request.queryStringParameters["amount"]
val amount = param?.toIntOrNull() ?: 10
val contacts = ContactService().generate(amount)
val jsonString = Json.encodeToString(
mapOf("data" to contacts)
)
return APIGatewayProxyResponseEvent()
.withStatusCode(HttpURLConnection.HTTP_OK)
.withHeaders(
mapOf("Content-Type" to "application/json")
)
.withBody(jsonString)
}
}
Response
—
• HTTP Status Code
• Content-Type
• JSON
50. $ aws lambda create-function --function-name NAME
--zip-file fileb://build/libs/SHADOWJAR.jar
--handler CLASS::METHOD
--runtime java11
--timeout 15
--memory-size 128
--role ROLE
—
• aws
• AWS Console
52. JDK 11 8 11
IntelliJ Plugin ✗ ✓ ✓
Gradle ✓ ✓
Framework ✓ ✓ ✓
+
—
57. plugins {
kotlin("jvm") version "1.3.72" apply true
kotlin("plugin.serialization") version "1.3.72"
id("io.kotless") version "0.1.6" apply true
}
dependencies {
implementation("io.kotless", "kotless-lang", "0.1.6")
}
Gradle
—
Gradle Plugins
• Kotlin 1.3.72
• Serialization 1.3.72
• Kotless 0.1.6
• Kotless
60. @Get("/...")
fun handler(): HttpResponse {
val param = KotlessContext.HTTP.request.params?.get("amount")
val amount = param?.toIntOrNull() ?: 10
val contacts = ContactService().generate(amount)
val json = Json(JsonConfiguration.Stable)
val jsonString = json.stringify(
Contact.serializer(), contacts
)
}
/
—
• Service
• JSON
61. @Get("/...")
fun handler(): HttpResponse {
val param = KotlessContext.HTTP.request.params?.get("amount")
val amount = param?.toIntOrNull() ?: 10
val contacts = ContactService().generate(amount)
val json = Json(JsonConfiguration.Stable)
val jsonString = json.stringify(
Contact.serializer(), contacts
)
return json(jsonString)
}
Response
—
• JSON
63. kotless {
config {
bucket = "..."
terraform {
profile = "..."
region = "..."
}
}
}
—
• AWS CLI Credentials
• S3 Bucket
• AWS Gradle
• Gradle Task
66. 1. Ktor (1.3.2)
2. Gradle Kotlin ❤
3. JDK 11
4. Server Features
• Routing
• ContentNegotiation
• Jackson
Ktor
—
67. plugins {
id("io.kotless") version "0.1.6" apply true
}
dependencies {
// ...
implementation("io.ktor:ktor-server-netty:$ktor_version")
// ...
implementation("io.kotless", "ktor-lang", “0.1.6")
// ...
}
Gradle
—
Gradle Plugins
• Kotless 0.1.6
• Netty
• Kotless for Ktor
68. class Server : Kotless() {
override fun prepare(app: Application) {
app.install(ContentNegotiation) {
jackson {
enable(SerializationFeature.INDENT_OUTPUT)
}
}
app.routing {
// ...
}
}
}
Kotless App
—
Kotless App
• Kotless
• prepare()
• Ktor app
71. % & %
Repository: https://github.com/shengyou/jetbrains-kotless-framework
Repository: https://github.com/shengyou/jetbrains-ktor-with-kotless