Skip to content

Commit f52f5c7

Browse files
feat(client): added structured fields to errors (#304)
1 parent d5c893a commit f52f5c7

10 files changed

Lines changed: 127 additions & 86 deletions

lithic-java-core/src/main/kotlin/com/lithic/api/errors/BadRequestException.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,8 @@ package com.lithic.api.errors
22

33
import com.google.common.collect.ListMultimap
44

5-
class BadRequestException
6-
constructor(
5+
class BadRequestException(
76
headers: ListMultimap<String, String>,
8-
private val error: LithicError,
9-
) : LithicServiceException(headers, "${error}") {
10-
override fun statusCode(): Int = 400
11-
12-
fun error(): LithicError = error
13-
}
7+
body: String,
8+
error: LithicError,
9+
) : LithicServiceException(400, headers, body, error)

lithic-java-core/src/main/kotlin/com/lithic/api/errors/InternalServerException.kt

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,9 @@ package com.lithic.api.errors
22

33
import com.google.common.collect.ListMultimap
44

5-
class InternalServerException
6-
constructor(
7-
private val statusCode: Int,
5+
class InternalServerException(
6+
statusCode: Int,
87
headers: ListMultimap<String, String>,
9-
private val error: LithicError,
10-
) : LithicServiceException(headers, "${error}") {
11-
override fun statusCode(): Int = statusCode
12-
13-
fun error(): LithicError = error
14-
}
8+
body: String,
9+
error: LithicError,
10+
) : LithicServiceException(statusCode, headers, body, error)

lithic-java-core/src/main/kotlin/com/lithic/api/errors/LithicServiceException.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,19 @@ import com.google.common.collect.ListMultimap
55
abstract class LithicServiceException
66
@JvmOverloads
77
constructor(
8+
private val statusCode: Int,
89
private val headers: ListMultimap<String, String>,
9-
message: String? = null,
10+
private val body: String,
11+
private val error: LithicError,
12+
message: String = "$statusCode: $error",
1013
cause: Throwable? = null
1114
) : LithicException(message, cause) {
12-
abstract fun statusCode(): Int
15+
16+
fun statusCode(): Int = statusCode
1317

1418
fun headers(): ListMultimap<String, String> = headers
19+
20+
fun body(): String = body
21+
22+
fun error(): LithicError = error
1523
}

lithic-java-core/src/main/kotlin/com/lithic/api/errors/NotFoundException.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,8 @@ package com.lithic.api.errors
22

33
import com.google.common.collect.ListMultimap
44

5-
class NotFoundException
6-
constructor(
5+
class NotFoundException(
76
headers: ListMultimap<String, String>,
8-
private val error: LithicError,
9-
) : LithicServiceException(headers, "${error}") {
10-
override fun statusCode(): Int = 404
11-
12-
fun error(): LithicError = error
13-
}
7+
body: String,
8+
error: LithicError,
9+
) : LithicServiceException(404, headers, body, error)

lithic-java-core/src/main/kotlin/com/lithic/api/errors/PermissionDeniedException.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,8 @@ package com.lithic.api.errors
22

33
import com.google.common.collect.ListMultimap
44

5-
class PermissionDeniedException
6-
constructor(
5+
class PermissionDeniedException(
76
headers: ListMultimap<String, String>,
8-
private val error: LithicError,
9-
) : LithicServiceException(headers, "${error}") {
10-
override fun statusCode(): Int = 403
11-
12-
fun error(): LithicError = error
13-
}
7+
body: String,
8+
error: LithicError,
9+
) : LithicServiceException(403, headers, body, error)

lithic-java-core/src/main/kotlin/com/lithic/api/errors/RateLimitException.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,8 @@ package com.lithic.api.errors
22

33
import com.google.common.collect.ListMultimap
44

5-
class RateLimitException
6-
constructor(
5+
class RateLimitException(
76
headers: ListMultimap<String, String>,
8-
private val error: LithicError,
9-
) : LithicServiceException(headers, "${error}") {
10-
override fun statusCode(): Int = 429
11-
12-
fun error(): LithicError = error
13-
}
7+
body: String,
8+
error: LithicError,
9+
) : LithicServiceException(429, headers, body, error)

lithic-java-core/src/main/kotlin/com/lithic/api/errors/UnauthorizedException.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,8 @@ package com.lithic.api.errors
22

33
import com.google.common.collect.ListMultimap
44

5-
class UnauthorizedException
6-
constructor(
5+
class UnauthorizedException(
76
headers: ListMultimap<String, String>,
8-
private val error: LithicError,
9-
) : LithicServiceException(headers, "${error}") {
10-
override fun statusCode(): Int = 401
11-
12-
fun error(): LithicError = error
13-
}
7+
body: String,
8+
error: LithicError,
9+
) : LithicServiceException(401, headers, body, error)

lithic-java-core/src/main/kotlin/com/lithic/api/errors/UnexpectedStatusCodeException.kt

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,9 @@ package com.lithic.api.errors
22

33
import com.google.common.collect.ListMultimap
44

5-
class UnexpectedStatusCodeException
6-
constructor(
7-
private val statusCode: Int,
5+
class UnexpectedStatusCodeException(
6+
statusCode: Int,
87
headers: ListMultimap<String, String>,
9-
private val body: String
10-
) : LithicServiceException(headers, "Unexpected status code: ${statusCode}") {
11-
override fun statusCode(): Int = statusCode
12-
13-
fun body() = body
14-
}
8+
body: String,
9+
error: LithicError,
10+
) : LithicServiceException(statusCode, headers, body, error)

lithic-java-core/src/main/kotlin/com/lithic/api/errors/UnprocessableEntityException.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,8 @@ package com.lithic.api.errors
22

33
import com.google.common.collect.ListMultimap
44

5-
class UnprocessableEntityException
6-
constructor(
5+
class UnprocessableEntityException(
76
headers: ListMultimap<String, String>,
8-
private val error: LithicError,
9-
) : LithicServiceException(headers, "${error}") {
10-
override fun statusCode(): Int = 422
11-
12-
fun error(): LithicError = error
13-
}
7+
body: String,
8+
error: LithicError,
9+
) : LithicServiceException(422, headers, body, error)

lithic-java-core/src/main/kotlin/com/lithic/api/services/Handlers.kt

Lines changed: 83 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package com.lithic.api.services
44

55
import com.fasterxml.jackson.databind.json.JsonMapper
66
import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
7+
import com.google.common.collect.ListMultimap
78
import com.lithic.api.core.http.BinaryResponseContent
89
import com.lithic.api.core.http.HttpResponse
910
import com.lithic.api.core.http.HttpResponse.Handler
@@ -17,6 +18,7 @@ import com.lithic.api.errors.RateLimitException
1718
import com.lithic.api.errors.UnauthorizedException
1819
import com.lithic.api.errors.UnexpectedStatusCodeException
1920
import com.lithic.api.errors.UnprocessableEntityException
21+
import java.io.ByteArrayInputStream
2022
import java.io.InputStream
2123
import java.io.OutputStream
2224
import java.util.Optional
@@ -87,35 +89,98 @@ internal fun <T> Handler<T>.withErrorHandler(errorHandler: Handler<LithicError>)
8789
return object : Handler<T> {
8890
override fun handle(response: HttpResponse): T {
8991
when (val statusCode = response.statusCode()) {
90-
in 200..299 -> return this@withErrorHandler.handle(response)
91-
400 -> throw BadRequestException(response.headers(), errorHandler.handle(response))
92-
401 ->
93-
throw UnauthorizedException(response.headers(), errorHandler.handle(response))
94-
403 ->
92+
in 200..299 -> {
93+
return this@withErrorHandler.handle(response)
94+
}
95+
400 -> {
96+
val buffered = response.buffered()
97+
throw BadRequestException(
98+
buffered.headers(),
99+
StringHandler.handle(buffered),
100+
errorHandler.handle(buffered),
101+
)
102+
}
103+
401 -> {
104+
val buffered = response.buffered()
105+
throw UnauthorizedException(
106+
buffered.headers(),
107+
StringHandler.handle(buffered),
108+
errorHandler.handle(buffered),
109+
)
110+
}
111+
403 -> {
112+
val buffered = response.buffered()
95113
throw PermissionDeniedException(
96-
response.headers(),
97-
errorHandler.handle(response)
114+
buffered.headers(),
115+
StringHandler.handle(buffered),
116+
errorHandler.handle(buffered),
98117
)
99-
404 -> throw NotFoundException(response.headers(), errorHandler.handle(response))
100-
422 ->
118+
}
119+
404 -> {
120+
val buffered = response.buffered()
121+
throw NotFoundException(
122+
buffered.headers(),
123+
StringHandler.handle(buffered),
124+
errorHandler.handle(buffered),
125+
)
126+
}
127+
422 -> {
128+
val buffered = response.buffered()
101129
throw UnprocessableEntityException(
102-
response.headers(),
103-
errorHandler.handle(response)
130+
buffered.headers(),
131+
StringHandler.handle(buffered),
132+
errorHandler.handle(buffered),
133+
)
134+
}
135+
429 -> {
136+
val buffered = response.buffered()
137+
throw RateLimitException(
138+
buffered.headers(),
139+
StringHandler.handle(buffered),
140+
errorHandler.handle(buffered),
104141
)
105-
429 -> throw RateLimitException(response.headers(), errorHandler.handle(response))
106-
in 500..599 ->
142+
}
143+
in 500..599 -> {
144+
val buffered = response.buffered()
107145
throw InternalServerException(
108146
statusCode,
109-
response.headers(),
110-
errorHandler.handle(response)
147+
buffered.headers(),
148+
StringHandler.handle(buffered),
149+
errorHandler.handle(buffered),
111150
)
112-
else ->
151+
}
152+
else -> {
153+
val buffered = response.buffered()
113154
throw UnexpectedStatusCodeException(
114155
statusCode,
115-
response.headers(),
116-
StringHandler.handle(response)
156+
buffered.headers(),
157+
StringHandler.handle(buffered),
158+
errorHandler.handle(buffered),
117159
)
160+
}
118161
}
119162
}
120163
}
121164
}
165+
166+
private fun HttpResponse.buffered(): HttpResponse {
167+
val body = body().readBytes()
168+
169+
return object : HttpResponse {
170+
override fun statusCode(): Int {
171+
return this@buffered.statusCode()
172+
}
173+
174+
override fun headers(): ListMultimap<String, String> {
175+
return this@buffered.headers()
176+
}
177+
178+
override fun body(): InputStream {
179+
return ByteArrayInputStream(body)
180+
}
181+
182+
override fun close() {
183+
this@buffered.close()
184+
}
185+
}
186+
}

0 commit comments

Comments
 (0)