Skip to content

Commit

Permalink
Add new property "config-key" and use configKey instead openApiSpecId…
Browse files Browse the repository at this point in the history
… on templating (#457)
  • Loading branch information
mcruzdev authored Sep 7, 2023
1 parent 7dde7bc commit b156700
Show file tree
Hide file tree
Showing 14 changed files with 858 additions and 5 deletions.
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,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`:

```java
/* omitted */
@RegisterRestClient(configKey="petstore_json")
public interface DefaultApi { /* omitted */ }
}
```

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:

```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 @@ -49,6 +49,7 @@ public enum ConfigName {
NORMALIZER("open-api-normalizer"),
RETURN_RESPONSE("return-response"),
ENABLE_SECURITY_GENERATION("enable-security-generation"),
CONFIG_KEY("config-key"),
GENERATE_PART_FILENAME("generate-part-filename"),
PART_FILENAME_VALUE("part-filename-value"),
USE_FIELD_NAME_IN_PART_FILENAME("use-field-name-in-part-filename");
Expand Down Expand Up @@ -89,6 +90,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 @@ -16,6 +16,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
Expand Down Expand Up @@ -47,6 +48,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 +179,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 @@ -242,16 +248,78 @@ 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));

return getConfigKeyValues(config, openApiFilePath, configName, propertyType)
.or(() -> 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) {

return getConfigKeyValues(config, openApiFilePath, configName, kClass, vClass)
.or(() -> 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 codegenConfigName) {
return config
.getOptionalValue(configName, propertyType)
.or(() -> config.getOptionalValue(CodegenConfig.getGlobalConfigName(codegenConfigName), propertyType));
}

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);
return config.getOptionalValue(configKey, String.class)
.filter(Predicate.not(String::isBlank));
}

private <T> Optional<T> getConfigKeyValues(final Config config, final Path openApiFilePath,
CodegenConfig.ConfigName configName,
Class<T> propertyType) {

Optional<String> possibleConfigKey = getConfigKeyValue(config, openApiFilePath);
if (possibleConfigKey.isPresent()) {
return getValuesByConfigKey(config, CodegenConfig.getSpecConfigNameByConfigKey(possibleConfigKey.get(), configName),
propertyType, configName);
}

return Optional.empty();
}

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

Optional<String> possibleConfigKey = getConfigKeyValue(config, openApiFilePath);
if (possibleConfigKey.isPresent()) {
return getValuesByConfigKey(config, configName, kClass, vClass, possibleConfigKey.get());
}

return Optional.empty();
}
}
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 @@ -216,4 +218,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

0 comments on commit b156700

Please sign in to comment.