Skip to content

Instantly share code, notes, and snippets.

@matey-jack
Last active November 14, 2019 10:19
Show Gist options
  • Save matey-jack/a1d65074db67c567ce3333bef5ce8bcb to your computer and use it in GitHub Desktop.
Save matey-jack/a1d65074db67c567ce3333bef5ce8bcb to your computer and use it in GitHub Desktop.
Automatically append query string parameters during a call to Spring's RestTemplate
import org.springframework.http.ResponseEntity
import org.springframework.web.client.RestTemplate
/**
* This method behaves like 'getForEntity', but automatically extends the uriTemplate
* with the queryString part, adding only query parameters for which the value is not null.
* Map keys of the 'queryParams' will be used as keys in the queryString.
*
* See unit tests for excellent usage examples.
*
* We're using this instead of the much more comfortable UriComponentsBuilder, because
* the http.client.requests metric requires the RestTemplate to be used with a templateUri
* and separate uriVariables to not overflow the metrics pipeline with all the variable
* values.
*/
inline fun <reified T : Any> RestTemplate.getForEntityWithQuery(
pathTemplate: String,
pathParams: Map<String, Any>,
queryParams: Map<String, Any?>
): ResponseEntity<T> {
// use SortedMap to avoid URIs varying with the hashing implementation
val nonNullQueryParams = queryParams.filterNotNullValues().toSortedMap()
val queryStringTemplate =
if (nonNullQueryParams.isEmpty()) ""
else "?" + nonNullQueryParams.map { it.key }.joinToString("&") { "$it={$it}" }
return getForEntity(pathTemplate + queryStringTemplate, T::class.java, pathParams + nonNullQueryParams)
}
// Explicitly .map on the Map so it autocasts the ? away.
fun <K, V> Map<K, V?>.filterNotNullValues(): Map<K, V> =
mapNotNull { (key, nullableValue) ->
nullableValue?.let { nonNullValue -> key to nonNullValue }
}.toMap()
package de.friday.services
import io.mockk.mockk
import io.mockk.verify
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.springframework.web.client.RestTemplate
class RestUtilitiesKtTest {
val restTemplate = mockk<RestTemplate>(relaxed = true)
@Test
fun optionalQueryParams_allNull() {
restTemplate.getForEntityWithQuery<String>(
"api/policies/{no}/",
mapOf("no" to "123"),
mapOf("name" to null as Any?, "age" to null))
verify {
restTemplate.getForEntity("api/policies/{no}/", String::class.java, mapOf("no" to "123"))
}
}
@Test
fun optionalQueryParams_oneNotNull() {
restTemplate.getForEntityWithQuery<String>(
"api/policies/{no}/",
mapOf("no" to "123"),
mapOf("name" to null as Any?, "age" to 23))
verify {
restTemplate.getForEntity("api/policies/{no}/?age={age}", String::class.java,
mapOf("age" to 23, "no" to "123"))
}
}
@Test
fun optionalQueryParams_twoNotNull_noPathParams() {
restTemplate.getForEntityWithQuery<String>(
"api/something/",
mapOf(),
mapOf("name" to "Jack", "age" to 23))
verify {
restTemplate.getForEntity("api/something/?age={age}&name={name}", String::class.java,
mapOf("age" to 23, "name" to "Jack"))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment