From a1f0aa6c81ca99a18856030388130b36ab0fa1bd Mon Sep 17 00:00:00 2001 From: Ashwini Desai Date: Thu, 9 Jul 2020 11:33:39 +0530 Subject: [PATCH] Revert "Clean up ApiBuilder. Using Operation extensions" This reverts commit c80c276fd87e126cccf2688d43fb17605a878781. --- .../main/kotlin/apifi/codegen/ApiBuilder.kt | 55 ++++++++++++------- .../apifi/codegen/OperationExtension.kt | 27 --------- .../kotlin/apifi/codegen/ApiBuilderTest.kt | 9 +++ 3 files changed, 44 insertions(+), 47 deletions(-) delete mode 100644 codegen/src/main/kotlin/apifi/codegen/OperationExtension.kt diff --git a/codegen/src/main/kotlin/apifi/codegen/ApiBuilder.kt b/codegen/src/main/kotlin/apifi/codegen/ApiBuilder.kt index 473c4b8..4e68a0a 100644 --- a/codegen/src/main/kotlin/apifi/codegen/ApiBuilder.kt +++ b/codegen/src/main/kotlin/apifi/codegen/ApiBuilder.kt @@ -1,11 +1,13 @@ package apifi.codegen import apifi.codegen.exceptions.Non200ResponseHandler +import apifi.helpers.toKotlinPoetType import apifi.helpers.toTitleCase import apifi.models.Operation +import apifi.models.ParamType import apifi.models.Path -import apifi.models.Response import com.squareup.kotlinpoet.* +import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy object ApiBuilder { @@ -39,13 +41,34 @@ object ApiBuilder { private fun generateOperationFunctions(paths: List, basePackageName: String, modelMapping: List>): List { return paths.flatMap { path -> path.operations?.map { operation -> - val serviceCallStatement = controllerCallStatement(operation, modelMapping) + val queryParams = operation.params?.filter { it.type == ParamType.Query }?.map(QueryParamBuilder::build) + ?: emptyList() + val pathParams = operation.params?.filter { it.type == ParamType.Path }?.map(PathVariableBuilder::build) + ?: emptyList() + val headerParams = operation.params?.filter { it.type == ParamType.Header }?.map(HeaderBuilder::build) + ?: emptyList() + val requestBodyParams = operation.request?.let { listOf(RequestBodyBuilder.build(it.type, modelMapping)) } + ?: emptyList() + + val serviceCallStatement = serviceCallStatement(operation, queryParams, pathParams, requestBodyParams) + + val responseType = operation.response?.firstOrNull { it.defaultOrStatus == "200" || it.defaultOrStatus == "201" }?.let { ClassName("io.micronaut.http", "HttpResponse").parameterizedBy(it.type.toKotlinPoetType(modelMapping)) } + + val non2xxStatusResponseFromOperation = operation.response?.filter { it.defaultOrStatus != "default" && it.defaultOrStatus != "200" && it.defaultOrStatus != "201" }?.map { it.defaultOrStatus.toInt() } + + val exceptionClassesForNon2xxResponses = non2xxStatusResponseFromOperation?.let { Non200ResponseHandler.getExceptionClassFor(it) } + + val exceptionAnnotations = exceptionClassesForNon2xxResponses?.map { exceptionClass -> + AnnotationSpec.builder(Throws::class) + .addMember("%T::class", ClassName("$basePackageName.exceptions", exceptionClass)) + .build()} + FunSpec.builder(operation.name) - .also { b -> operation.response?.let { b.addAnnotations(operationExceptionAnnotations(it, basePackageName)) } } + .also { b -> exceptionAnnotations?.let { b.addAnnotations(it) } } .addAnnotation(operationTypeAnnotation(operation, path)) .also { b -> operation.request?.consumes?.let { consumes -> b.addAnnotation(operationContentTypeAnnotation(consumes)) } } - .addParameters(operation.queryParams() + operation.pathParams() + operation.headerParams() + operation.requestParams(modelMapping)) - .also { operation.returnType(modelMapping)?.let { rt -> it.returns(rt) } } + .addParameters(queryParams + pathParams + headerParams + requestBodyParams) + .also { responseType?.let { res -> it.returns(res) } } .addStatement("return $serviceCallStatement") .build() } ?: emptyList() @@ -62,22 +85,14 @@ object ApiBuilder { .also { ab -> consumes.forEach { ab.addMember("%S", it) } } .build() - private fun operationExceptionAnnotations(responses: List, basePackageName: String): List { - val non2xxStatusResponseFromOperation = responses.filter { it.defaultOrStatus != "default" && it.defaultOrStatus != "200" && it.defaultOrStatus != "201" }.map { it.defaultOrStatus.toInt() } - val exceptionClassesForNon2xxResponses = non2xxStatusResponseFromOperation.let { Non200ResponseHandler.getExceptionClassFor(it) } - return exceptionClassesForNon2xxResponses.map { exceptionClass -> - AnnotationSpec.builder(Throws::class) - .addMember("%T::class", ClassName("$basePackageName.exceptions", exceptionClass)) - .build() - } - } - - private fun controllerCallStatement(operation: Operation, modelMapping: Map): String { - val queryParamNames = operation.queryParams().map { it.name } - val pathParamNames = operation.pathParams().map { it.name } - val requestBodyParams = operation.requestParams(modelMapping) - val requestParamNames = requestBodyParams.map { it.name } + private fun serviceCallStatement(operation: Operation, queryParams: List, pathParams: List, requestBodyParams: List): String { + val queryParamNames = queryParams.map { it.name } + val pathParamNames = pathParams.map { it.name } + val requestParamNames = operation.request?.let { req -> + if (req.consumes?.contains("multipart/form-data") == true) "java.io.File.createTempFile(body.filename, \"\").also { it.writeBytes(body.bytes) }" else requestBodyParams.joinToString { it.name } + }?.let { listOf(it) } ?: emptyList() return "HttpResponse.ok(controller.${operation.name}(${(queryParamNames + pathParamNames + requestParamNames).joinToString()}))" } + } diff --git a/codegen/src/main/kotlin/apifi/codegen/OperationExtension.kt b/codegen/src/main/kotlin/apifi/codegen/OperationExtension.kt deleted file mode 100644 index c97506c..0000000 --- a/codegen/src/main/kotlin/apifi/codegen/OperationExtension.kt +++ /dev/null @@ -1,27 +0,0 @@ -package apifi.codegen - -import apifi.helpers.toKotlinPoetType -import apifi.models.Operation -import apifi.models.ParamType -import com.squareup.kotlinpoet.ClassName -import com.squareup.kotlinpoet.ParameterSpec -import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy - -fun Operation.pathParams() = params?.filter { it.type == ParamType.Path }?.map(PathVariableBuilder::build) - ?: emptyList() - -fun Operation.queryParams() = params?.filter { it.type == ParamType.Query }?.map(QueryParamBuilder::build) - ?: emptyList() - -fun Operation.headerParams() = params?.filter { it.type == ParamType.Header }?.map(HeaderBuilder::build) - ?: emptyList() - -fun Operation.requestParams(modelMapping: Map) = request?.let { - listOf(ParameterSpec.builder("body", it.type.toKotlinPoetType(modelMapping)) - .addAnnotation(ClassName("io.micronaut.http.annotation", "Body")) - .build()) -} ?: emptyList() - -fun Operation.returnType(modelMapping: Map) = response - ?.firstOrNull { it.defaultOrStatus == "200" || it.defaultOrStatus == "201" } - ?.let { ClassName("io.micronaut.http", "HttpResponse").parameterizedBy(it.type.toKotlinPoetType(modelMapping)) } diff --git a/codegen/src/test/kotlin/apifi/codegen/ApiBuilderTest.kt b/codegen/src/test/kotlin/apifi/codegen/ApiBuilderTest.kt index 09ca3e7..8866a99 100644 --- a/codegen/src/test/kotlin/apifi/codegen/ApiBuilderTest.kt +++ b/codegen/src/test/kotlin/apifi/codegen/ApiBuilderTest.kt @@ -111,6 +111,15 @@ class ApiBuilderTest : DescribeSpec({ ")" } + it("should convert multipart file to java file") { + val operation = Operation(PathItem.HttpMethod.POST, "uploadDocument", emptyList(), emptyList(), Request("io.micronaut.http.multipart.CompleteFileUpload", listOf("multipart/form-data")), null) + + val api = ApiBuilder.build("pets", listOf(Path("/pets", listOf(operation))), "apifi.gen", modelMapping()) + + val apiClass = api.members[0] as TypeSpec + apiClass.funSpecs[0].body.toString().trimIndent() shouldBe "return HttpResponse.ok(controller.uploadDocument(java.io.File.createTempFile(body.filename, \"\").also { it.writeBytes(body.bytes) }))" + } + it("should add @throws annotation for all non 200 responses returned from an operation") { val request = Request("Pet", listOf("application/json", "text/plain")) val responses = listOf(Response("200", "PetResponse"), Response("400", "kotlin.String"), Response("403", "kotlin.String"))