Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/gradle/samples/lambda-kotlin-requ…
Browse files Browse the repository at this point in the history
…est-router-sample-proto/all-dependencies-6ac2cd69f9
  • Loading branch information
uberbinge authored Jan 10, 2025
2 parents e075d09 + 2e24cf1 commit c8f6255
Show file tree
Hide file tree
Showing 24 changed files with 227 additions and 160 deletions.
1 change: 1 addition & 0 deletions .java-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
17.0
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ buildscript {

plugins {
java
kotlin("jvm") version "2.0.0"
kotlin("jvm") version "2.1.0"
`maven-publish`
jacoco
id("com.github.kt3k.coveralls") version "2.12.2"
id("org.jmailen.kotlinter") version "4.3.0"
id("org.jmailen.kotlinter") version "5.0.1"
}

group = "com.github.moia-dev"
Expand Down
2 changes: 2 additions & 0 deletions mise.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[tools]
java = "17"
10 changes: 5 additions & 5 deletions router-openapi-request-validator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ dependencies {
implementation(kotlin("stdlib"))
implementation(kotlin("reflect"))

api("com.atlassian.oai:swagger-request-validator-core:2.42.0")
api("com.atlassian.oai:swagger-request-validator-core:2.44.1")
api(project(":router"))

testImplementation("org.junit.jupiter:junit-jupiter-engine:5.11.1")
testImplementation("org.assertj:assertj-core:3.26.0")
testImplementation("io.mockk:mockk:1.13.11")
testImplementation("org.slf4j:slf4j-simple:2.0.13")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.11.4")
testImplementation("org.assertj:assertj-core:3.27.2")
testImplementation("io.mockk:mockk:1.13.14")
testImplementation("org.slf4j:slf4j-simple:2.0.16")
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,30 @@ import com.atlassian.oai.validator.model.SimpleResponse
import com.atlassian.oai.validator.report.ValidationReport
import org.slf4j.LoggerFactory

class OpenApiValidator(val specUrlOrPayload: String) {
class OpenApiValidator(
val specUrlOrPayload: String,
) {
val validator = OpenApiInteractionValidator.createFor(specUrlOrPayload).build()

fun validate(
request: APIGatewayProxyRequestEvent,
response: APIGatewayProxyResponseEvent,
): ValidationReport {
return validator.validate(request.toRequest(), response.toResponse())
): ValidationReport =
validator
.validate(request.toRequest(), response.toResponse())
.also { if (it.hasErrors()) log.error("error validating request and response against $specUrlOrPayload - $it") }
}

fun assertValid(
request: APIGatewayProxyRequestEvent,
response: APIGatewayProxyResponseEvent,
) {
return validate(request, response).let {
if (it.hasErrors()) {
throw ApiInteractionInvalid(
specUrlOrPayload,
request,
response,
it,
)
}
) = validate(request, response).let {
if (it.hasErrors()) {
throw ApiInteractionInvalid(
specUrlOrPayload,
request,
response,
it,
)
}
}

Expand Down Expand Up @@ -72,7 +72,7 @@ class OpenApiValidator(val specUrlOrPayload: String) {

private fun APIGatewayProxyRequestEvent.toRequest(): Request {
val builder =
when (httpMethod.toLowerCase()) {
when (httpMethod.lowercase()) {
"get" -> SimpleRequest.Builder.get(path)
"post" -> SimpleRequest.Builder.post(path)
"put" -> SimpleRequest.Builder.put(path)
Expand All @@ -88,13 +88,12 @@ class OpenApiValidator(val specUrlOrPayload: String) {
return builder.build()
}

private fun APIGatewayProxyResponseEvent.toResponse(): Response {
return SimpleResponse.Builder
private fun APIGatewayProxyResponseEvent.toResponse(): Response =
SimpleResponse.Builder
.status(statusCode)
.withBody(body)
.also { headers.forEach { h -> it.withHeader(h.key, h.value) } }
.build()
}

companion object {
val log = LoggerFactory.getLogger(OpenApiValidator::class.java)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ class OpenApiValidatorTest {
}

class TestRequestHandler : RequestHandler() {
data class TestResponse(val name: String)
data class TestResponse(
val name: String,
)

override val router =
Router.router {
Expand All @@ -67,7 +69,9 @@ class OpenApiValidatorTest {
}

class TestInvalidRequestHandler : RequestHandler() {
data class TestResponseInvalid(val invalid: String)
data class TestResponseInvalid(
val invalid: String,
)

override val router =
Router.router {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ class ValidatingRequestRouterWrapperTest {
thenThrownBy {
ValidatingRequestRouterWrapper(InvalidTestRequestHandler(), "openapi.yml")
.handleRequest(GET("/tests").withAcceptHeader("application/json"), mockk())
}
.isInstanceOf(OpenApiValidator.ApiInteractionInvalid::class.java)
}.isInstanceOf(OpenApiValidator.ApiInteractionInvalid::class.java)
.hasMessageContaining("Response status 404 not defined for path")
}

Expand All @@ -36,8 +35,7 @@ class ValidatingRequestRouterWrapperTest {
thenThrownBy {
ValidatingRequestRouterWrapper(InvalidTestRequestHandler(), "openapi.yml")
.handleRequest(GET("/path-not-documented").withAcceptHeader("application/json"), mockk())
}
.isInstanceOf(OpenApiValidator.ApiInteractionInvalid::class.java)
}.isInstanceOf(OpenApiValidator.ApiInteractionInvalid::class.java)
.hasMessageContaining("No API path found that matches request")
}

Expand All @@ -59,10 +57,8 @@ class ValidatingRequestRouterWrapperTest {
delegate = OpenApiValidatorTest.TestRequestHandler(),
specUrlOrPayload = "openapi.yml",
additionalRequestValidationFunctions = listOf({ _ -> throw RequestValidationFailedException() }),
)
.handleRequest(GET("/tests").withAcceptHeader("application/json"), mockk())
}
.isInstanceOf(RequestValidationFailedException::class.java)
).handleRequest(GET("/tests").withAcceptHeader("application/json"), mockk())
}.isInstanceOf(RequestValidationFailedException::class.java)
}

@Test
Expand All @@ -72,10 +68,8 @@ class ValidatingRequestRouterWrapperTest {
delegate = OpenApiValidatorTest.TestRequestHandler(),
specUrlOrPayload = "openapi.yml",
additionalResponseValidationFunctions = listOf({ _, _ -> throw ResponseValidationFailedException() }),
)
.handleRequest(GET("/tests").withAcceptHeader("application/json"), mockk())
}
.isInstanceOf(ResponseValidationFailedException::class.java)
).handleRequest(GET("/tests").withAcceptHeader("application/json"), mockk())
}.isInstanceOf(ResponseValidationFailedException::class.java)
}

private class RequestValidationFailedException : RuntimeException("request validation failed")
Expand Down
14 changes: 7 additions & 7 deletions router-protobuf/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@ repositories {
mavenCentral()
}

val protoVersion = "4.27.2"
val protoVersion = "4.29.3"

dependencies {
implementation(kotlin("stdlib"))
implementation(kotlin("reflect"))

implementation("org.slf4j:slf4j-api:2.0.13")
implementation("org.slf4j:slf4j-api:2.0.16")
api("com.google.protobuf:protobuf-java:$protoVersion")
api("com.google.protobuf:protobuf-java-util:$protoVersion")
implementation("com.google.guava:guava:33.2.1-jre")
implementation("com.google.guava:guava:33.4.0-jre")
api(project(":router"))

testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.11.4")
testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.28.1")
testImplementation("org.assertj:assertj-core:3.26.0")
testImplementation("io.mockk:mockk:1.13.11")
testImplementation("org.slf4j:slf4j-simple:2.0.13")
testImplementation("org.assertj:assertj-core:3.27.2")
testImplementation("io.mockk:mockk:1.13.14")
testImplementation("org.slf4j:slf4j-simple:2.0.16")
testImplementation("com.jayway.jsonpath:json-path:2.9.0")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ import com.google.protobuf.util.JsonFormat

object ProtoBufUtils {
fun toJsonWithoutWrappers(proto: GeneratedMessage): String {
val message = JsonFormat.printer().omittingInsignificantWhitespace().alwaysPrintFieldsWithNoPresence().print(proto)
val message =
JsonFormat
.printer()
.omittingInsignificantWhitespace()
.alwaysPrintFieldsWithNoPresence()
.print(proto)
return removeWrapperObjects(message)
}

fun removeWrapperObjects(json: String): String {
return removeWrapperObjects(
fun removeWrapperObjects(json: String): String =
removeWrapperObjects(
jacksonObjectMapper().readTree(json),
).toString()
}

fun removeWrapperObjects(json: JsonNode): JsonNode {
if (json.isArray) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,5 @@ abstract class ProtoEnabledRequestHandler : RequestHandler() {
override fun <T> createResponse(
contentType: MediaType,
response: ResponseEntity<T>,
): APIGatewayProxyResponseEvent {
return super.createResponse(contentType, response).withIsBase64Encoded(true)
}
): APIGatewayProxyResponseEvent = super.createResponse(contentType, response).withIsBase64Encoded(true)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ class ProtoBufUtilsTest {
@Test
fun `should serialize empty list`() {
val message =
ComplexSample.newBuilder()
ComplexSample
.newBuilder()
.addAllSamples(emptyList())
.build()

Expand All @@ -23,7 +24,8 @@ class ProtoBufUtilsTest {
@Test
fun `should remove wrapper object`() {
val message =
ComplexSample.newBuilder()
ComplexSample
.newBuilder()
.setSomeString(StringValue.newBuilder().setValue("some").build())
.build()

Expand All @@ -35,7 +37,8 @@ class ProtoBufUtilsTest {
@Test
fun `should serialize value when it is the default`() {
val message =
ComplexSample.newBuilder()
ComplexSample
.newBuilder()
.setEnumAttribute(ONE) // enum zero value
.build()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,23 @@ class RequestHandlerTest {
)

assertThat(response.statusCode).isEqualTo(200)
assertThat(Sample.parseFrom(response.bodyAsBytes())).isEqualTo(Sample.newBuilder().setHello("Hello").setRequest("").build())
assertThat(Sample.parseFrom(response.bodyAsBytes())).isEqualTo(
Sample
.newBuilder()
.setHello("Hello")
.setRequest("")
.build(),
)
}

@Test
fun `should match request to proto handler and deserialize and return proto`() {
val request = Sample.newBuilder().setHello("Hello").setRequest("").build()
val request =
Sample
.newBuilder()
.setHello("Hello")
.setRequest("")
.build()

val response =
testRequestHandler.handleRequest(
Expand Down Expand Up @@ -104,7 +115,9 @@ class RequestHandlerTest {

assertThat(response.statusCode).isEqualTo(406)
assertThat(
io.moia.router.proto.sample.SampleOuterClass.ApiError.parseFrom(response.bodyAsBytes()).getCode(),
io.moia.router.proto.sample.SampleOuterClass.ApiError
.parseFrom(response.bodyAsBytes())
.getCode(),
).isEqualTo("NOT_ACCEPTABLE")
}

Expand All @@ -122,7 +135,10 @@ class RequestHandlerTest {
)

assertThat(response.statusCode).isEqualTo(400)
with(io.moia.router.proto.sample.SampleOuterClass.ApiError.parseFrom(response.bodyAsBytes())) {
with(
io.moia.router.proto.sample.SampleOuterClass.ApiError
.parseFrom(response.bodyAsBytes()),
) {
assertThat(getCode()).isEqualTo("BOOM")
assertThat(getMessage()).isEqualTo("boom")
}
Expand All @@ -146,14 +162,16 @@ class RequestHandlerTest {
}

override fun createErrorBody(error: ApiError): Any =
io.moia.router.proto.sample.SampleOuterClass.ApiError.newBuilder()
io.moia.router.proto.sample.SampleOuterClass.ApiError
.newBuilder()
.setMessage(error.message)
.setCode(error.code)
.build()

override fun createUnprocessableEntityErrorBody(errors: List<UnprocessableEntityError>): Any =
errors.map { error ->
io.moia.router.proto.sample.SampleOuterClass.UnprocessableEntityError.newBuilder()
io.moia.router.proto.sample.SampleOuterClass.UnprocessableEntityError
.newBuilder()
.setMessage(error.message)
.setCode(error.code)
.setPath(error.path)
Expand Down
20 changes: 10 additions & 10 deletions router/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ dependencies {
implementation(kotlin("stdlib"))
implementation(kotlin("reflect"))
api("com.amazonaws:aws-lambda-java-core:1.2.3")
api("com.amazonaws:aws-lambda-java-events:3.11.5")
api("com.amazonaws:aws-lambda-java-events:3.14.0")

implementation("org.slf4j:slf4j-api:2.0.13")
api("com.fasterxml.jackson.core:jackson-databind:2.17.1")
api("com.fasterxml.jackson.module:jackson-module-kotlin:2.17.1")
api("com.google.guava:guava:33.2.1-jre")
implementation("org.slf4j:slf4j-api:2.0.16")
api("com.fasterxml.jackson.core:jackson-databind:2.18.2")
api("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.2")
api("com.google.guava:guava:33.4.0-jre")

testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.11.4")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.11.4")
testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.28.1")
testImplementation("org.assertj:assertj-core:3.26.0")
testImplementation("io.mockk:mockk:1.13.11")
testImplementation("ch.qos.logback:logback-classic:1.5.6")
testImplementation("org.assertj:assertj-core:3.27.2")
testImplementation("io.mockk:mockk:1.13.14")
testImplementation("ch.qos.logback:logback-classic:1.5.16")
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ import java.net.URI
import java.util.Base64

/** Data class that represents an HTTP header */
data class Header(val name: String, val value: String)
data class Header(
val name: String,
val value: String,
)

fun APIGatewayProxyRequestEvent.acceptHeader() = getHeaderCaseInsensitive("accept")

Expand Down Expand Up @@ -120,7 +123,8 @@ private fun getCaseInsensitive(
key: String,
map: Map<String, String>?,
): String? =
map?.entries
map
?.entries
?.firstOrNull { key.equals(it.key, ignoreCase = true) }
?.value

Expand Down
5 changes: 2 additions & 3 deletions router/src/main/kotlin/io/moia/router/ApiException.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ open class ApiException(
val details: Map<String, Any> = emptyMap(),
cause: Throwable? = null,
) : RuntimeException(message, cause) {
override fun toString(): String {
return "ApiException(message='$message', code='$code', httpResponseStatus=$httpResponseStatus, details=$details, cause=$cause)"
}
override fun toString(): String =
"ApiException(message='$message', code='$code', httpResponseStatus=$httpResponseStatus, details=$details, cause=$cause)"

fun toApiError() = ApiError(super.message!!, code, details)

Expand Down
Loading

0 comments on commit c8f6255

Please sign in to comment.