Skip to content

Commit

Permalink
✨ [fuel-jackson] Added custom mapper support. (kittinunf#569)
Browse files Browse the repository at this point in the history
  • Loading branch information
guilherme-pohlmann authored and kittinunf committed Jan 24, 2019
1 parent e85be1a commit 63c5f4c
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 5 deletions.
56 changes: 55 additions & 1 deletion fuel-jackson/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,58 @@ compile 'com.github.kittinunf.fuel:fuel-jackson:<latest-version>'

## Usage

See `Fueljackson.kt`
The Fuel-Jackson module provides a built in support for Jackson deserialization.
This is done by adding the `responseObject` extension function into Fuel `Request` interface.

By default, the `responseObject` call will use the `defaultMapper` property defined in `FuelJackson.kt`.

```kotlin
data class HttpBinUserAgentModel(var userAgent: String = "")

Fuel.get("/user-agent")
.responseObject<HttpBinUserAgentModel>()
```

Alternatively, you can provide your own `ObjectMapper` as a parameter to it.

```kotlin
data class HttpBinUserAgentModel(var userAgent: String = "")

val mapper = ObjectMapper().registerKotlinModule()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

mapper.propertyNamingStrategy = PropertyNamingStrategy.SNAKE_CASE

Fuel.get("/user-agent")
.responseObject<HttpBinUserAgentModel>(mapper)
```

Also, the `responseObject` overloads allows you to pass `Response Handlers` as lambda functions

```kotlin
data class HttpBinUserAgentModel(var userAgent: String = "")

Fuel.get("/user-agent")
.responseObject<HttpBinUserAgentModel> { request, response, result ->
//handle here
}
```

or `ResponseHandler<T>` instances.

```kotlin
data class HttpBinUserAgentModel(var userAgent: String = "")

Fuel.get("/user-agent")
.responseObject(object : ResponseHandler<HttpBinUserAgentModel> {
override fun success(request: Request, response: Response, value: HttpBinUserAgentModel) {
//handle success
}

override fun failure(request: Request, response: Response, error: FuelError) {
//handle failure
}
})
```

Both overloads allows you to provide custom `ObjectMapper` if needed.
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,26 @@ import com.github.kittinunf.result.Result
import java.io.InputStream
import java.io.Reader

val mapper = ObjectMapper().registerKotlinModule()
val defaultMapper = ObjectMapper().registerKotlinModule()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

inline fun <reified T : Any> Request.responseObject(noinline handler: (Request, Response, Result<T, FuelError>) -> Unit) {
response(jacksonDeserializerOf(), handler)
}

inline fun <reified T : Any> Request.responseObject(mapper: ObjectMapper, noinline handler: (Request, Response, Result<T, FuelError>) -> Unit) {
response(jacksonDeserializerOf(mapper), handler)
}

inline fun <reified T : Any> Request.responseObject(mapper: ObjectMapper, handler: ResponseHandler<T>) = response(jacksonDeserializerOf(mapper), handler)

inline fun <reified T : Any> Request.responseObject(handler: ResponseHandler<T>) = response(jacksonDeserializerOf(), handler)

inline fun <reified T : Any> Request.responseObject() = response(jacksonDeserializerOf<T>())

inline fun <reified T : Any> jacksonDeserializerOf() = object : ResponseDeserializable<T> {
inline fun <reified T : Any> Request.responseObject(mapper: ObjectMapper) = response(jacksonDeserializerOf<T>(mapper))

inline fun <reified T : Any> jacksonDeserializerOf(mapper: ObjectMapper = defaultMapper) = object : ResponseDeserializable<T> {
override fun deserialize(reader: Reader): T? {
return mapper.readValue(reader)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.github.kittinunf.fuel

import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.PropertyNamingStrategy
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import com.github.kittinunf.fuel.core.FuelError
import com.github.kittinunf.fuel.core.ResponseHandler
import com.github.kittinunf.fuel.core.Request
Expand Down Expand Up @@ -40,6 +44,21 @@ class FuelJacksonTest : MockHttpTestCase() {
}
}

@Test
fun jacksonTestResponseObjectWithCustomMapper() {
mock.chain(
request = mock.request().withPath("/user-agent"),
response = mock.reflect()
)

Fuel.get(mock.path("user-agent"))
.responseObject(jacksonDeserializerOf<HttpBinUserAgentModel>(createCustomMapper())) { _, _, result ->
assertThat(result.component1(), instanceOf(HttpBinUserAgentModel::class.java))
assertThat(result.component1()?.userAgent, not(""))
assertThat(result.component2(), instanceOf(FuelError::class.java))
}
}

@Test
fun jacksonTestResponseObjectError() {
mock.chain(
Expand All @@ -54,6 +73,20 @@ class FuelJacksonTest : MockHttpTestCase() {
}
}

@Test
fun jacksonTestResponseObjectErrorWithCustomMapper() {
mock.chain(
request = mock.request().withPath("/user-agent"),
response = mock.response().withStatusCode(HttpURLConnection.HTTP_NOT_FOUND)
)

Fuel.get(mock.path("user-agent"))
.responseObject(jacksonDeserializerOf<HttpBinUserAgentModel>(createCustomMapper())) { _, _, result ->
assertThat(result.component1(), notNullValue())
assertThat(result.component2(), instanceOf(Result.Failure::class.java))
}
}

@Test
fun jacksonTestResponseDeserializerObject() {
mock.chain(
Expand All @@ -68,6 +101,20 @@ class FuelJacksonTest : MockHttpTestCase() {
}
}

@Test
fun jacksonTestResponseDeserializerObjectWithCustomMapper() {
mock.chain(
request = mock.request().withPath("/user-agent"),
response = mock.reflect()
)

Fuel.get(mock.path("user-agent"))
.responseObject<HttpBinUserAgentModel>(createCustomMapper()) { _, _, result ->
assertThat(result.component1(), notNullValue())
assertThat(result.component2(), notNullValue())
}
}

@Test
fun jacksonTestResponseDeserializerObjectError() {
mock.chain(
Expand Down Expand Up @@ -101,6 +148,25 @@ class FuelJacksonTest : MockHttpTestCase() {
})
}

@Test
fun jacksonTestResponseHandlerObjectWithCustomMapper() {
mock.chain(
request = mock.request().withPath("/user-agent"),
response = mock.reflect()
)

Fuel.get(mock.path("user-agent"))
.responseObject(createCustomMapper(), object : ResponseHandler<HttpBinUserAgentModel> {
override fun success(request: Request, response: Response, value: HttpBinUserAgentModel) {
assertThat(value, notNullValue())
}

override fun failure(request: Request, response: Response, error: FuelError) {
assertThat(error, notNullValue())
}
})
}

@Test
fun jacksonTestResponseHandlerObjectError() {
mock.chain(
Expand All @@ -109,7 +175,7 @@ class FuelJacksonTest : MockHttpTestCase() {
)

Fuel.get(mock.path("user-agent"))
.responseObject(object : ResponseHandler<HttpBinUserAgentModel> {
.responseObject(createCustomMapper(), object : ResponseHandler<HttpBinUserAgentModel> {
override fun success(request: Request, response: Response, value: HttpBinUserAgentModel) {
assertThat(value, notNullValue())
}
Expand All @@ -136,6 +202,23 @@ class FuelJacksonTest : MockHttpTestCase() {
assertThat(result, notNullValue())
}

@Test
fun jacksonTestResponseSyncObjectWithCustomMapper() {
mock.chain(
request = mock.request().withPath("/issues/1"),
response = mock.response().withBody(
"{ \"id\": 1, \"title\": \"issue 1\", \"number\": null, \"snake_property\": 10 }"
).withStatusCode(HttpURLConnection.HTTP_OK)
)

val (_, res, result) = Fuel.get(mock.path("issues/1")).responseObject<IssueInfo>(createCustomMapper())
assertThat(res, notNullValue())
assertThat(result.get(), notNullValue())
assertThat(result.get(), isA(IssueInfo::class.java))
assertThat(result.get().snakeProperty, equalTo(10))
assertThat(result, notNullValue())
}

@Test
fun jacksonTestResponseSyncObjectError() {
mock.chain(
Expand All @@ -151,7 +234,7 @@ class FuelJacksonTest : MockHttpTestCase() {
assertThat((error as FuelError).response.statusCode, equalTo(HttpURLConnection.HTTP_NOT_FOUND))
}

data class IssueInfo(val id: Int, val title: String, val number: Int)
data class IssueInfo(val id: Int, val title: String, val number: Int, val snakeProperty: Int)

@Test
fun testProcessingGenericList() {
Expand Down Expand Up @@ -206,4 +289,13 @@ class FuelJacksonTest : MockHttpTestCase() {
assertThat(issueList[0], isA(IssueInfo::class.java))
}
}

private fun createCustomMapper(): ObjectMapper {
val mapper = ObjectMapper().registerKotlinModule()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

mapper.propertyNamingStrategy = PropertyNamingStrategy.SNAKE_CASE

return mapper
}
}

0 comments on commit 63c5f4c

Please sign in to comment.