Skip to content

Commit 4237eb7

Browse files
committed
add APIGatewayRequestTest
1 parent 6a617db commit 4237eb7

File tree

7 files changed

+207
-23
lines changed

7 files changed

+207
-23
lines changed

gradle/libs.versions.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ logback = "1.5.6"
88
io = "0.5.1"
99
date-time = "0.6.0"
1010
ksp = "2.0.10-1.0.24"
11-
mockative = "2.2.2"
1211
allopen = "2.0.20"
1312
mokkery = "2.3.0"
13+
kotlinx-resources = "0.9.0"
1414

1515
[libraries]
1616
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
@@ -29,8 +29,7 @@ ktor-client-curl = { module = "io.ktor:ktor-client-curl", version.ref = "ktor" }
2929
ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
3030
ktor-content-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
3131
ktor-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
32-
mockative = { module = "io.mockative:mockative", version.ref = "mockative" }
33-
mockative-processor = { module = "io.mockative:mockative-processor", version.ref = "mockative" }
32+
kotlinx-resources = { module = "com.goncalossilva:resources", version.ref = "kotlinx-resources" }
3433

3534
[plugins]
3635
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
@@ -39,4 +38,5 @@ ktor = { id = "io.ktor.plugin", version.ref = "ktor" }
3938
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
4039
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
4140
allopen = { id = "org.jetbrains.kotlin.plugin.allopen", version.ref = "allopen" }
42-
mokkery = { id = "dev.mokkery", version.ref = "mokkery" }
41+
mokkery = { id = "dev.mokkery", version.ref = "mokkery" }
42+
kotlinx-resources = { id = "com.goncalossilva.resources", version.ref = "kotlinx-resources" }

lambda-events/build.gradle.kts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
plugins {
22
alias(libs.plugins.kotlin.multiplatform)
33
alias(libs.plugins.kotlin.serialization)
4+
alias(libs.plugins.kotlinx.resources)
45
}
56

67
kotlin {
@@ -18,8 +19,12 @@ kotlin {
1819
commonMain.dependencies {
1920
implementation(libs.kotlin.serialization.json)
2021
}
21-
commonTest.dependencies {
22-
implementation(libs.kotlin.test)
22+
23+
val nativeTest by creating {
24+
dependencies {
25+
implementation(libs.kotlin.test)
26+
implementation(libs.kotlinx.resources)
27+
}
2328
}
2429
}
25-
}
30+
}

lambda-events/src/commonMain/kotlin/io/github/trueangle/knative/lambda/runtime/events/apigateway/APIGateway.kt

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,52 +4,61 @@ import kotlinx.serialization.SerialName
44
import kotlinx.serialization.Serializable
55

