Skip to content

Commit

Permalink
Support GET methods with parameters (#513)
Browse files Browse the repository at this point in the history
  • Loading branch information
rjaros committed Feb 24, 2024
1 parent 0ef0ca4 commit 32b9626
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,11 @@ open class CallAgent {
val jsonRpcRequest = JsonRpcRequest(counter++, url, data)
val urlAddr = urlPrefix + url.drop(1)
val fetchUrl = if (method == HttpMethod.GET) {
urlAddr + "?" + URLSearchParams(obj { id = jsonRpcRequest.id }).toString()
val paramsObject = obj { id = jsonRpcRequest.id }
data.forEachIndexed { index, s ->
if (s != null) paramsObject["p$index"] = encodeURIComponent(s)
}
urlAddr + "?" + URLSearchParams(paramsObject).toString()
} else {
requestInit.body = RemoteSerialization.plain.encodeToString(jsonRpcRequest)
urlAddr
Expand Down Expand Up @@ -182,9 +186,18 @@ open class CallAgent {
if (response.headers.get("Content-Type") == "application/json") {
response.json().then { cont.resume(it) }
} else {
cont.cancel(ContentTypeException("Invalid response content type: ${response.headers.get("Content-Type")}"))
cont.cancel(
ContentTypeException(
"Invalid response content type: ${
response.headers.get(
"Content-Type"
)
}"
)
)
}
}

ResponseBodyType.TEXT -> response.text().then { cont.resume(it) }
ResponseBodyType.READABLE_STREAM -> cont.resume(response.body)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ open class KVServiceManagerJs<out T: Any> : KVServiceMgr<T> {
noinline function: suspend T.(PAR) -> RET,
method: HttpMethod, route: String?
) {
bindFunctionWithParamsInternal(method, route, function)
bindFunctionInternal(route, function, method)
}

/**
Expand All @@ -49,7 +49,7 @@ open class KVServiceManagerJs<out T: Any> : KVServiceMgr<T> {
noinline function: suspend T.(PAR1, PAR2) -> RET,
method: HttpMethod, route: String?
) {
bindFunctionWithParamsInternal(method, route, function)
bindFunctionInternal(route, function, method)
}

/**
Expand All @@ -62,7 +62,7 @@ open class KVServiceManagerJs<out T: Any> : KVServiceMgr<T> {
noinline function: suspend T.(PAR1, PAR2, PAR3) -> RET,
method: HttpMethod, route: String?
) {
bindFunctionWithParamsInternal(method, route, function)
bindFunctionInternal(route, function, method)
}

/**
Expand All @@ -75,7 +75,7 @@ open class KVServiceManagerJs<out T: Any> : KVServiceMgr<T> {
noinline function: suspend T.(PAR1, PAR2, PAR3, PAR4) -> RET,
method: HttpMethod, route: String?
) {
bindFunctionWithParamsInternal(method, route, function)
bindFunctionInternal(route, function, method)
}

/**
Expand All @@ -89,7 +89,7 @@ open class KVServiceManagerJs<out T: Any> : KVServiceMgr<T> {
noinline function: suspend T.(PAR1, PAR2, PAR3, PAR4, PAR5) -> RET,
method: HttpMethod, route: String?
) {
bindFunctionWithParamsInternal(method, route, function)
bindFunctionInternal(route, function, method)
}

/**
Expand All @@ -103,7 +103,7 @@ open class KVServiceManagerJs<out T: Any> : KVServiceMgr<T> {
noinline function: suspend T.(PAR1, PAR2, PAR3, PAR4, PAR5, PAR6) -> RET,
method: HttpMethod, route: String?
) {
bindFunctionWithParamsInternal(method, route, function)
bindFunctionInternal(route, function, method)
}

/**
Expand Down Expand Up @@ -141,13 +141,6 @@ open class KVServiceManagerJs<out T: Any> : KVServiceMgr<T> {
bindFunctionInternal(route, function, HttpMethod.GET, "/kvsse/")
}

@PublishedApi
internal fun bindFunctionWithParamsInternal(method: HttpMethod, route: String?, function: Function<*>) {
if (method == HttpMethod.GET)
throw UnsupportedOperationException("GET method is only supported for methods without parameters")
bindFunctionInternal(route, function, method)
}

@PublishedApi
internal fun bindFunctionInternal(
route: String?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ import kotlinx.browser.window
*/
external class Object

/**
* JavaScript encodeURIComponent function
*/
external fun encodeURIComponent(uri: String): String

/**
* Helper function for creating JavaScript objects.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ abstract class KVServiceBinder<out T, RH, WH, SH>(
abstract fun <RET> createRequestHandler(
method: HttpMethod,
function: suspend T.(params: List<String?>) -> RET,
numberOfParams: Int,
serializerFactory: () -> KSerializer<RET>
): RH

Expand All @@ -74,12 +75,13 @@ abstract class KVServiceBinder<out T, RH, WH, SH>(
internal inline fun <reified RET> bind(
method: HttpMethod,
route: String?,
numberOfParams: Int,
noinline function: suspend T.(params: List<String?>) -> RET
) {
routeMapRegistry.addRoute(
method,
"/kv/${route ?: generateRouteName()}",
createRequestHandler(method, function) { deSerializer.serializersModule.serializer() }
createRequestHandler(method, function, numberOfParams) { deSerializer.serializersModule.serializer() }
)
}

Expand All @@ -90,7 +92,7 @@ abstract class KVServiceBinder<out T, RH, WH, SH>(
* @param route a route
*/
inline fun <reified RET> bind(noinline function: suspend T.() -> RET, method: HttpMethod, route: String?) {
bind(method, route) {
bind(method, route, 0) {
requireParameterCountEqualTo(it, 0)
function.invoke(this)
}
Expand All @@ -107,8 +109,7 @@ abstract class KVServiceBinder<out T, RH, WH, SH>(
method: HttpMethod,
route: String?
) {
expectMethodSupportsParameters(method)
bind(method, route) {
bind(method, route, 1) {
requireParameterCountEqualTo(it, 1)
function.invoke(this, deserialize(it[0]))
}
Expand All @@ -125,8 +126,7 @@ abstract class KVServiceBinder<out T, RH, WH, SH>(
method: HttpMethod,
route: String?
) {
expectMethodSupportsParameters(method)
bind(method, route) {
bind(method, route, 2) {
requireParameterCountEqualTo(it, 2)
function.invoke(this, deserialize(it[0]), deserialize(it[1]))
}
Expand All @@ -143,8 +143,7 @@ abstract class KVServiceBinder<out T, RH, WH, SH>(
method: HttpMethod,
route: String?
) {
expectMethodSupportsParameters(method)
bind(method, route) {
bind(method, route, 3) {
requireParameterCountEqualTo(it, 3)
function.invoke(this, deserialize(it[0]), deserialize(it[1]), deserialize(it[2]))
}
Expand All @@ -161,8 +160,7 @@ abstract class KVServiceBinder<out T, RH, WH, SH>(
method: HttpMethod,
route: String?
) {
expectMethodSupportsParameters(method)
bind(method, route) {
bind(method, route, 4) {
requireParameterCountEqualTo(it, 4)
function.invoke(this, deserialize(it[0]), deserialize(it[1]), deserialize(it[2]), deserialize(it[3]))
}
Expand All @@ -179,8 +177,7 @@ abstract class KVServiceBinder<out T, RH, WH, SH>(
method: HttpMethod,
route: String?
) {
expectMethodSupportsParameters(method)
bind(method, route) {
bind(method, route, 5) {
requireParameterCountEqualTo(it, 5)
function.invoke(
this,
Expand All @@ -204,8 +201,7 @@ abstract class KVServiceBinder<out T, RH, WH, SH>(
method: HttpMethod,
route: String?
) {
expectMethodSupportsParameters(method)
bind(method, route) {
bind(method, route, 6) {
requireParameterCountEqualTo(it, 6)
function.invoke(
this,
Expand Down Expand Up @@ -285,10 +281,3 @@ abstract class KVServiceBinder<out T, RH, WH, SH>(
return deSerializer.deserialize(txt)
}
}


@PublishedApi
internal fun expectMethodSupportsParameters(method: HttpMethod) {
if (method == HttpMethod.GET)
throw UnsupportedOperationException("GET method is only supported for methods without parameters")
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,23 +141,6 @@ class KVServiceBinderTest {
return Array(BINDING_INITIALIZERS.size) { args(it, BINDING_INITIALIZERS[it]) }
}

@Test
fun bind_canUseGetMethod_ifNoArgs() {
// execution & evaluation
testBind(BINDING_INITIALIZERS[0], method = HttpMethod.GET, result = true)
}

@Test(
dataProvider = "provide_bindingInitializersWithArgs",
expectedExceptions = [UnsupportedOperationException::class]
)
fun bind_failsForGetMethod_ifHasArgs(bindingInitializer: BindingInitializer) {
// execution
testBind(bindingInitializer, method = HttpMethod.GET, result = true)

// evaluation handled by expectedExceptions
}

@DataProvider
fun provide_bindingInitializersWithArgs(): Array<BindingInitializer> =
BINDING_INITIALIZERS.copyOfRange(1, BINDING_INITIALIZERS.size)
Expand Down Expand Up @@ -218,6 +201,7 @@ private class KVServiceBinderImpl : KVServiceBinder<Any, RouteHandler, Websocket
override fun <RET> createRequestHandler(
method: HttpMethod,
function: suspend Any.(params: List<String?>) -> RET,
numberOfParams: Int,
serializerFactory: () -> KSerializer<RET>
): RouteHandler =
{ runBlocking { function.invoke(HANDLER_THIS, it) } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.modules.SerializersModule
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.net.URLDecoder
import java.nio.charset.StandardCharsets
import kotlin.reflect.KClass

typealias RequestHandler = (Context) -> Unit
Expand All @@ -69,12 +71,18 @@ actual open class KVServiceManager<out T : Any> actual constructor(private val s
override fun <RET> createRequestHandler(
method: HttpMethod,
function: suspend T.(params: List<String?>) -> RET,
numberOfParams: Int,
serializerFactory: () -> KSerializer<RET>
): RequestHandler {
val serializer by lazy { serializerFactory() }
return { ctx ->
val jsonRpcRequest = if (method == HttpMethod.GET) {
JsonRpcRequest(ctx.queryParamAsClass<Int>("id").get(), "", listOf())
val parameters = (0..<numberOfParams).map {
ctx.queryParam("p$it")?.let {
URLDecoder.decode(it, StandardCharsets.UTF_8)
}
}
JsonRpcRequest(ctx.queryParamAsClass<Int>("id").get(), "", parameters)
} else {
ctx.bodyAsClass()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.modules.SerializersModule
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.net.URLDecoder
import java.nio.charset.StandardCharsets
import kotlin.reflect.KClass

typealias RequestHandler = suspend HandlerContext.() -> Any
Expand All @@ -66,12 +68,18 @@ actual open class KVServiceManager<out T : Any> actual constructor(private val s
override fun <RET> createRequestHandler(
method: HttpMethod,
function: suspend T.(params: List<String?>) -> RET,
numberOfParams: Int,
serializerFactory: () -> KSerializer<RET>
): RequestHandler {
val serializer by lazy { serializerFactory() }
return {
val jsonRpcRequest = if (method == HttpMethod.GET) {
JsonRpcRequest(ctx.query("id").intValue(), "", listOf())
val parameters = (0..<numberOfParams).map {
ctx.query("p$it").valueOrNull()?.let {
URLDecoder.decode(it, StandardCharsets.UTF_8)
}
}
JsonRpcRequest(ctx.query("id").intValue(), "", parameters)
} else {
ctx.body(JsonRpcRequest::class.java)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import kotlinx.serialization.modules.SerializersModule
import org.koin.ktor.ext.getKoin
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.net.URLDecoder
import java.nio.charset.StandardCharsets
import kotlin.reflect.KClass

typealias RequestHandler = suspend PipelineContext<Unit, ApplicationCall>.(Unit) -> Unit
Expand All @@ -61,6 +63,7 @@ actual open class KVServiceManager<out T : Any> actual constructor(private val s
override fun <RET> createRequestHandler(
method: HttpMethod,
function: suspend T.(params: List<String?>) -> RET,
numberOfParams: Int,
serializerFactory: () -> KSerializer<RET>
): RequestHandler {
val serializer by lazy { serializerFactory() }
Expand All @@ -69,7 +72,12 @@ actual open class KVServiceManager<out T : Any> actual constructor(private val s
val service = call.getKoin().get<T>(serviceClass)
KoinModule.threadLocalApplicationCall.remove()
val jsonRpcRequest = if (method == HttpMethod.GET) {
JsonRpcRequest(call.request.queryParameters["id"]?.toInt() ?: 0, "", listOf())
val parameters = (0..<numberOfParams).map {
call.request.queryParameters["p$it"]?.let {
URLDecoder.decode(it, StandardCharsets.UTF_8)
}
}
JsonRpcRequest(call.request.queryParameters["id"]?.toInt() ?: 0, "", parameters)
} else {
call.receive()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.modules.SerializersModule
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.net.URLDecoder
import java.nio.charset.StandardCharsets
import kotlin.reflect.KClass

typealias RequestHandler = suspend PipelineContext<Unit, ApplicationCall>.(Unit) -> Unit
Expand All @@ -60,13 +62,19 @@ actual open class KVServiceManager<out T : Any> actual constructor(private val s
override fun <RET> createRequestHandler(
method: HttpMethod,
function: suspend T.(params: List<String?>) -> RET,
numberOfParams: Int,
serializerFactory: () -> KSerializer<RET>
): RequestHandler {
val serializer by lazy { serializerFactory() }
return {
val service = call.injector.createChildInjector(DummyWsSessionModule()).getInstance(serviceClass.java)
val jsonRpcRequest = if (method == HttpMethod.GET) {
JsonRpcRequest(call.request.queryParameters["id"]?.toInt() ?: 0, "", listOf())
val parameters = (0..<numberOfParams).map {
call.request.queryParameters["p$it"]?.let {
URLDecoder.decode(it, StandardCharsets.UTF_8)
}
}
JsonRpcRequest(call.request.queryParameters["id"]?.toInt() ?: 0, "", parameters)
} else {
call.receive()
}
Expand Down
Loading

0 comments on commit 32b9626

Please sign in to comment.