Skip to content

Commit c1abd2a

Browse files
committed
GH-33 Replace hard-coded void handlers with preconfigured HandlerResultConsumers
1 parent d241c23 commit c1abd2a

File tree

3 files changed

+52
-27
lines changed

3 files changed

+52
-27
lines changed

routing-annotations/routing-annotated/src/main/kotlin/io/javalin/community/routing/annotations/AnnotatedRoutingPlugin.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ fun interface HandlerResultConsumer<T> {
1818

1919
class AnnotatedRoutingPluginConfiguration {
2020
var apiVersionHeader: String = "X-API-Version"
21-
var resultHandlers: MutableMap<Class<*>, HandlerResultConsumer<*>> = mutableMapOf()
21+
var resultHandlers: MutableMap<Class<*>, HandlerResultConsumer<*>> = mutableMapOf(
22+
String::class.java to HandlerResultConsumer<String?> { ctx, value -> value?.also { ctx.result(it) } },
23+
Unit::class.java to HandlerResultConsumer<Unit?> { _, _ -> },
24+
Void::class.java to HandlerResultConsumer<Void?> { _, _ -> },
25+
Void.TYPE to HandlerResultConsumer<Void?> { _, _ -> },
26+
)
2227

2328
fun <T> registerResultHandler(type: Class<T>, handler: HandlerResultConsumer<T>): AnnotatedRoutingPluginConfiguration = also {
2429
this.resultHandlers[type] = handler

routing-annotations/routing-annotated/src/main/kotlin/io/javalin/community/routing/annotations/ReflectiveEndpointLoader.kt

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import io.javalin.community.routing.Route
44
import io.javalin.community.routing.dsl.DefaultDslException
55
import io.javalin.community.routing.dsl.DefaultDslRoute
66
import io.javalin.http.Context
7+
import io.javalin.http.HttpStatus
78
import io.javalin.validation.Validator
89
import java.lang.reflect.Method
910
import java.lang.reflect.Parameter
@@ -49,6 +50,7 @@ internal class ReflectiveEndpointLoader(
4950
createArgumentSupplier<Unit>(it) ?: throw IllegalArgumentException("Unsupported parameter type: $it")
5051
}
5152

53+
val status = method.getAnnotation(Status::class.java)
5254
val resultHandler = findResultHandler(method)
5355

5456
val route = AnnotatedRoute(
@@ -61,8 +63,8 @@ internal class ReflectiveEndpointLoader(
6163
.toTypedArray()
6264

6365
when (async) {
64-
true -> async { invokeAndUnwrapIfErrored(method, endpoint, *arguments, ctx = this, resultHandler = resultHandler) }
65-
else -> invokeAndUnwrapIfErrored(method, endpoint, *arguments, ctx = this, resultHandler = resultHandler)
66+
true -> async { invokeAndUnwrapIfErrored(method, endpoint, arguments, ctx = this, status = status, resultHandler = resultHandler) }
67+
else -> invokeAndUnwrapIfErrored(method, endpoint, arguments, ctx = this, status = status, resultHandler = resultHandler)
6668
}
6769
}
6870
)
@@ -89,6 +91,7 @@ internal class ReflectiveEndpointLoader(
8991
createArgumentSupplier<Exception>(it) ?: throw IllegalArgumentException("Unsupported parameter type: $it")
9092
}
9193

94+
val status = method.getAnnotation(Status::class.java)
9295
val resultHandler = findResultHandler(method)
9396

9497
val dslException = AnnotatedException(
@@ -102,6 +105,7 @@ internal class ReflectiveEndpointLoader(
102105
method = method,
103106
instance = endpoint,
104107
arguments = arguments,
108+
status = status,
105109
ctx = this,
106110
resultHandler = resultHandler
107111
)
@@ -115,39 +119,39 @@ internal class ReflectiveEndpointLoader(
115119
}
116120

117121
@Suppress("UNCHECKED_CAST")
118-
private fun findResultHandler(method: Method): HandlerResultConsumer<Any?>? =
119-
when {
120-
method.returnType !in arrayOf(Void.TYPE, Void::class.java, Unit::class.java) -> {
121-
resultHandlers.asSequence()
122-
.filter { it.key.isAssignableFrom(method.returnType) }
123-
.sortedWith { a, b ->
124-
when {
125-
a.key.isAssignableFrom(b.key) -> -1
126-
b.key.isAssignableFrom(a.key) -> 1
127-
else -> throw IllegalStateException("Unable to determine handler for type ${method.returnType}. Found two matching handlers: ${a.key} and ${b.key}")
128-
}
129-
}
130-
.toList()
131-
.takeIf { it.isNotEmpty() }
132-
?.last()
133-
?.value as HandlerResultConsumer<Any?>?
134-
?: throw IllegalStateException("Unsupported return type: ${method.returnType}")
122+
private fun findResultHandler(method: Method): HandlerResultConsumer<Any?> =
123+
resultHandlers.asSequence()
124+
.filter { it.key.isAssignableFrom(method.returnType) }
125+
.sortedWith { a, b ->
126+
when {
127+
a.key.isAssignableFrom(b.key) -> -1
128+
b.key.isAssignableFrom(a.key) -> 1
129+
else -> throw IllegalStateException("Unable to determine handler for type ${method.returnType}. Found two matching handlers: ${a.key} and ${b.key}")
130+
}
135131
}
136-
else -> null
137-
}
138-
132+
.toList()
133+
.lastOrNull()
134+
?.value as? HandlerResultConsumer<Any?>
135+
?: throw IllegalStateException("Unsupported return type: ${method.returnType}")
139136

140137
private fun invokeAndUnwrapIfErrored(
141138
method: Method,
142139
instance: Any,
143-
vararg arguments: Any?,
140+
arguments: Array<Any?>,
141+
status: Status?,
144142
ctx: Context,
145-
resultHandler: HandlerResultConsumer<Any?>?
143+
resultHandler: HandlerResultConsumer<Any?>
146144
): Any? =
147145
try {
148146
val result = method.invoke(instance, *arguments)
149-
resultHandler?.handle(ctx, result)
147+
status
148+
?.takeIf { it.success != HttpStatus.UNKNOWN }
149+
?.let { ctx.status(it.success) }
150+
resultHandler.handle(ctx, result)
150151
} catch (reflectionException: ReflectiveOperationException) {
152+
status
153+
?.takeIf { it.error != HttpStatus.UNKNOWN }
154+
?.let { ctx.status(it.error) }
151155
throw reflectionException.cause ?: reflectionException
152156
}
153157

routing-annotations/routing-annotated/src/test/java/io/javalin/community/routing/annotations/AnnotatedRoutingTest.kt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import io.javalin.Javalin
44
import io.javalin.community.routing.Route
55
import io.javalin.http.Context
66
import io.javalin.http.HandlerType
7-
import io.javalin.openapi.experimental.OpenApiAnnotationProcessorConfiguration
7+
import io.javalin.http.HttpStatus
88
import io.javalin.testtools.JavalinTest
99
import kong.unirest.Unirest
1010
import kong.unirest.Unirest.request
@@ -330,4 +330,20 @@ class AnnotatedRoutingTest {
330330
.hasMessageContaining("Unable to determine handler for type class")
331331
}
332332

333+
@Test
334+
fun `should use status code from result handler`() =
335+
JavalinTest.test(
336+
Javalin.create {
337+
it.registerAnnotatedEndpoints(
338+
object {
339+
@Get("/test")
340+
@Status(success = HttpStatus.IM_A_TEAPOT)
341+
fun test(ctx: Context): String = "abc"
342+
}
343+
)
344+
}
345+
) { _, client ->
346+
assertThat(Unirest.get("${client.origin}/test").asString().status).isEqualTo(HttpStatus.IM_A_TEAPOT.code)
347+
}
348+
333349
}

0 commit comments

Comments
 (0)