66
@Serializable
7-
data class APIGatewayRequest<T>(
7+
data class APIGatewayRequest(
88
@SerialName("resource") val resource: String,
99
@SerialName("path") val path: String,
1010
@SerialName("httpMethod") val httpMethod: String,
1111
@SerialName("queryStringParameters") val queryStringParameters: Map<String, String>? = null,
1212
@SerialName("multiValueQueryStringParameters") val multiValueQueryStringParameters: Map<String, List<String>>? = null,
1313
@SerialName("headers") val headers: Map<String, String>,
14-
@SerialName("multiValueHeaders") val multiValueHeaders: Map<String, String>,
14+
@SerialName("multiValueHeaders") val multiValueHeaders: Map<String, List<String>>,
1515
@SerialName("pathParameters") val pathParameters: Map<String, String>? = null,
1616
@SerialName("stageVariables") val stageVariables: Map<String, String>? = null,
1717
@SerialName("requestContext") val requestContext: Context,
18-
@SerialName("body") val body: T? = null,
19-
@SerialName("isBase64Encoded") val isBase64Encoded: Boolean
18+
@SerialName("body") val body: String,
19+
@SerialName("isBase64Encoded") val isBase64Encoded: Boolean? = null
2020
) {
2121
@Serializable
2222
data class Context(
2323
@SerialName("resourceId") val resourceId: String,
2424
@SerialName("apiId") val apiId: String,
25-
@SerialName("domainName") val domainName: String? = null,
25+
@SerialName("domainName") val domainName: String,
26+
@SerialName("domainPrefix") val domainPrefix: String,
2627
@SerialName("resourcePath") val resourcePath: String,
28+
@SerialName("protocol") val protocol: String,
2729
@SerialName("httpMethod") val httpMethod: String,
2830
@SerialName("requestId") val requestId: String,
2931
@SerialName("accountId") val accountId: String,
3032
@SerialName("stage") val stage: String,
3133
@SerialName("identity") val identity: Identity,
3234
@SerialName("authorizer") val authorizer: Authorizer? = null,
3335
@SerialName("extendedRequestId") val extendedRequestId: String? = null,
34-
@SerialName("path") val path: String
36+
@SerialName("path") val path: String,
37+
@SerialName("requestTime") val requestTime: String,
38+
@SerialName("requestTimeEpoch") val requestTimeEpoch: Long,
3539
) {
3640
@Serializable
3741
data class Identity(
3842
@SerialName("cognitoIdentityPoolId") val cognitoIdentityPoolId: String? = null,
3943
@SerialName("apiKey") val apiKey: String? = null,
44+
@SerialName("apiKeyId") val apiKeyId: String? = null,
45+
@SerialName("accessKey") val accessKey: String? = null,
4046
@SerialName("userArn") val userArn: String? = null,
4147
@SerialName("cognitoAuthenticationType") val cognitoAuthenticationType: String? = null,
4248
@SerialName("caller") val caller: String? = null,
43-
@SerialName("userAgent") val userAgent: String? = null,
49+
@SerialName("userAgent") val userAgent: String,
4450
@SerialName("user") val user: String? = null,
4551
@SerialName("cognitoAuthenticationProvider") val cognitoAuthenticationProvider: String? = null,
46-
@SerialName("sourceIp") val sourceIp: String? = null,
47-
@SerialName("accountId") val accountId: String? = null
52+
@SerialName("sourceIp") val sourceIp: String,
53+
@SerialName("accountId") val accountId: String? = null,
54+
@SerialName("cognitoIdentityId") val cognitoIdentityId: String? = null,
4855
)
4956

5057
@Serializable
5158
data class Authorizer(
52-
@SerialName("claims") val claims: Map<String, String>? = null
59+
@SerialName("principalId") val principalId: String,
60+
@SerialName("clientId") val clientId: Int,
61+
@SerialName("clientName") val clientName: String,
5362
)
5463
}
5564
}
@@ -61,4 +70,4 @@ data class APIGatewayResponse<T>(
6170
@SerialName("multiValueHeaders") val multiValueHeaders: Map<String, String>? = null,
6271
@SerialName("body") val body: T? = null,
6372
@SerialName("isBase64Encoded") val isBase64Encoded: Boolean? = null
64-
)
73+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package io.github.trueangle.knative.lambda.runtime.events.apigateway
2+
3+
import com.goncalossilva.resources.Resource
4+
import kotlinx.serialization.Serializable
5+
import kotlinx.serialization.json.Json
6+
import kotlin.test.Test
7+
import kotlin.test.assertEquals
8+
9+
const val RESOURCES_PATH = "src/nativeTest/resources"
10+
11+
class APIGatewayRequestTest {
12+
13+
@Test
14+
fun `GIVEN APIGatewayRequest json AND object body THEN parse`() {
15+
val jsonString = Resource("$RESOURCES_PATH/example-apigw-request.json").readText()
16+
Json.decodeFromString<APIGatewayRequest>(jsonString)
17+
}
18+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
{
2+
"resource": "/{proxy+}",
3+
"path": "/hello/world",
4+
"httpMethod": "POST",
5+
"headers": {
6+
"Accept": "*/*",
7+
"Accept-Encoding": "gzip, deflate",
8+
"cache-control": "no-cache",
9+
"CloudFront-Forwarded-Proto": "https",
10+
"CloudFront-Is-Desktop-Viewer": "true",
11+
"CloudFront-Is-Mobile-Viewer": "false",
12+
"CloudFront-Is-SmartTV-Viewer": "false",
13+
"CloudFront-Is-Tablet-Viewer": "false",
14+
"CloudFront-Viewer-Country": "US",
15+
"Content-Type": "application/json",
16+
"headerName": "headerValue",
17+
"Host": "gy415nuibc.execute-api.us-east-1.amazonaws.com",
18+
"Postman-Token": "9f583ef0-ed83-4a38-aef3-eb9ce3f7a57f",
19+
"User-Agent": "PostmanRuntime/2.4.5",
20+
"Via": "1.1 d98420743a69852491bbdea73f7680bd.cloudfront.net (CloudFront)",
21+
"X-Amz-Cf-Id": "pn-PWIJc6thYnZm5P0NMgOUglL1DYtl0gdeJky8tqsg8iS_sgsKD1A==",
22+
"X-Forwarded-For": "54.240.196.186, 54.182.214.83",
23+
"X-Forwarded-Port": "443",
24+
"X-Forwarded-Proto": "https"
25+
},
26+
"multiValueHeaders": {
27+
"Accept": [
28+
"*/*"
29+
],
30+
"Accept-Encoding": [
31+
"gzip, deflate"
32+
],
33+
"cache-control": [
34+
"no-cache"
35+
],
36+
"CloudFront-Forwarded-Proto": [
37+
"https"
38+
],
39+
"CloudFront-Is-Desktop-Viewer": [
40+
"true"
41+
],
42+
"CloudFront-Is-Mobile-Viewer": [
43+
"false"
44+
],
45+
"CloudFront-Is-SmartTV-Viewer": [
46+
"false"
47+
],
48+
"CloudFront-Is-Tablet-Viewer": [
49+
"false"
50+
],
51+
"CloudFront-Viewer-Country": [
52+
"US"
53+
],
54+
"Content-Type": [
55+
"application/json"
56+
],
57+
"headerName": [
58+
"headerValue"
59+
],
60+
"Host": [
61+
"gy415nuibc.execute-api.us-east-1.amazonaws.com"
62+
],
63+
"Postman-Token": [
64+
"9f583ef0-ed83-4a38-aef3-eb9ce3f7a57f"
65+
],
66+
"User-Agent": [
67+
"PostmanRuntime/2.4.5"
68+
],
69+
"Via": [
70+
"1.1 d98420743a69852491bbdea73f7680bd.cloudfront.net (CloudFront)"
71+
],
72+
"X-Amz-Cf-Id": [
73+
"pn-PWIJc6thYnZm5P0NMgOUglL1DYtl0gdeJky8tqsg8iS_sgsKD1A=="
74+
],
75+
"X-Forwarded-For": [
76+
"54.240.196.186, 54.182.214.83"
77+
],
78+
"X-Forwarded-Port": [
79+
"443"
80+
],
81+
"X-Forwarded-Proto": [
82+
"https"
83+
]
84+
},
85+
"queryStringParameters": {
86+
"name": "me"
87+
},
88+
"multiValueQueryStringParameters": {
89+
"name": [
90+
"me"
91+
]
92+
},
93+
"pathParameters": {
94+
"proxy": "hello/world"
95+
},
96+
"stageVariables": {
97+
"stageVariableName": "stageVariableValue"
98+
},
99+
"requestContext": {
100+
"accountId": "12345678912",
101+
"resourceId": "roq9wj",
102+
"path": "/hello/world",
103+
"stage": "testStage",
104+
"domainName": "gy415nuibc.execute-api.us-east-2.amazonaws.com",
105+
"domainPrefix": "y0ne18dixk",
106+
"requestId": "deef4878-7910-11e6-8f14-25afc3e9ae33",
107+
"protocol": "HTTP/1.1",
108+
"identity": {
109+
"cognitoIdentityPoolId": "theCognitoIdentityPoolId",
110+
"accountId": "theAccountId",
111+
"cognitoIdentityId": "theCognitoIdentityId",
112+
"caller": "theCaller",
113+
"apiKey": "theApiKey",
114+
"apiKeyId": "theApiKeyId",
115+
"accessKey": "ANEXAMPLEOFACCESSKEY",
116+
"sourceIp": "192.168.196.186",
117+
"cognitoAuthenticationType": "theCognitoAuthenticationType",
118+
"cognitoAuthenticationProvider": "theCognitoAuthenticationProvider",
119+
"userArn": "theUserArn",
120+
"userAgent": "PostmanRuntime/2.4.5",
121+
"user": "theUser"
122+
},
123+
"authorizer": {
124+
"principalId": "admin",
125+
"clientId": 1,
126+
"clientName": "Exata"
127+
},
128+
"resourcePath": "/{proxy+}",
129+
"httpMethod": "POST",
130+
"requestTime": "15/May/2020:06:01:09 +0000",
131+
"requestTimeEpoch": 1589522469693,
132+
"apiId": "gy415nuibc"
133+
},
134+
"body": "{\r\n\t\"a\": 1\r\n}"
135+
}

lambda-runtime/src/commonMain/kotlin/io/github/trueangle/knative/lambda/runtime/LambdaRuntime.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ object LambdaRuntime {
8585
val response = bufferedResponse(context) { handler.handleRequest(event, context) }
8686

8787
Log.info("$handlerName invocation completed")
88-
Log.debug("$handlerName response: ")
8988
Log.debug(response)
9089

9190
client.sendResponse(context, response, outputTypeInfo)

lambda-runtime/src/nativeTest/kotlin/io/github/trueangle/knative/lambda/runtime/JsonLogFormatterTest.kt

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class JsonLogFormatterTest {
6565

6666
@Test
6767
fun `GIVEN non-serializable message object WHEN format THEN json`() {
68-
val message = NoSerialObject("Hello world")
68+
val message = NonSerialObject("Hello world")
6969

7070
every { clock.now() } returns (timestamp)
7171

@@ -77,13 +77,31 @@ class JsonLogFormatterTest {
7777
awsRequestId = requestId
7878
)
7979
)
80-
val actual = formatter.format(LogLevel.INFO, message, typeInfo<NoSerialObject>())
80+
val actual = formatter.format(LogLevel.INFO, message, typeInfo<NonSerialObject>())
81+
82+
assertEquals(expected, actual)
83+
}
84+
85+
@Test
86+
fun `GIVEN null message WHEN format THEN json`() {
87+
val message = null
88+
89+
every { clock.now() } returns (timestamp)
90+
91+
val expected = Json.encodeToString(
92+
LogMessageDto(
93+
timestamp = timestamp.toString(),
94+
message = null,
95+
level = LogLevel.INFO,
96+
awsRequestId = requestId
97+
)
98+
)
99+
val actual = formatter.format(LogLevel.INFO, message, typeInfo<NonSerialObject>())
81100

82101
assertEquals(expected, actual)
83102
}
84103

85104
@Serializable
86105
private data class SampleObject(val hello: String)
87-
88-
private data class NoSerialObject(val hello: String)
106+
private data class NonSerialObject(val hello: String)
89107
}

0 commit comments

Comments
 (0)