Skip to content

Commit

Permalink
Fixed argument resolver & finer grained serialization (#298)
Browse files Browse the repository at this point in the history
Co-authored-by: nsimonides <[email protected]>
  • Loading branch information
nsmnds and nsimonides authored Nov 23, 2024
1 parent 82c1e83 commit c72287c
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import community.flock.wirespec.integration.jackson.java.WirespecModuleJava
import community.flock.wirespec.integration.spring.java.web.WirespecResponseBodyAdvice
import community.flock.wirespec.java.Wirespec
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
Expand All @@ -17,12 +18,35 @@ open class WirespecConfiguration {
open fun wirespecSerialization(objectMapper: ObjectMapper) = object : Wirespec.Serialization<String> {

private val wirespecObjectMapper = objectMapper.copy().registerModule(WirespecModuleJava())
private val stringListDelimiter = ","

override fun <T> serialize(body: T, type: Type?): String = wirespecObjectMapper.writeValueAsString(body)
override fun <T> serialize(body: T, type: Type?): String = when {
body is String -> body
isStringIterable(type) && body is Iterable<*> -> body.joinToString(stringListDelimiter)
else -> wirespecObjectMapper.writeValueAsString(body)
}

override fun <T : Any> deserialize(raw: String?, valueType: Type?): T? = raw?.let {
val type = wirespecObjectMapper.constructType(valueType)
wirespecObjectMapper.readValue(it, type)
when {
isStringIterable(valueType) -> {
@Suppress("UNCHECKED_CAST")
raw.split(stringListDelimiter) as T
}
else -> {
val type = wirespecObjectMapper.constructType(valueType)
wirespecObjectMapper.readValue(it, type)
}
}
}

private fun isStringIterable(type: Type?): Boolean {
if (type !is ParameterizedType) return false

val rawType = type.rawType as Class<*>
if (!Iterable::class.java.isAssignableFrom(rawType)) return false

val typeArgument = type.actualTypeArguments.firstOrNull()
return typeArgument == String::class.java
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package community.flock.wirespec.integration.spring.java.web

import community.flock.wirespec.integration.spring.shared.extractPath
import community.flock.wirespec.integration.spring.shared.extractQueries
import community.flock.wirespec.java.Wirespec
import jakarta.servlet.http.HttpServletRequest
import java.util.stream.Collectors
Expand Down Expand Up @@ -34,14 +36,8 @@ class WirespecMethodArgumentResolver(

fun HttpServletRequest.toRawRequest(): Wirespec.RawRequest = Wirespec.RawRequest(
method,
pathInfo.split("/"),
queryString
?.split("&")
?.associate {
val (key, value) = it.split("=")
key to value
}
.orEmpty(),
extractPath(),
extractQueries(),
headerNames.toList().associateWith(::getHeader),
reader.lines().collect(Collectors.joining(System.lineSeparator()))
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import community.flock.wirespec.kotlin.Wirespec
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.javaType

Expand All @@ -20,10 +21,26 @@ open class WirespecConfiguration {

private val wirespecObjectMapper = objectMapper.copy().registerModule(WirespecModuleKotlin())

override fun <T> serialize(t: T, kType: KType): String = wirespecObjectMapper.writeValueAsString(t)
private val stringListDelimiter = ","

override fun <T> deserialize(raw: String, kType: KType): T = wirespecObjectMapper
.constructType(kType.javaType)
.let { wirespecObjectMapper.readValue(raw, it) }
override fun <T> serialize(t: T, kType: KType): String =
when {
t is String -> t
isStringIterable(kType) && t is Iterable<*> -> t.joinToString(stringListDelimiter)
else -> wirespecObjectMapper.writeValueAsString(t)
}

override fun <T> deserialize(raw: String, kType: KType): T =
if (isStringIterable(kType)) {
raw.split(stringListDelimiter) as T
} else {
wirespecObjectMapper
.constructType(kType.javaType)
.let { wirespecObjectMapper.readValue(raw, it) }
}
}

fun isStringIterable(kType: KType) =
(kType.classifier as? KClass<*>)?.java?.let { Iterable::class.java.isAssignableFrom(it) } == true &&
kType.arguments.singleOrNull()?.type?.classifier == String::class
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package community.flock.wirespec.integration.spring.kotlin.web

import community.flock.wirespec.integration.spring.shared.extractPath
import community.flock.wirespec.integration.spring.shared.extractQueries
import community.flock.wirespec.kotlin.Wirespec
import jakarta.servlet.http.HttpServletRequest
import java.util.stream.Collectors
Expand Down Expand Up @@ -36,14 +38,8 @@ class WirespecMethodArgumentResolver(

fun HttpServletRequest.toRawRequest(): Wirespec.RawRequest = Wirespec.RawRequest(
method = method,
path = pathInfo?.split("/")?.drop(1) ?: emptyList(),
queries = queryString
?.split("&")
?.associate {
val (key, value) = it.split("=")
key to value
}
.orEmpty(),
path = extractPath(),
queries = extractQueries(),
headers = headerNames.toList().associateWith(::getHeader),
body = reader.lines().collect(Collectors.joining(System.lineSeparator()))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package community.flock.wirespec.integration.spring.shared

import jakarta.servlet.http.HttpServletRequest

/**
* Depending on how the controller is mapped:
*
* @RequestMapping("/pet/{id}")
* // or
* @GetMapping("/pet/{id}")
*
* The path ends up in either the pathInfo or servletPath
*/
fun HttpServletRequest.extractPath() = (if (pathInfo != null) pathInfo else servletPath)
.split("/")
.filter { it.isNotEmpty() }

fun HttpServletRequest.extractQueries() = queryString
?.split("&")
?.associate {
val (key, value) = it.split("=")
key to value
}
.orEmpty()

0 comments on commit c72287c

Please sign in to comment.