Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Register properly OpenApiConfigMapping at runtime #30055

Merged
merged 1 commit into from
Jan 13, 2023

Conversation

essobedo
Copy link
Contributor

@essobedo essobedo commented Dec 23, 2022

Motivation

While working on #29886, we realized that OpenApiConfigMapping was located in the deployment module instead of runtime which prevented the mapping to work.

Modifications:

  • Move OpenApiConfigMapping from deployment to runtime
  • Register OpenApiConfigMapping using ConfigBuilder instead of relying on the ServiceLoader for more flexibility

Result

The mapping defined in OpenApiConfigMapping is properly taken into account.

cc @zakkak @radcortez

Copy link
Member

@radcortez radcortez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My recommendation is to use StaticInitConfigBuilderBuildItem RunTimeConfigBuilderBuildItem, which allow you to register a custom io.quarkus.runtime.configuration.ConfigBuilder, and gives you access to the current SmallRyeConfigBuilder instance.

This is way more flexible since then you can register everything that you want and you don't need separate build steps for each type of registration. I intend to deprecate and remove the specific build steps and keep only these ones.

@essobedo
Copy link
Contributor Author

Hi @radcortez, thank you for your feedback.

Unless I miss something obvious I don't see any services or dynamic data that are currently registered using a ConfigBuilder. AFAIU, since you can only provide the FQN of the ConfigBuilder to call so If I want to register dynamically some services, either the code of the ConfigBuilder is static so the dynamic part (service discovery) would be done at runtime which is what we want to avoid or the code of the ConfigBuilder is generated dynamically, is it what you expect? or maybe I totally missed the idea?

Or maybe within the context of this PR, I only move OpenApiConfigMapping to runtime like I did here to make the mapping work.

WDYT?

@radcortez
Copy link
Member

The ConfigBuilder instance is executed with each build step (either static or runtime). So for instance if you want to register an interceptor only for runtime you could do:

@BuildStep
void runtimeOnly(BuildProducer<RunTimeConfigBuilderBuildItem> runTimeConfigBuilder) {
    runTimeConfigBuilder.produce(new RunTimeConfigBuilderBuildItem(RuntimeOnlyBuilder.class.getName()));
}

and

public class RuntimeOnlyBuilder implements ConfigBuilder {
    @Override
    public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) {
        return return builder.withInterceptors(new OpenApiConfigMapping());
    }
}

@radcortez
Copy link
Member

This would be done directly in the openapi extension.

If you have conditions to apply for the registration, these could be checked at the build step level. If you need to check something on runtime, then you can always register an interceptor factory instead and check it with the config context.

@essobedo
Copy link
Contributor Author

The ConfigBuilder instance is executed with each build step (either static or runtime). So for instance if you want to register an interceptor only for runtime you could do:

@BuildStep
void runtimeOnly(BuildProducer<RunTimeConfigBuilderBuildItem> runTimeConfigBuilder) {
    runTimeConfigBuilder.produce(new RunTimeConfigBuilderBuildItem(RuntimeOnlyBuilder.class.getName()));
}

and

public class RuntimeOnlyBuilder implements ConfigBuilder {
    @Override
    public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) {
        return return builder.withInterceptors(new OpenApiConfigMapping());
    }
}

That sounds a little bit cumbersome to create a new class for each service to register but ok, it is clear now, thx

@essobedo essobedo force-pushed the fix-open-api-config-mapping branch 2 times, most recently from a1dbbbd to 8368537 Compare December 23, 2022 14:30
@essobedo essobedo changed the title Allow to register ConfigSourceInterceptor at runtime or buildtime Register properly OpenApiConfigMapping at runtime Dec 23, 2022
@essobedo essobedo requested a review from radcortez December 23, 2022 14:33
@essobedo
Copy link
Contributor Author

In practice, it is a nice approach, thx for sharing 🙏

@essobedo essobedo force-pushed the fix-open-api-config-mapping branch from 8368537 to f20bddc Compare December 23, 2022 15:41
@essobedo
Copy link
Contributor Author

"First-time contributors need a maintainer to approve running workflows.", I should not be a "first-time contributor", that's weird?!

@radcortez
Copy link
Member

That sounds a little bit cumbersome to create a new class for each service to register but ok, it is clear now, thx

It is by extension. Remember you can write like:

public class RuntimeOnlyBuilder implements ConfigBuilder {
    @Override
    public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) {
        return return builder
                                   .withSources(...)
                                   .withMapping(...)
                                   .withInterceptors(...);
    }
}

So you can customize everything related to the configuration for the extension.

@radcortez
Copy link
Member

"First-time contributors need a maintainer to approve running workflows.", I should not be a "first-time contributor", that's weird?!

No idea... but it does show you as an author and contributor. Maybe some issue with GH? It is building now.

@zakkak zakkak added the triage/waiting-for-ci Ready to merge when CI successfully finishes label Dec 23, 2022
@essobedo
Copy link
Contributor Author

We have build failures with this approach because all over the place in the code base we get the config thanks to ConfigProvider.getConfig() before building the config with ConfigBuilders (runtime and static init), so it is initialized with ConfigUtils.emptyConfigBuilder().addDefaultSources().addDiscoveredSources().build() by QuarkusConfigFactory. With the previous code since OpenApiConfigMapping was defined as a service, it could be found by the ServiceLoader.

