Skip to content

Commit 1ced472

Browse files
committed
feat: Add ability to parse status code and headers to NetworkResponse.UnknownError
1 parent 76c3ae4 commit 1ced472

File tree

3 files changed

+73
-44
lines changed

3 files changed

+73
-44
lines changed

src/main/kotlin/com/haroldadmin/cnradapter/NetworkResponse.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,9 @@ sealed class NetworkResponse<out T : Any, out U : Any> {
3232
*
3333
* An example of such an error is JSON parsing exception thrown by a serialization library.
3434
*/
35-
data class UnknownError(val error: Throwable) : NetworkResponse<Nothing, Nothing>()
35+
data class UnknownError(
36+
val error: Throwable,
37+
val code: Int? = null,
38+
val headers: Headers? = null,
39+
) : NetworkResponse<Nothing, Nothing>()
3640
}

src/main/kotlin/com/haroldadmin/cnradapter/ResponseHandler.kt

Lines changed: 47 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -10,52 +10,56 @@ import java.lang.reflect.Type
1010
*/
1111
internal object ResponseHandler {
1212

13-
/**
14-
* Converts the given [response] to a subclass of [NetworkResponse] based on different conditions.
15-
*
16-
* If the server response is successful with:
17-
* => a non-empty body -> NetworkResponse.Success<S, E>
18-
* => an empty body (and [successBodyType] is Unit) -> NetworkResponse.Success<Unit, E>
19-
* => an empty body (and [successBodyType] is not Unit) -> NetworkResponse.ServerError<E>
20-
*
21-
* @param response Retrofit's response object supplied to the call adapter
22-
* @param successBodyType A [Type] representing the success body
23-
* @param errorConverter A retrofit converter to convert the error body into the error response type (E)
24-
* @param S The success body type generic parameter
25-
* @param E The error body type generic parameter
26-
*/
27-
fun <S : Any, E : Any> handle(
28-
response: Response<S>,
29-
successBodyType: Type,
30-
errorConverter: Converter<ResponseBody, E>
31-
): NetworkResponse<S, E> {
32-
val body = response.body()
33-
val headers = response.headers()
34-
val code = response.code()
35-
val errorBody = response.errorBody()
13+
/**
14+
* Converts the given [response] to a subclass of [NetworkResponse] based on different conditions.
15+
*
16+
* If the server response is successful with:
17+
* => a non-empty body -> NetworkResponse.Success<S, E>
18+
* => an empty body (and [successBodyType] is Unit) -> NetworkResponse.Success<Unit, E>
19+
* => an empty body (and [successBodyType] is not Unit) -> NetworkResponse.ServerError<E>
20+
*
21+
* @param response Retrofit's response object supplied to the call adapter
22+
* @param successBodyType A [Type] representing the success body
23+
* @param errorConverter A retrofit converter to convert the error body into the error response type (E)
24+
* @param S The success body type generic parameter
25+
* @param E The error body type generic parameter
26+
*/
27+
fun <S : Any, E : Any> handle(
28+
response: Response<S>,
29+
successBodyType: Type,
30+
errorConverter: Converter<ResponseBody, E>
31+
): NetworkResponse<S, E> {
32+
val body = response.body()
33+
val headers = response.headers()
34+
val code = response.code()
35+
val errorBody = response.errorBody()
3636

37-
return if (response.isSuccessful) {
38-
if (body != null) {
39-
NetworkResponse.Success(body, headers, code)
40-
} else {
41-
// Special case: If the response is successful and the body is null, return a successful response
42-
// if the service method declares the success body type as Unit. Otherwise, return a server error
43-
if (successBodyType == Unit::class.java) {
44-
@Suppress("UNCHECKED_CAST")
45-
NetworkResponse.Success(Unit, headers, code) as NetworkResponse<S, E>
46-
} else {
47-
NetworkResponse.ServerError(null, code, headers)
48-
}
49-
}
37+
return if (response.isSuccessful) {
38+
if (body != null) {
39+
NetworkResponse.Success(body, headers, code)
40+
} else {
41+
// Special case: If the response is successful and the body is null, return a successful response
42+
// if the service method declares the success body type as Unit. Otherwise, return a server error
43+
if (successBodyType == Unit::class.java) {
44+
@Suppress("UNCHECKED_CAST")
45+
NetworkResponse.Success(Unit, headers, code) as NetworkResponse<S, E>
5046
} else {
51-
val networkResponse: NetworkResponse<S, E> = try {
52-
val convertedBody = errorConverter.convert(errorBody)
53-
NetworkResponse.ServerError(convertedBody, code, headers)
54-
} catch (ex: Exception) {
55-
NetworkResponse.UnknownError(ex)
56-
}
57-
networkResponse
47+
NetworkResponse.ServerError(null, code, headers)
5848
}
49+
}
50+
} else {
51+
val networkResponse: NetworkResponse<S, E> = try {
52+
val convertedBody = if (errorBody == null) {
53+
null
54+
} else {
55+
errorConverter.convert(errorBody)
56+
}
57+
NetworkResponse.ServerError(convertedBody, code, headers)
58+
} catch (ex: Exception) {
59+
NetworkResponse.UnknownError(ex, code = code, headers = headers)
60+
}
61+
networkResponse
5962
}
63+
}
6064

6165
}

src/test/kotlin/com/haroldadmin/cnradapter/MoshiApplicationTest.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ internal data class GenericErrorResponse(val error: String)
2525

2626
internal data class GenericErrorResponseInvalid(@Json(name = "errrorrrr") val error: String)
2727

28+
@Suppress("DeferredIsResult")
2829
internal interface LaunchesService {
2930
@GET("launches/{flightNumber}")
3031
suspend fun launchForFlightNumber(
@@ -143,12 +144,32 @@ internal class MoshiApplicationTest: AnnotationSpec() {
143144
server.enqueue(MockResponse().apply {
144145
setBody(resourceFileContents("/falconsat_launch.json"))
145146
setResponseCode(200)
147+
setHeader("test", "true")
146148
})
147149
val response = app.getLaunchWithFailure(validFlightNumber)
148150

149151
response.shouldBeInstanceOf<NetworkResponse.UnknownError>()
150152
response as NetworkResponse.UnknownError
151153
response.error.shouldBeInstanceOf<JsonDataException>()
154+
response.code shouldBe null
155+
response.headers shouldBe null
156+
}
157+
158+
@Test
159+
fun shouldParseResponseCodeAndHeadersOfUnsuccessfulRequestWithInvalidBodyCorrectly() {
160+
val app = TestApplication(service)
161+
server.enqueue(MockResponse().apply {
162+
setBody("""{ "message": "Too many requests!" }""")
163+
setResponseCode(429)
164+
setHeader("test", "true")
165+
})
166+
val response = app.getLaunchWithFailure(validFlightNumber)
167+
168+
response.shouldBeInstanceOf<NetworkResponse.UnknownError>()
169+
response as NetworkResponse.UnknownError
170+
response.error.shouldBeInstanceOf<JsonDataException>()
171+
response.code shouldBe 429
172+
response.headers!!["test"] shouldBe "true"
152173
}
153174

154175
@Test

0 commit comments

Comments
 (0)