Skip to content

Commit

Permalink
fix: add a deprecated getBody to Responses, and fix operation executi…
Browse files Browse the repository at this point in the history
…on with empty responses (#564)

fix: add a deprecated getBody to Response
fix: execution of operations with empty responses
chore: add tests for TransactionId
chore: clean up apiExceptions mustache helper
chore: clean up mustache helpers
  • Loading branch information
anssari1 authored Jun 2, 2024
1 parent 33f5c85 commit 2d3b6d1
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ open class Response<T>(
)
}

override fun toString(): String = "Response(statusCode=$statusCode, data=$data, headers=$headers)"
override fun toString() = "Response(statusCode=$statusCode, data=$data, headers=$headers)"

@Deprecated("Use getData() instead", replaceWith = ReplaceWith("getData()"))
fun getBody() = data
}

class EmptyResponse(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.expediagroup.sdk.core.model

import io.mockk.every
import io.mockk.mockkStatic
import org.junit.jupiter.api.Test
import java.util.UUID

class TransactionIdTest {
private val uuids = listOf(UUID.randomUUID(), UUID.randomUUID())

@Test
fun `peek should return a UUID`() {
mockkStatic(UUID::class) {
every { UUID.randomUUID() } returnsMany uuids
val transactionId = TransactionId()
assert(transactionId.peek() == uuids[0])
assert(transactionId.peek() == uuids[0])
}
}

@Test
fun `dequeue should return a UUID and change it`() {
mockkStatic(UUID::class) {
every { UUID.randomUUID() } returnsMany uuids
val transactionId = TransactionId()
assert(transactionId.dequeue() == uuids[0])
assert(transactionId.peek() == uuids[1])
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,23 @@ import org.openapitools.codegen.CodegenOperation
import org.openapitools.codegen.CodegenProperty
import org.openapitools.codegen.model.ApiInfoMap

val fallbackBody = fun(dataType: String): String {
return if (dataType.startsWith("kotlin.collections.List")) {
"emptyList()"
} else if (dataType.startsWith("kotlin.collections.Map")) {
"emptyMap()"
} else if (dataType.startsWith("kotlin.collections.Set")) {
"emptySet()"
} else {
""
}
}

val mustacheHelpers = mapOf(
"isPaginatable" to {
Mustache.Lambda { fragment, writer ->
val operation = fragment.context() as CodegenOperation
if (operation.returnType == null) return@Lambda
val paginationHeaders = listOf("Pagination-Total-Results", "Link")
val availableHeaders = operation.responses.find { it.code == "200" }?.headers?.filter { it.baseName in paginationHeaders }
if (availableHeaders?.size == paginationHeaders.size) {
val context = mapOf("fallbackBody" to fallbackBody(operation.returnType))
val fallbackBody =
when {
operation.returnType.startsWith("kotlin.collections.List") -> "emptyList()"
operation.returnType.startsWith("kotlin.collections.Map") -> "emptyMap()"
operation.returnType.startsWith("kotlin.collections.Set") -> "emptySet()"
else -> ""
}

val context = mapOf("fallbackBody" to fallbackBody)
fragment.execute(context, writer)
}
}
Expand Down Expand Up @@ -83,10 +80,8 @@ val mustacheHelpers = mapOf(
}

dataTypes.forEach { dataType ->
writer.write(
"class ExpediaGroupApi${dataType}Exception(code: Int, override val errorObject: $dataType, transactionId: String?) : " +
"ExpediaGroupApiException(code, errorObject, transactionId)\n"
)
val context = mapOf("dataType" to dataType)
fragment.execute(context, writer)
}
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,27 +38,42 @@ class {{#lambda.titlecase}}{{namespace}}{{/lambda.titlecase}}Client private cons
throw ErrorObjectMapper.process(response, operationId)
}

private inline fun <reified RequestType, reified ResponseType> execute(operation: Operation<RequestType>) : Response<ResponseType> {
private suspend inline fun <reified RequestType> executeHttpRequest(operation: Operation<RequestType>): HttpResponse {
return httpClient.request {
method = HttpMethod.parse(operation.method)
url(operation.url)
operation.params.getHeaders()?.forEach { (key, value) ->
headers.append(key, value)
}

operation.params.getQueryParams()?.forEach { (key, value) ->
url.parameters.appendAll(key, value)
}

appendHeaders(operation.transactionId)
validateConstraints(operation.requestBody)
contentType(ContentType.Application.Json)
setBody(operation.requestBody)
}
}

private inline fun <reified RequestType> executeWithEmptyResponse(operation: Operation<RequestType>) : EmptyResponse {
try {
return GlobalScope.future(Dispatchers.IO) {
val response = httpClient.request {
method = HttpMethod.parse(operation.method)
url(operation.url)
operation.params.getHeaders()?.forEach { (key, value) ->
headers.append(key, value)
}

operation.params.getQueryParams()?.forEach { (key, value) ->
url.parameters.appendAll(key, value)
}

appendHeaders(operation.transactionId)
validateConstraints(operation.requestBody)
contentType(ContentType.Application.Json)
setBody(operation.requestBody)
}
val response = executeHttpRequest(operation)
throwIfError(response, operation.operationId)
EmptyResponse(response.status.value, response.headers.entries())
}.get()
} catch (exception: Exception) {
exception.handle()
}
}

private inline fun <reified RequestType, reified ResponseType> execute(operation: Operation<RequestType>) : Response<ResponseType> {
try {
return GlobalScope.future(Dispatchers.IO) {
val response = executeHttpRequest(operation)
throwIfError(response, operation.operationId)
Response(response.status.value, response.body<ResponseType>(), response.headers.entries())
}.get()
Expand All @@ -75,11 +90,13 @@ class {{#lambda.titlecase}}{{namespace}}{{/lambda.titlecase}}Client private cons
{{#throwsExceptions}}{{/throwsExceptions}}
* @return a [Response] object with a body of type {{{returnType}}}{{^returnType}}Nothing{{/returnType}}
*/
fun execute(operation: {{operationIdCamelCase}}Operation) : {{#returnType}}Response<{{{returnType}}}>{{/returnType}}{{^returnType}}Response<Nothing>{{/returnType}} {
return execute<
{{#bodyParam}}{{dataType}}{{/bodyParam}}{{^hasBodyParam}}Nothing{{/hasBodyParam}},
{{{returnType}}}{{^returnType}}Nothing{{/returnType}}
>(operation)
fun execute(operation: {{operationIdCamelCase}}Operation) : {{#returnType}}Response<{{{returnType}}}>{{/returnType}}{{^returnType}}EmptyResponse{{/returnType}} {
{{#returnType}}
return execute<{{#bodyParam}}{{dataType}}{{/bodyParam}}{{^hasBodyParam}}Nothing{{/hasBodyParam}}, {{{returnType}}}>(operation)
{{/returnType}}
{{^returnType}}
return executeWithEmptyResponse<{{#bodyParam}}{{dataType}}{{/bodyParam}}{{^hasBodyParam}}Nothing{{/hasBodyParam}}>(operation)
{{/returnType}}
}

private suspend {{^isKotlin}}inline {{/isKotlin}}fun {{^isKotlin}}k{{/isKotlin}}{{operationId}}WithResponse({{>client/apiParamsDecleration}}): {{#returnType}}Response<{{{returnType}}}>{{/returnType}}{{^returnType}}Response<Nothing>{{/returnType}} {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,7 @@ internal object ErrorObjectMapper {
}

{{#apiInfo}}
{{#defineApiExceptions}}{{/defineApiExceptions}}
{{#defineApiExceptions}}
class ExpediaGroupApi{{dataType}}Exception(code: Int, override val errorObject: {{dataType}}, transactionId: String?) : ExpediaGroupApiException(code, errorObject, transactionId)
{{/defineApiExceptions}}
{{/apiInfo}}

0 comments on commit 2d3b6d1

Please sign in to comment.