-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add client streaming flag protection (#314)
Co-authored-by: Alejandro Vera-baquero <[email protected]>
- Loading branch information
Showing
17 changed files
with
261 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
src/test/resources/examples/byteArrayStream/client/ApiClient.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package examples.byteArrayStream.client | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import com.fasterxml.jackson.module.kotlin.jacksonTypeRef | ||
import okhttp3.Headers | ||
import okhttp3.HttpUrl | ||
import okhttp3.HttpUrl.Companion.toHttpUrl | ||
import okhttp3.MediaType.Companion.toMediaType | ||
import okhttp3.OkHttpClient | ||
import okhttp3.Request | ||
import okhttp3.RequestBody.Companion.toRequestBody | ||
import kotlin.ByteArray | ||
import kotlin.String | ||
import kotlin.Suppress | ||
import kotlin.collections.Map | ||
import kotlin.jvm.Throws | ||
|
||
@Suppress("unused") | ||
public class BinaryDataClient( | ||
private val objectMapper: ObjectMapper, | ||
private val baseUrl: String, | ||
private val client: OkHttpClient, | ||
) { | ||
/** | ||
* | ||
* | ||
* @param applicationOctetStream | ||
*/ | ||
@Throws(ApiException::class) | ||
public fun postBinaryData( | ||
applicationOctetStream: ByteArray, | ||
additionalHeaders: Map<String, String> = emptyMap(), | ||
additionalQueryParameters: Map<String, String> = emptyMap(), | ||
): ApiResponse<ByteArray> { | ||
val httpUrl: HttpUrl = "$baseUrl/binary-data" | ||
.toHttpUrl() | ||
.newBuilder() | ||
.also { builder -> additionalQueryParameters.forEach { builder.queryParam(it.key, it.value) } } | ||
.build() | ||
|
||
val headerBuilder = Headers.Builder() | ||
additionalHeaders.forEach { headerBuilder.header(it.key, it.value) } | ||
val httpHeaders: Headers = headerBuilder.build() | ||
|
||
val request: Request = Request.Builder() | ||
.url(httpUrl) | ||
.headers(httpHeaders) | ||
.post(objectMapper.writeValueAsString(applicationOctetStream).toRequestBody("application/octet-stream".toMediaType())) | ||
.build() | ||
|
||
return request.execute(client, objectMapper, jacksonTypeRef()) | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
src/test/resources/examples/byteArrayStream/client/ApiModels.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package examples.byteArrayStream.client | ||
|
||
import okhttp3.Headers | ||
|
||
/** | ||
* API 2xx success response returned by API call. | ||
* | ||
* @param <T> The type of data that is deserialized from response body | ||
*/ | ||
data class ApiResponse<T>(val statusCode: Int, val headers: Headers, val data: T? = null) | ||
|
||
/** | ||
* API non-2xx failure responses returned by API call. | ||
*/ | ||
open class ApiException(override val message: String) : RuntimeException(message) | ||
|
||
/** | ||
* API 3xx redirect response returned by API call. | ||
*/ | ||
open class ApiRedirectException(val statusCode: Int, val headers: Headers, override val message: String) : ApiException(message) | ||
|
||
/** | ||
* API 4xx failure responses returned by API call. | ||
*/ | ||
data class ApiClientException(val statusCode: Int, val headers: Headers, override val message: String) : | ||
ApiException(message) | ||
|
||
/** | ||
* API 5xx failure responses returned by API call. | ||
*/ | ||
data class ApiServerException(val statusCode: Int, val headers: Headers, override val message: String) : | ||
ApiException(message) |
39 changes: 39 additions & 0 deletions
39
src/test/resources/examples/byteArrayStream/client/ApiService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package examples.byteArrayStream.client | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry | ||
import okhttp3.OkHttpClient | ||
import kotlin.ByteArray | ||
import kotlin.String | ||
import kotlin.Suppress | ||
import kotlin.collections.Map | ||
import kotlin.jvm.Throws | ||
|
||
/** | ||
* The circuit breaker registry should have the proper configuration to correctly action on circuit | ||
* breaker transitions based on the client exceptions [ApiClientException], [ApiServerException] and | ||
* [IOException]. | ||
* | ||
* @see ApiClientException | ||
* @see ApiServerException | ||
*/ | ||
@Suppress("unused") | ||
public class BinaryDataService( | ||
private val circuitBreakerRegistry: CircuitBreakerRegistry, | ||
objectMapper: ObjectMapper, | ||
baseUrl: String, | ||
client: OkHttpClient, | ||
) { | ||
public var circuitBreakerName: String = "binaryDataClient" | ||
|
||
private val apiClient: BinaryDataClient = BinaryDataClient(objectMapper, baseUrl, client) | ||
|
||
@Throws(ApiException::class) | ||
public fun postBinaryData( | ||
applicationOctetStream: ByteArray, | ||
additionalHeaders: Map<String, String> = emptyMap(), | ||
): ApiResponse<ByteArray> = | ||
withCircuitBreaker(circuitBreakerRegistry, circuitBreakerName) { | ||
apiClient.postBinaryData(applicationOctetStream, additionalHeaders) | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
src/test/resources/examples/byteArrayStream/client/HttpResilience4jUtil.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package examples.byteArrayStream.client | ||
|
||
import io.github.resilience4j.circuitbreaker.CircuitBreaker | ||
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry | ||
|
||
fun <T> withCircuitBreaker( | ||
circuitBreakerRegistry: CircuitBreakerRegistry, | ||
apiClientName: String, | ||
apiCall: () -> ApiResponse<T> | ||
): ApiResponse<T> { | ||
val circuitBreaker = circuitBreakerRegistry.circuitBreaker(apiClientName) | ||
return CircuitBreaker.decorateSupplier(circuitBreaker, apiCall).get() | ||
} |
69 changes: 69 additions & 0 deletions
69
src/test/resources/examples/byteArrayStream/client/HttpUtil.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package examples.byteArrayStream.client | ||
|
||
import com.fasterxml.jackson.core.type.TypeReference | ||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import okhttp3.FormBody | ||
import okhttp3.Headers | ||
import okhttp3.HttpUrl | ||
import okhttp3.OkHttpClient | ||
import okhttp3.Request | ||
import okhttp3.Response | ||
import okhttp3.ResponseBody | ||
|
||
@Suppress("unused") | ||
fun <T : Any> HttpUrl.Builder.queryParam(key: String, value: T?): HttpUrl.Builder = this.apply { | ||
if (value != null) this.addQueryParameter(key, value.toString()) | ||
} | ||
|
||
@Suppress("unused") | ||
fun <T : Any> FormBody.Builder.formParam(key: String, value: T?): FormBody.Builder = this.apply { | ||
if (value != null) this.add(key, value.toString()) | ||
} | ||
|
||
@Suppress("unused") | ||
fun HttpUrl.Builder.queryParam(key: String, values: List<String>?, explode: Boolean = true) = this.apply { | ||
if (values != null) { | ||
if (explode) values.forEach { addQueryParameter(key, it) } | ||
else addQueryParameter(key, values.joinToString(",")) | ||
} | ||
} | ||
|
||
@Suppress("unused") | ||
fun Headers.Builder.header(key: String, value: String?): Headers.Builder = this.apply { | ||
if (value != null) this.add(key, value) | ||
} | ||
|
||
@Throws(ApiException::class) | ||
fun <T> Request.execute(client: OkHttpClient, objectMapper: ObjectMapper, typeRef: TypeReference<T>): ApiResponse<T> = | ||
client.newCall(this).execute().use { response -> | ||
when { | ||
response.isSuccessful -> | ||
ApiResponse(response.code, response.headers, response.body?.deserialize(objectMapper, typeRef)) | ||
response.isRedirection() -> | ||
throw ApiRedirectException(response.code, response.headers, response.errorMessage()) | ||
response.isBadRequest() -> | ||
throw ApiClientException(response.code, response.headers, response.errorMessage()) | ||
response.isServerError() -> | ||
throw ApiServerException(response.code, response.headers, response.errorMessage()) | ||
else -> throw ApiException("[${response.code}]: ${response.errorMessage()}") | ||
} | ||
} | ||
|
||
@Suppress("unused") | ||
fun String.pathParam(vararg params: Pair<String, Any>): String = params.asSequence() | ||
.joinToString { param -> | ||
this.replace(param.first, param.second.toString()) | ||
} | ||
|
||
fun <T> ResponseBody.deserialize(objectMapper: ObjectMapper, typeRef: TypeReference<T>): T? = | ||
this.string().isNotBlankOrNull()?.let { objectMapper.readValue(it, typeRef) } | ||
|
||
fun String?.isNotBlankOrNull() = if (this.isNullOrBlank()) null else this | ||
|
||
private fun Response.errorMessage(): String = this.body?.string() ?: this.message | ||
|
||
private fun Response.isBadRequest(): Boolean = this.code in 400..499 | ||
|
||
private fun Response.isServerError(): Boolean = this.code in 500..599 | ||
|
||
private fun Response.isRedirection(): Boolean = this.code in 300..399 |
22 changes: 22 additions & 0 deletions
22
src/test/resources/examples/byteArrayStream/client/OAuth.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package examples.byteArrayStream.client | ||
|
||
import okhttp3.Authenticator | ||
import okhttp3.Interceptor | ||
import okhttp3.Request | ||
import okhttp3.Response | ||
import okhttp3.Route | ||
|
||
class OAuth2(val accessToken: () -> String) : Authenticator, Interceptor { | ||
|
||
override fun authenticate(route: Route?, response: Response): Request = | ||
response.request.newBuilder() | ||
.header("Authorization", "Bearer ${accessToken().trim()}") | ||
.build() | ||
|
||
override fun intercept(chain: Interceptor.Chain): Response { | ||
val request = chain.request().newBuilder() | ||
.header("Authorization", "Bearer ${accessToken().trim()}") | ||
.build() | ||
return chain.proceed(request) | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
src/test/resources/examples/byteArrayStream/models/ClientModels.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package examples.byteArrayStream.models | ||
|
||
import com.fasterxml.jackson.`annotation`.JsonProperty | ||
import kotlin.ByteArray | ||
|
||
public data class BinaryData( | ||
@param:JsonProperty("binaryValue") | ||
@get:JsonProperty("binaryValue") | ||
public val binaryValue: ByteArray? = null, | ||
) |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.