-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathApiResponse.kt
90 lines (81 loc) · 2.86 KB
/
ApiResponse.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/**
* Common class used by API responses.
* @param <T> the type of the response object
</T> */
@Suppress("unused") // T is used in extending classes
sealed class ApiResponse<T> {
companion object {
fun <T> create(error: Throwable): ApiErrorResponse<T> {
return ApiErrorResponse(error.message ?: "unknown error")
}
fun <T> create(response: Response<T>): ApiResponse<T> {
return if (response.isSuccessful) {
val body = response.body()
if (body == null || response.code() == 204) {
ApiEmptyResponse()
} else {
ApiSuccessResponse(
body = body,
linkHeader = response.headers()?.get("link")
)
}
} else {
val msg = response.errorBody()?.string()
val errorMsg = if (msg.isNullOrEmpty()) {
response.message()
} else {
msg
}
ApiErrorResponse(errorMsg ?: "unknown error")
}
}
}
}
/**
* separate class for HTTP 204 responses so that we can make ApiSuccessResponse's body non-null.
*/
class ApiEmptyResponse<T> : ApiResponse<T>()
/**
* Class representing a SuccessResponse, it retrieves the header value of _link_ if present
*/
data class ApiSuccessResponse<T>(
val body: T,
val links: Map<String, String>
) : ApiResponse<T>() {
constructor(body: T, linkHeader: String?) : this(
body = body,
links = linkHeader?.extractLinks() ?: emptyMap()
)
val nextPage: Int? by lazy(LazyThreadSafetyMode.NONE) {
links[NEXT_LINK]?.let { next ->
val matcher = PAGE_PATTERN.matcher(next)
if (!matcher.find() || matcher.groupCount() != 1) {
null
} else {
try {
Integer.parseInt(matcher.group(1))
} catch (ex: NumberFormatException) {
Timber.w("cannot parse next page from %s", next)
null
}
}
}
}
companion object {
private val LINK_PATTERN = Pattern.compile("<([^>]*)>[\\s]*;[\\s]*rel=\"([a-zA-Z0-9]+)\"")
private val PAGE_PATTERN = Pattern.compile("\\bpage=(\\d+)")
private const val NEXT_LINK = "next"
private fun String.extractLinks(): Map<String, String> {
val links = mutableMapOf<String, String>()
val matcher = LINK_PATTERN.matcher(this)
while (matcher.find()) {
val count = matcher.groupCount()
if (count == 2) {
links[matcher.group(2)] = matcher.group(1)
}
}
return links
}
}
}
data class ApiErrorResponse<T>(val errorMessage: String) : ApiResponse<T>()