Skip to content

Commit

Permalink
enable-security-generation | additional-api-type-annotations (#431)
Browse files Browse the repository at this point in the history
* enable-security-generation | additional-api-type-annotations

* removal of custom-register-providers and client-headers-factory

* documentation | fixes

* Update README.md

Co-authored-by: Helber Belmiro <[email protected]>

* global configs | fixes

* config name validation | default-security-schema | refactor

* fixes

* OpenApiConfigValidator

* config items refactor

* Update deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiConfigValidator.java

Co-authored-by: Helber Belmiro <[email protected]>

* Update deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorCodeGenBase.java

Co-authored-by: Helber Belmiro <[email protected]>

* Update deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/OpenApiConfigValidatorTest.java

Co-authored-by: Helber Belmiro <[email protected]>

* last fixes

* switch default values

* token propagation factory

---------

Co-authored-by: Helber Belmiro <[email protected]>
  • Loading branch information
michalsomora and hbelmiro authored Aug 15, 2023
1 parent 60d6605 commit 30d5501
Show file tree
Hide file tree
Showing 28 changed files with 504 additions and 1,397 deletions.
35 changes: 12 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ If a base package name is not provided, it will be used the default `org.openapi

Configuring `additional-model-type-annotations` will add all annotations to the generated model files (extra details can be found in [OpenApi Generator Doc](https://openapi-generator.tech/docs/generators/java/#config-options)).

The same way you can add any additional annotations to the generated api files with `additional-api-type-annotations`. Given you want to include Foo and Bar annotations, you must define additional-api-type-annotations as:

```properties
quarkus.openapi-generator.codegen.spec.petstore_json.additional-api-type-annotations[email protected];@org.test.Bar
```

> **⚠️** Note that the file name`petstore_json`is used to configure the specific information for each spec. We follow the [Environment Variables Mapping Rules](https://github.com/eclipse/microprofile-config/blob/master/spec/src/main/asciidoc/configsources.asciidoc#environment-variables-mapping-rules) from Microprofile Configuration to sanitize the OpenAPI spec filename. Any non-alphabetic characters are replaced by an underscore `_`.
Run `mvn compile` to generate your classes in `target/generated-sources/open-api-json` path:
Expand Down Expand Up @@ -237,9 +243,9 @@ pattern: `quarkus.openapi-generator.[filename].auth.[security_scheme_name].[auth
If the OpenAPI specification file has `securitySchemes` definitions, but no [Security Requirement Object](https://spec.openapis.org/oas/v3.1.0#security-requirement-object) definitions, the generator can be configured to create these by default. In this case, for all operations without a security requirement the default one will be created. Note that the property value needs to match the name of a security scheme object definition, eg. `api_key` or `basic_auth` in the `securitySchemes` list above.

| Description | Property Key | Example |
| -------------------- | -------------------------------------------------------------- | ---------------------------------------------------- |
| Create security for the referenced security scheme | `quarkus.openapi-generator.codegen.default.security.scheme` | `quarkus.openapi-generator.codegen.default.security.scheme=api_key` |
| Description | Property Key | Example |
| -------------------- |-------------------------------------------------------------|---------------------------------------------------------------------|
| Create security for the referenced security scheme | `quarkus.openapi-generator.codegen.default-security-scheme` | `quarkus.openapi-generator.codegen.default-security-scheme=api_key` |

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

Expand Down Expand Up @@ -331,6 +337,9 @@ RESTEasy Reactive:
<artifactId>quarkus-oidc-client-reactive-filter</artifactId>
</dependency>
```
If authentication support doesn't suit your needs you can decide to disable it with `enable-security-generation=false`. In such case CompositeAuthenticationProvider and AuthenticationPropagationHeadersFactory wont be generated and used with your api.
The option can be set globally with `quarkus.openapi-generator.codegen.enable-security-generation` or per api `quarkus.openapi-generator.codegen.spec.my_spec_yml.enable-security-generation`
Custom authentication provider can be used with `additional-api-type-annotations`

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

Expand Down Expand Up @@ -419,17 +428,7 @@ The token propagation can be used with type "oauth2" or "bearer" security scheme
| `quarkus.openapi-generator.[filename].auth.[security_scheme_name].token-propagation=[true,false]` | `quarkus.openapi-generator.petstore_json.auth.petstore_auth.token-propagation=true`<br/>Enables the token propagation for all the operations that are secured with the `petstore_auth` scheme in the `petstore_json` file.
| `quarkus.openapi-generator.[filename].auth.[security_scheme_name].header-name=[http_header_name]` | `quarkus.openapi-generator.petstore_json.auth.petstore_auth.header-name=MyHeaderName`<br/>Says that the authorization token to propagate will be read from the HTTP header `MyHeaderName` instead of the standard HTTP `Authorization` header.

## Headers propagation
Custom headers propagation can be set via MicroProfile configuration `org.eclipse.microprofile.rest.client.propagateHeaders`.
In order to consider this configuration you must force `@RegisterClientHeaders` to use its default MicroProfile `ClientHeadersFactory` implementation. Therefore there is an option `client-headers-factory` where you can set any implementation of `ClientHeadersFactory`.

| Description | Property Key | Example |
| -------------|----------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Bearer Token | `quarkus.openapi-generator.codegen.spec.[fileName].client-headers-factory` | `quarkus.openapi-generator.codegen.spec.open_weather_yaml.client-headers-factory=org.eclipse.microprofile.rest.client.ext.DefaultClientHeadersFactoryImpl` |

If `client-headers-factory` is set to `none` `@RegisterClientHeaders` will use its default implicit implementation as in the example above.

If no option is set then default generated `AuthenticationPropagationHeadersFactory` class is used.

## Circuit Breaker

Expand Down Expand Up @@ -676,16 +675,6 @@ file. By default, these operations are generated. You can fine tune this behavio

Use the property key `<base_package>.api.MyClass.generateDeprecated=false` to disable the deprecated operations in the given API. For example `org.acme.openapi.simple.api.DefaultApi.generatedDeprecated=false`.

## Custom Register Providers for generated api

In some cases, we need custom `RegisterProvider` for generated api, e.g. logging. You can define your own Providers in `application.properties` :

| Property Key | Example |
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
| `quarkus.openapi-generator.codegen.spec.[filename].custom-register-providers` | `quarkus.openapi-generator.codegen.spec.simple_openapi_json.custom-register-providers=org.test.Foo,org.test.Bar`<br/>Provider classes are separated by commas |

With the above configuration, the extension generates your Rest Clients with a code similar to the following:

```java
package org.acme.openapi.simple.api;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package io.quarkiverse.openapi.generator.deployment;

import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import io.quarkiverse.openapi.generator.deployment.codegen.OpenApiGeneratorOutputPaths;
import io.quarkus.runtime.annotations.ConfigItem;
Expand All @@ -11,56 +14,56 @@

// This configuration is read in codegen phase (before build time), the annotation is for document purposes and avoiding quarkus warns
@ConfigRoot(name = CodegenConfig.CODEGEN_TIME_CONFIG_PREFIX, phase = ConfigPhase.BUILD_TIME)
public class CodegenConfig {
public class CodegenConfig extends GlobalCodegenConfig {

static final String CODEGEN_TIME_CONFIG_PREFIX = "openapi-generator.codegen";

public static final String API_PKG_SUFFIX = ".api";
public static final String MODEL_PKG_SUFFIX = ".model";
public static final String VERBOSE_PROPERTY_NAME = "quarkus." + CODEGEN_TIME_CONFIG_PREFIX + ".verbose";
public static final String INPUT_BASE_DIR = "quarkus." + CODEGEN_TIME_CONFIG_PREFIX + ".input-base-dir";
public static final String INCLUDE_FILES = "quarkus." + CODEGEN_TIME_CONFIG_PREFIX + ".include";
public static final String EXCLUDE_FILES = "quarkus." + CODEGEN_TIME_CONFIG_PREFIX + ".exclude";
public static final String VALIDATE_SPEC_PROPERTY_NAME = "quarkus." + CODEGEN_TIME_CONFIG_PREFIX + ".validateSpec";
public static final String DEFAULT_SECURITY_SCHEME = "quarkus." + CODEGEN_TIME_CONFIG_PREFIX + ".default.security.scheme";
// package visibility for unit tests
static final String BUILD_TIME_GLOBAL_PREFIX_FORMAT = "quarkus." + CODEGEN_TIME_CONFIG_PREFIX + ".%s";
static final String BUILD_TIME_SPEC_PREFIX_FORMAT = "quarkus." + CODEGEN_TIME_CONFIG_PREFIX + ".spec.%s";
private static final String BASE_PACKAGE_PROP_FORMAT = "%s.base-package";
private static final String SKIP_FORM_MODEL_PROP_FORMAT = "%s.skip-form-model";
private static final String MUTINY_PROP_FORMAT = "%s.mutiny";
private static final String ADDITIONAL_MODEL_TYPE_ANNOTATIONS_PROP_FORMAT = "%s.additional-model-type-annotations";
private static final String TYPE_MAPPINGS_PROP_FORMAT = "%s.type-mappings";
private static final String IMPORT_MAPPINGS_PROP_FORMAT = "%s.import-mappings";
private static final String NORMALIZER_PROP_FORMAT = "%s.open-api-normalizer";
private static final String CUSTOM_REGISTER_PROVIDERS_FORMAT = "%s.custom-register-providers";

private static final String RETURN_RESPONSE_PROP_FORMAT = "%s.return-response";
private static final String CLIENT_HEADER_FACTORY_PROP_FORMAT = "%s.client-headers-factory";

public static final List<String> SUPPORTED_CONFIGURATIONS = Arrays.stream(ConfigName.values()).map(cn -> cn.name)
.collect(Collectors.toList());

public enum ConfigName {
//global configs
VERBOSE("verbose"),
INPUT_BASE_DIR("input-base-dir"),
INCLUDE("include"),
EXCLUDE("exclude"),
VALIDATE_SPEC("validateSpec"),
DEFAULT_SECURITY_SCHEME("default-security-scheme"),

//spec configs only
BASE_PACKAGE("base-package"),

//global & spec configs
SKIP_FORM_MODEL("skip-form-model"),
MUTINY("mutiny"),
ADDITIONAL_MODEL_TYPE_ANNOTATIONS("additional-model-type-annotations"),
ADDITIONAL_API_TYPE_ANNOTATIONS("additional-api-type-annotations"),
TYPE_MAPPINGS("type-mappings"),
IMPORT_MAPPINGS("import-mappings"),
NORMALIZER("open-api-normalizer"),
RETURN_RESPONSE("return-response"),
ENABLE_SECURITY_GENERATION("enable-security-generation");

private final String name;

ConfigName(String name) {
this.name = name;
}

}

/**
* OpenAPI Spec details for codegen configuration.
*/
@ConfigItem(name = "spec")
public Map<String, SpecItemConfig> specItem;

/**
* Whether to log the internal generator codegen process in the default output or not.
*/
@ConfigItem(name = "verbose", defaultValue = "false")
public boolean verbose;

/**
* Whether or not to skip validating the input spec prior to generation. By default, invalid specifications will result in
* an error.
*/
@ConfigItem(name = "validateSpec", defaultValue = "true")
public boolean validateSpec;
/**
* Security type for which security constraints should be created automatically if not explicitly defined
*/
@ConfigItem(name = "default.security.scheme", defaultValue = "none")
public String defaultSecurityScheme;

public static String resolveApiPackage(final String basePackage) {
return String.format("%s%s", basePackage, API_PKG_SUFFIX);
}
Expand All @@ -69,32 +72,18 @@ public static String resolveModelPackage(final String basePackage) {
return String.format("%s%s", basePackage, MODEL_PKG_SUFFIX);
}

public static String getBasePackagePropertyName(final Path openApiFilePath) {
return String.format(BASE_PACKAGE_PROP_FORMAT, getBuildTimeSpecPropertyPrefix(openApiFilePath));
}

public static String getSkipFormModelPropertyName(final Path openApiFilePath) {
return String.format(SKIP_FORM_MODEL_PROP_FORMAT, getBuildTimeSpecPropertyPrefix(openApiFilePath));
}

public static String getMutinyPropertyName(final Path openApiFilePath) {
return String.format(MUTINY_PROP_FORMAT, getBuildTimeSpecPropertyPrefix(openApiFilePath));
}

public static String getAdditionalModelTypeAnnotationsPropertyName(final Path openApiFilePath) {
return String.format(ADDITIONAL_MODEL_TYPE_ANNOTATIONS_PROP_FORMAT, getBuildTimeSpecPropertyPrefix(openApiFilePath));
}

public static String getTypeMappingsPropertyName(final Path openApiFilePath) {
return String.format(TYPE_MAPPINGS_PROP_FORMAT, getBuildTimeSpecPropertyPrefix(openApiFilePath));
}

public static String getImportMappingsPropertyName(final Path openApiFilePath) {
return String.format(IMPORT_MAPPINGS_PROP_FORMAT, getBuildTimeSpecPropertyPrefix(openApiFilePath));
/**
* Return global config name, openapi-generator.codegen.config-name
*/
public static String getGlobalConfigName(ConfigName configName) {
return String.format(BUILD_TIME_GLOBAL_PREFIX_FORMAT, configName.name);
}

public static String getNormalizerPropertyName(final Path openApiFilePath) {
return String.format(NORMALIZER_PROP_FORMAT, getBuildTimeSpecPropertyPrefix(openApiFilePath));
/**
* Return spec config name openapi-generator.codegen.spec.%s.config-name
*/
public static String getSpecConfigName(ConfigName configName, final Path openApiFilePath) {
return String.format("%s.%s", getBuildTimeSpecPropertyPrefix(openApiFilePath), configName.name);
}

/**
Expand All @@ -111,16 +100,4 @@ public static String getSanitizedFileName(final Path openApiFilePath) {
return StringUtil
.replaceNonAlphanumericByUnderscores(OpenApiGeneratorOutputPaths.getRelativePath(openApiFilePath).toString());
}

public static String getCustomRegisterProvidersFormat(final Path openApiFilePath) {
return String.format(CUSTOM_REGISTER_PROVIDERS_FORMAT, getBuildTimeSpecPropertyPrefix(openApiFilePath));
}

public static String getReturnResponsePropertyName(final Path openApiFilePath) {
return String.format(RETURN_RESPONSE_PROP_FORMAT, getBuildTimeSpecPropertyPrefix(openApiFilePath));
}

public static String getClientHeaderFactoryPropertyName(final Path openApiFilePath) {
return String.format(CLIENT_HEADER_FACTORY_PROP_FORMAT, getBuildTimeSpecPropertyPrefix(openApiFilePath));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package io.quarkiverse.openapi.generator.deployment;

import java.util.Map;
import java.util.Optional;

import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;

/*
* Model for the configuration of this extension.
* It's used for documentation purposes only.
* The configuration is consumed in the codegen phase, before build time.
* Not meant to be used outside this scope.
* Config items can be applied on spec and globally as well
*/
@ConfigGroup
public class CommonItemConfig {

/**
* Whether to skip the generation of models for form parameters
*/
@ConfigItem(name = "skip-form-model")
public Optional<Boolean> skipFormModel;

/**
* Type Mapping is an OpenAPI Generator configuration specifying which Java types (the values) should be used for a
* given OAS datatype (the keys of this map)
*/
@ConfigItem(name = "type-mappings")
public Map<String, String> typeMappings;

/**
* Import Mapping is an OpenAPI Generator configuration specifying which Java types (the values) should be
* imported when a given OAS datatype (the keys of this map) is used
*/
@ConfigItem(name = "import-mappings")
public Map<String, String> importMappings;

/**
* The specified annotations will be added to the generated model files
*/
@ConfigItem(name = "additional-model-type-annotations")
public Optional<String> additionalModelTypeAnnotations;

/**
* The specified annotations will be added to the generated api files
*/
@ConfigItem(name = "additional-api-type-annotations")
public Optional<String> additionalApiTypeAnnotations;

/**
* Defines if the methods should return {@link javax.ws.rs.core.Response} or a model. Default is <code>false</code>.
*/
@ConfigItem(name = "return-response")
public Optional<Boolean> returnResponse;

/**
* Defines if security support classes should be generated
*/
@ConfigItem(name = "enable-security-generation")
public Optional<String> enableSecurityGeneration;

/**
* Defines the normalizer options.
*/
@ConfigItem(name = "open-api-normalizer")
public Map<String, String> normalizer;

/**
* Enable SmallRye Mutiny support. If you set this to `true`, all return types will be wrapped in `io.smallrye.mutiny.Uni`.
*/
@ConfigItem(name = "mutiny")
public Optional<Boolean> supportMutiny;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.quarkiverse.openapi.generator.deployment;

import java.util.Optional;

import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;

/*
* Model for the configuration of this extension.
* It's used for documentation purposes only.
* The configuration is consumed in the codegen phase, before build time.
* Not meant to be used outside this scope.
* Config items can be applied only globally
*/
@ConfigGroup
public class GlobalCodegenConfig extends CommonItemConfig {

/**
* Whether to log the internal generator codegen process in the default output or not.
*/
@ConfigItem(name = "verbose", defaultValue = "false")
public boolean verbose;

/**
* Option to change the directory where OpenAPI files must be found.
*/
@ConfigItem(name = "input-base-dir")
public Optional<String> inputBaseDir;

/**
* Whether or not to skip validating the input spec prior to generation. By default, invalid specifications will result in
* an error.
*/
@ConfigItem(name = "validateSpec", defaultValue = "true")
public boolean validateSpec;

/**
* Option to specify files for which generation should be executed only
*/
@ConfigItem(name = "include")
public Optional<String> include;

/**
* Option to exclude file from generation
*/
@ConfigItem(name = "exclude")
public Optional<String> exclude;

/**
* Create security for the referenced security scheme
*/
@ConfigItem(name = "default-security-scheme")
public Optional<String> defaultSecuritySchema;

}
Loading

0 comments on commit 30d5501

Please sign in to comment.