micronaut-projects / micronaut-openapi

Generates OpenAPI / Swagger Documentation for Micronaut projects
https://micronaut-projects.github.io/micronaut-openapi/latest/guide/index.html
Apache License 2.0
80 stars 95 forks source link

Invalid OpenAPI file generated from Kotlin #1776

Closed mmwinther closed 1 month ago

mmwinther commented 1 month ago

Expected Behavior

The generated OpenAPI file is always valid

Actual Behaviour

Summary

When changes to the OpenAPI annotations are done on any operation, the OpenAPI YAML file is generated without an info section. Also the name of the file reverts to swagger.yml, not the name and version of the service as expected.

The expected behaviour can be achieved by always running clean prior to kspKotlin or run.

Screenshot

Screenshot 2024-09-20 at 08 28 44

Build log

08:21:35: Executing 'kspKotlin'...

> Task :checkKotlinGradlePluginConfigurationErrors SKIPPED

> Task :kspKotlin
w: [ksp] Can't find class false in classpath. Skip it.

BUILD SUCCESSFUL in 8s
1 actionable task: 1 executed
08:21:44: Execution finished 'kspKotlin'.

Build config

I have set the following in build.gradle.kts, though the problem existed before this also. Not sure if this is the correct syntax for ksp since the docs provide an example for kapt only

ksp {
    // Necessary for incremental processing ref: https://micronaut-projects.github.io/micronaut-openapi/latest/guide/index.html#kotlin
    arg("micronaut.openapi.project.dir", projectDir.toString())
}

Steps To Reproduce

  1. In the given environment
  2. Make a change to OpenAPI annotations on an operation
  3. Run kspKotlin
  4. See the invalid OpenAPI file

Environment Information

Example Application

No response

Version

4.6.1

altro3 commented 1 month ago

Did you add annotation @OpenApiDefinition for any class? micronaut-openapi can't set automattically version and name. You need to set it in this annotation, but you can set placeholders like this:

@OpenAPIDefinition(
    info = @Info(
        title = "${app.title}",
        version = "${app.version}"
    )
)
altro3 commented 1 month ago

We can change this logic and generate info block automatically... But what values we can set by default?

mmwinther commented 1 month ago

@altro3 yes I have @OpenAPIDefinition set on object Api in my Application.kt.

As I mentioned the info block is correctly generated when I run clean first, so my guess is there's a bug in the incremental processing.

altro3 commented 1 month ago

Yes, I remember about this problem. Well. I can only suggest you to disable incremental in ksp settings:

ksp.incremental = false

The problem is that incremental processing means processing only changed classes, so the openapi specification will be built only based on the processed classes.

Here is the problem that I couldn't figure out how to solve with incremental processing:

  1. Imagine that you have 2 controller classes with 2 endpoints in each.

  2. On the first run, you will have 4 endpoints in the specification.

  3. Then you decide to delete one endpoint in the second controller and insert 2 new ones.

  4. You run openapi again and get 3 endpoints only from the second controller.

So, now you have a specification file generated the first time with 4 endpoints, and a specification generated after the second run with 3 endpoints from the second controller.

Yes, you can try to merge these 2 specs and you will partially succeed - in the final spec you will see 6 endpoints instead of 5.

Why? Because how can you understand that the endpoint was deleted without having full information about all current endpoints?

If you know how to solve this problem - we are only glad. Suggest your solution in this situation. I could not come up with any adequate solution for this case.

Therefore, I advise you to simply disable incremental processing and that's it.

We need to add information about this in the documentation.

mmwinther commented 1 month ago

Hi @altro3 thanks for the reply. Unfortunately I don't know how this can be fixed. It would be very helpful to specify in the documentation that incremental processing in ksp is not supported by Micronaut OpenAPI and should be disabled.

graemerocher commented 1 month ago

if incremental processing is correctly implemented and this was an aggregating processor as it should be it would always visit the originating elements which should always regenerate the full OpenAPI definition ensuring correctness. As it stands incremental processing is not currently implemented correctly in this module.

altro3 commented 1 month ago

@graemerocher I don't quite understand what the implementation error could be here... I described a case where you can't correctly regenerate the openapi specification without fully processing all the files.

Or should the aggregating annotation processor process all the files at each startup, simply because it is aggregating and if incremental processing is enabled?

I'm starting to remember something like that, we already discussed this and I had some problem, because of which everything remained as is

altro3 commented 1 month ago

It's strange, the default value of the annotation processor type is used everywhere - aggregating. I'm inclined to think that the problem is in the implementation of the core for KSP, because this problem manifests itself exclusively when using KSP

{B0F42CC6-2DA3-46EC-B3B5-6BF3387472E7}
altro3 commented 1 month ago

@mmwinther could you test it with kapt?

graemerocher commented 1 month ago

yes basically in aggregating mode it should revisit all the files involved in generating the open api spec. If it doesn't it is likely some kind of bug either in the module or as you say in the KSP implementation in core.

altro3 commented 1 month ago

Created issue in core module: https://github.com/micronaut-projects/micronaut-core/issues/11215