Skip to content

Commit

Permalink
Add new x-async-support extension for generating async controllers (#…
Browse files Browse the repository at this point in the history
…305)

* Add new x-async-support extension for generating async controllers

* Allow only x-async-support:true

* Reuse same test file, refactor code

---------

Co-authored-by: Jeet Banerjee <jeet.banerjee@zalando.ie>
  • Loading branch information
jeet23 and Jeet Banerjee authored Jul 31, 2024
1 parent b39eae7 commit 46487ff
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 7 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ This library was built to take advantage of the complex modeling features availa
- GraalVM Native Reflection Registration
- Json Merge Patch (via `JsonNullable`) (add `x-json-merge-patch: true` to schemas)
- Override Jackson Include NonNull (via `JsonInclude`) (add `x-jackson-include-non-null: true` to schemas)
- Generate CompletionStage wrapped controllers (add `x-async-support: true` to path)

More than just bootstrapping, this library can be permanently integrated into a gradle or maven build and will ensure contract and code always match, even as APIs evolve in complexity.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class SpringControllerInterfaceGenerator(
private val options: Set<ControllerCodeGenOptionType> = emptySet(),
) : ControllerInterfaceGenerator, AnnotationBasedControllerInterfaceGenerator(packages, api, validationAnnotations) {

private val EXTENSION_ASYNC_SUPPORT = "x-async-support"
private val addAuthenticationParameter: Boolean
get() = options.any { it == ControllerCodeGenOptionType.AUTHENTICATION }

Expand Down Expand Up @@ -78,7 +79,10 @@ class SpringControllerInterfaceGenerator(
.addSpringFunAnnotation(op, verb, path.pathString)
.addSuspendModifier()

val funcSpec = if (options.contains(ControllerCodeGenOptionType.COMPLETION_STAGE)) {
val explicitAsyncSupport = op.extensions[EXTENSION_ASYNC_SUPPORT] as? Boolean
val asyncSupport = explicitAsyncSupport ?: options.contains(ControllerCodeGenOptionType.COMPLETION_STAGE)

val funcSpec = if (asyncSupport) {
baseFunSpec.returns(
SpringImports.COMPLETION_STAGE.parameterizedBy(
SpringImports.RESPONSE_ENTITY.parameterizedBy(returnType)
Expand All @@ -88,7 +92,7 @@ class SpringControllerInterfaceGenerator(
baseFunSpec.returns(SpringImports.RESPONSE_ENTITY.parameterizedBy(returnType))
}

parameters
parameters
.map {
when (it) {
is BodyParameter ->
Expand All @@ -97,6 +101,7 @@ class SpringControllerInterfaceGenerator(
.addAnnotation(SpringAnnotations.requestBodyBuilder().build())
.maybeAddAnnotation(validationAnnotations.parameterValid())
.build()

is RequestParameter ->
it
.toParameterSpecBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ class SpringControllerGeneratorTest {
@Test
fun `ensure that subresource specific controllers are created`() {
val api = SourceApi(readTextResource("/examples/githubApi/api.yaml"))
val controllers = SpringControllerInterfaceGenerator(Packages(basePackage), api, JavaxValidationAnnotations).generate()
val controllers =
SpringControllerInterfaceGenerator(Packages(basePackage), api, JavaxValidationAnnotations).generate()

assertThat(controllers.files).size().isEqualTo(6)
assertThat(controllers.files.map { it.name }).containsAll(
Expand Down Expand Up @@ -225,7 +226,9 @@ class SpringControllerGeneratorTest {
@Test
fun `controller parameters should have spring DateTimeFormat annotations`() {
val api = SourceApi(readTextResource("/examples/springFormatDateAndDateTime/api.yaml"))
val controllers = SpringControllerInterfaceGenerator(Packages(basePackage), api, JavaxValidationAnnotations).generate().toSingleFile()
val controllers =
SpringControllerInterfaceGenerator(Packages(basePackage), api, JavaxValidationAnnotations).generate()
.toSingleFile()
val expectedControllers = readTextResource("/examples/springFormatDateAndDateTime/controllers/Controllers.kt")

assertThat(controllers.trim()).isEqualTo(expectedControllers.trim())
Expand All @@ -234,7 +237,9 @@ class SpringControllerGeneratorTest {
@Test
fun `ensure generates ByteArray body parameter and response for string with format binary`() {
val api = SourceApi(readTextResource("/examples/binary/api.yaml"))
val controllers = SpringControllerInterfaceGenerator(Packages(basePackage), api, JavaxValidationAnnotations).generate().toSingleFile()
val controllers =
SpringControllerInterfaceGenerator(Packages(basePackage), api, JavaxValidationAnnotations).generate()
.toSingleFile()
val expectedControllers = readTextResource("/examples/binary/controllers/spring/Controllers.kt")

assertThat(controllers.trim()).isEqualTo(expectedControllers.trim())
Expand All @@ -244,7 +249,25 @@ class SpringControllerGeneratorTest {
fun `controller functions are wrapped by CompletionStage`() {
val basePackage = "examples.completionStage"
val api = SourceApi(readTextResource("/examples/githubApi/api.yaml"))
val expectedControllers = readTextResource("/examples/githubApi/controllers/spring-completion-stage/Controllers.kt")
val expectedControllers =
readTextResource("/examples/githubApi/controllers/spring-completion-stage/Controllers.kt")

val controllers = SpringControllerInterfaceGenerator(
Packages(basePackage),
api,
JavaxValidationAnnotations,
setOf(ControllerCodeGenOptionType.COMPLETION_STAGE),
).generate().toSingleFile()

assertThat(controllers).isEqualTo(expectedControllers)
}

@Test
fun `controller functions with x-async-support=false extension are NOT wrapped by CompletionStage`() {
val basePackage = "examples.completionStage"
val api = SourceApi(readTextResource("/examples/githubApi/api.yaml"))
val expectedControllers =
readTextResource("/examples/githubApi/controllers/spring-completion-stage/Controllers.kt")

val controllers = SpringControllerInterfaceGenerator(
Packages(basePackage),
Expand Down
1 change: 1 addition & 0 deletions src/test/resources/examples/githubApi/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ paths:
post:
tags:
- events
x-async-support: false
summary: Generate change events for a list of entities
requestBody:
$ref: '#/components/requestBodies/BulkEventGenerationBody'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public interface InternalEventsController {
method = [RequestMethod.POST],
consumes = ["application/json"],
)
public fun post(@RequestBody @Valid bulkEntityDetails: BulkEntityDetails): CompletionStage<ResponseEntity<EventResults>>
public fun post(@RequestBody @Valid bulkEntityDetails: BulkEntityDetails): ResponseEntity<EventResults>
}

@Controller
Expand Down

0 comments on commit 46487ff

Please sign in to comment.