Any ideas about how to workaround/fix this new problem?

I'm wondering if my initial fix is not the best compromise?

@quarkus-bot

This comment has been minimized.

@geoand
Copy link
Contributor

geoand commented Jan 3, 2023

It seems like CI is not happy with the fix :)

@geoand geoand removed the triage/waiting-for-ci Ready to merge when CI successfully finishes label Jan 3, 2023
@radcortez
Copy link
Member

We have build failures with this approach because all over the place in the code base we get the config thanks to ConfigProvider.getConfig() before building the config with ConfigBuilders (runtime and static init), so it is initialized with ConfigUtils.emptyConfigBuilder().addDefaultSources().addDiscoveredSources().build() by QuarkusConfigFactory. With the previous code since OpenApiConfigMapping was defined as a service, it could be found by the ServiceLoader.

Any ideas about how to workaround/fix this new problem?

I'm wondering if my initial fix is not the best compromise?

I had a look, and the issue is that the interceptor is relocating properties that are build only and runtime only, meaning that if you have the interceptor in deployment (as of now), it will only work for the build time properties. If you move it to runtime, it will work for runtime properties but will stop working for the build time properties.

The relocate interceptor will need to be split into one that handles build time only and another that handles runtime only.

@essobedo
Copy link
Contributor Author

essobedo commented Jan 4, 2023

TBH, I'm a bit lost with your comment, indeed according to my investigations, the test ConfigMappingTest fails because the open API annotation model is built from a Config instance (retrieved from ConfigProvider.getConfig()) that is itself created during the Augmentation phase by https://github.com/quarkusio/quarkus/blob/main/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java#L184 (called by QuarkusAugmentor) which happens before the static and the runtime initializations so it cannot be aware of the relocate interceptors defined with ConfigBuilder, so far, the only way to define an interceptor during this phase is by using the SPI approach like before.

Could you please clarify a bit more what you have in mind? Any example to share?

@radcortez
Copy link
Member

the only way to define an interceptor during this phase is by using the SPI approach like before.

Correct, because some properties are build time only, so they are needed during Quarkus augmentation. I never added a way to register Config builds for the build phase because we never needed them before, so the only way to do it is by using the ServiceLoader.

I recommend splitting the relocation rules, adding an interceptor relocator (deployment module) with the build time properties rules, and registering it with the ServiceLoader. Add another interceptor (runtime module) for the runtime properties rules and use the ConfigBuilder registration approach.

@essobedo essobedo force-pushed the fix-open-api-config-mapping branch from f20bddc to 2d4772c Compare January 5, 2023 16:00
@quarkus-bot
Copy link

quarkus-bot bot commented Jan 5, 2023

Failing Jobs - Building 2d4772c

Status Name Step Failures Logs Raw logs
✔️ JVM Tests - JDK 11
✔️ JVM Tests - JDK 17
JVM Tests - JDK 17 MacOS M1 Set up runner ⚠️ Check → Logs Raw logs
✔️ JVM Tests - JDK 18

@essobedo
Copy link
Contributor Author

essobedo commented Jan 5, 2023

FYI, the build fails because of "JDK 17 MacOS M1"

@geoand
Copy link
Contributor

geoand commented Jan 5, 2023

Don't worry about that check, it's very flaky

@essobedo essobedo requested a review from radcortez January 6, 2023 18:17
@essobedo
Copy link
Contributor Author

essobedo commented Jan 9, 2023

@radcortez Does my last commit match with what you had in mind?

@radcortez
Copy link
Member

In this case, I believe the configuration relocate between quarkus.smallrye-openapi.info-title and mp.openapi.extensions.smallrye.info.title will not work (because it is a build time only property - other info properties too). Can you add a test for it so we can be sure? Thanks!

@essobedo
Copy link
Contributor Author

essobedo commented Jan 12, 2023

In this case, I believe the configuration relocate between quarkus.smallrye-openapi.info-title and mp.openapi.extensions.smallrye.info.title will not work (because it is a build time only property - other info properties too). Can you add a test for it so we can be sure? Thanks!

It is already tested by https://github.com/quarkusio/quarkus/blob/main/extensions/smallrye-openapi/deployment/src/test/java/io/quarkus/smallrye/openapi/test/jaxrs/ConfigMappingTest.java#L21 and https://github.com/quarkusio/quarkus/blob/main/extensions/smallrye-openapi/deployment/src/test/java/io/quarkus/smallrye/openapi/test/jaxrs/OpenApiWithConfigTestCase.java#L21, and both tests pass so it seems to work properly, doesn't it?

@radcortez
Copy link
Member

Ah, yes, and the interceptor is also applied to deployment. Thank you for your patience.

@radcortez radcortez merged commit 5563e39 into quarkusio:main Jan 13, 2023
@quarkus-bot quarkus-bot bot added this to the 2.17 - main milestone Jan 13, 2023
@essobedo essobedo deleted the fix-open-api-config-mapping branch January 13, 2023 13:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants