kotlin-client.libraries.jvm-okhttp.infrastructure.ApiClient.kt.mustache Maven / Gradle / Ivy
package {{packageName}}.infrastructure
import okhttp3.Credentials
import okhttp3.OkHttpClient
import okhttp3.RequestBody
{{#jvm-okhttp3}}
import okhttp3.MediaType
{{/jvm-okhttp3}}
{{#jvm-okhttp4}}
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
{{/jvm-okhttp4}}
import okhttp3.FormBody
{{#jvm-okhttp3}}
import okhttp3.HttpUrl
{{/jvm-okhttp3}}
{{#jvm-okhttp4}}
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
{{/jvm-okhttp4}}
import okhttp3.ResponseBody
{{#jvm-okhttp4}}
import okhttp3.MediaType.Companion.toMediaTypeOrNull
{{/jvm-okhttp4}}
import okhttp3.Request
import java.io.File
{{#nonPublicApi}}internal {{/nonPublicApi}}open class ApiClient(val baseUrl: String) {
{{#nonPublicApi}}internal {{/nonPublicApi}}companion object {
protected const val ContentType = "Content-Type"
protected const val Accept = "Accept"
protected const val Authorization = "Authorization"
protected const val JsonMediaType = "application/json"
protected const val FormDataMediaType = "multipart/form-data"
protected const val FormUrlEncMediaType = "application/x-www-form-urlencoded"
protected const val XmlMediaType = "application/xml"
val apiKey: MutableMap = mutableMapOf()
val apiKeyPrefix: MutableMap = mutableMapOf()
var username: String? = null
var password: String? = null
var accessToken: String? = null
@JvmStatic
val client: OkHttpClient by lazy {
builder.build()
}
@JvmStatic
val builder: OkHttpClient.Builder = OkHttpClient.Builder()
}
protected inline fun requestBody(content: T, mediaType: String = JsonMediaType): RequestBody =
when {
{{#jvm-okhttp3}}
content is File -> RequestBody.create(
MediaType.parse(mediaType), content
)
{{/jvm-okhttp3}}
{{#jvm-okhttp4}}
content is File -> content.asRequestBody(
mediaType.toMediaTypeOrNull()
)
{{/jvm-okhttp4}}
mediaType == FormDataMediaType || mediaType == FormUrlEncMediaType -> {
FormBody.Builder().apply {
// content's type *must* be Map
@Suppress("UNCHECKED_CAST")
(content as Map).forEach { (key, value) ->
add(key, value)
}
}.build()
}
{{#jvm-okhttp3}}
mediaType == JsonMediaType -> RequestBody.create(
{{#moshi}}
MediaType.parse(mediaType), Serializer.moshi.adapter(T::class.java).toJson(content)
{{/moshi}}
{{#gson}}
MediaType.parse(mediaType), Serializer.gson.toJson(content, T::class.java)
{{/gson}}
{{#jackson}}
MediaType.parse(mediaType), Serializer.jackson.toJson(content, T::class.java)
{{/jackson}}
)
{{/jvm-okhttp3}}
{{#jvm-okhttp4}}
mediaType == JsonMediaType -> {{#moshi}}Serializer.moshi.adapter(T::class.java).toJson(content){{/moshi}}{{#gson}}Serializer.gson.toJson(content, T::class.java){{/gson}}{{#jackson}}Serializer.jacksonObjectMapper.writeValueAsString(content){{/jackson}}.toRequestBody(
mediaType.toMediaTypeOrNull()
)
{{/jvm-okhttp4}}
mediaType == XmlMediaType -> throw UnsupportedOperationException("xml not currently supported.")
// TODO: this should be extended with other serializers
else -> throw UnsupportedOperationException("requestBody currently only supports JSON body and File body.")
}
protected inline fun responseBody(body: ResponseBody?, mediaType: String? = JsonMediaType): T? {
if(body == null) {
return null
}
val bodyContent = body.string()
if (bodyContent.isEmpty()) {
return null
}
return when(mediaType) {
JsonMediaType -> {{#moshi}}Serializer.moshi.adapter(T::class.java).fromJson(bodyContent){{/moshi}}{{#gson}}Serializer.gson.fromJson(bodyContent, T::class.java){{/gson}}{{#jackson}}Serializer.jacksonObjectMapper.readValue(bodyContent, T::class.java){{/jackson}}
else -> throw UnsupportedOperationException("responseBody currently only supports JSON body.")
}
}
{{#hasAuthMethods}}
protected fun updateAuthParams(requestConfig: RequestConfig) {
{{#authMethods}}
{{#isApiKey}}
{{#isKeyInHeader}}
if (requestConfig.headers["{{keyParamName}}"].isNullOrEmpty()) {
{{/isKeyInHeader}}
{{#isKeyInQuery}}
if (requestConfig.query["{{keyParamName}}"].isNullOrEmpty()) {
{{/isKeyInQuery}}
if (apiKey["{{keyParamName}}"] != null) {
if (apiKeyPrefix["{{keyParamName}}"] != null) {
{{#isKeyInHeader}}
requestConfig.headers["{{keyParamName}}"] = apiKeyPrefix["{{keyParamName}}"]!! + " " + apiKey["{{keyParamName}}"]!!
{{/isKeyInHeader}}
{{#isKeyInQuery}}
requestConfig.query["{{keyParamName}}"] = apiKeyPrefix["{{keyParamName}}"]!! + " " + apiKey["{{keyParamName}}"]!!
{{/isKeyInQuery}}
} else {
{{#isKeyInHeader}}
requestConfig.headers["{{keyParamName}}"] = apiKey["{{keyParamName}}"]!!
{{/isKeyInHeader}}
{{#isKeyInQuery}}
requestConfig.query["{{keyParamName}}"] = apiKey["{{keyParamName}}"]!!
{{/isKeyInQuery}}
}
}
}
{{/isApiKey}}
{{#isBasic}}
{{#isBasicBasic}}
if (requestConfig.headers[Authorization].isNullOrEmpty()) {
username?.let { username ->
password?.let { password ->
requestConfig.headers[Authorization] = Credentials.basic(username, password)
}
}
}
{{/isBasicBasic}}
{{#isBasicBearer}}
if (requestConfig.headers[Authorization].isNullOrEmpty()) {
accessToken?.let { accessToken ->
requestConfig.headers[Authorization] = "Bearer $accessToken"
}
}
{{/isBasicBearer}}
{{/isBasic}}
{{#isOAuth}}
if (requestConfig.headers[Authorization].isNullOrEmpty()) {
accessToken?.let { accessToken ->
requestConfig.headers[Authorization] = "Bearer $accessToken "
}
}
{{/isOAuth}}
{{/authMethods}}
}
{{/hasAuthMethods}}
protected inline fun request(requestConfig: RequestConfig, body : Any? = null): ApiInfrastructureResponse {
{{#jvm-okhttp3}}
val httpUrl = HttpUrl.parse(baseUrl) ?: throw IllegalStateException("baseUrl is invalid.")
{{/jvm-okhttp3}}
{{#jvm-okhttp4}}
val httpUrl = baseUrl.toHttpUrlOrNull() ?: throw IllegalStateException("baseUrl is invalid.")
{{/jvm-okhttp4}}
{{#hasAuthMethods}}
// take authMethod from operation
updateAuthParams(requestConfig)
{{/hasAuthMethods}}
val url = httpUrl.newBuilder()
.addPathSegments(requestConfig.path.trimStart('/'))
.apply {
requestConfig.query.forEach { query ->
query.value.forEach { queryValue ->
addQueryParameter(query.key, queryValue)
}
}
}.build()
// take content-type/accept from spec or set to default (application/json) if not defined
if (requestConfig.headers[ContentType].isNullOrEmpty()) {
requestConfig.headers[ContentType] = JsonMediaType
}
if (requestConfig.headers[Accept].isNullOrEmpty()) {
requestConfig.headers[Accept] = JsonMediaType
}
val headers = requestConfig.headers
if(headers[ContentType] ?: "" == "") {
throw kotlin.IllegalStateException("Missing Content-Type header. This is required.")
}
if(headers[Accept] ?: "" == "") {
throw kotlin.IllegalStateException("Missing Accept header. This is required.")
}
// TODO: support multiple contentType options here.
val contentType = (headers[ContentType] as String).substringBefore(";").toLowerCase()
val request = when (requestConfig.method) {
RequestMethod.DELETE -> Request.Builder().url(url).delete(requestBody(body, contentType))
RequestMethod.GET -> Request.Builder().url(url)
RequestMethod.HEAD -> Request.Builder().url(url).head()
RequestMethod.PATCH -> Request.Builder().url(url).patch(requestBody(body, contentType))
RequestMethod.PUT -> Request.Builder().url(url).put(requestBody(body, contentType))
RequestMethod.POST -> Request.Builder().url(url).post(requestBody(body, contentType))
RequestMethod.OPTIONS -> Request.Builder().url(url).method("OPTIONS", null)
}.apply {
headers.forEach { header -> addHeader(header.key, header.value) }
}.build()
val response = client.newCall(request).execute()
val accept = response.header(ContentType)?.substringBefore(";")?.toLowerCase()
// TODO: handle specific mapping types. e.g. Map>
when {
response.isRedirect -> return Redirection(
response.code{{#jvm-okhttp3}}(){{/jvm-okhttp3}},
response.headers{{#jvm-okhttp3}}(){{/jvm-okhttp3}}.toMultimap()
)
response.isInformational -> return Informational(
response.message{{#jvm-okhttp3}}(){{/jvm-okhttp3}},
response.code{{#jvm-okhttp3}}(){{/jvm-okhttp3}},
response.headers{{#jvm-okhttp3}}(){{/jvm-okhttp3}}.toMultimap()
)
response.isSuccessful -> return Success(
responseBody(response.body{{#jvm-okhttp3}}(){{/jvm-okhttp3}}, accept),
response.code{{#jvm-okhttp3}}(){{/jvm-okhttp3}},
response.headers{{#jvm-okhttp3}}(){{/jvm-okhttp3}}.toMultimap()
)
response.isClientError -> return ClientError(
response.message{{#jvm-okhttp3}}(){{/jvm-okhttp3}},
response.body{{#jvm-okhttp3}}(){{/jvm-okhttp3}}?.string(),
response.code{{#jvm-okhttp3}}(){{/jvm-okhttp3}},
response.headers{{#jvm-okhttp3}}(){{/jvm-okhttp3}}.toMultimap()
)
else -> return ServerError(
response.message{{#jvm-okhttp3}}(){{/jvm-okhttp3}},
response.body{{#jvm-okhttp3}}(){{/jvm-okhttp3}}?.string(),
response.code{{#jvm-okhttp3}}(){{/jvm-okhttp3}},
response.headers{{#jvm-okhttp3}}(){{/jvm-okhttp3}}.toMultimap()
)
}
}
{{^jackson}}
protected inline fun parseDateToQueryString(value : T): String {
{{#toJson}}
/*
.replace("\"", "") converts the json object string to an actual string for the query parameter.
The moshi or gson adapter allows a more generic solution instead of trying to use a native
formatter. It also easily allows to provide a simple way to define a custom date format pattern
inside a gson/moshi adapter.
*/
{{#moshi}}
return Serializer.moshi.adapter(T::class.java).toJson(value).replace("\"", "")
{{/moshi}}
{{#gson}}
return Serializer.gson.toJson(value, T::class.java).replace("\"", "")
{{/gson}}
{{/toJson}}
{{^toJson}}
return value.toString()
{{/toJson}}
}
{{/jackson}}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy