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

Add new property "config-key" and use configKey instead openApiSpecId on templating #457

Merged
merged 11 commits into from
Sep 7, 2023
Merged
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,52 @@ It's also possible to only use a type mapping with a fully qualified name, for i

See the module [type-mapping](integration-tests/type-mapping) for an example of how to use this feature.

## Config key

By default the `@RegisterRestClient` `configKey` property is the sanitized name of the file containing the OpenAPI spec. For example, if the file name is `petstore.json`, the `configKey` will be `petstore_json`:
mcruzdev marked this conversation as resolved.
Show resolved Hide resolved

```java
/* omitted */
@RegisterRestClient(configKey="petstore_json")
public interface DefaultApi { /* omitted */ }
}
```
hbelmiro marked this conversation as resolved.
Show resolved Hide resolved

If you want to use a different configKey than the default one, you can set the `quarkus.openapi-generator.codegen.spec.petstore_json.[config-key]` property.

Using the `config-key` the extension allow you to define all allowed properties with `quarkus.openapi-generator.codegen.spec.[my_custom_config_key].*` prefix. For example:

```properties
quarkus.openapi-generator.codegen.spec.petstore_json.config-key=petstore
quarkus.openapi-generator.codegen.spec.petstore.additional-api-type-annotations=@org.test.Foo
```

With it you will have the following result:
mcruzdev marked this conversation as resolved.
Show resolved Hide resolved

```java
/* omitted */
@RegisterRestClient(configKey="petstore")
@org.test.Foo
public interface DefaultApi { /* omitted */ }
```

> **️⚠️ NOTE:** If you configure the property config-key, it will override the sanitized file name (will not consider the order of the configurations). For example, having the following configuration:

```properties
quarkus.openapi-generator.codegen.spec.petstore_json.config-key=custom_config_key
quarkus.openapi-generator.codegen.spec.custom_config_key.additional-api-type-annotations=@org.test.Foo
quarkus.openapi-generator.codegen.spec.petstore_json.additional-api-type-annotations=@org.test.Bar
```

The generated code will be:

```java
/* omitted */
@RegisterRestClient(configKey="custom_config_key")
@org.test.Foo
public interface DefaultApi { /* omitted */ }
```

## Template Customization

You have the option to swap out the [templates used by this extension](deployment/src/main/resources/templates/libraries/microprofile) with your customized versions. To achieve this, place your custom templates under the `resources/templates` directory. It's crucial that the filename of each custom template matches that of the original template.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ public enum ConfigName {
IMPORT_MAPPINGS("import-mappings"),
NORMALIZER("open-api-normalizer"),
RETURN_RESPONSE("return-response"),
ENABLE_SECURITY_GENERATION("enable-security-generation");
ENABLE_SECURITY_GENERATION("enable-security-generation"),
CONFIG_KEY("config-key");

private final String name;

Expand Down Expand Up @@ -86,6 +87,17 @@ public static String getSpecConfigName(ConfigName configName, final Path openApi
return String.format("%s.%s", getBuildTimeSpecPropertyPrefix(openApiFilePath), configName.name);
}

/**
* Return spec config name by config-key (<b>openapi-generator.codegen.spec.%s.config-key</b>) property.
* For example, given a configuration <code>quarkus.openapi.generator.codegen.spec.spec_yaml.config-key=petstore</code>, the
* returned value is
* <code>openapi.generator.codegen.spec.petstore.mutiny</code>.
*/
public static String getSpecConfigNameByConfigKey(final String configKey, final ConfigName configName) {
String buildTimeSpecPropertyPrefix = String.format(BUILD_TIME_SPEC_PREFIX_FORMAT, configKey);
return String.format("%s.%s", buildTimeSpecPropertyPrefix, configName.name);
}

/**
* Gets the config prefix for a given OpenAPI file in the path.
* For example, given a path like /home/luke/projects/petstore.json, the returned value is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public abstract class OpenApiGeneratorCodeGenBase implements CodeGenProvider {
static final String JSON = ".json";

private static final String DEFAULT_PACKAGE = "org.openapi.quarkus";
private static final String CONFIG_KEY_PROPERTY = "config-key";

/**
* The input base directory from
Expand Down Expand Up @@ -177,6 +178,10 @@ protected void generate(final Config config, final Path openApiFilePath, final P
getValues(config, openApiFilePath, CodegenConfig.ConfigName.ADDITIONAL_API_TYPE_ANNOTATIONS, String.class)
.ifPresent(generator::withAdditionalApiTypeAnnotationsConfig);

getConfigKeyValue(config, openApiFilePath)
.ifPresentOrElse(generator::withConfigKey,
() -> generator.withConfigKey(getSanitizedFileName(openApiFilePath)));

generator.withReturnResponse(
getValues(config, openApiFilePath, CodegenConfig.ConfigName.RETURN_RESPONSE, Boolean.class).orElse(false));

Expand Down Expand Up @@ -230,16 +235,70 @@ private Optional<String> getInputBaseDirRelativeToModule(final Path sourceDir, f

private <T> Optional<T> getValues(final Config config, final Path openApiFilePath, CodegenConfig.ConfigName configName,
Class<T> propertyType) {
return config
.getOptionalValue(CodegenConfig.getSpecConfigName(configName, openApiFilePath), propertyType)
.or(() -> config.getOptionalValue(CodegenConfig.getGlobalConfigName(configName), propertyType));

Optional<String> possibleConfigKey = getConfigKeyValue(config, openApiFilePath);
boolean userConfiguredConfigKeyProperty = possibleConfigKey.isPresent() && !possibleConfigKey.get().isBlank();

if (userConfiguredConfigKeyProperty) {
return getValuesByConfigKey(config, CodegenConfig.getSpecConfigNameByConfigKey(possibleConfigKey.get(), configName),
propertyType, configName);
}

return getValuesBySpecConfigName(config, openApiFilePath, configName, propertyType);
}

private <K, V> Optional<Map<K, V>> getValues(final SmallRyeConfig config, final Path openApiFilePath,
CodegenConfig.ConfigName configName,
Class<K> kClass, Class<V> vClass) {

Optional<String> possibleConfigKey = getConfigKeyValue(config, openApiFilePath);
boolean userConfiguredConfigKey = possibleConfigKey.isPresent() && !possibleConfigKey.get().isBlank();
ricardozanini marked this conversation as resolved.
Show resolved Hide resolved

if (userConfiguredConfigKey) {
return getValuesByConfigKey(config, configName, kClass, vClass, possibleConfigKey.get());
}

return getValuesBySpecConfigName(config, openApiFilePath, configName, kClass, vClass);
}

private static <T> Optional<T> getValuesBySpecConfigName(Config config, Path openApiFilePath,
CodegenConfig.ConfigName configName,
Class<T> propertyType) {
return config
.getOptionalValue(CodegenConfig.getSpecConfigName(configName, openApiFilePath), propertyType)
.or(() -> config.getOptionalValue(CodegenConfig.getGlobalConfigName(configName), propertyType));
}

private static <K, V> Optional<Map<K, V>> getValuesBySpecConfigName(SmallRyeConfig config, Path openApiFilePath,
CodegenConfig.ConfigName configName, Class<K> kClass, Class<V> vClass) {
return config
.getOptionalValues(CodegenConfig.getSpecConfigName(configName, openApiFilePath), kClass, vClass)
.or(() -> config.getOptionalValues(CodegenConfig.getGlobalConfigName(configName), kClass, vClass));
}

private static <T> Optional<T> getValuesByConfigKey(Config config, String configName, Class<T> propertyType,
CodegenConfig.ConfigName configName1) {
return config
.getOptionalValue(configName, propertyType)
.or(() -> config.getOptionalValue(CodegenConfig.getGlobalConfigName(configName1), propertyType));
ricardozanini marked this conversation as resolved.
Show resolved Hide resolved
}

private static <K, V> Optional<Map<K, V>> getValuesByConfigKey(SmallRyeConfig config, CodegenConfig.ConfigName configName,
Class<K> kClass, Class<V> vClass, String configKey) {
return config
.getOptionalValues(CodegenConfig.getSpecConfigNameByConfigKey(configKey, configName), kClass,
vClass)
.or(() -> config.getOptionalValues(CodegenConfig.getGlobalConfigName(configName), kClass, vClass));
}

private static Optional<String> getConfigKeyValue(Config config, Path openApiFilePath) {
String configKey = String.format("quarkus.openapi-generator.codegen.spec.%s.%s", getSanitizedFileName(openApiFilePath),
CONFIG_KEY_PROPERTY);
Optional<String> value = config.getOptionalValue(configKey, String.class);
if (value.isPresent()) {
return value.get().isBlank() ? Optional.empty() : value;
} else {
return Optional.empty();
}
mcruzdev marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import org.openapitools.codegen.DefaultGenerator;
import org.openapitools.codegen.config.GlobalSettings;

import io.smallrye.config.common.utils.StringUtil;

/**
* Wrapper for the OpenAPIGen tool.
* This is the same as calling the Maven plugin or the CLI.
Expand Down Expand Up @@ -201,4 +203,10 @@ private void consolidatePackageNames() {
this.configurator.setModelPackage(modelPackage);
this.configurator.setInvokerPackage(apiPackage);
}

public void withConfigKey(final String config) {
if (config != null && !config.isBlank()) {
this.configurator.addAdditionalProperty("configKey", StringUtil.replaceNonAlphanumericByUnderscores(config));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import io.quarkiverse.openapi.generator.annotations.GeneratedParam;
*/
{/if}
@Path("{#if useAnnotatedBasePath}{contextPath}{/if}{commonPath}")
@RegisterRestClient({#if defaultServerUrl}baseUri="{defaultServerUrl}",{/if} configKey="{quarkus-generator.openApiSpecId}")
@RegisterRestClient({#if defaultServerUrl}baseUri="{defaultServerUrl}",{/if} configKey="{configKey}")
@GeneratedClass(value="{openapi:parseUri(inputSpec)}", tag = "{baseName}")
{#if enable-security-generation && hasAuthMethods}
@RegisterProvider(CompositeAuthenticationProvider.class)
Expand Down
89 changes: 89 additions & 0 deletions integration-tests/config-key/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>quarkus-openapi-generator-integration-tests</artifactId>
<groupId>io.quarkiverse.openapi.generator</groupId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>quarkus-openapi-generator-it-config-key</artifactId>
<name>Quarkus - Openapi Generator - Integration Tests - Config Key</name>

<dependencies>
<dependency>
<groupId>io.quarkiverse.openapi.generator</groupId>
<artifactId>quarkus-openapi-generator</artifactId>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native-image</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>${native.surefire.skip}</skipTests>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<quarkus.package.type>native</quarkus.package.type>
</properties>
</profile>
</profiles>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.quarkiverse.openapi.generator.configkey;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface AnotherCustomAnnotation {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.quarkiverse.openapi.generator.configkey;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
}
Loading