Skip to content

Commit 9acdca4

Browse files
Move to multi module to be able to modularize serialization and deserialization (#5)
* Build multi module project and start proto module * Add serialization and deserialization modularity
1 parent 962d172 commit 9acdca4

28 files changed

+324
-130
lines changed

.DS_Store

8 KB
Binary file not shown.

build.gradle.kts

+24-57
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11

2-
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
3-
import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer
4-
import com.google.protobuf.gradle.protobuf
5-
import com.google.protobuf.gradle.protoc
62
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
73

84
buildscript {
@@ -13,13 +9,10 @@ buildscript {
139

1410
plugins {
1511
java
16-
kotlin("jvm") version "1.3.10"
17-
idea
12+
kotlin("jvm") version "1.3.21"
1813
maven
1914
`maven-publish`
20-
id("com.github.johnrengelman.shadow") version "4.0.3"
21-
id("org.jmailen.kotlinter") version "1.20.1"
22-
id("com.google.protobuf") version "0.8.7"
15+
id("org.jmailen.kotlinter") version "1.22.0"
2316
}
2417

2518
group = "com.github.mduesterhoeft"
@@ -32,59 +25,33 @@ repositories {
3225
dependencies {
3326
compile(kotlin("stdlib-jdk8"))
3427
compile(kotlin("reflect"))
35-
compile("com.amazonaws:aws-lambda-java-core:1.2.0")
36-
compile("com.amazonaws:aws-lambda-java-events:2.2.5")
37-
38-
39-
compile("org.slf4j:slf4j-api:1.7.26")
40-
compile("com.fasterxml.jackson.core:jackson-databind:2.9.8")
41-
compile("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.8")
42-
compile("com.google.guava:guava:23.0")
43-
compile("com.google.protobuf:protobuf-java:3.6.1")
44-
compile("com.google.protobuf:protobuf-java-util:3.6.1")
45-
46-
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.4.0")
47-
testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.12")
48-
testImplementation("org.assertj:assertj-core:3.11.1")
49-
testImplementation("io.mockk:mockk:1.8.13.kotlin13")
50-
testImplementation("org.slf4j:slf4j-simple:1.7.26")
51-
}
52-
53-
tasks.withType<ShadowJar> {
54-
baseName = project.name
55-
classifier = ""
56-
version = ""
57-
transform(Log4j2PluginsCacheFileTransformer::class.java)
58-
minimize()
5928
}
6029

61-
tasks {
62-
withType<KotlinCompile> {
63-
kotlinOptions.jvmTarget = "1.8"
64-
}
65-
66-
withType<Test> {
67-
useJUnitPlatform()
68-
}
69-
70-
val deploy by creating(Exec::class) {
71-
72-
dependsOn("test", "shadowJar")
73-
commandLine("serverless", "deploy")
30+
subprojects {
31+
repositories {
32+
mavenCentral()
7433
}
75-
}
34+
35+
apply(plugin = "java")
36+
apply(plugin = "kotlin")
37+
apply(plugin = "maven-publish")
38+
apply(plugin = "org.jmailen.kotlinter")
39+
40+
tasks {
41+
withType<KotlinCompile> {
42+
kotlinOptions.jvmTarget = "1.8"
43+
}
7644

77-
publishing {
78-
publications {
79-
create<MavenPublication>("maven") {
80-
from(components["java"])
45+
withType<Test> {
46+
useJUnitPlatform()
8147
}
8248
}
83-
}
84-
85-
protobuf {
86-
protoc {
87-
// The artifact spec for the Protobuf Compiler
88-
artifact = "com.google.protobuf:protoc:3.6.1"
49+
50+
publishing {
51+
publications {
52+
create<MavenPublication>("maven") {
53+
from(components["java"])
54+
}
55+
}
8956
}
9057
}
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-5.2-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-5.3-bin.zip
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists

router-protobuf/build.gradle.kts

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import com.google.protobuf.gradle.protobuf
2+
import com.google.protobuf.gradle.protoc
3+
4+
plugins {
5+
id("com.google.protobuf") version "0.8.7"
6+
}
7+
8+
repositories {
9+
mavenCentral()
10+
}
11+
12+
dependencies {
13+
compile(kotlin("stdlib-jdk8"))
14+
compile(kotlin("reflect"))
15+
16+
compile("org.slf4j:slf4j-api:1.7.26")
17+
compile("com.google.protobuf:protobuf-java:3.6.1")
18+
compile("com.google.protobuf:protobuf-java-util:3.6.1")
19+
compile("com.google.guava:guava:23.0")
20+
compile(project(":router"))
21+
22+
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.4.0")
23+
testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.12")
24+
testImplementation("org.assertj:assertj-core:3.11.1")
25+
testImplementation("io.mockk:mockk:1.8.13.kotlin13")
26+
testImplementation("org.slf4j:slf4j-simple:1.7.26")
27+
}
28+
29+
protobuf {
30+
protoc {
31+
// The artifact spec for the Protobuf Compiler
32+
artifact = "com.google.protobuf:protoc:3.6.1"
33+
}
34+
}

src/main/kotlin/com/github/mduesterhoeft/router/ProtoBufUtils.kt renamed to router-protobuf/src/main/kotlin/com/github/mduesterhoeft/router/proto/ProtoBufUtils.kt

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.github.mduesterhoeft.router
1+
package com.github.mduesterhoeft.router.proto
22

33
import com.fasterxml.jackson.databind.JsonNode
44
import com.fasterxml.jackson.databind.node.ArrayNode
@@ -14,17 +14,19 @@ object ProtoBufUtils {
1414
}
1515

1616
fun removeWrapperObjects(json: String): String {
17-
return removeWrapperObjects(jacksonObjectMapper().readTree(json)).toString()
17+
return ProtoBufUtils.removeWrapperObjects(
18+
jacksonObjectMapper().readTree(json)
19+
).toString()
1820
}
1921

2022
fun removeWrapperObjects(json: JsonNode): JsonNode {
2123
if (json.isArray) {
22-
return removeWrapperObjects(json as ArrayNode)
24+
return ProtoBufUtils.removeWrapperObjects(json as ArrayNode)
2325
} else if (json.isObject) {
2426
if (json.has("value") && json.size() == 1) {
2527
return json.get("value")
2628
}
27-
return removeWrapperObjects(json as ObjectNode)
29+
return ProtoBufUtils.removeWrapperObjects(json as ObjectNode)
2830
}
2931
return json
3032
}
@@ -34,7 +36,9 @@ object ProtoBufUtils {
3436
for (entry in json.fields()) {
3537
if (entry.value.isContainerNode) {
3638
if (entry.value.size() > 0) {
37-
result.set(entry.key, removeWrapperObjects(entry.value))
39+
result.set(entry.key,
40+
ProtoBufUtils.removeWrapperObjects(entry.value)
41+
)
3842
} else {
3943
result.set(entry.key, jacksonObjectMapper().nodeFactory.nullNode())
4044
}
@@ -48,7 +52,7 @@ object ProtoBufUtils {
4852
private fun removeWrapperObjects(json: ArrayNode): ArrayNode {
4953
val result = jacksonObjectMapper().createArrayNode()
5054
for (entry in json) {
51-
result.add(removeWrapperObjects(entry))
55+
result.add(ProtoBufUtils.removeWrapperObjects(entry))
5256
}
5357
return result
5458
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.github.mduesterhoeft.router.proto
2+
3+
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent
4+
import com.github.mduesterhoeft.router.DeserializationHandler
5+
import com.github.mduesterhoeft.router.contentType
6+
import com.google.common.net.MediaType
7+
import com.google.protobuf.Parser
8+
import java.util.Base64
9+
import kotlin.reflect.KType
10+
11+
class ProtoDeserializationHandler : DeserializationHandler {
12+
private val proto = MediaType.parse("application/x-protobuf")
13+
14+
override fun supports(input: APIGatewayProxyRequestEvent): Boolean =
15+
MediaType.parse(input.contentType()).`is`(proto)
16+
17+
override fun deserialize(input: APIGatewayProxyRequestEvent, target: KType?): Any {
18+
val bytes = Base64.getDecoder().decode(input.body)
19+
val parser = target.staticFunctions.first { it.name == "parser" }.call() as Parser<*>
20+
return parser.parseFrom(bytes)
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.github.mduesterhoeft.router.proto
2+
3+
import com.github.mduesterhoeft.router.JsonDeserializationHandler
4+
import com.github.mduesterhoeft.router.JsonSerializationHandler
5+
import com.github.mduesterhoeft.router.RequestHandler
6+
7+
abstract class ProtoEnabledRequestHandler : RequestHandler() {
8+
9+
override fun serializationHandlers() =
10+
listOf(ProtoSerializationHandler(), JsonSerializationHandler(objectMapper))
11+
12+
override fun deserializationHandlers() =
13+
listOf(ProtoDeserializationHandler(), JsonDeserializationHandler(objectMapper))
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.github.mduesterhoeft.router.proto
2+
3+
import com.github.mduesterhoeft.router.ResponseEntity
4+
import com.github.mduesterhoeft.router.SerializationHandler
5+
import com.google.common.net.MediaType
6+
import com.google.protobuf.GeneratedMessageV3
7+
import java.util.Base64
8+
9+
class ProtoSerializationHandler : SerializationHandler {
10+
11+
private val json = MediaType.parse("application/json")
12+
13+
override fun supports(acceptHeader: MediaType, response: ResponseEntity<*>): Boolean =
14+
response.body is GeneratedMessageV3
15+
16+
override fun serialize(acceptHeader: MediaType, response: ResponseEntity<*>): String {
17+
val message = response.body as GeneratedMessageV3
18+
return if (acceptHeader.`is`(json)) {
19+
ProtoBufUtils.toJsonWithoutWrappers(message)
20+
} else {
21+
Base64.getEncoder().encodeToString(message.toByteArray())
22+
}
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package com.github.mduesterhoeft.router.proto
2+
3+
import assertk.assert
4+
import assertk.assertions.isEqualTo
5+
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent
6+
import com.github.mduesterhoeft.router.Request
7+
import com.github.mduesterhoeft.router.ResponseEntity
8+
import com.github.mduesterhoeft.router.Router.Companion.router
9+
import com.github.mduesterhoeft.router.bodyAsBytes
10+
import io.mockk.mockk
11+
import org.junit.jupiter.api.Test
12+
import com.github.mduesterhoeft.router.proto.sample.SampleOuterClass.Sample
13+
import java.util.Base64
14+
15+
class RequestHandlerTest {
16+
17+
val testRequestHandler = TestRequestHandler()
18+
19+
@Test
20+
fun `should match request to proto handler and return json`() {
21+
22+
val response = testRequestHandler.handleRequest(
23+
APIGatewayProxyRequestEvent()
24+
.withPath("/some-proto")
25+
.withHttpMethod("GET")
26+
.withHeaders(mapOf("Accept" to "application/json")), mockk()
27+
)!!
28+
29+
assert(response.statusCode).isEqualTo(200)
30+
assert(response.body).isEqualTo("""{"hello":"Hello","request":""}""")
31+
}
32+
33+
@Test
34+
fun `should match request to proto handler and return proto`() {
35+
36+
val response = testRequestHandler.handleRequest(
37+
APIGatewayProxyRequestEvent()
38+
.withPath("/some-proto")
39+
.withHttpMethod("GET")
40+
.withHeaders(mapOf("Accept" to "application/x-protobuf")), mockk()
41+
)!!
42+
43+
assert(response.statusCode).isEqualTo(200)
44+
assert(Sample.parseFrom(response.bodyAsBytes())).isEqualTo(Sample.newBuilder().setHello("Hello").setRequest("").build())
45+
}
46+
47+
@Test
48+
fun `should match request to proto handler and deserialize and return proto`() {
49+
50+
val request = Sample.newBuilder().setHello("Hello").setRequest("").build()
51+
52+
val response = testRequestHandler.handleRequest(
53+
APIGatewayProxyRequestEvent()
54+
.withPath("/some-proto")
55+
.withHttpMethod("POST")
56+
.withBody(Base64.getEncoder().encodeToString(request.toByteArray()))
57+
.withHeaders(mapOf(
58+
"Accept" to "application/x-protobuf",
59+
"Content-Type" to "application/x-protobuf"
60+
)), mockk()
61+
)!!
62+
63+
assert(response.statusCode).isEqualTo(200)
64+
assert(Sample.parseFrom(response.bodyAsBytes())).isEqualTo(request)
65+
}
66+
67+
class TestRequestHandler : ProtoEnabledRequestHandler() {
68+
69+
override val router = router {
70+
GET("/some-proto") { _: Request<Unit> ->
71+
ResponseEntity.ok(Sample.newBuilder().setHello("Hello").build())
72+
}
73+
POST("/some-proto") { r: Request<Sample> ->
74+
ResponseEntity.ok(r.body)
75+
}
76+
}
77+
}
78+
}

src/test/proto/Sample.proto renamed to router-protobuf/src/test/proto/Sample.proto

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
syntax = "proto3";
22

3-
package com.github.mduesterhoeft.router.sample.proto;
3+
package com.github.mduesterhoeft.router.proto.sample;
44

55
message Sample {
66
string hello = 1;

router/build.gradle.kts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
3+
dependencies {
4+
compile(kotlin("stdlib-jdk8"))
5+
compile(kotlin("reflect"))
6+
compile("com.amazonaws:aws-lambda-java-core:1.2.0")
7+
compile("com.amazonaws:aws-lambda-java-events:2.2.5")
8+
9+
compile("org.slf4j:slf4j-api:1.7.26")
10+
compile("com.fasterxml.jackson.core:jackson-databind:2.9.8")
11+
compile("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.8")
12+
compile("com.google.guava:guava:23.0")
13+
14+
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.4.0")
15+
testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.12")
16+
testImplementation("org.assertj:assertj-core:3.11.1")
17+
testImplementation("io.mockk:mockk:1.8.13.kotlin13")
18+
testImplementation("org.slf4j:slf4j-simple:1.7.26")
19+
}

0 commit comments

Comments
 (0)