diff --git a/.all-contributorsrc b/.all-contributorsrc index 548f8f025..b552c03cc 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -378,6 +378,73 @@ "contributions": [ "doc" ] + }, + { + "login": "marko-bekhta", + "name": "Marko Bekhta", + "avatar_url": "https://avatars.githubusercontent.com/u/4004823?v=4", + "profile": "https://github.com/marko-bekhta", + "contributions": [ + "code" + ] + }, + { + "login": "andybarilla", + "name": "Andy Barilla", + "avatar_url": "https://avatars.githubusercontent.com/u/5983808?v=4", + "profile": "https://kranzilla.com/", + "contributions": [ + "code", + "test", + "doc" + ] + }, + { + "login": "yuhaibohotmail", + "name": "yuhaibohotmail", + "avatar_url": "https://avatars.githubusercontent.com/u/48646226?v=4", + "profile": "https://github.com/yuhaibohotmail", + "contributions": [ + "code", + "test" + ] + }, + { + "login": "JPSantistebanQ", + "name": "Juan Piero Santisteban Quiroz", + "avatar_url": "https://avatars.githubusercontent.com/u/49204973?v=4", + "profile": "https://jpsantistebanq.github.io/portfolio", + "contributions": [ + "code", + "test" + ] + }, + { + "login": "joschi", + "name": "Jochen Schalanda", + "avatar_url": "https://avatars.githubusercontent.com/u/43951?v=4", + "profile": "https://github.com/joschi", + "contributions": [ + "code" + ] + }, + { + "login": "dellamas", + "name": "Luis Fabrício De Llamas", + "avatar_url": "https://avatars.githubusercontent.com/u/80655200?v=4", + "profile": "https://dev.to/dellamas", + "contributions": [ + "doc" + ] + }, + { + "login": "jghagemann", + "name": "João Guilherme Hagemann", + "avatar_url": "https://avatars.githubusercontent.com/u/42047435?v=4", + "profile": "https://github.com/jghagemann", + "contributions": [ + "doc" + ] } ], "contributorsPerLine": 7, diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 000000000..4083272ee --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,100 @@ +name: Bug Report +description: Create a bug report +labels: ["type: bug"] +body: + + - type: markdown + attributes: + value: | + Thank you for contributing with Quarkus OpenAPI Generator with this bug report! Submit your issue below: + + - type: markdown + attributes: + value: | + ## Bug Report + + - type: dropdown + id: extension + attributes: + label: "Tell us the extension you're using" + multiple: false + options: + - Client + - Server + - Mock + validations: + required: true + + - type: textarea + id: i-tried-this + attributes: + label: "I tried this:" + placeholder: "What did you try to do? A code snippet or example helps." + validations: + required: true + + - type: textarea + id: instead-what-happened + attributes: + label: "This happened:" + placeholder: "What happened instead of what you've expected?" + validations: + required: true + + - type: textarea + id: what-did-you-expect + attributes: + label: "I expected this:" + placeholder: "What did you expect to happen? Describe the output or behavior you expected to see (unless it's obvious)." + + - type: textarea + id: workaround + attributes: + label: "Is there a workaround?" + placeholder: "What's the workaround to avoid this issue?" + + - type: textarea + id: reproduce + attributes: + label: "How can we try to reproduce the issue?" + placeholder: "What steps or configuration do we need to reproduce the erratic behavior?" + + - type: textarea + attributes: + label: Anything else? + placeholder: | + Links? References? Logs? Anything that will give us more context about the issue you are encountering. + Tip: You can attach images or log files by dragging files in. + + - type: markdown + attributes: + value: | + ## Environment + + - type: input + attributes: + label: Output of `uname -a` or `ver` + - type: input + attributes: + label: Output of `java -version` + - type: input + attributes: + label: Quarkus OpenApi version or git rev + - type: input + attributes: + label: Build tool (ie. output of `mvnw --version` or `gradlew --version`) + - type: textarea + attributes: + label: Additional information + description: > + If you have any additional information for us, please feel free to use the field below. + You can attach screenshots or screen recordings here, by + dragging and dropping files in the field below. + + - type: textarea + attributes: + label: Community Notes + value: | + + * Please vote by adding a 👍 reaction to the issue to help us prioritize. + * If you are interested to work on this issue, please leave a comment.name: Bug Report 🐞 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 000000000..5c965d0ad --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,60 @@ +name: Feature Request +description: Request a new feature +labels: ["type: feature"] +body: + + - type: markdown + attributes: + value: | + :pray: Thanks for taking the time to fill out this feature request! + + - type: markdown + attributes: + value: | + ## Feature Request + + - type: dropdown + id: extension + attributes: + label: "Tell us the extension to which you'd like to add the feature." + multiple: false + options: + - Client + - Server + - Mock + validations: + required: true + + - type: textarea + id: what-would-you-like-to-add + attributes: + label: "What kind of feature would you like to add?" + placeholder: "Description of the feature you'd like to see." + validations: + required: true + + - type: textarea + id: proposals + attributes: + label: "Proposal(s):" + placeholder: "Describe your proposal(s) and any relevant details here." + + - type: textarea + id: alternatives + attributes: + label: "Alternative(s):" + placeholder: "Describe any alternative approaches, options, or suggestions you’d like to consider." + + - type: textarea + id: additional-info + attributes: + label: "Additional info:" + placeholder: "Provide any supplementary details, context, or supporting information here." + + - type: textarea + attributes: + label: Community Notes + value: | + + * Please vote by adding a 👍 reaction to the feature to help us prioritize. + * If you want to work on this feature, please leave a comment. \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f19146646..a2e416a38 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,17 +1,35 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - version: 2 updates: + # Maven updates for main branch - package-ecosystem: "maven" directory: "/" schedule: interval: "daily" + target-branch: "main" ignore: - dependency-name: "org.apache.maven.plugins:maven-compiler-plugin" + + # Maven updates for main-lts branch + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "daily" + target-branch: "main-lts" + ignore: + - dependency-name: "org.apache.maven.plugins:maven-compiler-plugin" + - dependency-name: "io.quarkus:quarkus-bom" + - dependency-name: "io.quarkus.platform:quarkus-bom" + + # GitHub Actions updates for main branch + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + target-branch: "main" + + # GitHub Actions updates for main-lts branch - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "weekly" \ No newline at end of file + interval: "weekly" + target-branch: "main-lts" diff --git a/.github/project.yml b/.github/project.yml index ed4633709..7e5cd2964 100644 --- a/.github/project.yml +++ b/.github/project.yml @@ -1,3 +1,3 @@ release: - current-version: 2.5.0 + current-version: 2.10.0 next-version: 3.0.0-SNAPSHOT diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 28d9a1cbd..0f9f8c49c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,6 +4,7 @@ on: push: branches: - "main" + - "main-lts" paths-ignore: - '.gitignore' - 'CODEOWNERS' @@ -27,7 +28,10 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, windows-latest] + os: [ + ubuntu-latest, + windows-latest + ] java: [ { 'version': '17' }, { 'version': '21' } @@ -46,14 +50,17 @@ jobs: cache: 'maven' - name: Build with Maven - run: mvn '-Dorg.slf4j.simpleLogger.log.org.openapitools=off' -B formatter:validate impsort:check verify --file pom.xml + run: mvn '-Dorg.slf4j.simpleLogger.log.org.openapitools=off' -B formatter:validate impsort:check install --file pom.xml build_reactive: name: Build - RESTEasy Reactive runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, windows-latest] + os: [ + ubuntu-latest, + windows-latest + ] java: [ { 'version': '17' }, { 'version': '21' } @@ -72,4 +79,4 @@ jobs: cache: 'maven' - name: Build with Maven - run: mvn -Presteasy-reactive '-Dorg.slf4j.simpleLogger.log.org.openapitools=off' -B formatter:validate impsort:check verify --file pom.xml + run: mvn -Presteasy-reactive '-Dorg.slf4j.simpleLogger.log.org.openapitools=off' -B formatter:validate impsort:check install --file pom.xml diff --git a/.github/workflows/build_website.yml b/.github/workflows/build_website.yml index bd5a8822f..9057da4f2 100644 --- a/.github/workflows/build_website.yml +++ b/.github/workflows/build_website.yml @@ -4,6 +4,7 @@ on: push: branches: - "main" + - "main-lts" paths-ignore: - '.gitignore' - 'CODEOWNERS' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index af121a859..a39aa4b71 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,9 +2,9 @@ name: "CodeQL" on: push: - branches: [ "main" ] + branches: [ "main", "main-lts" ] pull_request: - branches: [ "main" ] + branches: [ "main", "main-lts" ] schedule: - cron: "2 0 * * 6" diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 8b865d9a0..33b149eed 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -12,7 +12,7 @@ jobs: if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' steps: - name: Download PR Artifact - uses: dawidd6/action-download-artifact@v6 + uses: dawidd6/action-download-artifact@v10 with: workflow: ${{ github.event.workflow_run.workflow_id }} workflow_conclusion: success diff --git a/.github/workflows/quarkus-snapshot.yaml b/.github/workflows/quarkus-snapshot.yaml index 07a116ad6..e9b14c6c5 100644 --- a/.github/workflows/quarkus-snapshot.yaml +++ b/.github/workflows/quarkus-snapshot.yaml @@ -26,9 +26,6 @@ jobs: if: github.actor == 'quarkusbot' || github.actor == 'quarkiversebot' || github.actor == '' steps: - - name: Install yq - run: sudo add-apt-repository ppa:rmescandon/yq && sudo apt update && sudo apt install yq -y - - name: Set up Java uses: actions/setup-java@v4 with: diff --git a/.github/workflows/snapshot_deploy.yml b/.github/workflows/snapshot_deploy.yml index b2df3b887..5ce3af9c7 100644 --- a/.github/workflows/snapshot_deploy.yml +++ b/.github/workflows/snapshot_deploy.yml @@ -3,6 +3,7 @@ name: Snapshot Deploy concurrency: group: ${{ github.ref }}-${{ github.workflow }} cancel-in-progress: true + on: workflow_dispatch: push: @@ -12,28 +13,16 @@ defaults: run: shell: bash +permissions: + attestations: write + id-token: write + contents: read + jobs: deploy-snapshot: - runs-on: ubuntu-latest - name: Deploy Snapshot artifacts - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: 17 - cache: 'maven' - server-id: 'ossrh' - server-username: MAVEN_USERNAME - server-password: MAVEN_PASSWORD - gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} - gpg-passphrase: MAVEN_GPG_PASSPHRASE - - - name: Deploy ${{steps.metadata.outputs.next-version}} - run: | - mvn -B clean deploy -DskipTests -DperformRelease=true -Drelease - env: - MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} - MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} - MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} \ No newline at end of file + name: Deploy Snapshots + uses: quarkiverse/.github/.github/workflows/perform-release.yml@main + secrets: inherit + with: + ref: main + version: 3.0.0-SNAPSHOT diff --git a/.github/workflows/stale_issues.yml b/.github/workflows/stale_issues.yml index fa3422c05..e2a3ca78b 100644 --- a/.github/workflows/stale_issues.yml +++ b/.github/workflows/stale_issues.yml @@ -11,7 +11,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v9.0.0 + - uses: actions/stale@v9.1.0 with: exempt-issue-labels: "pinned" stale-issue-message: "@ricardozanini @hbelmiro This is being labeled as Stale." diff --git a/README.md b/README.md index 609bdc955..9cf435d39 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,39 @@ + +
+ + + # Quarkus - OpenAPI Generator +
+
-[![All Contributors](https://img.shields.io/badge/all_contributors-40-orange.svg?style=flat-square)](#contributors-) +[![All Contributors](https://img.shields.io/badge/all_contributors-47-orange.svg?style=flat-square)](#contributors-) [![Build]()](https://github.com/quarkiverse/quarkus-openapi-generator/actions?query=workflow%3ABuild) [![Maven Central](https://img.shields.io/maven-central/v/io.quarkiverse.openapi.generator/quarkus-openapi-generator.svg?label=Maven%20Central&style=flat-square)](https://search.maven.org/artifact/io.quarkiverse.openapi.generator/quarkus-openapi-generator) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square)](https://opensource.org/licenses/Apache-2.0) - -> **⚠️** This is the instructions for the latest SNAPSHOT version (main branch). Please, see the [latest **released** documentation](https://docs.quarkiverse.io/quarkus-openapi-generator/dev/index.html) if you are looking for instructions. - -> **⚠️** This extension, [like Quarkus 3.7](https://quarkus.io/blog/java-17/), requires Java 17. The last version of this extension that supports earlier versions of Java is [2.2.16](https://github.com/quarkiverse/quarkus-openapi-generator/releases/tag/2.2.16). - -> **⚠️** Check versions 1.x.x if you're still using Quarkus 2. But be aware that we no longer support Quarkus 2. That means there are no updates planned for those versions. - Quarkus' extensions for generation of [Rest Clients](https://quarkus.io/guides/rest-client) and server stubs generation based on the [Apicurio Codegen](https://github.com/Apicurio/apicurio-codegen) capabilities based on OpenAPI specification files. -This client-side extension is based on the [OpenAPI Generator Tool](https://openapi-generator.tech/). Please consider donation to help them maintain the +This client-side extension is based on the [OpenAPI Generator Tool](https://openapi-generator.tech/). Please consider a donation to help them maintain the project: https://opencollective.com/openapi_generator/donate -This repository holds two Quarkus extensions. The one located on the client folder is for REST code generation for client side only. The extension located in the server folder can be used for server stubs generation. +This repository holds two Quarkus extensions. The one located in the client folder is for REST code generation for client-side only. The extension located in the server folder can be used for server stubs generation. + +> [!WARNING] +> This extension, [like Quarkus 3.7](https://quarkus.io/blog/java-17/), requires Java 17. The last version of this extension that supports earlier versions of Java is [2.2.16](https://github.com/quarkiverse/quarkus-openapi-generator/releases/tag/2.2.16). + +> [!CAUTION] +> Check versions 1.x.x if you're still using Quarkus 2. But be aware that we no longer support Quarkus 2. That means there are no updates planned for those versions. **Want to contribute? Great!** We try to make it easy, and all contributions, even the smaller ones, are more than welcome. This includes bug reports, fixes, documentation, examples... But first, read [this page](CONTRIBUTING.md). ## Getting Started -You can learn more in [Quarkus Openapi Generator Documentation](http://docs.quarkiverse.io/quarkus-openapi-generator/dev/index.html). - +You can learn more in [Quarkus OpenAPI Generator Documentation](http://docs.quarkiverse.io/quarkus-openapi-generator/dev/index.html). +> [!TIP] > If you want to improve the docs, please feel free to contribute editing the docs in [Docs](https://github.com/quarkiverse/quarkus-openapi-generator/tree/main/docs/modules/ROOT). But first, read [this page](CONTRIBUTING.md). ## Contributors ✨ @@ -90,6 +96,15 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Karl Ferdinand Ebert
Karl Ferdinand Ebert

💻 📖 Michał Kolenda
Michał Kolenda

💻 rednalyn
rednalyn

📖 + Marko Bekhta
Marko Bekhta

💻 + Andy Barilla
Andy Barilla

💻 ⚠️ 📖 + + + yuhaibohotmail
yuhaibohotmail

💻 ⚠️ + Juan Piero Santisteban Quiroz
Juan Piero Santisteban Quiroz

💻 ⚠️ + Jochen Schalanda
Jochen Schalanda

💻 + Luis Fabrício De Llamas
Luis Fabrício De Llamas

📖 + João Guilherme Hagemann
João Guilherme Hagemann

📖 diff --git a/client/deployment/pom.xml b/client/deployment/pom.xml index 722b3bc55..34f31f572 100644 --- a/client/deployment/pom.xml +++ b/client/deployment/pom.xml @@ -8,13 +8,12 @@ ../pom.xml quarkus-openapi-generator-deployment - Quarkus - Openapi Generator - Client - Deployment + Quarkus - OpenAPI Generator - Client - Deployment - 7.8.0 - 2.0.16 + 7.13.0 + 2.0.17 4.3.1 - 2.1.23 @@ -36,6 +35,11 @@ quarkus-openapi-generator ${project.version} + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-oidc + ${project.version} + @@ -164,9 +168,6 @@ ${quarkus.version} - - -AlegacyConfigRoot=true - diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CodegenConfig.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CodegenConfig.java index c913586e8..6927b2275 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CodegenConfig.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CodegenConfig.java @@ -7,30 +7,32 @@ import java.util.stream.Collectors; import io.quarkiverse.openapi.generator.deployment.codegen.OpenApiGeneratorOutputPaths; -import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithName; import io.smallrye.config.common.utils.StringUtil; // 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 extends GlobalCodegenConfig { +@ConfigRoot(phase = ConfigPhase.BUILD_TIME) +@ConfigMapping(prefix = "quarkus." + CodegenConfig.CODEGEN_TIME_CONFIG_PREFIX) +public interface CodegenConfig extends GlobalCodegenConfig { - static final String CODEGEN_TIME_CONFIG_PREFIX = "openapi-generator.codegen"; + String CODEGEN_TIME_CONFIG_PREFIX = "openapi-generator.codegen"; - public static final String API_PKG_SUFFIX = ".api"; - public static final String MODEL_PKG_SUFFIX = ".model"; + String API_PKG_SUFFIX = ".api"; + String MODEL_PKG_SUFFIX = ".model"; - public static final String ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER_NAME_DEFAULT = "UNEXPECTED"; - public static final String ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER_STRING_VALUE_DEFAULT = "unexpected"; + String ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER_NAME_DEFAULT = "UNEXPECTED"; + String ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER_STRING_VALUE_DEFAULT = "unexpected"; // 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"; + String BUILD_TIME_GLOBAL_PREFIX_FORMAT = "quarkus." + CODEGEN_TIME_CONFIG_PREFIX + ".%s"; + String BUILD_TIME_SPEC_PREFIX_FORMAT = "quarkus." + CODEGEN_TIME_CONFIG_PREFIX + ".spec.%s"; - public static final List SUPPORTED_CONFIGURATIONS = Arrays.stream(ConfigName.values()).map(cn -> cn.name) + List SUPPORTED_CONFIGURATIONS = Arrays.stream(ConfigName.values()).map(cn -> cn.name) .collect(Collectors.toList()); - public enum ConfigName { + enum ConfigName { //global configs VERBOSE("verbose"), INPUT_BASE_DIR("input-base-dir"), @@ -58,6 +60,7 @@ public enum ConfigName { ADDITIONAL_API_TYPE_ANNOTATIONS("additional-api-type-annotations"), TYPE_MAPPINGS("type-mappings"), IMPORT_MAPPINGS("import-mappings"), + SCHEMA_MAPPINGS("schema-mappings"), NORMALIZER("open-api-normalizer"), RETURN_RESPONSE("return-response"), ENABLE_SECURITY_GENERATION("enable-security-generation"), @@ -66,8 +69,17 @@ public enum ConfigName { PART_FILENAME_VALUE("part-filename-value"), USE_FIELD_NAME_IN_PART_FILENAME("use-field-name-in-part-filename"), ADDITIONAL_PROPERTIES_AS_ATTRIBUTE("additional-properties-as-attribute"), + INITIALIZE_EMPTY_COLLECTIONS("initialize-empty-collections"), ADDITIONAL_REQUEST_ARGS("additional-request-args"), - BEAN_VALIDATION("use-bean-validation"); + REMOVE_OPERATION_ID_PREFIX("remove-operation-id-prefix"), + REMOVE_OPERATION_ID_PREFIX_DELIMITER("remove-operation-id-prefix-delimiter"), + REMOVE_OPERATION_ID_PREFIX_COUNT("remove-operation-id-prefix-count"), + GENERATE_APIS("generate-apis"), + GENERATE_MODELS("generate-models"), + BEAN_VALIDATION("use-bean-validation"), + SERIALIZABLE_MODEL("serializable-model"), + EQUALS_HASHCODE("equals-hashcode"), + USE_DYNAMIC_URL("use-dynamic-url"); private final String name; @@ -80,28 +92,28 @@ public enum ConfigName { /** * OpenAPI Spec details for codegen configuration. */ - @ConfigItem(name = "spec") - public Map specItem; + @WithName("spec") + Map specItem(); - public static String resolveApiPackage(final String basePackage) { + static String resolveApiPackage(final String basePackage) { return String.format("%s%s", basePackage, API_PKG_SUFFIX); } - public static String resolveModelPackage(final String basePackage) { + static String resolveModelPackage(final String basePackage) { return String.format("%s%s", basePackage, MODEL_PKG_SUFFIX); } /** * Return global config name, openapi-generator.codegen.config-name */ - public static String getGlobalConfigName(ConfigName configName) { + static String getGlobalConfigName(ConfigName configName) { return String.format(BUILD_TIME_GLOBAL_PREFIX_FORMAT, configName.name); } /** * Return spec config name openapi-generator.codegen.spec.%s.config-name */ - public static String getSpecConfigName(ConfigName configName, final Path openApiFilePath) { + static String getSpecConfigName(ConfigName configName, final Path openApiFilePath) { return String.format("%s.%s", getBuildTimeSpecPropertyPrefix(openApiFilePath), configName.name); } @@ -111,7 +123,7 @@ public static String getSpecConfigName(ConfigName configName, final Path openApi * returned value is * openapi.generator.codegen.spec.petstore.mutiny. */ - public static String getSpecConfigNameByConfigKey(final String configKey, final ConfigName configName) { + 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); } @@ -122,11 +134,11 @@ public static String getSpecConfigNameByConfigKey(final String configKey, final * `quarkus.openapi-generator."petstore_json"`. * Every the periods (.) in the file name will be replaced by underscore (_). */ - public static String getBuildTimeSpecPropertyPrefix(final Path openApiFilePath) { + static String getBuildTimeSpecPropertyPrefix(final Path openApiFilePath) { return String.format(BUILD_TIME_SPEC_PREFIX_FORMAT, getSanitizedFileName(openApiFilePath)); } - public static String getSanitizedFileName(final Path openApiFilePath) { + static String getSanitizedFileName(final Path openApiFilePath) { return StringUtil .replaceNonAlphanumericByUnderscores(OpenApiGeneratorOutputPaths.getRelativePath(openApiFilePath).toString()); } diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CommonItemConfig.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CommonItemConfig.java index 9a6baed5a..218ef12e5 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CommonItemConfig.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CommonItemConfig.java @@ -3,8 +3,7 @@ import java.util.Map; import java.util.Optional; -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; +import io.smallrye.config.WithName; /* * Model for the configuration of this extension. @@ -13,85 +12,91 @@ * Not meant to be used outside this scope. * Config items can be applied on spec and globally as well */ -@ConfigGroup -public class CommonItemConfig { +public interface CommonItemConfig { /** * Whether to skip the generation of models for form parameters */ - @ConfigItem(name = "skip-form-model") - public Optional skipFormModel; + @WithName("skip-form-model") + Optional 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 typeMappings; + @WithName("type-mappings") + Map 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 importMappings; + @WithName("import-mappings") + Map importMappings(); + + /** + * Schema Mapping is an OpenAPI Generator configuration specifying which Java types (the values) should be + * imported when a given schema type (the keys of this map) is used + */ + @WithName("schema-mappings") + Map schemaMappings(); /** * The specified annotations will be added to the generated model files */ - @ConfigItem(name = "additional-model-type-annotations") - public Optional additionalModelTypeAnnotations; + @WithName("additional-model-type-annotations") + Optional additionalModelTypeAnnotations(); /** * Defines if the enums should have an `UNEXPECTED` member to convey values that cannot be parsed. Default is * {@code false}. */ - @ConfigItem(name = "additional-enum-type-unexpected-member") - public Optional additionalEnumTypeUnexpectedMemberAnnotations; + @WithName("additional-enum-type-unexpected-member") + Optional additionalEnumTypeUnexpectedMemberAnnotations(); /** * The specified annotations will be added to the generated api files */ - @ConfigItem(name = "additional-api-type-annotations") - public Optional additionalApiTypeAnnotations; + @WithName("additional-api-type-annotations") + Optional additionalApiTypeAnnotations(); /** * Add custom/additional HTTP Headers or other args to every request */ - @ConfigItem(name = "additional-request-args") - public Optional additionalRequestArgs; + @WithName("additional-request-args") + Optional additionalRequestArgs(); /** * Defines if the methods should return {@link jakarta.ws.rs.core.Response} or a model. Default is {@code false}. */ - @ConfigItem(name = "return-response") - public Optional returnResponse; + @WithName("return-response") + Optional returnResponse(); /** * Defines if security support classes should be generated */ - @ConfigItem(name = "enable-security-generation") - public Optional enableSecurityGeneration; + @WithName("enable-security-generation") + Optional enableSecurityGeneration(); /** * Defines the normalizer options. */ - @ConfigItem(name = "open-api-normalizer") - public Map normalizer; + @WithName("open-api-normalizer") + Map normalizer(); /** * Enable SmallRye Mutiny support. If you set this to {@code true}, all return types will be wrapped in * {@link io.smallrye.mutiny.Uni}. */ - @ConfigItem(name = "mutiny") - public Optional supportMutiny; + @WithName("mutiny") + Optional supportMutiny(); /** * Defines with SmallRye Mutiny enabled if methods should return {@link jakarta.ws.rs.core.Response} or a model. Default is * {@code false}. */ - @ConfigItem(name = "mutiny.return-response") - public Optional mutinyReturnResponse; + @WithName("mutiny.return-response") + Optional mutinyReturnResponse(); /** * Handles the return type for each operation, depending on the configuration. @@ -118,16 +123,16 @@ public class CommonItemConfig { * - If the operation has a void return type, it will return {@link io.smallrye.mutiny.Uni}. * - Otherwise, it will return {@link io.smallrye.mutiny.Uni}`. */ - @ConfigItem(name = "mutiny.operation-ids") - public Optional> mutinyMultiOperationIds; + @WithName("mutiny.operation-ids") + Map mutinyMultiOperationIds(); /** * Defines, whether the `PartFilename` ({@link org.jboss.resteasy.reactive.PartFilename} or * {@link org.jboss.resteasy.annotations.providers.multipart.PartFilename}) annotation should be generated for * MultipartForm POJOs. By setting to {@code false}, the annotation will not be generated. */ - @ConfigItem(name = "generate-part-filename") - public Optional generatePartFilename; + @WithName("generate-part-filename") + Optional generatePartFilename(); /** * Defines the filename for a part in case the `PartFilename` annotation @@ -136,21 +141,52 @@ public class CommonItemConfig { * In case no value is set, the default one is `<fieldName>File` or `file`, depending on the * {@link CommonItemConfig#useFieldNameInPartFilename} configuration. */ - @ConfigItem(name = "part-filename-value") - public Optional partFilenameValue; + @WithName("part-filename-value") + Optional partFilenameValue(); /** * Defines, whether the filename should also include the property name in case the `PartFilename` annotation * ({@link org.jboss.resteasy.reactive.PartFilename} or * {@link org.jboss.resteasy.annotations.providers.multipart.PartFilename}) is generated. */ - @ConfigItem(name = "use-field-name-in-part-filename") - public Optional useFieldNameInPartFilename; + @WithName("use-field-name-in-part-filename") + Optional useFieldNameInPartFilename(); /** * Enable bean validation. If you set this to {@code true}, validation annotations are added to generated sources E.g. * {@code @Size}. */ - @ConfigItem(name = "use-bean-validation") - public Optional useBeanValidation; + @WithName("use-bean-validation") + Optional useBeanValidation(); + + /** + * Enable the generation of APIs. If you set this to {@code false}, APIs will not be generated. + */ + @WithName("generate-apis") + Optional generateApis(); + + /** + * Enable the generation of models. If you set this to {@code false}, models will not be generated. + */ + @WithName("generate-models") + Optional generateModels(); + + /** + * Enable the generation of equals and hashcode in models. If you set this to {@code false}, the models + * will not have equals and hashcode. + */ + @WithName("equals-hashcode") + Optional equalsHashcode(); + + /** + * Add additional properties as attribute. + */ + @WithName("additional-properties-as-attribute") + Optional additionalPropertiesAsAttribute(); + + /** + * Initialise collections as empty instead of null + */ + @WithName("initialize-empty-collections") + Optional initializeEmptyCollections(); } diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/GeneratorProcessor.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/GeneratorProcessor.java index 2cc8c3b33..1d48b9849 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/GeneratorProcessor.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/GeneratorProcessor.java @@ -1,5 +1,7 @@ package io.quarkiverse.openapi.generator.deployment; +import static io.quarkus.bootstrap.classloading.QuarkusClassLoader.isClassPresentAtRuntime; + import java.util.Collection; import java.util.List; import java.util.Map; @@ -12,23 +14,27 @@ import org.jboss.jandex.ClassType; import org.jboss.jandex.DotName; import org.jboss.jandex.ParameterizedType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import io.quarkiverse.openapi.generator.AuthName; import io.quarkiverse.openapi.generator.AuthenticationRecorder; -import io.quarkiverse.openapi.generator.ClassicOidcClientRequestFilterDelegate; import io.quarkiverse.openapi.generator.OidcClient; import io.quarkiverse.openapi.generator.OpenApiGeneratorConfig; import io.quarkiverse.openapi.generator.OpenApiSpec; -import io.quarkiverse.openapi.generator.ReactiveOidcClientRequestFilterDelegate; import io.quarkiverse.openapi.generator.markers.ApiKeyAuthenticationMarker; import io.quarkiverse.openapi.generator.markers.BasicAuthenticationMarker; import io.quarkiverse.openapi.generator.markers.BearerAuthenticationMarker; import io.quarkiverse.openapi.generator.markers.OauthAuthenticationMarker; import io.quarkiverse.openapi.generator.markers.OperationMarker; +import io.quarkiverse.openapi.generator.oidc.ClassicOidcClientRequestFilterDelegate; +import io.quarkiverse.openapi.generator.oidc.OidcAuthenticationRecorder; +import io.quarkiverse.openapi.generator.oidc.ReactiveOidcClientRequestFilterDelegate; +import io.quarkiverse.openapi.generator.oidc.providers.OAuth2AuthenticationProvider; import io.quarkiverse.openapi.generator.providers.ApiKeyIn; import io.quarkiverse.openapi.generator.providers.AuthProvider; -import io.quarkiverse.openapi.generator.providers.CompositeAuthenticationProvider; -import io.quarkiverse.openapi.generator.providers.OAuth2AuthenticationProvider.OidcClientRequestFilterDelegate; +import io.quarkiverse.openapi.generator.providers.BaseCompositeAuthenticationProvider; +import io.quarkiverse.openapi.generator.providers.CredentialsProvider; import io.quarkiverse.openapi.generator.providers.OperationAuthInfo; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; @@ -48,43 +54,64 @@ public class GeneratorProcessor { private static final DotName BASIC_AUTHENTICATION_MARKER = DotName.createSimple(BasicAuthenticationMarker.class); private static final DotName BEARER_AUTHENTICATION_MARKER = DotName.createSimple(BearerAuthenticationMarker.class); private static final DotName API_KEY_AUTHENTICATION_MARKER = DotName.createSimple(ApiKeyAuthenticationMarker.class); - private static final DotName OPERATION_MARKER = DotName.createSimple(OperationMarker.class); + private static final String ABSTRACT_TOKEN_PRODUCER = "io.quarkus.oidc.client.runtime.AbstractTokensProducer"; + + private static final Logger LOGGER = LoggerFactory.getLogger(GeneratorProcessor.class); + + private static String sanitizeAuthName(String schemeName) { + return OpenApiGeneratorConfig.getSanitizedSecuritySchemeName(schemeName); + } + + private static Map> getOperationsBySpec(CombinedIndexBuildItem beanArchiveBuildItem) { + return beanArchiveBuildItem.getIndex().getAnnotationsWithRepeatable(OPERATION_MARKER, beanArchiveBuildItem.getIndex()) + .stream().collect(Collectors.groupingBy( + marker -> marker.value("openApiSpecId").asString() + "_" + marker.value("name").asString())); + } + + private static List getOperations(Map> operationsBySpec, + String openApiSpecId, String name) { + return operationsBySpec.getOrDefault(openApiSpecId + "_" + name, List.of()).stream() + .map(op -> OperationAuthInfo.builder().withPath(op.value("path").asString()) + .withId(op.value("operationId").asString()).withMethod(op.value("method").asString()).build()) + .collect(Collectors.toList()); + } + @BuildStep FeatureBuildItem feature() { return new FeatureBuildItem(FEATURE); } @BuildStep - void additionalBean( - Capabilities capabilities, - BuildProducer producer) { + void additionalBean(Capabilities capabilities, BuildProducer producer) { + + if (!isClassPresentAtRuntime(ABSTRACT_TOKEN_PRODUCER)) { + LOGGER.debug("{} class not found in runtime, skipping OidcClientRequestFilterDelegate bean generation", + ABSTRACT_TOKEN_PRODUCER); + return; + } + LOGGER.debug("{} class found in runtime, producing OidcClientRequestFilterDelegate bean generation", + ABSTRACT_TOKEN_PRODUCER); if (capabilities.isPresent(Capability.REST_CLIENT_REACTIVE)) { - producer.produce( - AdditionalBeanBuildItem.builder().addBeanClass(ReactiveOidcClientRequestFilterDelegate.class) - .setDefaultScope(DotName.createSimple(Dependent.class)) - .setUnremovable() - .build()); + producer.produce(AdditionalBeanBuildItem.builder().addBeanClass(ReactiveOidcClientRequestFilterDelegate.class) + .setDefaultScope(DotName.createSimple(Dependent.class)).setUnremovable().build()); } else { - producer.produce( - AdditionalBeanBuildItem.builder().addBeanClass(ClassicOidcClientRequestFilterDelegate.class) - .setDefaultScope(DotName.createSimple(Dependent.class)) - .setUnremovable() - .build()); + producer.produce(AdditionalBeanBuildItem.builder().addBeanClass(ClassicOidcClientRequestFilterDelegate.class) + .setDefaultScope(DotName.createSimple(Dependent.class)).setUnremovable().build()); } + } @BuildStep @Record(ExecutionTime.STATIC_INIT) - void produceCompositeProviders(AuthenticationRecorder recorder, - List authProviders, + void produceCompositeProviders(AuthenticationRecorder recorder, List authProviders, BuildProducer beanProducer) { Map> providersBySpec = authProviders.stream() .collect(Collectors.groupingBy(AuthProviderBuildItem::getOpenApiSpecId)); providersBySpec.forEach((openApiSpecId, providers) -> { - beanProducer.produce(SyntheticBeanBuildItem.configure(CompositeAuthenticationProvider.class) + beanProducer.produce(SyntheticBeanBuildItem.configure(BaseCompositeAuthenticationProvider.class) .scope(Dependent.class) .addQualifier() .annotation(OpenApiSpec.class) @@ -92,12 +119,9 @@ void produceCompositeProviders(AuthenticationRecorder recorder, .done() .addInjectionPoint( ParameterizedType.create(Instance.class, ClassType.create(AuthProvider.class)), - AnnotationInstance.builder(OpenApiSpec.class) - .add("openApiSpecId", openApiSpecId) - .build()) + AnnotationInstance.builder(OpenApiSpec.class).add("openApiSpecId", openApiSpecId).build()) .createWith(recorder.recordCompositeProvider(openApiSpecId)) .done()); - }); } @@ -106,9 +130,34 @@ void produceCompositeProviders(AuthenticationRecorder recorder, void produceOauthAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, BuildProducer authenticationProviders, BuildProducer beanProducer, - AuthenticationRecorder recorder) { + OidcAuthenticationRecorder oidcRecorder) { + Collection authenticationMarkers = beanArchiveBuildItem.getIndex() - .getAnnotationsWithRepeatable(OAUTH_AUTHENTICATION_MARKER, beanArchiveBuildItem.getIndex()); + .getAnnotationsWithRepeatable(OAUTH_AUTHENTICATION_MARKER, beanArchiveBuildItem.getIndex()) + .stream() + .collect(Collectors.toMap( + AnnotationInstance::equivalenceHashCode, + marker -> marker, + (existing, duplicate) -> existing)) + .values(); + + if (!isClassPresentAtRuntime(ABSTRACT_TOKEN_PRODUCER)) { + if (!authenticationMarkers.isEmpty()) { + throw new IllegalStateException( + "OAuth2 flows detected in spec(s) " + + authenticationMarkers.stream() + .map(m -> m.value("openApiSpecId").asString()) + .distinct() + .collect(Collectors.joining(", ")) + + + " but quarkus-openapi-generator-oidc and quarkus-rest-client-oidc-filter or quarkus-oidc-client-reactive-filter are not on the classpath. " + + + "Please add those dependencies to your project. See https://docs.quarkiverse.io/quarkus-openapi-generator/dev/client.html#_oauth2_authentication"); + } + LOGGER.debug("{} class not found in runtime, skipping OAuth bean generation", ABSTRACT_TOKEN_PRODUCER); + return; + } + LOGGER.debug("{} class found in runtime, producing OAuth bean generation", ABSTRACT_TOKEN_PRODUCER); Map> operationsBySpec = getOperationsBySpec(beanArchiveBuildItem); @@ -127,14 +176,9 @@ void produceOauthAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, .annotation(OpenApiSpec.class) .addValue("openApiSpecId", openApiSpecId) .done() - .addInjectionPoint(ClassType.create(OidcClientRequestFilterDelegate.class), - AnnotationInstance.builder(OidcClient.class) - .add("name", sanitizeAuthName(name)) - .build()) - .createWith(recorder.recordOauthAuthProvider( - sanitizeAuthName(name), - openApiSpecId, - operations)) + .addInjectionPoint(ClassType.create(OAuth2AuthenticationProvider.OidcClientRequestFilterDelegate.class), + AnnotationInstance.builder(OidcClient.class).add("name", sanitizeAuthName(name)).build()) + .createWith(oidcRecorder.recordOauthAuthProvider(sanitizeAuthName(name), openApiSpecId, operations)) .unremovable() .done()); } @@ -143,12 +187,17 @@ void produceOauthAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, @BuildStep @Record(ExecutionTime.STATIC_INIT) void produceBasicAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, - BuildProducer authenticationProviders, - BuildProducer beanProducer, + BuildProducer authenticationProviders, BuildProducer beanProducer, AuthenticationRecorder recorder) { Collection authenticationMarkers = beanArchiveBuildItem.getIndex() - .getAnnotationsWithRepeatable(BASIC_AUTHENTICATION_MARKER, beanArchiveBuildItem.getIndex()); + .getAnnotationsWithRepeatable(BASIC_AUTHENTICATION_MARKER, beanArchiveBuildItem.getIndex()) + .stream() + .collect(Collectors.toMap( + AnnotationInstance::equivalenceHashCode, + marker -> marker, + (existing, duplicate) -> existing)) + .values(); Map> operationsBySpec = getOperationsBySpec(beanArchiveBuildItem); for (AnnotationInstance authenticationMarker : authenticationMarkers) { @@ -158,21 +207,17 @@ void produceBasicAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, List operations = getOperations(operationsBySpec, openApiSpecId, name); authenticationProviders.produce(new AuthProviderBuildItem(openApiSpecId, name)); - beanProducer.produce(SyntheticBeanBuildItem.configure(AuthProvider.class) .scope(Dependent.class) .addQualifier() .annotation(AuthName.class) - .addValue("name", name) - .done() + .addValue("name", name).done() .addQualifier() .annotation(OpenApiSpec.class) .addValue("openApiSpecId", openApiSpecId) .done() - .createWith(recorder.recordBasicAuthProvider( - sanitizeAuthName(name), - openApiSpecId, - operations)) + .addInjectionPoint(ClassType.create(DotName.createSimple(CredentialsProvider.class))) + .createWith(recorder.recordBasicAuthProvider(sanitizeAuthName(name), openApiSpecId, operations)) .unremovable() .done()); } @@ -181,12 +226,17 @@ void produceBasicAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, @BuildStep @Record(ExecutionTime.STATIC_INIT) void produceBearerAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, - BuildProducer authenticationProviders, - BuildProducer beanProducer, + BuildProducer authenticationProviders, BuildProducer beanProducer, AuthenticationRecorder recorder) { Collection authenticationMarkers = beanArchiveBuildItem.getIndex() - .getAnnotationsWithRepeatable(BEARER_AUTHENTICATION_MARKER, beanArchiveBuildItem.getIndex()); + .getAnnotationsWithRepeatable(BEARER_AUTHENTICATION_MARKER, beanArchiveBuildItem.getIndex()) + .stream() + .collect(Collectors.toMap( + AnnotationInstance::equivalenceHashCode, + marker -> marker, + (existing, duplicate) -> existing)) + .values(); Map> operationsBySpec = getOperationsBySpec(beanArchiveBuildItem); for (AnnotationInstance authenticationMarker : authenticationMarkers) { @@ -206,11 +256,8 @@ void produceBearerAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, .annotation(OpenApiSpec.class) .addValue("openApiSpecId", openApiSpecId) .done() - .createWith(recorder.recordBearerAuthProvider( - sanitizeAuthName(name), - scheme, - openApiSpecId, - operations)) + .addInjectionPoint(ClassType.create(DotName.createSimple(CredentialsProvider.class))) + .createWith(recorder.recordBearerAuthProvider(sanitizeAuthName(name), scheme, openApiSpecId, operations)) .unremovable() .done()); @@ -220,12 +267,18 @@ void produceBearerAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, @BuildStep @Record(ExecutionTime.STATIC_INIT) void produceApiKeyAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, - BuildProducer authenticationProviders, - BuildProducer beanProducer, + BuildProducer authenticationProviders, BuildProducer beanProducer, AuthenticationRecorder recorder) { Collection authenticationMarkers = beanArchiveBuildItem.getIndex() - .getAnnotationsWithRepeatable(API_KEY_AUTHENTICATION_MARKER, beanArchiveBuildItem.getIndex()); + .getAnnotationsWithRepeatable(API_KEY_AUTHENTICATION_MARKER, beanArchiveBuildItem.getIndex()) + .stream() + .collect(Collectors.toMap( + AnnotationInstance::equivalenceHashCode, + marker -> marker, + (existing, duplicate) -> existing)) + .values(); + Map> operationsBySpec = getOperationsBySpec(beanArchiveBuildItem); for (AnnotationInstance authenticationMarker : authenticationMarkers) { String name = authenticationMarker.value("name").asString(); @@ -236,7 +289,6 @@ void produceApiKeyAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, List operations = getOperations(operationsBySpec, openApiSpecId, name); authenticationProviders.produce(new AuthProviderBuildItem(openApiSpecId, name)); - beanProducer.produce(SyntheticBeanBuildItem.configure(AuthProvider.class) .scope(Dependent.class) .addQualifier() @@ -247,38 +299,12 @@ void produceApiKeyAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, .annotation(OpenApiSpec.class) .addValue("openApiSpecId", openApiSpecId) .done() - .createWith(recorder.recordApiKeyAuthProvider( - sanitizeAuthName(name), - openApiSpecId, - apiKeyIn, - apiKeyName, + .addInjectionPoint(ClassType.create(DotName.createSimple(CredentialsProvider.class))) + .createWith(recorder.recordApiKeyAuthProvider(sanitizeAuthName(name), openApiSpecId, apiKeyIn, apiKeyName, operations)) .unremovable() .done()); } } - - private static String sanitizeAuthName(String schemeName) { - return OpenApiGeneratorConfig.getSanitizedSecuritySchemeName(schemeName); - } - - private static Map> getOperationsBySpec(CombinedIndexBuildItem beanArchiveBuildItem) { - Map> operationsBySpec = beanArchiveBuildItem.getIndex() - .getAnnotationsWithRepeatable(OPERATION_MARKER, beanArchiveBuildItem.getIndex()).stream() - .collect(Collectors.groupingBy( - marker -> marker.value("openApiSpecId").asString() + "_" + marker.value("name").asString())); - return operationsBySpec; - } - - private static List getOperations(Map> operationsBySpec, - String openApiSpecId, String name) { - return operationsBySpec.getOrDefault(openApiSpecId + "_" + name, List.of()).stream() - .map(op -> OperationAuthInfo.builder() - .withPath(op.value("path").asString()) - .withId(op.value("operationId").asString()) - .withMethod(op.value("method").asString()) - .build()) - .collect(Collectors.toList()); - } } diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/GlobalCodegenConfig.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/GlobalCodegenConfig.java index aff927be0..0c9f5888c 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/GlobalCodegenConfig.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/GlobalCodegenConfig.java @@ -2,8 +2,8 @@ import java.util.Optional; -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; +import io.smallrye.config.WithDefault; +import io.smallrye.config.WithName; /* * Model for the configuration of this extension. @@ -12,50 +12,51 @@ * Not meant to be used outside this scope. * Config items can be applied only globally */ -@ConfigGroup -public class GlobalCodegenConfig extends CommonItemConfig { +public interface 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; + @WithDefault("false") + @WithName("verbose") + boolean verbose(); /** * Option to change the directory where OpenAPI files must be found. */ - @ConfigItem(name = "input-base-dir") - public Optional inputBaseDir; + @WithName("input-base-dir") + Optional inputBaseDir(); /** * Option to change the directory where template files must be found. */ - @ConfigItem(name = "template-base-dir") - public Optional templateBaseDir; + @WithName("template-base-dir") + Optional templateBaseDir(); /** * 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; + @WithName("validateSpec") + @WithDefault("true") + boolean validateSpec(); /** * Option to specify files for which generation should be executed only */ - @ConfigItem(name = "include") - public Optional include; + @WithName("include") + Optional include(); /** * Option to exclude file from generation */ - @ConfigItem(name = "exclude") - public Optional exclude; + @WithName("exclude") + Optional exclude(); /** * Create security for the referenced security scheme */ - @ConfigItem(name = "default-security-scheme") - public Optional defaultSecuritySchema; + @WithName("default-security-scheme") + Optional defaultSecuritySchema(); } diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/SpecItemConfig.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/SpecItemConfig.java index 36a42cbb4..8c6652124 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/SpecItemConfig.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/SpecItemConfig.java @@ -2,8 +2,8 @@ import java.util.Optional; -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; +import io.smallrye.config.WithDefault; +import io.smallrye.config.WithName; /* * Model for the configuration of this extension. @@ -12,30 +12,71 @@ * Not meant to be used outside this scope. * Config items can be applied only on spec */ -@ConfigGroup -public class SpecItemConfig extends CommonItemConfig { +public interface SpecItemConfig extends CommonItemConfig { /** * Base package for where the generated code for the given OpenAPI specification will be added. */ - @ConfigItem(name = "base-package") - public Optional basePackage; + @WithName("base-package") + Optional basePackage(); + + /** + * Custom config key to use in place of the openapi spec file + */ + @WithName("config-key") + Optional configKey(); /** * Suffix name for generated api classes */ - @ConfigItem(name = "api-name-suffix") - public Optional apiNameSuffix; + @WithName("api-name-suffix") + Optional apiNameSuffix(); /** * Suffix name for generated model classes */ - @ConfigItem(name = "model-name-suffix") - public Optional modelNameSuffix; + @WithName("model-name-suffix") + Optional modelNameSuffix(); /** * Prefix name for generated model classes */ - @ConfigItem(name = "model-name-prefix") - public Optional modelNamePrefix; + @WithName("model-name-prefix") + Optional modelNamePrefix(); + + /** + * Remove operation id prefix + */ + + @WithName("remove-operation-id-prefix") + Optional removeOperationIdPrefix(); + + /** + * Remove operation id prefix + */ + @WithName("remove-operation-id-prefix-delimiter") + Optional removeOperationIdPrefixDelimiter(); + + /** + * Remove operation id prefix + */ + @WithName("remove-operation-id-prefix-count") + Optional removeOperationIdPrefixCount(); + + /** + * Set serializable model + */ + @WithName("serializable-model") + Optional serializableModel(); + + /** + * Whether to enable Dynamic URLs on APIs methods. + * By enabling this property every method on `RestClients` will be annotated with `io.quarkus.rest.client.reactive.Url`. + * + * @see Dynamic base URLs + */ + @WithName("use-dynamic-url") + @WithDefault("false") + Optional useDynamicUrl(); + } diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiConfigValidator.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiConfigValidator.java index e6cf62dd6..e4c55cade 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiConfigValidator.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiConfigValidator.java @@ -7,7 +7,6 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,7 +18,7 @@ public final class OpenApiConfigValidator { private static final Logger LOGGER = LoggerFactory.getLogger(OpenApiConfigValidator.class); static final Pattern CONFIG_PATTERN = Pattern.compile( - "quarkus\\.openapi-generator\\.codegen\\.(spec.(?\\w*)\\.)?(?[A-Za-z0-9_\\-]*)\\.?(?.+)?"); + "quarkus\\.openapi-generator\\.codegen\\.(spec.(?[\\w\\-]*)\\.)?(?[A-Za-z0-9_\\-]*)\\.?(?.+)?"); private OpenApiConfigValidator() { } @@ -29,7 +28,7 @@ public static void validateInputConfiguration(List configNames) throws C .filter(pn -> pn.startsWith("quarkus.openapi-generator.codegen")) .map(CONFIG_PATTERN::matcher) .filter(Matcher::find) - .collect(Collectors.toList()); + .toList(); if (!userOpenApiConfigurations.isEmpty()) { Set unsupportedConfigNames = new HashSet<>(); diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorCodeGen.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorCodeGen.java new file mode 100644 index 000000000..499304bc9 --- /dev/null +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorCodeGen.java @@ -0,0 +1,13 @@ +package io.quarkiverse.openapi.generator.deployment.codegen; + +public class OpenApiGeneratorCodeGen extends OpenApiGeneratorCodeGenBase { + @Override + public String providerId() { + return OpenApiGeneratorOutputPaths.OPENAPI_PATH; + } + + @Override + public String[] inputExtensions() { + return new String[] { JSON, YAML, YML }; + } +} diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorCodeGenBase.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorCodeGenBase.java index c2b0dd8f0..34ad096ec 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorCodeGenBase.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorCodeGenBase.java @@ -13,12 +13,16 @@ import static io.quarkiverse.openapi.generator.deployment.CodegenConfig.ConfigName.INPUT_BASE_DIR; import static io.quarkiverse.openapi.generator.deployment.CodegenConfig.ConfigName.MODEL_NAME_PREFIX; import static io.quarkiverse.openapi.generator.deployment.CodegenConfig.ConfigName.MODEL_NAME_SUFFIX; +import static io.quarkiverse.openapi.generator.deployment.CodegenConfig.ConfigName.REMOVE_OPERATION_ID_PREFIX; +import static io.quarkiverse.openapi.generator.deployment.CodegenConfig.ConfigName.REMOVE_OPERATION_ID_PREFIX_COUNT; +import static io.quarkiverse.openapi.generator.deployment.CodegenConfig.ConfigName.REMOVE_OPERATION_ID_PREFIX_DELIMITER; import static io.quarkiverse.openapi.generator.deployment.CodegenConfig.ConfigName.TEMPLATE_BASE_DIR; import static io.quarkiverse.openapi.generator.deployment.CodegenConfig.ConfigName.VALIDATE_SPEC; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; @@ -129,7 +133,7 @@ public boolean trigger(CodeGenContext context) throws CodeGenException { .filter(Files::isRegularFile) .filter(path -> { String fileName = path.getFileName().toString(); - return fileName.endsWith(inputExtension()) + return Arrays.stream(this.inputExtensions()).anyMatch(fileName::endsWith) && !filesToExclude.contains(fileName) && (filesToInclude.isEmpty() || filesToInclude.contains(fileName)); }).toList(); @@ -228,6 +232,15 @@ protected void generate(OpenApiGeneratorOptions options) { getModelNamePrefix(config, openApiFilePath) .ifPresent(generator::withModelNamePrefix); + getRemoveOperationIdPrefix(config, openApiFilePath) + .ifPresent(generator::withRemoveOperationIdPrefix); + + getRemoveOperationIdPrefixDelimiter(config, openApiFilePath) + .ifPresent(generator::withRemoveOperationIdPrefixDelimiter); + + getRemoveOperationIdPrefixCount(config, openApiFilePath) + .ifPresent(generator::withRemoveOperationIdPrefixCount); + getValues(config, openApiFilePath, CodegenConfig.ConfigName.MUTINY, Boolean.class) .ifPresent(generator::withMutiny); @@ -288,6 +301,12 @@ protected void generate(OpenApiGeneratorOptions options) { getValues(config, openApiFilePath, CodegenConfig.ConfigName.BEAN_VALIDATION, Boolean.class) .ifPresent(generator::withUseBeanValidation); + getValues(config, openApiFilePath, CodegenConfig.ConfigName.GENERATE_APIS, Boolean.class) + .ifPresent(generator::withGenerateApis); + + getValues(config, openApiFilePath, CodegenConfig.ConfigName.GENERATE_MODELS, Boolean.class) + .ifPresent(generator::withGenerateModels); + SmallRyeConfig smallRyeConfig = config.unwrap(SmallRyeConfig.class); getValues(smallRyeConfig, openApiFilePath, CodegenConfig.ConfigName.TYPE_MAPPINGS, String.class, String.class) @@ -296,6 +315,15 @@ protected void generate(OpenApiGeneratorOptions options) { getValues(smallRyeConfig, openApiFilePath, CodegenConfig.ConfigName.IMPORT_MAPPINGS, String.class, String.class) .ifPresent(generator::withImportMappings); + getValues(smallRyeConfig, openApiFilePath, CodegenConfig.ConfigName.SCHEMA_MAPPINGS, String.class, String.class) + .ifPresent(generator::withSchemaMappings); + + getValues(smallRyeConfig, openApiFilePath, CodegenConfig.ConfigName.SERIALIZABLE_MODEL, Boolean.class) + .ifPresent(generator::withSerialiableModel); + + getValues(smallRyeConfig, openApiFilePath, CodegenConfig.ConfigName.EQUALS_HASHCODE, Boolean.class) + .ifPresent(generator::withEqualsHashcode); + getValues(smallRyeConfig, openApiFilePath, CodegenConfig.ConfigName.NORMALIZER, String.class, String.class) .ifPresent(generator::withOpenApiNormalizer); @@ -307,10 +335,19 @@ protected void generate(OpenApiGeneratorOptions options) { .ifPresent(generator::withMutinyReturnTypes); generator.withAdditionalPropertiesAsAttribute(additionalPropertiesAsAttribute); + + Boolean initialiseEmptyCollection = getValues(smallRyeConfig, openApiFilePath, + CodegenConfig.ConfigName.INITIALIZE_EMPTY_COLLECTIONS, Boolean.class) + .orElse(Boolean.FALSE); + generator.withInitializeEmptyCollections(initialiseEmptyCollection); + GlobalSettings.setProperty( OpenApiClientGeneratorWrapper.SUPPORTS_ADDITIONAL_PROPERTIES_AS_ATTRIBUTE, additionalPropertiesAsAttribute.toString()); + getValues(smallRyeConfig, openApiFilePath, CodegenConfig.ConfigName.USE_DYNAMIC_URL, Boolean.class) + .ifPresent(generator::withUseDynamicUrl); + generator.generate(basePackage); } @@ -350,6 +387,21 @@ private Optional getModelNamePrefix(final Config config, final Path open .getOptionalValue(getSpecConfigName(MODEL_NAME_PREFIX, openApiFilePath), String.class); } + private Optional getRemoveOperationIdPrefix(final Config config, final Path openApiFilePath) { + return config + .getOptionalValue(getSpecConfigName(REMOVE_OPERATION_ID_PREFIX, openApiFilePath), Boolean.class); + } + + private Optional getRemoveOperationIdPrefixDelimiter(final Config config, final Path openApiFilePath) { + return config + .getOptionalValue(getSpecConfigName(REMOVE_OPERATION_ID_PREFIX_DELIMITER, openApiFilePath), String.class); + } + + private Optional getRemoveOperationIdPrefixCount(final Config config, final Path openApiFilePath) { + return config + .getOptionalValue(getSpecConfigName(REMOVE_OPERATION_ID_PREFIX_COUNT, openApiFilePath), Integer.class); + } + private Optional getInputBaseDirRelativeToModule(final Path sourceDir, final Config config) { return config.getOptionalValue(getGlobalConfigName(INPUT_BASE_DIR), String.class).map(baseDir -> { int srcIndex = sourceDir.toString().lastIndexOf("src"); diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorJsonCodeGen.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorJsonCodeGen.java deleted file mode 100644 index f20ac5123..000000000 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorJsonCodeGen.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.quarkiverse.openapi.generator.deployment.codegen; - -public class OpenApiGeneratorJsonCodeGen extends OpenApiGeneratorCodeGenBase { - - @Override - public String providerId() { - return OpenApiGeneratorOutputPaths.JSON_PATH; - } - - @Override - public String inputExtension() { - return JSON; - } -} diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorOutputPaths.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorOutputPaths.java index 0a22d8bda..2bfc71f6a 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorOutputPaths.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorOutputPaths.java @@ -9,9 +9,7 @@ public class OpenApiGeneratorOutputPaths { - public static final String YAML_PATH = "open-api-yaml"; - public static final String YML_PATH = "open-api-yml"; - public static final String JSON_PATH = "open-api-json"; + public static final String OPENAPI_PATH = "open-api"; public static final String STREAM_PATH = "open-api-stream"; private static final Collection rootPaths = Arrays.asList(STREAM_PATH); diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorYamlCodeGen.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorYamlCodeGen.java deleted file mode 100644 index e3be71761..000000000 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorYamlCodeGen.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.quarkiverse.openapi.generator.deployment.codegen; - -public class OpenApiGeneratorYamlCodeGen extends OpenApiGeneratorCodeGenBase { - - @Override - public String providerId() { - return OpenApiGeneratorOutputPaths.YAML_PATH; - } - - @Override - public String inputExtension() { - return YAML; - } -} \ No newline at end of file diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorYmlCodeGen.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorYmlCodeGen.java deleted file mode 100644 index 5c14d8154..000000000 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorYmlCodeGen.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.quarkiverse.openapi.generator.deployment.codegen; - -public class OpenApiGeneratorYmlCodeGen extends OpenApiGeneratorCodeGenBase { - - @Override - public String providerId() { - return OpenApiGeneratorOutputPaths.YML_PATH; - } - - @Override - public String inputExtension() { - return YML; - } -} \ No newline at end of file diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/ExprEvaluator.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/ExprEvaluator.java new file mode 100644 index 000000000..01074a88d --- /dev/null +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/ExprEvaluator.java @@ -0,0 +1,33 @@ +package io.quarkiverse.openapi.generator.deployment.template; + +import java.util.List; +import java.util.concurrent.ExecutionException; + +import io.quarkus.qute.EvalContext; +import io.quarkus.qute.Expression; + +final class ExprEvaluator { + + private ExprEvaluator() { + } + + @SuppressWarnings("unchecked") + public static T evaluate(EvalContext context, Expression expression) throws ExecutionException, InterruptedException { + return (T) context.evaluate(expression).toCompletableFuture().get(); + } + + @SuppressWarnings("unchecked") + public static T[] evaluate(EvalContext context, List expressions, Class type) + throws ExecutionException, InterruptedException { + T[] results = (T[]) java.lang.reflect.Array.newInstance(type, expressions.size()); + + for (int i = 0; i < expressions.size(); i++) { + Expression expression = expressions.get(i); + T result = type.cast(context.evaluate(expression).toCompletableFuture().get()); + results[i] = result; + } + + return results; + } + +} diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/OpenApiNamespaceResolver.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/OpenApiNamespaceResolver.java index 140ed1bfe..7576290dd 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/OpenApiNamespaceResolver.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/OpenApiNamespaceResolver.java @@ -1,11 +1,20 @@ package io.quarkiverse.openapi.generator.deployment.template; import java.io.File; +import java.lang.reflect.Method; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +import org.openapitools.codegen.CodegenSecurity; +import org.openapitools.codegen.model.OperationMap; import io.quarkiverse.openapi.generator.deployment.codegen.OpenApiGeneratorOutputPaths; import io.quarkus.qute.EvalContext; @@ -18,9 +27,8 @@ * implement and use them. */ public class OpenApiNamespaceResolver implements NamespaceResolver { - private static final String GENERATE_DEPRECATED_PROP = "generateDeprecated"; - static final OpenApiNamespaceResolver INSTANCE = new OpenApiNamespaceResolver(); + private static final String GENERATE_DEPRECATED_PROP = "generateDeprecated"; private OpenApiNamespaceResolver() { } @@ -31,6 +39,7 @@ private OpenApiNamespaceResolver() { * @param codegenConfig Map with the model codegen properties * @return true if the given model class should generate the deprecated attributes */ + @SuppressWarnings("unused") public boolean genDeprecatedModelAttr(final String pkg, final String classname, final HashMap codegenConfig) { final String key = String.format("%s.%s.%s", pkg, classname, GENERATE_DEPRECATED_PROP); @@ -43,34 +52,92 @@ public boolean genDeprecatedModelAttr(final String pkg, final String classname, * @param codegenConfig Map with the model codegen properties * @return true if the given model class should generate the deprecated attributes */ + @SuppressWarnings("unused") public boolean genDeprecatedApiAttr(final String pkg, final String classname, final HashMap codegenConfig) { final String key = String.format("%s.%s.%s", pkg, classname, GENERATE_DEPRECATED_PROP); return Boolean.parseBoolean(codegenConfig.getOrDefault(key, "true").toString()); } + @SuppressWarnings("unused") public String parseUri(String uri) { return OpenApiGeneratorOutputPaths.getRelativePath(Path.of(uri)).toString().replace(File.separatorChar, '/'); } + @SuppressWarnings("unused") + public boolean hasAuthMethods(OperationMap operations) { + return operations != null && operations.getOperation().stream().anyMatch(operation -> operation.hasAuthMethods); + } + + /** + * Ignore the OAuth flows by filtering every oauth instance by name. The inner openapi-generator library duplicates the + * OAuth instances per flow in the openapi spec. + * So a specification file with more than one flow defined has two entries in the list. For now, we do not use this + * information in runtime so it can be safely filtered and ignored. + * + * @param oauthOperations passed through the Qute template + * @see "resources/templates/libraries/microprofile/auth/compositeAuthenticationProvider.qute" + * @return The list filtered by unique auth name + */ + @SuppressWarnings("unused") + public List getUniqueOAuthOperations(List oauthOperations) { + if (oauthOperations != null) { + return new ArrayList<>(oauthOperations.stream() + .collect(Collectors.toMap(security -> security.name, security -> security, + (existing, replacement) -> existing, LinkedHashMap::new)) + .values()); + } + return Collections.emptyList(); + } + @Override public CompletionStage resolve(EvalContext context) { try { - Class[] classArgs = new Class[context.getParams().size()]; Object[] args = new Object[context.getParams().size()]; + Class[] classArgs = new Class[context.getParams().size()]; + int i = 0; for (Expression expr : context.getParams()) { args[i] = context.evaluate(expr).toCompletableFuture().get(); classArgs[i] = args[i].getClass(); i++; } - return CompletableFuture - .completedFuture(this.getClass().getMethod(context.getName(), classArgs).invoke(this, args)); + + Method targetMethod = findCompatibleMethod(context.getName(), classArgs); + if (targetMethod == null) { + throw new NoSuchMethodException("No compatible method found for: " + context.getName()); + } + + return CompletableFuture.completedFuture(targetMethod.invoke(this, args)); } catch (ReflectiveOperationException | InterruptedException | ExecutionException ex) { return CompletableFuture.failedStage(ex); } } + private Method findCompatibleMethod(String methodName, Class[] argTypes) { + for (Method method : this.getClass().getMethods()) { + if (method.getName().equals(methodName)) { + Class[] paramTypes = method.getParameterTypes(); + if (isAssignable(paramTypes, argTypes)) { + return method; + } + } + } + return null; + } + + private boolean isAssignable(Class[] paramTypes, Class[] argTypes) { + if (paramTypes.length != argTypes.length) { + return false; + } + for (int i = 0; i < paramTypes.length; i++) { + if (!paramTypes[i].isAssignableFrom(argTypes[i])) { + return false; + } + } + return true; + } + @Override public String getNamespace() { return "openapi"; diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/QuteTemplatingEngineAdapter.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/QuteTemplatingEngineAdapter.java index 5498f8543..651ee91b3 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/QuteTemplatingEngineAdapter.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/QuteTemplatingEngineAdapter.java @@ -1,6 +1,5 @@ package io.quarkiverse.openapi.generator.deployment.template; -import java.io.IOException; import java.util.Map; import org.openapitools.codegen.api.AbstractTemplatingEngineAdapter; @@ -12,13 +11,14 @@ public class QuteTemplatingEngineAdapter extends AbstractTemplatingEngineAdapter { - public static final String IDENTIFIER = "qute"; - public static final String[] INCLUDE_TEMPLATES = { + private static final String IDENTIFIER = "qute"; + private static final String[] DEFAULT_TEMPLATES = { "additionalEnumTypeAnnotations.qute", "additionalEnumTypeUnexpectedMember.qute", "additionalModelTypeAnnotations.qute", "beanValidation.qute", "beanValidationCore.qute", + "beanValidationInlineCore.qute", "beanValidationHeaderParams.qute", "bodyParams.qute", "enumClass.qute", @@ -42,8 +42,9 @@ public QuteTemplatingEngineAdapter() { .addDefaults() .addValueResolver(new ReflectionValueResolver()) .addNamespaceResolver(OpenApiNamespaceResolver.INSTANCE) + .addNamespaceResolver(StrNamespaceResolver.INSTANCE) .removeStandaloneLines(true) - .strictRendering(false) + .strictRendering(true) .build(); } @@ -58,8 +59,7 @@ public String[] getFileExtensions() { } @Override - public String compileTemplate(TemplatingExecutor executor, Map bundle, String templateFile) - throws IOException { + public String compileTemplate(TemplatingExecutor executor, Map bundle, String templateFile) { this.cacheTemplates(executor); Template template = engine.getTemplate(templateFile); if (template == null) { @@ -70,7 +70,7 @@ public String compileTemplate(TemplatingExecutor executor, Map b } public void cacheTemplates(TemplatingExecutor executor) { - for (String templateId : INCLUDE_TEMPLATES) { + for (String templateId : DEFAULT_TEMPLATES) { Template incTemplate = engine.getTemplate(templateId); if (incTemplate == null) { incTemplate = engine.parse(executor.getFullTemplateContents(templateId)); diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/StrNamespaceResolver.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/StrNamespaceResolver.java new file mode 100644 index 000000000..86ee7873b --- /dev/null +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/StrNamespaceResolver.java @@ -0,0 +1,58 @@ +package io.quarkiverse.openapi.generator.deployment.template; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; + +import io.quarkus.qute.EvalContext; +import io.quarkus.qute.NamespaceResolver; + +/** + * Namespace resolver to mimic the function of io.quarkus.qute.runtime.extensions.StringTemplateExtensions. + * This extension is built-in with Qute when used in a context with Quarkus DI. + * Since these extensions are built in build time by Qute we can't use them because our process also runs in build time without + * a CDI context. + * So any namespace resolver auto-generated by Quarkus won't be added to our engine. + * + * @see Template Extension Methods + */ +public class StrNamespaceResolver implements NamespaceResolver { + + static final StrNamespaceResolver INSTANCE = new StrNamespaceResolver(); + + private StrNamespaceResolver() { + } + + @Override + public String getNamespace() { + return "str"; + } + + /** + * @see io.quarkus.qute.runtime.extensions.StringTemplateExtensions#fmt(String, String, Object...) + */ + public String fmt(String format, Object... args) { + return String.format(format, args); + } + + @Override + public CompletionStage resolve(EvalContext context) { + switch (context.getName()) { + case "fmt": + if (context.getParams().size() < 2) { + throw new IllegalArgumentException( + "Missing required parameter for 'fmt'. Make sure that the function has at least two parameters"); + } + try { + return CompletableFuture.completedFuture( + fmt(ExprEvaluator.evaluate(context, context.getParams().get(0)), + ExprEvaluator.evaluate(context, context.getParams().subList(1, context.getParams().size()), + Object.class))); + } catch (Exception e) { + throw new RuntimeException(e); + } + default: + throw new IllegalArgumentException("There's no method named '" + context.getName() + "'"); + } + } + +} diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapper.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapper.java index f7aa635c0..3b1320afb 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapper.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapper.java @@ -21,8 +21,6 @@ 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. @@ -33,17 +31,14 @@ public abstract class OpenApiClientGeneratorWrapper { public static final String VERBOSE = "verbose"; - private static final String ONCE_LOGGER = "org.openapitools.codegen.utils.oncelogger.enabled"; /** * Security scheme for which to apply security constraints even if the OpenAPI definition has no security definition */ public static final String DEFAULT_SECURITY_SCHEME = "defaultSecurityScheme"; public static final String SUPPORTS_ADDITIONAL_PROPERTIES_AS_ATTRIBUTE = "supportsAdditionalPropertiesWithComposedSchema"; - private static final Map defaultTypeMappings = Map.of( - "date", "LocalDate", - "DateTime", "OffsetDateTime"); - private static final Map defaultImportMappings = Map.of( - "LocalDate", "java.time.LocalDate", + private static final String ONCE_LOGGER = "org.openapitools.codegen.utils.oncelogger.enabled"; + private static final Map defaultTypeMappings = Map.of("date", "LocalDate", "DateTime", "OffsetDateTime"); + private static final Map defaultImportMappings = Map.of("LocalDate", "java.time.LocalDate", "OffsetDateTime", "java.time.OffsetDateTime"); private final QuarkusCodegenConfigurator configurator; private final DefaultGenerator generator; @@ -53,8 +48,8 @@ public abstract class OpenApiClientGeneratorWrapper { private String modelPackage = ""; OpenApiClientGeneratorWrapper(final QuarkusCodegenConfigurator configurator, final Path specFilePath, final Path outputDir, - final boolean verbose, - final boolean validateSpec) { + final boolean verbose, final boolean validateSpec) { + // do not generate docs nor tests GlobalSettings.setProperty(CodegenConstants.API_DOCS, FALSE.toString()); GlobalSettings.setProperty(CodegenConstants.API_TESTS, FALSE.toString()); @@ -78,17 +73,46 @@ public abstract class OpenApiClientGeneratorWrapper { defaultTypeMappings.forEach(this.configurator::addTypeMapping); defaultImportMappings.forEach(this.configurator::addImportMapping); - this.generator = new DefaultGenerator(); - } + this.setDefaults(); - public OpenApiClientGeneratorWrapper withApiPackage(final String pkg) { - this.apiPackage = pkg; - return this; + this.generator = new DefaultGenerator(); } - public OpenApiClientGeneratorWrapper withModelPackage(final String pkg) { - this.modelPackage = pkg; - return this; + /** + * A few properties from the "with*" methods must be injected in the Qute context by default since we turned strict model + * rendering. + * This way we avoid side effects in the model such as "NOT_FOUND" strings printed everywhere. + * + * @see Qute - Configuration Reference + */ + private void setDefaults() { + // Set default values directly here + this.configurator.addAdditionalProperty("additionalApiTypeAnnotations", new String[0]); + this.configurator.addAdditionalProperty("additionalPropertiesAsAttribute", FALSE); + this.configurator.addAdditionalProperty("initializeEmptyCollections", FALSE); + this.configurator.addAdditionalProperty("additionalEnumTypeUnexpectedMember", FALSE); + this.configurator.addAdditionalProperty("additionalEnumTypeUnexpectedMemberName", ""); + this.configurator.addAdditionalProperty("additionalEnumTypeUnexpectedMemberStringValue", ""); + this.configurator.addAdditionalProperty("additionalRequestArgs", new String[0]); + this.configurator.addAdditionalProperty("classes-codegen", new HashMap<>()); + this.configurator.addAdditionalProperty("circuit-breaker", new HashMap<>()); + this.configurator.addAdditionalProperty("configKey", ""); + this.configurator.addAdditionalProperty("datatypeWithEnum", ""); + this.configurator.addAdditionalProperty("enable-security-generation", TRUE); + this.configurator.addAdditionalProperty("generate-part-filename", FALSE); + this.configurator.addAdditionalProperty("mutiny", FALSE); + this.configurator.addAdditionalProperty("mutiny-operation-ids", new HashMap<>()); + this.configurator.addAdditionalProperty("mutiny-return-response", FALSE); + this.configurator.addAdditionalProperty("part-filename-value", ""); + this.configurator.addAdditionalProperty("return-response", FALSE); + this.configurator.addAdditionalProperty("skipFormModel", TRUE); + this.configurator.addAdditionalProperty("templateDir", ""); + this.configurator.addAdditionalProperty("use-bean-validation", FALSE); + this.configurator.addAdditionalProperty("use-field-name-in-part-filename", FALSE); + this.configurator.addAdditionalProperty("verbose", FALSE); + this.configurator.addAdditionalProperty(CodegenConstants.SERIALIZABLE_MODEL, FALSE); + this.configurator.addAdditionalProperty("equals-hashcode", TRUE); + this.configurator.addAdditionalProperty("use-dynamic-url", FALSE); } /** @@ -98,30 +122,30 @@ public OpenApiClientGeneratorWrapper withModelPackage(final String pkg) { * @return this wrapper */ public OpenApiClientGeneratorWrapper withCircuitBreakerConfig(final Map> config) { - if (config != null) { - configurator.addAdditionalProperty("circuit-breaker", config); - } + Optional.ofNullable(config).ifPresent(cfg -> { + this.configurator.addAdditionalProperty("circuit-breaker", config); + }); return this; } public OpenApiClientGeneratorWrapper withClassesCodeGenConfig(final Map config) { - if (config != null) { - configurator.addAdditionalProperty("classes-codegen", config); - } + Optional.ofNullable(config).ifPresent(cfg -> { + this.configurator.addAdditionalProperty("classes-codegen", cfg); + }); return this; } public OpenApiClientGeneratorWrapper withMutiny(final Boolean config) { - if (config != null) { - configurator.addAdditionalProperty("mutiny", config); - } + Optional.ofNullable(config).ifPresent(cfg -> { + this.configurator.addAdditionalProperty("mutiny", cfg); + }); return this; } public OpenApiClientGeneratorWrapper withMutinyReturnResponse(final Boolean config) { - if (config != null) { - configurator.addAdditionalProperty("mutiny-return-response", config); - } + Optional.ofNullable(config).ifPresent(cfg -> { + this.configurator.addAdditionalProperty("mutiny-return-response", cfg); + }); return this; } @@ -164,11 +188,26 @@ public OpenApiClientGeneratorWrapper withImportMappings(final Map typeMappings) { + typeMappings.forEach(configurator::addSchemaMapping); + return this; + } + public OpenApiClientGeneratorWrapper withOpenApiNormalizer(final Map openApiNormalizer) { configurator.setOpenapiNormalizer(openApiNormalizer); return this; } + public OpenApiClientGeneratorWrapper withSerialiableModel(final Boolean serialiableModel) { + this.configurator.addAdditionalProperty(CodegenConstants.SERIALIZABLE_MODEL, serialiableModel); + return this; + } + + public OpenApiClientGeneratorWrapper withEqualsHashcode(final Boolean equalsHashcode) { + this.configurator.addAdditionalProperty("equals-hashcode", equalsHashcode); + return this; + } + /** * Sets the global 'additionalModelTypeAnnotations' setting. If not set this setting will default to empty. * @@ -209,16 +248,16 @@ public OpenApiClientGeneratorWrapper withAdditionalEnumTypeUnexpectedMemberStrin * @return this wrapper */ public OpenApiClientGeneratorWrapper withAdditionalApiTypeAnnotationsConfig(final String additionalApiTypeAnnotations) { - if (additionalApiTypeAnnotations != null) { + Optional.ofNullable(additionalApiTypeAnnotations).ifPresent(cfg -> { this.configurator.addAdditionalProperty("additionalApiTypeAnnotations", additionalApiTypeAnnotations.split(";")); - } + }); return this; } public OpenApiClientGeneratorWrapper withAdditionalRequestArgs(final String additionalRequestArgs) { - if (additionalRequestArgs != null) { + Optional.ofNullable(additionalRequestArgs).ifPresent(cfg -> { this.configurator.addAdditionalProperty("additionalRequestArgs", additionalRequestArgs.split(";")); - } + }); return this; } @@ -246,6 +285,16 @@ public OpenApiClientGeneratorWrapper withUseBeanValidation(Boolean config) { return this; } + public OpenApiClientGeneratorWrapper withGenerateApis(Boolean config) { + configurator.addAdditionalProperty("generate-apis", config); + return this; + } + + public OpenApiClientGeneratorWrapper withGenerateModels(Boolean config) { + configurator.addAdditionalProperty("generate-models", config); + return this; + } + public OpenApiClientGeneratorWrapper withApiNameSuffix(final String apiNameSuffix) { this.configurator.setApiNameSuffix(apiNameSuffix); return this; @@ -256,11 +305,37 @@ public OpenApiClientGeneratorWrapper withModelNameSuffix(final String modelNameS return this; } + public OpenApiClientGeneratorWrapper withRemoveOperationIdPrefix(final Boolean removeOperationIdPrefix) { + this.configurator.setRemoveOperationIdPrefix(removeOperationIdPrefix); + return this; + } + + public OpenApiClientGeneratorWrapper withRemoveOperationIdPrefixDelimiter(final String removeOperationIdPrefixDelimiter) { + this.configurator.addAdditionalProperty("removeOperationIdPrefixDelimiter", removeOperationIdPrefixDelimiter); + return this; + } + + public OpenApiClientGeneratorWrapper withRemoveOperationIdPrefixCount(final Integer removeOperationIdPrefixCount) { + this.configurator.addAdditionalProperty("removeOperationIdPrefixCount", removeOperationIdPrefixCount); + return this; + } + public OpenApiClientGeneratorWrapper withModelNamePrefix(final String modelNamePrefix) { this.configurator.setModelNamePrefix(modelNamePrefix); return this; } + public OpenApiClientGeneratorWrapper withUseDynamicUrl(final Boolean useDynamicUrl) { + this.configurator.addAdditionalProperty("use-dynamic-url", useDynamicUrl); + return this; + } + + /** + * Main entrypoint, or where to generate the files based on the given base package. + * + * @param basePackage Java package name, e.g. org.acme + * @return a list of generated files + */ public List generate(final String basePackage) { this.basePackage = basePackage; this.consolidatePackageNames(); @@ -286,11 +361,19 @@ private void consolidatePackageNames() { public void withConfigKey(final String config) { if (config != null && !config.isBlank()) { - this.configurator.addAdditionalProperty("configKey", StringUtil.replaceNonAlphanumericByUnderscores(config)); + this.configurator.addAdditionalProperty("configKey", transformToSafeConfigKey(config)); } } public void withAdditionalPropertiesAsAttribute(final Boolean enable) { this.configurator.addAdditionalProperty("additionalPropertiesAsAttribute", Optional.ofNullable(enable).orElse(FALSE)); } + + public void withInitializeEmptyCollections(final Boolean enable) { + this.configurator.addAdditionalProperty("initializeEmptyCollections", Optional.ofNullable(enable).orElse(FALSE)); + } + + public static String transformToSafeConfigKey(String input) { + return input.replaceAll("[^a-zA-Z0-9\\-_]", "_"); + } } diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/QuarkusJavaClientCodegen.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/QuarkusJavaClientCodegen.java index 97d87d1a1..cfe51cde9 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/QuarkusJavaClientCodegen.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/QuarkusJavaClientCodegen.java @@ -70,6 +70,8 @@ private void replaceWithQuarkusTemplateFiles() { supportingFiles.clear(); Boolean enableSecurityGeneration = (Boolean) this.additionalProperties.get("enable-security-generation"); + Boolean generateApis = (Boolean) this.additionalProperties.getOrDefault("generate-apis", Boolean.TRUE); + Boolean generateModels = (Boolean) this.additionalProperties.getOrDefault("generate-models", Boolean.TRUE); if (enableSecurityGeneration == null || enableSecurityGeneration) { if (ProcessUtils.hasHttpBasicMethods(this.openAPI) || @@ -92,10 +94,14 @@ private void replaceWithQuarkusTemplateFiles() { } apiTemplateFiles.clear(); - apiTemplateFiles.put("api.qute", ".java"); + if (generateApis) { + apiTemplateFiles.put("api.qute", ".java"); + } modelTemplateFiles.clear(); - modelTemplateFiles.put("model.qute", ".java"); + if (generateModels) { + modelTemplateFiles.put("model.qute", ".java"); + } } public String authFileFolder() { diff --git a/client/deployment/src/main/resources/META-INF/services/io.quarkus.deployment.CodeGenProvider b/client/deployment/src/main/resources/META-INF/services/io.quarkus.deployment.CodeGenProvider index 075e856f5..da12e914a 100644 --- a/client/deployment/src/main/resources/META-INF/services/io.quarkus.deployment.CodeGenProvider +++ b/client/deployment/src/main/resources/META-INF/services/io.quarkus.deployment.CodeGenProvider @@ -1,4 +1,2 @@ -io.quarkiverse.openapi.generator.deployment.codegen.OpenApiGeneratorJsonCodeGen -io.quarkiverse.openapi.generator.deployment.codegen.OpenApiGeneratorYamlCodeGen -io.quarkiverse.openapi.generator.deployment.codegen.OpenApiGeneratorYmlCodeGen +io.quarkiverse.openapi.generator.deployment.codegen.OpenApiGeneratorCodeGen io.quarkiverse.openapi.generator.deployment.codegen.OpenApiGeneratorStreamCodeGen \ No newline at end of file diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/additionalEnumTypeUnexpectedMember.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/additionalEnumTypeUnexpectedMember.qute index d130f7d41..9aa8cf918 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/additionalEnumTypeUnexpectedMember.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/additionalEnumTypeUnexpectedMember.qute @@ -2,4 +2,4 @@ * Special value if the API response contains some new value not declared in this enum. * You should react accordingly. */ -{additionalEnumTypeUnexpectedMemberName}({#if e.isContainer}{e.items.dataType}{#else}{e.dataType}{/if}.valueOf("{additionalEnumTypeUnexpectedMemberStringValue}")){#if e.allowableValues},{/if} +{additionalEnumTypeUnexpectedMemberName}({#if e.isContainer.or(false)}{e.items.dataType}{#else}{e.dataType}{/if}.valueOf("{additionalEnumTypeUnexpectedMemberStringValue}")){#if e.allowableValues},{/if} diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/api.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/api.qute index 738a52b08..7b4ddc519 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/api.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/api.qute @@ -17,10 +17,10 @@ import {imp.import}; * {#if appDescription}

{appDescription}

{/if} */ {/if} -@jakarta.ws.rs.Path("{#if useAnnotatedBasePath}{contextPath}{/if}{commonPath}") -@org.eclipse.microprofile.rest.client.inject.RegisterRestClient({#if defaultServerUrl}baseUri="{defaultServerUrl}",{/if} configKey="{configKey}") +@jakarta.ws.rs.Path("{#if useAnnotatedBasePath.or(false)}{contextPath}{/if}{commonPath}") +@org.eclipse.microprofile.rest.client.inject.RegisterRestClient({#if defaultServerUrl.or('') != ''}baseUri="{defaultServerUrl}", {/if}configKey="{configKey}") @io.quarkiverse.openapi.generator.annotations.GeneratedClass(value="{openapi:parseUri(inputSpec)}", tag = "{baseName}") -{#if enable-security-generation && hasAuthMethods} +{#if enable-security-generation && openapi:hasAuthMethods(operations) } @org.eclipse.microprofile.rest.client.annotation.RegisterProvider({package}.auth.CompositeAuthenticationProvider.class) @org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders({package}.auth.AuthenticationPropagationHeadersFactory.class) {/if} @@ -43,7 +43,7 @@ public interface {classname} { @io.quarkiverse.openapi.generator.markers.OperationMarker(name="{defaultSecurityScheme}", openApiSpecId="{quarkus-generator.openApiSpecId}", operationId="{op.operationId}", method="{op.httpMethod}", path="{contextPath}{commonPath}{op.path.orEmpty}") {/if} @jakarta.ws.rs.{op.httpMethod} - {#if op.subresourceOperation} + {#if op.subresourceOperation || op.path eq '/'} @jakarta.ws.rs.Path("{op.path}") {/if} {#if op.hasConsumes} @@ -53,7 +53,7 @@ public interface {classname} { @jakarta.ws.rs.Produces(\{{#for produce in op.produces}"{produce.mediaType}"{#if produce_hasNext}, {/if}{/for}\}) {/if} @io.quarkiverse.openapi.generator.annotations.GeneratedMethod("{op.operationIdOriginal}") - {#for cbClassConfig in circuit-breaker.orEmpty}{#if cbClassConfig.key == package + classname} + {#for cbClassConfig in circuit-breaker.orEmpty}{#if cbClassConfig.key == str:fmt("%s.%s", package, classname)} {#for cbMethod in cbClassConfig.value.orEmpty}{#if cbMethod == op.nickname} @org.eclipse.microprofile.faulttolerance.CircuitBreaker {/if}{/for} @@ -126,7 +126,11 @@ public interface {classname} { {#if additionalRequestArgs} {#for arg in additionalRequestArgs}{! !}{arg}{#if arg_hasNext}, {/if}{/for}{! - !}{#if op.hasFormParams || op.allParams},{/if} + !}{#if op.allParams || op.hasFormParams},{/if} + {/if} + {#if is-resteasy-reactive && use-dynamic-url} + // See https://quarkus.io/version/3.20/guides/rest-client#dynamic-base-urls + @io.quarkus.rest.client.reactive.Url String dynUrl{#if op.allParams || op.hasFormParams},{/if} {/if} {#if op.hasFormParams} {#if is-resteasy-reactive} @@ -135,21 +139,21 @@ public interface {classname} { @org.jboss.resteasy.annotations.providers.multipart.MultipartForm {op.operationIdCamelCase}MultipartForm multipartForm{#if op.hasPathParams},{/if} {/if} {#if use-bean-validation} - {#for p in op.pathParams}@jakarta.validation.Valid {#include pathParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasQueryParams},{/if} - {#for p in op.queryParams}@jakarta.validation.Valid {#include queryParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasCookieParams},{/if} - {#for p in op.cookieParams}@jakarta.validation.Valid {#include cookieParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasHeaderParams},{/if} - {#for p in op.headerParams}@jakarta.validation.Valid {#include headerParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParams}, - {#for p in op.bodyParams}@jakarta.validation.Valid {#include bodyParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{/if} + {#for p in op.pathParams}{#include beanValidationInlineCore.qute param=p/}{#include pathParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasQueryParams},{/if} + {#for p in op.queryParams}{#include beanValidationInlineCore.qute param=p/}{#include queryParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasCookieParams},{/if} + {#for p in op.cookieParams}{#include beanValidationInlineCore.qute param=p/}{#include cookieParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasHeaderParams},{/if} + {#for p in op.headerParams}{#include beanValidationInlineCore.qute param=p/}{#include headerParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParam}, + {#for p in op.bodyParams}{#include beanValidationInlineCore.qute param=p/}{#include bodyParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{/if} {#else} {#for p in op.pathParams}{#include pathParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasQueryParams},{/if} {#for p in op.queryParams}{#include queryParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasCookieParams},{/if} {#for p in op.cookieParams}{#include cookieParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasHeaderParams},{/if} - {#for p in op.headerParams}{#include headerParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParams},{/if} + {#for p in op.headerParams}{#include headerParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParam},{/if} {#for p in op.bodyParams}{#include bodyParams.qute param=p/}{#if p_hasNext}, {/if}{/for} {/if} {#else} {#for p in op.allParams} - {#if use-bean-validation}@jakarta.validation.Valid {/if}{! + {#if use-bean-validation}{#include beanValidationInlineCore.qute param=p/}{/if}{! !}{#include pathParams.qute param=p/}{#include queryParams.qute param=p/}{#include bodyParams.qute param=p/}{#include headerParams.qute param=p/}{#include cookieParams.qute param=p/}{#if p_hasNext}, {/if} {/for}{/if} ); diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/auth/compositeAuthenticationProvider.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/auth/compositeAuthenticationProvider.qute index c1fd07985..297ce5f34 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/auth/compositeAuthenticationProvider.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/auth/compositeAuthenticationProvider.qute @@ -1,8 +1,8 @@ package {apiPackage}.auth; @jakarta.annotation.Priority(jakarta.ws.rs.Priorities.AUTHENTICATION) -{#for auth in oauthMethods.orEmpty} -@io.quarkiverse.openapi.generator.markers.OauthAuthenticationMarker(name="{auth.name}", openApiSpecId="{configKey}") +{#for auth in openapi:getUniqueOAuthOperations(oauthMethods.orEmpty)} +@io.quarkiverse.openapi.generator.markers.OauthAuthenticationMarker(name="{auth.name}", openApiSpecId="{quarkus-generator.openApiSpecId}") {/for} {#for auth in httpBasicMethods.orEmpty} @io.quarkiverse.openapi.generator.markers.BasicAuthenticationMarker(name="{auth.name}", openApiSpecId="{quarkus-generator.openApiSpecId}") @@ -22,8 +22,8 @@ package {apiPackage}.auth; public class CompositeAuthenticationProvider implements jakarta.ws.rs.client.ClientRequestFilter { @jakarta.inject.Inject - @io.quarkiverse.openapi.generator.OpenApiSpec(openApiSpecId="{configKey}") - io.quarkiverse.openapi.generator.providers.CompositeAuthenticationProvider compositeProvider; + @io.quarkiverse.openapi.generator.OpenApiSpec(openApiSpecId="{quarkus-generator.openApiSpecId}") + io.quarkiverse.openapi.generator.providers.BaseCompositeAuthenticationProvider compositeProvider; @java.lang.Override public void filter(jakarta.ws.rs.client.ClientRequestContext context) throws java.io.IOException { diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/auth/headersFactory.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/auth/headersFactory.qute index 0fc3f221c..ddebaffec 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/auth/headersFactory.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/auth/headersFactory.qute @@ -3,7 +3,7 @@ package {apiPackage}.auth; public class AuthenticationPropagationHeadersFactory extends io.quarkiverse.openapi.generator.providers.AbstractAuthenticationPropagationHeadersFactory { @jakarta.inject.Inject - public AuthenticationPropagationHeadersFactory(@io.quarkiverse.openapi.generator.OpenApiSpec(openApiSpecId="{configKey}") io.quarkiverse.openapi.generator.providers.CompositeAuthenticationProvider compositeProvider, io.quarkiverse.openapi.generator.OpenApiGeneratorConfig generatorConfig, io.quarkiverse.openapi.generator.providers.HeadersProvider headersProvider) { + public AuthenticationPropagationHeadersFactory(@io.quarkiverse.openapi.generator.OpenApiSpec(openApiSpecId="{quarkus-generator.openApiSpecId}") io.quarkiverse.openapi.generator.providers.BaseCompositeAuthenticationProvider compositeProvider, io.quarkiverse.openapi.generator.OpenApiGeneratorConfig generatorConfig, io.quarkiverse.openapi.generator.providers.HeadersProvider headersProvider) { super(compositeProvider, generatorConfig, headersProvider); } diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/beanValidationCore.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/beanValidationCore.qute index ede24017c..5c7b032e6 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/beanValidationCore.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/beanValidationCore.qute @@ -33,4 +33,7 @@ {#if p.maximum} @jakarta.validation.constraints.DecimalMax("{p.maximum}") {/if} + {/if} + {#if use-bean-validation} + @jakarta.validation.Valid {/if} \ No newline at end of file diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/beanValidationInlineCore.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/beanValidationInlineCore.qute new file mode 100644 index 000000000..accba8091 --- /dev/null +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/beanValidationInlineCore.qute @@ -0,0 +1 @@ +{#if p.required}@jakarta.validation.constraints.NotNull {/if}{#if p.pattern}@jakarta.validation.constraints.Pattern(regexp = "{p.pattern}") {/if}{#if p.minLength || p.minItems}@jakarta.validation.constraints.Size(min = {p.minLength}{p.minItems}) {/if}{#if p.maxLength || p.maxItems}@jakarta.validation.constraints.Size(max = {p.maxLength}{p.maxItems}) {/if}{#if p.isInteger}{#if p.minimum}@jakarta.validation.constraints.Min({p.minimum}) {/if}{#if p.maximum}@jakarta.validation.constraints.Max({p.maximum}) {/if}{/if}{#if p.isLong}{#if p.minimum}@jakarta.validation.constraints.Min({p.minimum}L) {/if}{#if p.maximum}@jakarta.validation.constraints.Max({p.maximum}L) {/if}{/if}{#if !p.isInteger && !p.isLong}{#if p.minimum}@jakarta.validation.constraints.DecimalMin("{p.minimum}") {/if}{#if p.maximum}@jakarta.validation.constraints.DecimalMax("{p.maximum}") {/if}{/if}{#if use-bean-validation}@jakarta.validation.Valid {/if} \ No newline at end of file diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/enumClass.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/enumClass.qute index 75a41699b..45f921047 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/enumClass.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/enumClass.qute @@ -1,16 +1,8 @@ -{#if e.withXml} - @jakarta.xml.bind.annotation.XmlType(name={#if e.isEnum}"{e.items.enumName}"{#else}"{e.enumName}"{/if}) - @akarta.xml.bind.annotation.XmlEnum({#if e.isEnum}{e.items.dataType}{#else}{e.dataType}{/if}.class) -{/if} {#include additionalEnumTypeAnnotations.qute e=e /}public enum {e.enumName} { {#if e.allowableValues} {#if additionalEnumTypeUnexpectedMember}{#include additionalEnumTypeUnexpectedMember.qute e=e/}{/if} - {#if e.withXml} - {#for v in e.allowableValues.enumVars}@XmlEnumValue({#if v.isInteger || v.isDouble || v.isLong || v.isFloat}"{/if}{v.value}{#if v.isInteger || v.isDouble || v.isLong || v.isFloat}"{/if}) {v.name}({#if e.isEnum}{e.items.dataType}{#else}{e.dataType}{/if}.valueOf({v.value})){#if v_hasNext}, {#else}; {/if}{/for} - {#else} {#for v in e.allowableValues.enumVars}{v.name}({#if eq e.isNumeric}{v.value}{#else}{#if e.isContainer}{e.items.dataType}{#else}{e.dataType}{/if}.valueOf({v.value}){/if}){#if v_hasNext}, {#else};{/if}{/for} {/if} - {/if} // caching enum access private static final java.util.EnumSet<{e.enumName}> values = java.util.EnumSet.allOf({e.enumName}.class); @@ -38,6 +30,6 @@ return b; } } - {#if e.useNullForUnknownEnumValue}return null;{#else if additionalEnumTypeUnexpectedMember}return {additionalEnumTypeUnexpectedMemberName};{#else}throw new IllegalArgumentException("Unexpected value '" + v + "'");{/if} + {#if e.isNullable}return null;{#else if additionalEnumTypeUnexpectedMember}return {additionalEnumTypeUnexpectedMemberName};{#else}throw new IllegalArgumentException("Unexpected value '" + v + "'");{/if} } } diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/enumOuterClass.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/enumOuterClass.qute index c47e1944a..d5f1dbecb 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/enumOuterClass.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/enumOuterClass.qute @@ -1,25 +1,25 @@ /** - * {#insert e.description}Gets or Sets {e.name}{/}{#if e.description}{description}{/} + * {#insert e.description}Gets or Sets {e.name}{/}{#if e.description}{e.description}{/} */ @com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown = true) -{#include additionalEnumTypeAnnotations.qute e=e/}public enum {#if e.datatypeWithEnum}{e.datatypeWithEnum}{#else}{e.classname}{/if} { +{#include additionalEnumTypeAnnotations.qute e=e/}public enum {#if e.datatypeWithEnum.or(false)}{e.datatypeWithEnum}{#else}{e.classname}{/if} { {#if e.allowableValues} {#if additionalEnumTypeUnexpectedMember}{#include additionalEnumTypeUnexpectedMember.qute e=e/}{/if} {#for v in e.allowableValues.enumVars}{v.name}({e.dataType}.valueOf({v.value})){#if v_hasNext}, {#else};{/if}{/for} {/if} // caching enum access - private static final java.util.EnumSet<{#if e.datatypeWithEnum}{e.datatypeWithEnum}{#else}{e.classname}{/if}> values = java.util.EnumSet.allOf({#if e.datatypeWithEnum}{e.datatypeWithEnum}{#else}{e.classname}{/if}.class); + private static final java.util.EnumSet<{#if e.datatypeWithEnum.or(false)}{e.datatypeWithEnum}{#else}{e.classname}{/if}> values = java.util.EnumSet.allOf({#if e.datatypeWithEnum.or(false)}{e.datatypeWithEnum}{#else}{e.classname}{/if}.class); private {e.dataType} value; - {#if e.datatypeWithEnum}{e.datatypeWithEnum}{#else}{e.classname}{/if}({e.dataType} value){ + {#if e.datatypeWithEnum.or(false)}{e.datatypeWithEnum}{#else}{e.classname}{/if}({e.dataType} value){ this.value = value; } @com.fasterxml.jackson.annotation.JsonValue - public {#if e.isContainer}{e.items.dataType}{#else}{e.dataType}{/if} value() { + public {#if e.isContainer.or(false)}{e.items.dataType}{#else}{e.dataType}{/if} value() { return value; } @@ -29,12 +29,12 @@ } @com.fasterxml.jackson.annotation.JsonCreator - public static {#if e.datatypeWithEnum}{e.datatypeWithEnum}{#else}{e.classname}{/if} fromString(String text) { - for ({#if e.datatypeWithEnum}{e.datatypeWithEnum}{#else}{e.classname}{/if} b : values) { + public static {#if e.datatypeWithEnum.or(false)}{e.datatypeWithEnum}{#else}{e.classname}{/if} fromString(String text) { + for ({#if e.datatypeWithEnum.or(false)}{e.datatypeWithEnum}{#else}{e.classname}{/if} b : values) { if (String.valueOf(b.value).equalsIgnoreCase(text)) { return b; } } - {#if e.useNullForUnknownEnumValue}return null;{#else if additionalEnumTypeUnexpectedMember}return {additionalEnumTypeUnexpectedMemberName};{#else}throw new IllegalArgumentException("Unexpected value '" + text + "'");{/if} + {#if e.isNullable}return null;{#else if additionalEnumTypeUnexpectedMember}return {additionalEnumTypeUnexpectedMemberName};{#else}throw new IllegalArgumentException("Unexpected value '" + text + "'");{/if} } } diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/model.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/model.qute index 7b266fdc1..ab9917758 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/model.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/model.qute @@ -2,6 +2,7 @@ package {package}; {#if use-bean-validation} {! https://github.com/OpenAPITools/openapi-generator/issues/18974 !} +import jakarta.validation.constraints.*; import jakarta.validation.Valid; {/if} @@ -17,5 +18,5 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; {#for m in models} {#if m.model.isEnum}{#include enumOuterClass.qute e=m.model/} -{#else}{#include pojo.qute m=m.model withXml=withXml codegen=classes-codegen package=modelPackage/}{/if} +{#else}{#include pojo.qute m=m.model codegen=classes-codegen package=modelPackage/}{/if} {/for} \ No newline at end of file diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/pojo.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/pojo.qute index a8662e818..ef43ddd2b 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/pojo.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/pojo.qute @@ -1,35 +1,27 @@ {@org.openapitools.codegen.CodegenModel m} -{#if withXml} -{#else} import com.fasterxml.jackson.annotation.JsonProperty; -{/if} {#if m.description} /** * {m.description} **/ {/if} -{#if withXml} -@jakarta.xml.bind.annotation.XmlAccessorType(jakarta.xml.bind.annotation.XmlAccessType.FIELD) -{#if m.hasVars}@jakarta.xml.bind.annotation.XmlType(name = "{m.classname}", propOrder = - { {#for var in m.vars}"{var.name}"{#if var_hasNext}, {/if}{/for} -}){#else} -@jakarta.xml.bind.annotation.XmlType(name = "{m.classname}") -{/if} -{#if !m.parent || m.parent.isEmpty}@jakarta.xml.bind.annotation.XmlRootElement(name = "{m.classname}"){/if} -{#else} -@com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown = true) -{/if} {#include additionalModelTypeAnnotations.qute m=m/} -{#if m.discriminator && m.discriminator.mappedModels && !m.discriminator.mappedModels.empty} -@com.fasterxml.jackson.annotation.JsonTypeInfo(use = com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME, include = com.fasterxml.jackson.annotation.JsonTypeInfo.As.EXISTING_PROPERTY, property = "{m.discriminator.propertyBaseName}") +{#if m.discriminator && m.discriminator.mappedModels && !m.discriminator.mappedModels.empty && m.children} +@com.fasterxml.jackson.annotation.JsonIgnoreProperties( + value = "{m.discriminator.propertyBaseName}", // ignore manually set {m.discriminator.propertyBaseName}, it will be automatically generated by Jackson during serialization + allowSetters = true // allows the {m.discriminator.propertyBaseName} to be set during deserialization +) +@com.fasterxml.jackson.annotation.JsonTypeInfo(use = com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME, include = com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY, property = "{m.discriminator.propertyBaseName}", visible = true) @com.fasterxml.jackson.annotation.JsonSubTypes({ {#for child in m.discriminator.mappedModels} - @com.fasterxml.jackson.annotation.JsonSubTypes.Type(value = {child.model.classname}.class, name = "{child.mappingName}"), + @com.fasterxml.jackson.annotation.JsonSubTypes.Type(value = {child.modelName}.class, name = "{#if m.vendorExtensions.x-discriminator-value.or('') == ''}{child.mappingName}{#else}{m.vendorExtensions.x-discriminator-value}{/if}"), {/for} }) +{#else} +@com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown = true) {/if} -public class {m.classname} {#if m.parent}extends {m.parent}{/if}{#if m.serializableModel} implements java.io.Serializable{/if} { +public class {m.classname} {#if m.parent}extends {m.parent}{/if}{#if serializableModel} implements java.io.Serializable{/if} { {#for v in m.vars} {#if !v.deprecated || openapi:genDeprecatedModelAttr(package, m.classname, codegen)} @@ -40,9 +32,6 @@ public class {m.classname} {#if m.parent}extends {m.parent}{/if}{#if m.serializa {#include enumClass.qute e=v/}{/if} {/if} - {#if withXml} - @jakarta.xml.bind.annotation.XmlElement(name="{v.basename}"{#if v.required}, required = {v.required}{/if}) - {/if} {#if v.description} /** * {v.description} @@ -50,7 +39,7 @@ public class {m.classname} {#if m.parent}extends {m.parent}{/if}{#if m.serializa {/if} {#if v.hasValidation || v.required}{#include beanValidation.qute p=v/}{/if} {#if v.isContainer} - private {v.datatypeWithEnum} {v.name}{#if v.required && v.defaultValue} = {v.defaultValue}{/if}; + private {v.datatypeWithEnum} {v.name}{#if v.required && v.defaultValue} = {v.defaultValue}{#else if v.isArray && initializeEmptyCollections} = new {#if v.uniqueItems}LinkedHashSet{/if}{#if !v.uniqueItems}ArrayList{/if}<>(){#else if v.isMap && initializeEmptyCollections} = new HashMap<>(){/if}; {#else} private {v.datatypeWithEnum} {v.name}{#if v.defaultValue} = {v.defaultValue}{/if}; {/if} @@ -73,12 +62,10 @@ public class {m.classname} {#if m.parent}extends {m.parent}{/if}{#if m.serializa {/if} * @return {v.name} **/ - {#if !withXml} @JsonProperty("{v.baseName}") {#if !v.required} @com.fasterxml.jackson.annotation.JsonInclude(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL) {/if} - {/if} {#for ext in v.vendorExtensions.x-extra-annotation.orEmpty} {ext} {/for} @@ -116,11 +103,11 @@ public class {m.classname} {#if m.parent}extends {m.parent}{/if}{#if m.serializa } {/if} {#if v.isMap} - public {m.classname} put{v.nameInPascalCase}Item(String key, {v.items.datatypeWithEnum} {v.name}Item) { + public {m.classname} put{v.nameInPascalCase}Item(String mapKey, {v.items.datatypeWithEnum} {v.name}Item) { if (this.{v.name} == null){ {v.name} = {#if v.defaultValue}{v.defaultValue}{#else}new HashMap<>(){/if}; } - this.{v.name}.put(key, {v.name}Item); + this.{v.name}.put(mapKey, {v.name}Item); return this; } {/if} @@ -128,7 +115,7 @@ public class {m.classname} {#if m.parent}extends {m.parent}{/if}{#if m.serializa {/if} {/for} - {#include pojoAdditionalProperties.qute m=m.model additionalPropertiesAsAttribute=additionalPropertiesAsAttribute /} + {#include pojoAdditionalProperties.qute m=m additionalPropertiesAsAttribute=additionalPropertiesAsAttribute /} /** * Create a string representation of this pojo. **/ @@ -148,6 +135,68 @@ public class {m.classname} {#if m.parent}extends {m.parent}{/if}{#if m.serializa return sb.toString(); } + {#if equals-hashcode} + {#if m.vars.size > 0} + /** + * Compares this object to the specified object. The result is + * \{@code true\} if and only if the argument is not + * \{@code null\} and is a \{@code {m.classname}\} object that + * contains the same values as this object. + * + * @param obj the object to compare with. + * @return \{@code true\} if the objects are the same; + * \{@code false\} otherwise. + **/ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + + {m.classname} model = ({m.classname}) obj; + + {#if m.vars.size == 1} + return java.util.Objects.equals({m.vars.0.name}, model.{m.vars.0.name}); + {#else} + {#for v in m.vars} + {#if !v.deprecated || openapi:genDeprecatedModelAttr(package, m.classname, codegen)} + {#if v_isFirst} + return java.util.Objects.equals({v.name}, model.{v.name}) && + {#else if v_isLast} + java.util.Objects.equals({v.name}, model.{v.name}); + {#else} + java.util.Objects.equals({v.name}, model.{v.name}) && + {/if} + {/if} + {/for} + {/if} + } + + /** + * Returns a hash code for a \{@code {m.classname}\}. + * + * @return a hash code value for a \{@code {m.classname}\}. + **/ + @Override + public int hashCode() { + {#if m.vars.size == 1} + return java.util.Objects.hash({m.vars.0.name}); + {#else} + {#for v in m.vars} + {#if !v.deprecated || openapi:genDeprecatedModelAttr(package, m.classname, codegen)} + {#if v_isFirst} + return java.util.Objects.hash({v.name}, + {#else if v_isLast} + {v.name}); + {#else} + {v.name}, + {/if} + {/if} + {/for} + {/if} + } + {/if} + {/if} + /** * Convert the given object to string with each line indented by 4 spaces * (except the first line). @@ -159,5 +208,5 @@ public class {m.classname} {#if m.parent}extends {m.parent}{/if}{#if m.serializa return o.toString().replace("\n", "\n "); } - {#include pojoQueryParam.qute m=m.model withXml=withXml codegen=classes-codegen package=modelPackage/} + {#include pojoQueryParam.qute m=m codegen=classes-codegen package=modelPackage/} } diff --git a/client/deployment/src/main/resources/templates/libraries/microprofile/pojoQueryParam.qute b/client/deployment/src/main/resources/templates/libraries/microprofile/pojoQueryParam.qute index 26ef23f25..91612c3c5 100644 --- a/client/deployment/src/main/resources/templates/libraries/microprofile/pojoQueryParam.qute +++ b/client/deployment/src/main/resources/templates/libraries/microprofile/pojoQueryParam.qute @@ -3,19 +3,9 @@ * {m.description} **/ {/if} - {#if withXml} - @jakarta.xml.bind.annotation.XmlAccessorType(jakarta.xml.bind.annotation.XmlType.XmlAccessType.FIELD) - {#if m.hasVars}@jakarta.xml.bind.annotation.XmlType(name = "{m.classname}", propOrder = - { {#for var in m.vars}"{var.name}"{#if var_hasNext}, {/if}{/for} - }){#else} - @jakarta.xml.bind.annotation.XmlType(name = "{m.classname}") - {/if} - {#if !m.parent || m.parent.isEmpty}@jakarta.xml.bind.annotation.XmlRootElement(name = "{m.classname}"){/if} - {#else} @com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown = true) - {/if} {#include additionalModelTypeAnnotations.qute m=m/} - public static class {m.classname}QueryParam {#if m.parent}extends {m.parent}{/if}{#if m.serializableModel} implements java.io.Serializable{/if} { + public static class {m.classname}QueryParam {#if m.parent}extends {m.parent}{/if}{#if serializableModel} implements java.io.Serializable{/if} { {#for v in m.vars} {#if !v.deprecated || openapi:genDeprecatedModelAttr(package, m.classname, codegen)} @@ -26,9 +16,6 @@ {#include enumClass.qute e=v/}{/if} {/if} - {#if withXml} - @jakarta.xml.bind.annotation.XmlType.XmlElement(name="{v.basename}"{#if v.required}, required = {v.required}{/if}) - {/if} {#if m.description} /** * {m.description} @@ -41,7 +28,7 @@ {#include beanValidation.qute p=v/} {/if} {#if v.isContainer} - private {v.datatypeWithEnum} {v.name}{#if v.required&&v.defaultValue} = {v.defaultValue}{#else} = null{/if}; + private {v.datatypeWithEnum} {v.name}{#if v.required && v.defaultValue} = {v.defaultValue}{#else} = null{/if}; {#else} private {v.datatypeWithEnum} {v.name}{#if v.defaultValue} = {v.defaultValue}{/if}; {/if} @@ -64,9 +51,7 @@ {/if} * @return {v.name} **/ - {#if !withXml} @com.fasterxml.jackson.annotation.JsonProperty("{v.baseName}") - {/if} {#for ext in v.vendorExtensions.x-extra-annotation.orEmpty} {ext} {/for} @@ -104,8 +89,8 @@ } {/if} {#if v.isMap} - public {m.classname}QueryParam put{v.nameInPascalCase}Item(String key, {v.items.datatypeWithEnum} {v.name}Item) { - this.{v.name}.put(key, {v.name}Item); + public {m.classname}QueryParam put{v.nameInPascalCase}Item(String mapKey, {v.items.datatypeWithEnum} {v.name}Item) { + this.{v.name}.put(mapKey, {v.name}Item); return this; } {/if} diff --git a/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/OpenApiConfigValidatorTest.java b/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/OpenApiConfigValidatorTest.java index 9fb6c26e2..daec221de 100644 --- a/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/OpenApiConfigValidatorTest.java +++ b/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/OpenApiConfigValidatorTest.java @@ -18,7 +18,7 @@ class OpenApiConfigValidatorTest { "quarkus.openapi-generator.codegen.additional-api-type-annotations", "quarkus.openapi-generator.codegen.spec.spec_yaml.enable-security-generation", "quarkus.openapi-generator.codegen.type-mappings.UUID=String", - "quarkus.openapi-generator.codegen.spec.spec_yaml.type-mappings.UUID=String" + "quarkus.openapi-generator.codegen.spec.spec_yaml.type-mappings.UUID=String", }) void test_known_configs_ok(String validConfiguration) { assertThatCode(() -> OpenApiConfigValidator.validateInputConfiguration(List.of(validConfiguration))) diff --git a/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/authentication/OpenApiSpecProviderTest.java b/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/authentication/OpenApiSpecProviderTest.java index cf3e03c4f..579b2a32c 100644 --- a/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/authentication/OpenApiSpecProviderTest.java +++ b/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/authentication/OpenApiSpecProviderTest.java @@ -17,11 +17,11 @@ import io.quarkiverse.openapi.generator.markers.ApiKeyAuthenticationMarker; import io.quarkiverse.openapi.generator.markers.BasicAuthenticationMarker; import io.quarkiverse.openapi.generator.markers.OauthAuthenticationMarker; +import io.quarkiverse.openapi.generator.oidc.providers.OAuth2AuthenticationProvider; import io.quarkiverse.openapi.generator.providers.ApiKeyAuthenticationProvider; import io.quarkiverse.openapi.generator.providers.AuthProvider; +import io.quarkiverse.openapi.generator.providers.BaseCompositeAuthenticationProvider; import io.quarkiverse.openapi.generator.providers.BasicAuthenticationProvider; -import io.quarkiverse.openapi.generator.providers.CompositeAuthenticationProvider; -import io.quarkiverse.openapi.generator.providers.OAuth2AuthenticationProvider; import io.quarkus.test.QuarkusUnitTest; public class OpenApiSpecProviderTest { @@ -35,24 +35,25 @@ public class OpenApiSpecProviderTest { quarkus.oidc-client.oauth_auth.auth-server-url=localhost quarkus.oidc-client.oauth_auth1.auth-server-url=localhost quarkus.oidc-client.oauth_auth2.auth-server-url=localhost + quarkus.keycloak.devservices.enabled=false """), "application.properties")); @Inject @OpenApiSpec(openApiSpecId = "spec_1") - CompositeAuthenticationProvider spec1CompositeProvider; + BaseCompositeAuthenticationProvider spec1CompositeProvider; @Inject @OpenApiSpec(openApiSpecId = "spec_2") - CompositeAuthenticationProvider spec2CompositeProvider; + BaseCompositeAuthenticationProvider spec2CompositeProvider; @Inject @OpenApiSpec(openApiSpecId = "spec_3") - CompositeAuthenticationProvider spec3CompositeProvider; + BaseCompositeAuthenticationProvider spec3CompositeProvider; @Inject @OpenApiSpec(openApiSpecId = "spec_multi") - CompositeAuthenticationProvider multiCompositeProvider; + BaseCompositeAuthenticationProvider multiCompositeProvider; @Test public void checkCompositeProvider() { diff --git a/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/authentication/OperationTest.java b/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/authentication/OperationTest.java index ea2f7e4fa..05abba0a3 100644 --- a/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/authentication/OperationTest.java +++ b/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/authentication/OperationTest.java @@ -20,7 +20,7 @@ import io.quarkiverse.openapi.generator.markers.BasicAuthenticationMarker; import io.quarkiverse.openapi.generator.markers.OauthAuthenticationMarker; import io.quarkiverse.openapi.generator.markers.OperationMarker; -import io.quarkiverse.openapi.generator.providers.CompositeAuthenticationProvider; +import io.quarkiverse.openapi.generator.providers.BaseCompositeAuthenticationProvider; import io.quarkiverse.openapi.generator.providers.OperationAuthInfo; import io.quarkus.test.QuarkusUnitTest; @@ -32,15 +32,18 @@ public class OperationTest { .addClass(PetApi.class) .addClass(LocalAuthenticationProvider.class) .addAsResource( - new StringAsset("quarkus.oidc-client.oauth_auth.auth-server-url=localhost\n"), + new StringAsset(""" + quarkus.oidc-client.oauth_auth.auth-server-url=localhost + quarkus.keycloak.devservices.enabled=false + """), "application.properties")); @Inject @OpenApiSpec(openApiSpecId = "petstore_json") - CompositeAuthenticationProvider compositeProvider; + BaseCompositeAuthenticationProvider compositeProvider; @Inject @OpenApiSpec(openApiSpecId = "other_spec_json") - CompositeAuthenticationProvider otherProvider; + BaseCompositeAuthenticationProvider otherProvider; @Test public void test() { diff --git a/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapperTest.java b/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapperTest.java index 2a80f9ec2..6fde9d9ac 100644 --- a/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapperTest.java +++ b/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapperTest.java @@ -33,8 +33,9 @@ import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.body.VariableDeclarator; import com.github.javaparser.ast.expr.AnnotationExpr; +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.MemberValuePair; import com.github.javaparser.ast.expr.SimpleName; -import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr; import com.github.javaparser.ast.nodeTypes.NodeWithName; import com.github.javaparser.ast.type.Type; @@ -45,6 +46,90 @@ public class OpenApiClientGeneratorWrapperTest { + private static Optional getMethodDeclarationByIdentifier(List methodDeclarations, + String methodName) { + return methodDeclarations.stream().filter(md -> md.getName().getIdentifier().equals(methodName)).findAny(); + } + + @Test + void verifyOAuthDuplicateAnnotationOnCompositeAuthProvider() throws URISyntaxException, FileNotFoundException { + OpenApiClientGeneratorWrapper generatorWrapper = createGeneratorWrapper("issue-933-security.yaml"); + final List generatedFiles = generatorWrapper.generate("org.issue933"); + + assertNotNull(generatedFiles); + assertFalse(generatedFiles.isEmpty()); + + final Optional authProviderFile = generatedFiles.stream() + .filter(f -> f.getName().endsWith("CompositeAuthenticationProvider.java")).findFirst(); + assertThat(authProviderFile).isPresent(); + + CompilationUnit compilationUnit = StaticJavaParser.parse(authProviderFile.orElseThrow()); + // Get the class declaration + ClassOrInterfaceDeclaration classDeclaration = compilationUnit.findFirst(ClassOrInterfaceDeclaration.class) + .orElseThrow(() -> new AssertionError("Class not found in the file")); + + // Collect all OauthAuthenticationMarker annotations + long oauthAnnotationsCount = classDeclaration + .getAnnotations().stream().filter( + annotation -> annotation.getNameAsString() + .equals("io.quarkiverse.openapi.generator.markers.OauthAuthenticationMarker")) + .filter(Expression::isNormalAnnotationExpr) + .filter(annotation -> annotation + .findFirst(MemberValuePair.class, + pair -> pair.getNameAsString().equals("name") && pair.getValue().toString().equals("\"oauth\"")) + .isPresent()) + .count(); + + // Assert that there's exactly one annotation with name=oauth + assertThat(oauthAnnotationsCount).isEqualTo(1); + } + + /** + * If the specification component has `oneOf` specified instead of `allOf`, the inner generator won't create a hierarchy of + * classes. + * In this situation, we can't generate the `JsonSubTypes` annotations. + */ + @Test + void verifyOneOfDiscriminatorGeneration() throws java.net.URISyntaxException, FileNotFoundException { + OpenApiClientGeneratorWrapper generatorWrapper = createGeneratorWrapper("issue-852.json"); + final List generatedFiles = generatorWrapper.generate("org.issue852"); + + assertNotNull(generatedFiles); + assertFalse(generatedFiles.isEmpty()); + + final Optional classWithDiscriminator = generatedFiles.stream() + .filter(f -> f.getName().endsWith("PostRevisionForDocumentRequest.java")).findFirst(); + assertThat(classWithDiscriminator).isPresent(); + + final CompilationUnit compilationUnit = StaticJavaParser.parse(classWithDiscriminator.orElseThrow()); + assertThat(compilationUnit.findFirst(ClassOrInterfaceDeclaration.class) + .flatMap(first -> first.getAnnotationByClass(com.fasterxml.jackson.annotation.JsonSubTypes.class))) + .isNotPresent(); + } + + /** + * Only generates `JsonSubTypes` annotations in case the class has children, otherwise skip since Jackson will complain in + * runtime. + * The file issue-1022.json is a classic example of a spec with allOf to denote how the Java POJO should be and how Jackson + * would serialize. + */ + @Test + void verifyAllOfDiscriminatorGeneration() throws java.net.URISyntaxException, FileNotFoundException { + OpenApiClientGeneratorWrapper generatorWrapper = createGeneratorWrapper("issue-1022.json"); + final List generatedFiles = generatorWrapper.generate("org.issue1022"); + + assertNotNull(generatedFiles); + assertFalse(generatedFiles.isEmpty()); + + final Optional classWithDiscriminator = generatedFiles.stream().filter(f -> f.getName().startsWith("Thing.java")) + .findFirst(); + assertThat(classWithDiscriminator).isPresent(); + + final CompilationUnit compilationUnit = StaticJavaParser.parse(classWithDiscriminator.orElseThrow()); + assertThat(compilationUnit.findFirst(ClassOrInterfaceDeclaration.class) + .flatMap(first -> first.getAnnotationByClass(com.fasterxml.jackson.annotation.JsonSubTypes.class))).isPresent(); + } + @Test void verifyFlink() throws URISyntaxException, FileNotFoundException { OpenApiClientGeneratorWrapper generatorWrapper = createGeneratorWrapper("issue-flink.yaml"); @@ -61,8 +146,7 @@ void verifyFlink() throws URISyntaxException, FileNotFoundException { assertThat(vars).isNotEmpty(); // This openApi file has a duplicated field - assertThat(vars.stream() - .filter(v -> "failureCause".equals(v.getNameAsString())).count()).isEqualTo(4); + assertThat(vars.stream().filter(v -> "failureCause".equals(v.getNameAsString())).count()).isEqualTo(4); } @Test @@ -117,8 +201,7 @@ void verifyAuthBasicWithMissingSecurityDefinition(String defaultSecurityScheme) List methodDeclarations = compilationUnit.findAll(MethodDeclaration.class); assertThat(methodDeclarations).isNotEmpty(); - Optional initMethod = methodDeclarations.stream() - .filter(m -> m.getNameAsString().equals("filter")) + Optional initMethod = methodDeclarations.stream().filter(m -> m.getNameAsString().equals("filter")) .findAny(); assertThat(initMethod).isPresent(); @@ -135,17 +218,14 @@ void verifyAuthBearerGenerated() throws URISyntaxException { @Test void verifyEnumGeneration() throws URISyntaxException, FileNotFoundException { - final List generatedFiles = createGeneratorWrapper("issue-28.yaml") - .generate("org.issue28"); + final List generatedFiles = createGeneratorWrapper("issue-28.yaml").generate("org.issue28"); final Optional enumFile = generatedFiles.stream() .filter(f -> f.getName().endsWith("ConnectorNamespaceState.java")).findFirst(); assertThat(enumFile).isPresent(); final CompilationUnit cu = StaticJavaParser.parse(enumFile.orElseThrow()); final List constants = cu.findAll(EnumConstantDeclaration.class); - assertThat(constants) - .hasSize(3) - .extracting(EnumConstantDeclaration::getNameAsString) + assertThat(constants).hasSize(3).extracting(EnumConstantDeclaration::getNameAsString) .containsExactlyInAnyOrder("DISCONNECTED", "READY", "DELETING"); } @@ -153,23 +233,20 @@ void verifyEnumGeneration() throws URISyntaxException, FileNotFoundException { void verifyDeprecatedFields() throws URISyntaxException, FileNotFoundException { final Map codegenConfig = ClassCodegenConfigParser .parse(MockConfigUtils.getTestConfig("/codegen/application.properties"), "org.issue38"); - final List generatedFiles = this.createGeneratorWrapper("issue-38.yaml") - .withClassesCodeGenConfig(codegenConfig) + final List generatedFiles = this.createGeneratorWrapper("issue-38.yaml").withClassesCodeGenConfig(codegenConfig) .generate("org.issue38"); // we have two attributes that will be generated with the same name, one of them is deprecated - final Optional metaV1Condition = generatedFiles.stream() - .filter(f -> f.getName().endsWith("MetaV1Condition.java")).findFirst(); + final Optional metaV1Condition = generatedFiles.stream().filter(f -> f.getName().endsWith("MetaV1Condition.java")) + .findFirst(); assertThat(metaV1Condition).isPresent(); final CompilationUnit cu = StaticJavaParser.parse(metaV1Condition.orElseThrow()); final List fields = cu.findAll(FieldDeclaration.class); assertThat(fields).extracting(FieldDeclaration::getVariables).hasSize(10); - assertThat(fields.stream() - .flatMap(v -> v.getVariables().stream()) - .anyMatch(f -> f.getNameAsString().equals("lastTransitionTime"))) - .isTrue(); + assertThat(fields.stream().flatMap(v -> v.getVariables().stream()) + .anyMatch(f -> f.getNameAsString().equals("lastTransitionTime"))).isTrue(); // this one we optionally removed the deprecated attribute final Optional connectorDeploymentSpec = generatedFiles.stream() @@ -178,10 +255,8 @@ void verifyDeprecatedFields() throws URISyntaxException, FileNotFoundException { final CompilationUnit cu2 = StaticJavaParser.parse(connectorDeploymentSpec.orElseThrow()); final List fields2 = cu2.findAll(FieldDeclaration.class); - assertThat(fields2.stream() - .flatMap(v -> v.getVariables().stream()) - .anyMatch(f -> f.getNameAsString().equals("allowUpgrade"))) - .isFalse(); + assertThat(fields2.stream().flatMap(v -> v.getVariables().stream()) + .anyMatch(f -> f.getNameAsString().equals("allowUpgrade"))).isFalse(); // this class has a deprecated attribute, so we check the default behavior final Optional connectorDeploymentStatus = generatedFiles.stream() @@ -190,24 +265,19 @@ void verifyDeprecatedFields() throws URISyntaxException, FileNotFoundException { final CompilationUnit cu3 = StaticJavaParser.parse(connectorDeploymentStatus.orElseThrow()); final List fields3 = cu3.findAll(FieldDeclaration.class); - assertThat(fields3.stream() - .flatMap(v -> v.getVariables().stream()) - .anyMatch(f -> f.getNameAsString().equals("availableUpgrades"))) - .isTrue(); + assertThat(fields3.stream().flatMap(v -> v.getVariables().stream()) + .anyMatch(f -> f.getNameAsString().equals("availableUpgrades"))).isTrue(); } @Test void verifyDeprecatedOperations() throws URISyntaxException, FileNotFoundException { final Map codegenConfig = ClassCodegenConfigParser .parse(MockConfigUtils.getTestConfig("/deprecated/application.properties"), "org.deprecated"); - List generatedFiles = this.createGeneratorWrapper("deprecated.json") - .withClassesCodeGenConfig(codegenConfig) + List generatedFiles = this.createGeneratorWrapper("deprecated.json").withClassesCodeGenConfig(codegenConfig) .generate("org.deprecated"); assertFalse(generatedFiles.isEmpty()); - Optional file = generatedFiles.stream() - .filter(f -> f.getName().endsWith("DefaultApi.java")) - .findAny(); + Optional file = generatedFiles.stream().filter(f -> f.getName().endsWith("DefaultApi.java")).findAny(); assertThat(file).isNotEmpty(); @@ -223,14 +293,12 @@ void verifyDeprecatedOperations() throws URISyntaxException, FileNotFoundExcepti // hello operation is NOT deprecated and should be generated Optional helloMethod = methodDeclarations.stream() - .filter(m -> m.getNameAsString().equals("helloGet")) - .findAny(); + .filter(m -> m.getNameAsString().equals("helloGet")).findAny(); assertThat(helloMethod).isNotEmpty(); // bye operation is deprecated and should NOT be generated - Optional byeMethod = methodDeclarations.stream() - .filter(m -> m.getNameAsString().equals("byeGet")) + Optional byeMethod = methodDeclarations.stream().filter(m -> m.getNameAsString().equals("byeGet")) .findAny(); assertThat(byeMethod).isEmpty(); @@ -243,9 +311,7 @@ void checkAnnotations() throws URISyntaxException, FileNotFoundException { assertNotNull(restClientFiles); assertFalse(restClientFiles.isEmpty()); - Optional file = restClientFiles.stream() - .filter(f -> f.getName().endsWith("DefaultApi.java")) - .findAny(); + Optional file = restClientFiles.stream().filter(f -> f.getName().endsWith("DefaultApi.java")).findAny(); assertThat(file).isNotEmpty(); @@ -272,30 +338,23 @@ void checkAnnotations() throws URISyntaxException, FileNotFoundException { }); Optional byeMethod = methodDeclarations.stream() - .filter(m -> m.getNameAsString().equals(byeMethodGet)) - .findAny(); + .filter(m -> m.getNameAsString().equals(byeMethodGet)).findAny(); assertThat(byeMethod).isNotEmpty(); - assertThat(byeMethod.orElseThrow()) - .hasCircuitBreakerAnnotation() - .doesNotHaveAnyCircuitBreakerAttribute(); + assertThat(byeMethod.orElseThrow()).hasCircuitBreakerAnnotation().doesNotHaveAnyCircuitBreakerAttribute(); - methodDeclarations.stream() - .filter(m -> !m.getNameAsString().equals(byeMethodGet)) + methodDeclarations.stream().filter(m -> !m.getNameAsString().equals(byeMethodGet)) .forEach(m -> assertThat(m).doesNotHaveCircuitBreakerAnnotation()); } @Test void verifyMultipartFormAnnotationIsGeneratedForParameter() throws URISyntaxException, FileNotFoundException { - List generatedFiles = createGeneratorWrapper("multipart-openapi.yml") - .withSkipFormModelConfig("false") + List generatedFiles = createGeneratorWrapper("multipart-openapi.yml").withSkipFormModelConfig("false") .generate("org.acme"); assertThat(generatedFiles).isNotEmpty(); - Optional file = generatedFiles.stream() - .filter(f -> f.getName().endsWith("UserProfileDataApi.java")) - .findAny(); + Optional file = generatedFiles.stream().filter(f -> f.getName().endsWith("UserProfileDataApi.java")).findAny(); assertThat(file).isPresent(); CompilationUnit compilationUnit = StaticJavaParser.parse(file.orElseThrow()); @@ -303,8 +362,7 @@ void verifyMultipartFormAnnotationIsGeneratedForParameter() throws URISyntaxExce assertThat(methodDeclarations).isNotEmpty(); Optional multipartPostMethod = methodDeclarations.stream() - .filter(m -> m.getNameAsString().equals("postUserProfileData")) - .findAny(); + .filter(m -> m.getNameAsString().equals("postUserProfileData")).findAny(); assertThat(multipartPostMethod).isPresent(); List parameters = multipartPostMethod.orElseThrow().getParameters(); @@ -316,21 +374,16 @@ void verifyMultipartFormAnnotationIsGeneratedForParameter() throws URISyntaxExce @Test void verifyMultipartPojoGeneratedAndFieldsHaveAnnotations() throws URISyntaxException, FileNotFoundException { - List generatedFiles = createGeneratorWrapper("multipart-openapi.yml") - .withSkipFormModelConfig("false") + List generatedFiles = createGeneratorWrapper("multipart-openapi.yml").withSkipFormModelConfig("false") .generate("org.acme"); assertFalse(generatedFiles.isEmpty()); - Optional file = generatedFiles.stream() - .filter(f -> f.getName().endsWith("UserProfileDataApi.java")) - .findAny(); + Optional file = generatedFiles.stream().filter(f -> f.getName().endsWith("UserProfileDataApi.java")).findAny(); assertThat(file).isNotEmpty(); CompilationUnit compilationUnit = StaticJavaParser.parse(file.orElseThrow()); Optional multipartPojo = compilationUnit.findAll(ClassOrInterfaceDeclaration.class) - .stream() - .filter(c -> c.getNameAsString().equals("PostUserProfileDataMultipartForm")) - .findAny(); + .stream().filter(c -> c.getNameAsString().equals("PostUserProfileDataMultipartForm")).findAny(); assertThat(multipartPojo).isNotEmpty(); List fields = multipartPojo.orElseThrow().getFields(); @@ -347,20 +400,15 @@ void verifyMultipartPojoGeneratedAndFieldsHaveAnnotations() throws URISyntaxExce @Test void shouldMapFileTypeToFullyQualifiedInputStream() throws URISyntaxException, FileNotFoundException { List generatedFiles = createGeneratorWrapper("multipart-openapi.yml") - .withTypeMappings(Map.of("File", "java.io.InputStream")) - .generate("org.acme"); + .withTypeMappings(Map.of("File", "java.io.InputStream")).generate("org.acme"); assertFalse(generatedFiles.isEmpty()); - Optional file = generatedFiles.stream() - .filter(f -> f.getName().endsWith("UserProfileDataApi.java")) - .findAny(); + Optional file = generatedFiles.stream().filter(f -> f.getName().endsWith("UserProfileDataApi.java")).findAny(); assertThat(file).isNotEmpty(); CompilationUnit compilationUnit = StaticJavaParser.parse(file.orElseThrow()); Optional multipartPojo = compilationUnit.findAll(ClassOrInterfaceDeclaration.class) - .stream() - .filter(c -> c.getNameAsString().equals("PostUserProfileDataMultipartForm")) - .findAny(); + .stream().filter(c -> c.getNameAsString().equals("PostUserProfileDataMultipartForm")).findAny(); assertThat(multipartPojo).isNotEmpty(); Optional fileUploadVariable = findVariableByName(multipartPojo.orElseThrow().getFields(), @@ -370,76 +418,55 @@ void shouldMapFileTypeToFullyQualifiedInputStream() throws URISyntaxException, F @Test void shouldReplaceFileImportWithInputStream() throws URISyntaxException, FileNotFoundException { - List generatedFiles = createGeneratorWrapper("multipart-openapi.yml") - .withSkipFormModelConfig("false") - .withTypeMappings(Map.of("File", "InputStream")) - .withImportMappings(Map.of("File", "java.io.InputStream")) + List generatedFiles = createGeneratorWrapper("multipart-openapi.yml").withSkipFormModelConfig("false") + .withTypeMappings(Map.of("File", "InputStream")).withImportMappings(Map.of("File", "java.io.InputStream")) .generate("org.acme"); assertFalse(generatedFiles.isEmpty()); - Optional file = generatedFiles.stream() - .filter(f -> f.getName().endsWith("UserProfileDataApi.java")) - .findAny(); + Optional file = generatedFiles.stream().filter(f -> f.getName().endsWith("UserProfileDataApi.java")).findAny(); assertThat(file).isNotEmpty(); CompilationUnit compilationUnit = StaticJavaParser.parse(file.orElseThrow()); Optional multipartPojo = compilationUnit.findAll(ClassOrInterfaceDeclaration.class) - .stream() - .filter(c -> c.getNameAsString().equals("PostUserProfileDataMultipartForm")) - .findAny(); + .stream().filter(c -> c.getNameAsString().equals("PostUserProfileDataMultipartForm")).findAny(); assertThat(multipartPojo).isNotEmpty(); Optional fileUploadVariable = findVariableByName(multipartPojo.orElseThrow().getFields(), "profileImage"); assertThat(fileUploadVariable.orElseThrow().getType().asString()).isEqualTo("InputStream"); - List imports = compilationUnit.findAll(ImportDeclaration.class) - .stream() - .map(importDeclaration -> importDeclaration.getName().asString()) - .collect(Collectors.toList()); - assertThat(imports).contains("java.io.InputStream") - .doesNotContain("java.io.File"); + List imports = compilationUnit.findAll(ImportDeclaration.class).stream() + .map(importDeclaration -> importDeclaration.getName().asString()).collect(Collectors.toList()); + assertThat(imports).contains("java.io.InputStream").doesNotContain("java.io.File"); } @Test void withoutAnyTypeOrImportMappingsItShouldGenerateUsingJava8DatesAndTimes() throws URISyntaxException, FileNotFoundException { - List generatedFiles = createGeneratorWrapper("datetime-regression.yml") - .generate("org.datetime.regression"); + List generatedFiles = createGeneratorWrapper("datetime-regression.yml").generate("org.datetime.regression"); - Optional file = generatedFiles.stream() - .filter(f -> f.getName().endsWith("SomeName.java")) - .findAny(); + Optional file = generatedFiles.stream().filter(f -> f.getName().endsWith("SomeName.java")).findAny(); assertThat(file).isNotEmpty(); CompilationUnit compilationUnit = StaticJavaParser.parse(file.orElseThrow()); List classes = compilationUnit.findAll(ClassOrInterfaceDeclaration.class); assertThat(classes).hasSize(2); ClassOrInterfaceDeclaration generatedPojoClass = classes.get(0); - verifyGeneratedDateAndTimeTypes( - generatedPojoClass, - Map.of( - "someDate", "LocalDate", - "someDateTime", "OffsetDateTime", - "dateArray", "List", - "dateTimeArray", "List", - "dateSet", "Set", - "dateTimeSet", "Set", - "dateMap", "Map", - "dateTimeMap", "Map")); - assertThat(compilationUnit.getImports().stream().map(NodeWithName::getNameAsString)) - .contains("java.time.LocalDate", "java.time.OffsetDateTime"); + verifyGeneratedDateAndTimeTypes(generatedPojoClass, + Map.of("someDate", "LocalDate", "someDateTime", "OffsetDateTime", "dateArray", "List", + "dateTimeArray", "List", "dateSet", "Set", "dateTimeSet", + "Set", "dateMap", "Map", "dateTimeMap", + "Map")); + assertThat(compilationUnit.getImports().stream().map(NodeWithName::getNameAsString)).contains("java.time.LocalDate", + "java.time.OffsetDateTime"); } @Test void shouldBeAbleToEnableMutiny() throws URISyntaxException, FileNotFoundException { - List generatedFiles = createGeneratorWrapper("simple-openapi.json") - .withMutiny(true) + List generatedFiles = createGeneratorWrapper("simple-openapi.json").withMutiny(true) .generate("org.mutiny.enabled"); - Optional file = generatedFiles.stream() - .filter(f -> f.getName().endsWith("DefaultApi.java")) - .findAny(); + Optional file = generatedFiles.stream().filter(f -> f.getName().endsWith("DefaultApi.java")).findAny(); assertThat(file).isNotEmpty(); CompilationUnit compilationUnit = StaticJavaParser.parse(file.orElseThrow()); List methodDeclarations = compilationUnit.findAll(MethodDeclaration.class); @@ -455,14 +482,10 @@ void shouldBeAbleToEnableMutiny() throws URISyntaxException, FileNotFoundExcepti @Test void shouldBeAbleToApplyMutinyOnSpecificEndpoints() throws URISyntaxException, FileNotFoundException { - List generatedFiles = createGeneratorWrapper("simple-openapi.json") - .withMutiny(true) - .withMutinyReturnTypes(Map.of("helloMethod", "Uni", "Bye method_get", "Multi")) - .generate("org.mutiny.enabled"); + List generatedFiles = createGeneratorWrapper("simple-openapi.json").withMutiny(true) + .withMutinyReturnTypes(Map.of("helloMethod", "Uni", "Bye method_get", "Multi")).generate("org.mutiny.enabled"); - Optional file = generatedFiles.stream() - .filter(f -> f.getName().endsWith("DefaultApi.java")) - .findAny(); + Optional file = generatedFiles.stream().filter(f -> f.getName().endsWith("DefaultApi.java")).findAny(); assertThat(file).isNotEmpty(); CompilationUnit compilationUnit = StaticJavaParser.parse(file.orElseThrow()); List methodDeclarations = compilationUnit.findAll(MethodDeclaration.class); @@ -472,10 +495,8 @@ void shouldBeAbleToApplyMutinyOnSpecificEndpoints() throws URISyntaxException, F "helloMethod"); Optional byeMethodGetDeclaration = getMethodDeclarationByIdentifier(methodDeclarations, "byeMethodGet"); - Optional getUserDeclaration = getMethodDeclarationByIdentifier(methodDeclarations, - "getUser"); - Optional getNumbersDeclaration = getMethodDeclarationByIdentifier(methodDeclarations, - "getNumbers"); + Optional getUserDeclaration = getMethodDeclarationByIdentifier(methodDeclarations, "getUser"); + Optional getNumbersDeclaration = getMethodDeclarationByIdentifier(methodDeclarations, "getNumbers"); assertThat(helloMethodDeclaration).hasValueSatisfying( methodDeclaration -> assertEquals("io.smallrye.mutiny.Uni", methodDeclaration.getType().toString())); @@ -483,21 +504,18 @@ void shouldBeAbleToApplyMutinyOnSpecificEndpoints() throws URISyntaxException, F methodDeclaration -> assertEquals("io.smallrye.mutiny.Multi", methodDeclaration.getType().toString())); assertThat(getUserDeclaration).hasValueSatisfying( methodDeclaration -> assertEquals("GetUser200Response", methodDeclaration.getType().toString())); - assertThat(getNumbersDeclaration).hasValueSatisfying( - methodDeclaration -> assertEquals("List", methodDeclaration.getType().toString())); + assertThat(getNumbersDeclaration) + .hasValueSatisfying(methodDeclaration -> assertEquals("List", methodDeclaration.getType().toString())); } @Test void shouldBeAbleToApplyMutinyOnSpecificEndpointsWhenUserDefineWrongConfiguration() throws URISyntaxException, FileNotFoundException { - List generatedFiles = createGeneratorWrapper("simple-openapi.json") - .withMutiny(true) + List generatedFiles = createGeneratorWrapper("simple-openapi.json").withMutiny(true) .withMutinyReturnTypes(Map.of("helloMethod", "Uni", "Bye method_get", "BadConfig")) .generate("org.mutiny.enabled"); - Optional file = generatedFiles.stream() - .filter(f -> f.getName().endsWith("DefaultApi.java")) - .findAny(); + Optional file = generatedFiles.stream().filter(f -> f.getName().endsWith("DefaultApi.java")).findAny(); assertThat(file).isNotEmpty(); CompilationUnit compilationUnit = StaticJavaParser.parse(file.orElseThrow()); List methodDeclarations = compilationUnit.findAll(MethodDeclaration.class); @@ -507,10 +525,8 @@ void shouldBeAbleToApplyMutinyOnSpecificEndpointsWhenUserDefineWrongConfiguratio "helloMethod"); Optional byeMethodGetDeclaration = getMethodDeclarationByIdentifier(methodDeclarations, "byeMethodGet"); - Optional getUserDeclaration = getMethodDeclarationByIdentifier(methodDeclarations, - "getUser"); - Optional getNumbersDeclaration = getMethodDeclarationByIdentifier(methodDeclarations, - "getNumbers"); + Optional getUserDeclaration = getMethodDeclarationByIdentifier(methodDeclarations, "getUser"); + Optional getNumbersDeclaration = getMethodDeclarationByIdentifier(methodDeclarations, "getNumbers"); assertThat(helloMethodDeclaration).hasValueSatisfying( methodDeclaration -> assertEquals("io.smallrye.mutiny.Uni", methodDeclaration.getType().toString())); @@ -518,54 +534,40 @@ void shouldBeAbleToApplyMutinyOnSpecificEndpointsWhenUserDefineWrongConfiguratio methodDeclaration -> assertEquals("io.smallrye.mutiny.Uni", methodDeclaration.getType().toString())); assertThat(getUserDeclaration).hasValueSatisfying( methodDeclaration -> assertEquals("GetUser200Response", methodDeclaration.getType().toString())); - assertThat(getNumbersDeclaration).hasValueSatisfying( - methodDeclaration -> assertEquals("List", methodDeclaration.getType().toString())); + assertThat(getNumbersDeclaration) + .hasValueSatisfying(methodDeclaration -> assertEquals("List", methodDeclaration.getType().toString())); } @Test void shouldBeAbleToAddCustomDateAndTimeMappings() throws URISyntaxException, FileNotFoundException { List generatedFiles = createGeneratorWrapper("datetime-regression.yml") - .withTypeMappings(Map.of( - "date", "ThaiBuddhistDate", - "DateTime", "Instant")) - .withImportMappings(Map.of( - "ThaiBuddhistDate", "java.time.chrono.ThaiBuddhistDate", - "Instant", "java.time.Instant")) + .withTypeMappings(Map.of("date", "ThaiBuddhistDate", "DateTime", "Instant")) + .withImportMappings( + Map.of("ThaiBuddhistDate", "java.time.chrono.ThaiBuddhistDate", "Instant", "java.time.Instant")) .generate("org.datetime.mappings"); - Optional file = generatedFiles.stream() - .filter(f -> f.getName().endsWith("SomeName.java")) - .findAny(); + Optional file = generatedFiles.stream().filter(f -> f.getName().endsWith("SomeName.java")).findAny(); assertThat(file).isNotEmpty(); CompilationUnit compilationUnit = StaticJavaParser.parse(file.orElseThrow()); List classes = compilationUnit.findAll(ClassOrInterfaceDeclaration.class); assertThat(classes).hasSize(2); ClassOrInterfaceDeclaration generatedPojoClass = classes.get(0); - verifyGeneratedDateAndTimeTypes( - generatedPojoClass, - Map.of( - "someDate", "ThaiBuddhistDate", - "someDateTime", "Instant", - "dateArray", "List", - "dateTimeArray", "List", - "dateSet", "Set", - "dateTimeSet", "Set", - "dateMap", "Map", - "dateTimeMap", "Map")); + verifyGeneratedDateAndTimeTypes(generatedPojoClass, + Map.of("someDate", "ThaiBuddhistDate", "someDateTime", "Instant", "dateArray", "List", + "dateTimeArray", "List", "dateSet", "Set", "dateTimeSet", "Set", + "dateMap", "Map", "dateTimeMap", "Map")); assertThat(compilationUnit.getImports().stream().map(NodeWithName::getNameAsString)) .contains("java.time.chrono.ThaiBuddhistDate", "java.time.Instant"); } - private void verifyGeneratedDateAndTimeTypes( - ClassOrInterfaceDeclaration classDeclaration, + private void verifyGeneratedDateAndTimeTypes(ClassOrInterfaceDeclaration classDeclaration, Map expectedFieldsAndTypes) { expectedFieldsAndTypes.forEach((fieldName, expectedFieldType) -> { Optional fieldDeclaration = classDeclaration.getFieldByName(fieldName); assertThat(fieldDeclaration).isPresent(); Optional fieldVariableDeclaration = fieldDeclaration.orElseThrow().getChildNodes().stream() - .filter(it -> it instanceof VariableDeclarator) - .findFirst(); + .filter(it -> it instanceof VariableDeclarator).findFirst(); assertThat(fieldVariableDeclaration).isPresent(); Type fieldType = ((VariableDeclarator) fieldVariableDeclaration.orElseThrow()).getType(); @@ -580,19 +582,17 @@ void verifyAdditionalModelTypeAnnotations() throws URISyntaxException { .generate("org.additionalmodeltypeannotations"); assertFalse(generatedFiles.isEmpty()); - generatedFiles.stream() - .filter(file -> file.getPath().matches(".*/model/.*.java")) + generatedFiles.stream().filter(file -> file.getPath().matches(".*/model/.*.java")) .forEach(this::verifyModelAdditionalAnnotations); } private void verifyModelAdditionalAnnotations(File file) { try { CompilationUnit compilationUnit = StaticJavaParser.parse(file); - compilationUnit.findAll(ClassOrInterfaceDeclaration.class) - .forEach(c -> { - assertThat(c.getAnnotationByName("Foo")).isPresent(); - assertThat(c.getAnnotationByName("Bar")).isPresent(); - }); + compilationUnit.findAll(ClassOrInterfaceDeclaration.class).forEach(c -> { + assertThat(c.getAnnotationByName("Foo")).isPresent(); + assertThat(c.getAnnotationByName("Bar")).isPresent(); + }); } catch (FileNotFoundException e) { throw new RuntimeException(e.getMessage()); } @@ -600,25 +600,22 @@ private void verifyModelAdditionalAnnotations(File file) { @Test void verifyAdditionalApiTypeAnnotations() throws URISyntaxException { - List generatedFiles = createGeneratorWrapper("petstore-openapi.json") - .withEnabledSecurityGeneration(false) + List generatedFiles = createGeneratorWrapper("petstore-openapi.json").withEnabledSecurityGeneration(false) .withAdditionalApiTypeAnnotationsConfig("@org.test.Foo;@org.test.Bar") .generate("org.additionalapitypeannotations"); assertFalse(generatedFiles.isEmpty()); - generatedFiles.stream() - .filter(file -> file.getPath().matches(".*api.*Api.java")) + generatedFiles.stream().filter(file -> file.getPath().matches(".*api.*Api.java")) .forEach(this::verifyApiAdditionalAnnotations); } private void verifyApiAdditionalAnnotations(File file) { try { CompilationUnit compilationUnit = StaticJavaParser.parse(file); - compilationUnit.findAll(ClassOrInterfaceDeclaration.class) - .forEach(c -> { - assertThat(c.getAnnotationByName("Foo")).isPresent(); - assertThat(c.getAnnotationByName("Bar")).isPresent(); - }); + compilationUnit.findAll(ClassOrInterfaceDeclaration.class).forEach(c -> { + assertThat(c.getAnnotationByName("Foo")).isPresent(); + assertThat(c.getAnnotationByName("Bar")).isPresent(); + }); } catch (FileNotFoundException e) { throw new RuntimeException(e.getMessage()); } @@ -626,86 +623,34 @@ private void verifyApiAdditionalAnnotations(File file) { @Test void verifyAdditionalRequestArgs() throws URISyntaxException { - List generatedFiles = createGeneratorWrapper("petstore-openapi.json") - .withEnabledSecurityGeneration(false) + List generatedFiles = createGeneratorWrapper("petstore-openapi.json").withEnabledSecurityGeneration(false) .withAdditionalRequestArgs( "@HeaderParam(\"jaxrs-style-header\") String headerValue;@HeaderParam(\"x-correlation-id\") String correlationId;@PathParam(\"stream\") String stream") .generate("org.additionalHTTPHeaders"); assertFalse(generatedFiles.isEmpty()); - generatedFiles.stream() - .filter(file -> file.getPath() - .matches(".*api.*Api.java")) + generatedFiles.stream().filter(file -> file.getPath().matches(".*api.*Api.java")) .forEach(this::verifyApiAdditionalHTTPHeaders); } - private void verifyApiAdditionalHTTPHeaders(File file) { - try { - CompilationUnit compilationUnit = StaticJavaParser.parse(file); - compilationUnit.findAll(MethodDeclaration.class) - .forEach(c -> { - assertParameter(c.getParameterByName("correlationId"), - "String", - Map.of("HeaderParam", - "\"x-correlation-id\"")); - assertParameter(c.getParameterByName("headerValue"), - "String", - Map.of("HeaderParam", - "\"jaxrs-style-header\"")); - assertParameter(c.getParameterByName("stream"), - "String", - Map.of("PathParam", - "\"stream\"")); - }); - } catch (FileNotFoundException e) { - throw new RuntimeException(e.getMessage()); - } - } - - private void assertParameter(Optional optionalParameter, - String type, - Map annotations) { - assertThat(optionalParameter).isPresent(); - var parameter = optionalParameter.orElseThrow(); - assertThat(parameter.getTypeAsString()).isEqualTo(type); - annotations.forEach((annotationName, annotationValue) -> { - var parameterAnnotation = parameter.getAnnotationByName(annotationName); - assertThat(parameterAnnotation).isPresent(); - assertThat(parameterAnnotation.get() - .asSingleMemberAnnotationExpr() - .getMemberValue() - .toString()).hasToString(annotationValue); - }); - } - @Test void verifyCookieParams() throws URISyntaxException { - List generatedFiles = createGeneratorWrapper("petstore-openapi.json") - .generate("org.cookieParams"); - - generatedFiles.stream() - .filter(file -> file.getPath() - .matches("PetApi.java")) - .forEach(file -> { - try { - CompilationUnit compilationUnit = StaticJavaParser.parse(file); - var positiveFounds = compilationUnit.findAll(MethodDeclaration.class) - .stream() - .filter(c -> c.getNameAsString() - .equals("findPetsByStatus")) - .filter(c -> { - assertParameter(c.getParameterByName("exampleCookie"), - "String", - Map.of("CookieParam", - "\"example-cookie\"")); - return true; - }) - .count(); - assertThat(positiveFounds).isEqualTo(1); - } catch (FileNotFoundException e) { - throw new RuntimeException(e.getMessage()); - } - }); + List generatedFiles = createGeneratorWrapper("petstore-openapi.json").generate("org.cookieParams"); + + generatedFiles.stream().filter(file -> file.getPath().matches("PetApi.java")).forEach(file -> { + try { + CompilationUnit compilationUnit = StaticJavaParser.parse(file); + var positiveFounds = compilationUnit.findAll(MethodDeclaration.class).stream() + .filter(c -> c.getNameAsString().equals("findPetsByStatus")).filter(c -> { + assertParameter(c.getParameterByName("exampleCookie"), "String", + Map.of("CookieParam", "\"example-cookie\"")); + return true; + }).count(); + assertThat(positiveFounds).isEqualTo(1); + } catch (FileNotFoundException e) { + throw new RuntimeException(e.getMessage()); + } + }); } @Test @@ -717,9 +662,7 @@ void verifyAPINormalization() throws Exception { assertNotNull(generatedFiles); assertFalse(generatedFiles.isEmpty()); - Optional file = generatedFiles.stream() - .filter(f -> f.getName().endsWith("Primate.java")) - .findAny(); + Optional file = generatedFiles.stream().filter(f -> f.getName().endsWith("Primate.java")).findAny(); assertThat(file).isNotEmpty(); CompilationUnit compilationUnit = StaticJavaParser.parse(file.orElseThrow()); List types = compilationUnit.findAll(ClassOrInterfaceDeclaration.class); @@ -729,31 +672,53 @@ void verifyAPINormalization() throws Exception { assertThat(types.get(0).getExtendedTypes(0).getName()).isEqualTo(new SimpleName("Mammal")); } - private Optional getRegisterProviderAnnotation(ClassOrInterfaceDeclaration declaration, - String annotationValue) { - return declaration.getAnnotations() + @Test + void verifyDynamicUrlAnnotation() throws Exception { + List generatedFiles = createGeneratorWrapperReactive("petstore-openapi.json") + .withUseDynamicUrl(true) + .generate("org.dynamic.url") .stream() - .filter(annotationExpr -> "RegisterProvider".equals(annotationExpr.getNameAsString()) && - annotationExpr instanceof SingleMemberAnnotationExpr && - annotationValue.equals(((SingleMemberAnnotationExpr) annotationExpr).getMemberValue() - .toString())) - .findFirst(); + .filter(file -> file.getPath().endsWith("PetApi.java")).toList(); + + assertThat(generatedFiles).isNotEmpty(); + for (File file : generatedFiles) { + try { + CompilationUnit compilationUnit = StaticJavaParser.parse(file); + var positiveFounds = compilationUnit.findAll(MethodDeclaration.class).stream() + .filter(c -> c.getNameAsString().equals("findPetsByStatus")).filter(c -> { + assertMarkerAnnotationParameter(c.getParameterByName("dynUrl"), "Url"); + return true; + }).count(); + assertThat(positiveFounds).isEqualTo(1); + } catch (FileNotFoundException e) { + throw new RuntimeException(e.getMessage()); + } + } } private List generateRestClientFiles() throws URISyntaxException { - OpenApiClientGeneratorWrapper generatorWrapper = createGeneratorWrapper("simple-openapi.json") - .withCircuitBreakerConfig(Map.of( - "org.openapitools.client.api.DefaultApi", List.of("opThatDoesNotExist", "byeMethodGet"))); + OpenApiClientGeneratorWrapper generatorWrapper = createGeneratorWrapper("simple-openapi.json").withCircuitBreakerConfig( + Map.of("org.openapitools.client.api.DefaultApi", List.of("opThatDoesNotExist", "byeMethodGet"))); return generatorWrapper.generate("org.openapitools.client"); } + private OpenApiClientGeneratorWrapper createGeneratorWrapperReactive(String specFileName) throws URISyntaxException { + final Path openApiSpec = getOpenApiSpecPath(specFileName); + return new OpenApiReactiveClientGeneratorWrapper(openApiSpec, getOpenApiTargetPath(openApiSpec), false, true); + } + private OpenApiClientGeneratorWrapper createGeneratorWrapper(String specFileName) throws URISyntaxException { - final Path openApiSpec = Path - .of(requireNonNull(this.getClass().getResource(String.format("/openapi/%s", specFileName))).toURI()); - final Path targetPath = Paths.get(getTargetDir(), "openapi-gen"); + final Path openApiSpec = getOpenApiSpecPath(specFileName); + return new OpenApiClassicClientGeneratorWrapper(openApiSpec, getOpenApiTargetPath(openApiSpec), false, true); + } - return new OpenApiClassicClientGeneratorWrapper(openApiSpec, targetPath, false, true); + private Path getOpenApiSpecPath(String specFileName) throws URISyntaxException { + return Path.of(requireNonNull(this.getClass().getResource(String.format("/openapi/%s", specFileName))).toURI()); + } + + private Path getOpenApiTargetPath(Path openApiSpec) throws URISyntaxException { + return Paths.get(getTargetDir(), "openapi-gen"); } private String getTargetDir() throws URISyntaxException { @@ -762,12 +727,39 @@ private String getTargetDir() throws URISyntaxException { private Optional findVariableByName(List fields, String name) { return fields.stream().map(field -> field.getVariable(0)) - .filter((VariableDeclarator variable) -> name.equals(variable.getName().asString())) - .findFirst(); + .filter((VariableDeclarator variable) -> name.equals(variable.getName().asString())).findFirst(); } - private static Optional getMethodDeclarationByIdentifier(List methodDeclarations, - String methodName) { - return methodDeclarations.stream().filter(md -> md.getName().getIdentifier().equals(methodName)).findAny(); + private void verifyApiAdditionalHTTPHeaders(File file) { + try { + CompilationUnit compilationUnit = StaticJavaParser.parse(file); + compilationUnit.findAll(MethodDeclaration.class).forEach(c -> { + assertParameter(c.getParameterByName("correlationId"), "String", Map.of("HeaderParam", "\"x-correlation-id\"")); + assertParameter(c.getParameterByName("headerValue"), "String", Map.of("HeaderParam", "\"jaxrs-style-header\"")); + assertParameter(c.getParameterByName("stream"), "String", Map.of("PathParam", "\"stream\"")); + }); + } catch (FileNotFoundException e) { + throw new RuntimeException(e.getMessage()); + } + } + + private void assertParameter(Optional optionalParameter, String type, Map annotations) { + assertThat(optionalParameter).isPresent(); + var parameter = optionalParameter.orElseThrow(); + assertThat(parameter.getTypeAsString()).isEqualTo(type); + annotations.forEach((annotationName, annotationValue) -> { + var parameterAnnotation = parameter.getAnnotationByName(annotationName); + assertThat(parameterAnnotation).isPresent(); + assertThat(parameterAnnotation.get().asSingleMemberAnnotationExpr().getMemberValue().toString()) + .hasToString(annotationValue); + }); + } + + private void assertMarkerAnnotationParameter(Optional optionalParameter, String annotationName) { + assertThat(optionalParameter).isPresent(); + var parameter = optionalParameter.orElseThrow(); + var parameterAnnotation = parameter.getAnnotationByName(annotationName); + assertThat(parameterAnnotation).isPresent(); + assertThat(parameterAnnotation.get().asMarkerAnnotationExpr()).isNotNull(); } } diff --git a/client/deployment/src/test/resources/openapi/issue-1022.json b/client/deployment/src/test/resources/openapi/issue-1022.json new file mode 100644 index 000000000..3d7c6e957 --- /dev/null +++ b/client/deployment/src/test/resources/openapi/issue-1022.json @@ -0,0 +1,118 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "tuto API", + "version": "1.0.0-SNAPSHOT" + }, + "servers": [ + { + "url": "http://localhost:8080", + "description": "Auto generated value" + }, + { + "url": "http://0.0.0.0:8080", + "description": "Auto generated value" + } + ], + "paths": { + "/": { + "get": { + "tags": [ + "Default" + ], + "operationId": "get", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Data" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Data": { + "required": [ + "things" + ], + "type": "object", + "properties": { + "things": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Thing" + }, + "anyOf": [ + { + "$ref": "#/components/schemas/SomeThing" + }, + { + "$ref": "#/components/schemas/OtherThing" + } + ] + } + } + }, + "OtherThing": { + "description": "Other thing", + "required": [ + "other" + ], + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/Thing" + } + ], + "properties": { + "other": { + "type": "string" + } + } + }, + "SomeThing": { + "description": "Some thing", + "required": [ + "some" + ], + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/Thing" + } + ], + "properties": { + "some": { + "type": "string" + } + } + }, + "Thing": { + "description": "Thing", + "required": [ + "thing" + ], + "type": "object", + "properties": { + "thing": { + "type": "string" + } + }, + "discriminator": { + "propertyName": "@type", + "mapping": { + "SomeThing": "#/components/schemas/SomeThing", + "OtherThing": "#/components/schemas/OtherThing" + } + } + } + } + } +} diff --git a/client/deployment/src/test/resources/openapi/issue-852.json b/client/deployment/src/test/resources/openapi/issue-852.json new file mode 100644 index 000000000..679ffb7ad --- /dev/null +++ b/client/deployment/src/test/resources/openapi/issue-852.json @@ -0,0 +1,6160 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "OnBase Foundation 22.1", + "title": "OnBase Document API", + "description": "Provides core OnBase Document Management functionality.", + "x-public": true, + "contact": { + "name": "Hyland Community", + "url": "https://community.hyland.com/technical/rest-apis" + } + }, + "servers": [ + { + "url": "{protocol}://{server}/{product}", + "variables": { + "protocol": { + "enum": [ + "http", + "https" + ], + "default": "https" + }, + "server": { + "default": "localhost/apiserver", + "description": "This is the server being used to host the API in the current environment." + }, + "product": { + "default": "onbase/core", + "description": "This is the portion of the url to add to each base-url before each path defined below." + } + } + } + ], + "security": [ + { + "Bearer": [] + } + ], + "tags": [ + { + "name": "Autofills", + "description": "AutoFill Keyword Set metadata retrieval." + }, + { + "name": "Currency Formats", + "description": "Configuration items related to currency formats." + }, + { + "name": "Custom Queries", + "description": "Configuration items related to custom queries." + }, + { + "name": "Document", + "description": "Document archival and metadata retrieval." + }, + { + "name": "Document Content", + "description": "Document content retrieval." + }, + { + "name": "Document Keywords", + "description": "Document keywords." + }, + { + "name": "Document Notes", + "description": "Notes retrieval from a document's revision." + }, + { + "name": "Document Queries", + "description": "Document search." + }, + { + "name": "Document Renditions", + "description": "Document renditions." + }, + { + "name": "Document Revisions", + "description": "Document revisions." + }, + { + "name": "Document Types", + "description": "Configuration items related to document types." + }, + { + "name": "Document Type Groups", + "description": "Configuration items related to document type groups." + }, + { + "name": "File Types", + "description": "Configuration items related to file types." + }, + { + "name": "File Upload", + "description": "File upload for document archival." + }, + { + "name": "Locks", + "description": "Locking and unlocking documents." + }, + { + "name": "Keyword Types", + "description": "Keyword types metadata retrieval." + }, + { + "name": "Keyword Type Groups", + "description": "Keyword type groups metadata retrieval." + }, + { + "name": "Notes", + "description": "Note metadata retrieval." + }, + { + "name": "Note Types", + "description": "Configuration items related to Note types." + } + ], + "paths": { + "/currency-formats": { + "get": { + "summary": "Get the metadata for all currency formats.", + "description": "Get all the currency formats configured. This list will not include the currency\nused to support Workstation Region Settings.", + "operationId": "GetCurrencyFormatCollection", + "tags": [ + "Currency Formats" + ], + "parameters": [ + { + "in": "query", + "name": "id", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "The unique identifiers of currency formats. This parameter cannot be used in conjunction with the `systemName` parameter. Multiple values are supported and in a URL should be joined using the \"&\" character. Ex:?id=102&id=103" + }, + { + "in": "query", + "name": "systemName", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "The unique configured system names of currency formats. This parameter cannot be used in conjunction with the `id` parameter. Multiple values are supported and in a URL should be joined using the \"&\" character. Ex:?systemName=currencyFormat1&systemName=currencyFormat2" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CurrencyFormatCollection" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400-COLLECTIONPARAMETERS-RESPONSE" + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + } + } + } + }, + "/currency-formats/{currencyFormatId}": { + "get": { + "summary": "Gets currency format metadata.", + "description": "Gets currency format metadata. When Keyword Type is configured to use Workstation Regional Settings\n'default' can be used.", + "operationId": "GetCurrencyFormatById", + "tags": [ + "Currency Formats" + ], + "parameters": [ + { + "$ref": "#/components/parameters/currencyFormatId" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CurrencyFormat" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/custom-queries": { + "get": { + "summary": "Gets custom queries", + "description": "Currently only returns Custom Queries the user has permissions to and that the /documents/queries end point is capable of executing.", + "operationId": "GetCustomQueryCollection", + "tags": [ + "Custom Queries" + ], + "parameters": [ + { + "in": "query", + "name": "id", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "The unique identifiers of custom queries. This parameter cannot be used in conjunction with the `systemName` parameter. Multiple values are supported and in a URL should be joined using the \"&\" character. Ex:?id=102&id=103" + }, + { + "in": "query", + "name": "systemName", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "The unique configured system names of custom queries. This parameter cannot be used in conjunction with the `id` parameter. Multiple values are supported and in a URL should be joined using the \"&\" character. Ex:?systemName=customQuery1&systemName=customQuery2" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomQueryCollection" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400-COLLECTIONPARAMETERS-RESPONSE" + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + } + } + } + }, + "/custom-queries/{customQueryId}": { + "get": { + "summary": "Gets custom query metadata", + "description": "Gets custom query metadata", + "operationId": "GetCustomQueryById", + "tags": [ + "Custom Queries" + ], + "parameters": [ + { + "$ref": "#/components/parameters/customQueryId" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomQuery" + } + } + } + }, + "400": { + "description": "Response for when user attempts to retrieve a type of custom query that is not supported.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "Custom query type is not supported.", + "instance": "/example-resource" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/custom-queries/{customQueryId}/keyword-types": { + "get": { + "summary": "Gets keyword types for a custom query", + "description": "Gets a keyword type collection for the custom query", + "operationId": "GetKeywordTypeCollectionForCustomQuery", + "tags": [ + "Custom Queries" + ], + "parameters": [ + { + "$ref": "#/components/parameters/customQueryId" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomQueryKeywordTypeCollection" + } + } + } + }, + "400": { + "description": "Response for when user attempts to retrieve a type of custom query that is not supported.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "Custom query type is not supported.", + "instance": "/example-resource" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/document-types": { + "get": { + "summary": "Get a list of document types.", + "description": "Get all the document types the logged in user has permissions to view.", + "operationId": "GetDocumentTypeCollection", + "tags": [ + "Document Types" + ], + "parameters": [ + { + "in": "query", + "name": "id", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "The unique identifiers of document types.This parameter cannot be used in conjunction with the `systemName` parameter. Multiple values are supported and in a URL should be joined using the \"&\" character. Ex:?id=102&id=103" + }, + { + "in": "query", + "name": "systemName", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "The unique configured system names of document types. This parameter cannot be used in conjunction with the `id` parameter. Multiple values are supported and in a URL should be joined using the \"&\" character. Ex:?systemName=docType1&systemName=docType2" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DocumentTypeCollection" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400-COLLECTIONPARAMETERS-RESPONSE" + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + } + } + } + }, + "/document-types/{documentTypeId}": { + "get": { + "summary": "Get a document type", + "description": "Gets the document type with the associated id.", + "operationId": "GetDocumentTypeById", + "tags": [ + "Document Types" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentTypeId" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DocumentType" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/document-types/{documentTypeId}/keyword-type-groups": { + "get": { + "summary": "Get keyword type group metadata for a document type.", + "description": "Gets the associated keyword type groups or associated list of keyword types for standalone keywords.\n\nThe keyword type group metadata will be returned in the display order as it has\nbeen configured on the document type.", + "operationId": "GetKeywordTypeGroupCollectionForDocumentType", + "tags": [ + "Document Types" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentTypeId" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KeywordTypeGroupCollectionModel" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/document-types/{documentTypeId}/default-keywords": { + "get": { + "summary": "Gets default keywords for a new document.", + "description": "Gets the default keyword values for a document type grouped by keyword type group and\nkeyword type.", + "operationId": "GetDefaultKeywordCollectionForDocumentType", + "tags": [ + "Document Types" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentTypeId" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KeywordCollectionResponse" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "description": "Response when the user does not have the document privilege `Create Document' or\ndocument privilege 'ReIndex Document'", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/forbidden", + "title": "Forbidden", + "status": 403, + "detail": "Do not have the document privilege Create Document or document privilege ReIndex Document", + "instance": "/example-resource" + } + } + } + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/document-type-groups": { + "get": { + "summary": "Get a list of document type groups.", + "description": "Get all the document type groups the logged in user has permissions to view.", + "operationId": "GetDocumentTypeGroupCollection", + "tags": [ + "Document Type Groups" + ], + "parameters": [ + { + "in": "query", + "name": "id", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "The unique identifiers of document type groups. This parameter cannot be used in conjunction with the `systemName` parameter. Multiple values are supported and in a URL should be joined using the \"&\" character. Ex:?id=102&id=103" + }, + { + "in": "query", + "name": "systemName", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "The unique system names of document type groups. This parameter cannot be used in conjunction with the `id` parameter. Multiple values are supported and in a URL should be joined using the \"&\" character. Ex:?systemName=docTypeGroup1&systemName=docTypeGroup2" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DocumentTypeGroupCollection" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400-COLLECTIONPARAMETERS-RESPONSE" + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + } + } + } + }, + "/document-type-groups/{documentTypeGroupId}": { + "get": { + "summary": "Get a document type group", + "description": "Gets the document type group with the associated id.", + "operationId": "GetDocumentTypeGroupById", + "tags": [ + "Document Type Groups" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentTypeGroupId" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DocumentTypeGroup" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/document-type-groups/{documentTypeGroupId}/document-types": { + "get": { + "summary": "Gets the associated document types for a document type group", + "description": "Gets the associated document type collection for the document type group", + "operationId": "GetDocumentTypeCollectionForDocumentTypeGroup", + "tags": [ + "Document Type Groups" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentTypeGroupId" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DocumentTypeGroupDocumentTypeCollection" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/file-types": { + "get": { + "summary": "Get file type metadata for all file types.", + "description": "Get the file type metadata for all file types in\nthe system.", + "operationId": "GetFileTypeCollection", + "tags": [ + "File Types" + ], + "parameters": [ + { + "in": "query", + "name": "id", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "The unique identifiers of file types. This parameter cannot be used in conjunction with the `systemName` parameter. Multiple values are supported and in a URL should be joined using the \"&\" character. Ex:?id=2&id=16" + }, + { + "in": "query", + "name": "systemName", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "The unique configured system names of file types. This parameter cannot be used in conjunction with the `id` parameter. Multiple values are supported and in a URL should be joined using the \"&\" character. Ex:?systemName=Image File Format&systemName=PDF" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FileTypeCollection" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400-COLLECTIONPARAMETERS-RESPONSE" + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + } + } + } + }, + "/file-types/{fileTypeId}": { + "get": { + "summary": "Get file type metadata.", + "description": "Get file type metadata for the specified file type id.", + "operationId": "GetFileTypeById", + "tags": [ + "File Types" + ], + "parameters": [ + { + "$ref": "#/components/parameters/fileTypeId" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FileType" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/default-upload-file-types": { + "get": { + "summary": "Gets the \"best guess\" file type for upload based on a given extension.", + "description": "Gets the \"best guess\" file type for upload based on a given extension.", + "operationId": "GetFileTypeForUpload", + "tags": [ + "File Types" + ], + "parameters": [ + { + "in": "query", + "name": "extension", + "required": true, + "schema": { + "type": "string" + }, + "description": "The extension that will determine the File Type that is appropriate for upload." + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DefaultUploadFileType" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/keyword-types": { + "get": { + "summary": "Get keyword type metadata for all keyword types.", + "description": "Get the keyword type metadata for all keyword types in\nthe system.", + "operationId": "GetKeywordTypeCollection", + "tags": [ + "Keyword Types" + ], + "parameters": [ + { + "in": "query", + "name": "id", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "The unique identifiers of keyword types. This parameter cannot be used in conjunction with the `systemName` parameter. Multiple values are supported and in a URL should be joined using the \"&\" character. Ex:?id=102&id=103" + }, + { + "in": "query", + "name": "systemName", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "The unique configured system names of keyword types. This parameter cannot be used in conjunction with the `id` parameter. Multiple values are supported and in a URL should be joined using the \"&\" character. Ex:?systemName=keywordType1&systemName=keywordType2" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KeywordTypeCollection" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400-COLLECTIONPARAMETERS-RESPONSE" + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + } + } + } + }, + "/keyword-types/{keywordTypeId}": { + "get": { + "summary": "Get keyword type metadata.", + "description": "Get keyword type metadata for the specified keyword type id.", + "operationId": "GetKeywordTypeById", + "tags": [ + "Keyword Types" + ], + "parameters": [ + { + "$ref": "#/components/parameters/keywordTypeId" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KeywordType" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/keyword-type-groups": { + "get": { + "summary": "Get keyword type group metadata for all keyword type groups.", + "description": "Get the keyword type group metadata for all keyword type groups in\nthe system.", + "operationId": "GetKeywordTypeGroupCollection", + "tags": [ + "Keyword Type Groups" + ], + "parameters": [ + { + "in": "query", + "name": "id", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "The unique identifiers of keyword type groups. This parameter cannot be used in conjunction with the `systemName` parameter. Multiple values are supported and in a URL should be joined using the \"&\" character. Ex:?id=102&id=103" + }, + { + "in": "query", + "name": "systemName", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "The unique configured system names of keyword type groups. This parameter cannot be used in conjunction with the `id` parameter. Multiple values are supported and in a URL should be joined using the \"&\" character. Ex:?systemName=keywordTypeGroup1&systemName=keywordTypeGroup2" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KeywordTypeGroupCollection" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400-COLLECTIONPARAMETERS-RESPONSE" + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + } + } + } + }, + "/keyword-type-groups/{keywordTypeGroupId}": { + "get": { + "summary": "Get keyword type group metadata.", + "description": "Get keyword type group metadata for the specified keyword type group id.", + "operationId": "GetKeywordTypeGroupById", + "tags": [ + "Keyword Type Groups" + ], + "parameters": [ + { + "$ref": "#/components/parameters/keywordTypeGroupId" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KeywordTypeGroup" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/keyword-type-groups/{keywordTypeGroupId}/keyword-types": { + "get": { + "summary": "Get a list of keyword types in the keyword type group.", + "description": "Get a list of keyword types in the keyword type group with the specified id.", + "operationId": "GetKeywordTypeCollectionForKeywordTypeGroup", + "tags": [ + "Keyword Type Groups" + ], + "parameters": [ + { + "$ref": "#/components/parameters/keywordTypeGroupId" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KeywordTypeGroupKeywordTypeCollection" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/autofill-keyword-sets": { + "get": { + "summary": "Get autofill keyword set metadata for all autofill keyword sets.", + "description": "Get the autofill keyword set metadata for all autofill keyword sets in\nthe system.", + "operationId": "GetAutofillKeywordSetCollection", + "tags": [ + "Autofills" + ], + "parameters": [ + { + "in": "query", + "name": "id", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "The unique identifiers of autofill keyword sets. This parameter cannot be used in conjunction with the `systemName` parameter. Multiple values are supported and in a URL should be joined using the \"&\" character. Ex:?id=102&id=103" + }, + { + "in": "query", + "name": "systemName", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "The unique configured system names of autofill keyword sets. This parameter cannot be used in conjunction with the `id` parameter. Multiple values are supported and in a URL should be joined using the \"&\" character. Ex:?systemName=testAutofill1&systemName=testAutofill2" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AutoFillKeywordSetCollection" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400-COLLECTIONPARAMETERS-RESPONSE" + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + } + } + } + }, + "/autofill-keyword-sets/{autoFillKeywordSetId}": { + "get": { + "summary": "Get autofill keyword set metadata.", + "description": "Get autofill keyword set metadata for the specified autofill keyword set id.", + "operationId": "GetAutofillKeywordSetById", + "tags": [ + "Autofills" + ], + "parameters": [ + { + "$ref": "#/components/parameters/autoFillKeywordSetId" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AutoFillKeywordSet" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/autofill-keyword-sets/{autoFillKeywordSetId}/keyword-types": { + "get": { + "summary": "Get keyword metadata for a autofill type.", + "description": "Gets the associated keyword types.", + "operationId": "GetKeywordTypeCollectionForAutofillKeywordSet", + "tags": [ + "Autofills" + ], + "parameters": [ + { + "$ref": "#/components/parameters/autoFillKeywordSetId" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AutoFillKeywordSetKeywordTypeCollection" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/autofill-keyword-sets/{autoFillKeywordSetId}/keyword-set-data": { + "get": { + "summary": "Get the keyword set data.", + "description": "Get the keyword set data instances based on query parameters.", + "operationId": "GetKeywordDataCollectionForAutofillKeywordSet", + "tags": [ + "Autofills" + ], + "parameters": [ + { + "$ref": "#/components/parameters/autoFillKeywordSetId" + }, + { + "$ref": "#/components/parameters/Accept-Language" + }, + { + "in": "query", + "name": "primaryValue", + "schema": { + "type": "string" + }, + "description": "The primary keyword value associated with the particular autofill keyword set." + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KeywordSetDataCollection" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/documents": { + "get": { + "summary": "Get a list of documents.", + "description": "Get the list of documents with the given ids that the user has permission to view. An empty list is returned if the user does not have access to any documents or the documents cannot be found.", + "operationId": "GetDocumentCollection", + "tags": [ + "Document" + ], + "parameters": [ + { + "in": "query", + "name": "id", + "description": "The unique identifiers of Document. Multiple values are supported and in a URL should be joined using the '&' character.\nEx: ?id=101&id=102&id=103.", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DocumentCollection" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + } + } + }, + "post": { + "summary": "Archive the document.", + "description": "Finishes the document upload by archiving the document into the given document type. Can also optionally specify the document date, comments if the document type is Revisable/Renditionable and also a boolean to indicate if this needs to be stored as a new document regardless of the document type settings. If fileTypeId is not specified, then the default file type for the document type will be used. Providing a keyword collection with a keyword guid is required. Takes a list of references to uploaded file resources.", + "operationId": "PostDocument", + "tags": [ + "Document" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DocumentArchiveProperties" + } + } + } + }, + "responses": { + "201": { + "description": "Document successfully archived.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DocumentsPostResponse" + } + } + }, + "headers": { + "Location": { + "$ref": "#/components/headers/Location" + } + } + }, + "300": { + "description": "Document(s) are found for which the new document can be added as Revision/Rendition.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MatchedDocumentCollectionResponse" + } + } + } + }, + "400": { + "description": "Document Type Id and/or File Type Id is invalid,\nKeyword information is invalid, unique handle(s)\nare invalid, no comments are provided when the document type is set to \"Force Comments\",\nor an invalid archival option has been provided for Revisable/Renditionable document type.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "Invalid document type.", + "instance": "/example-resource" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "description": "User does not have rights to create document or when the user is trying to add read-only and hidden keywords without 'Access Restricted Keywords' privilege.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/forbidden", + "title": "Forbidden", + "status": 403, + "detail": "User does not have rights to create document.", + "instance": "/example-resource" + } + } + } + } + } + } + }, + "/documents/{documentId}": { + "get": { + "summary": "Gets document metadata.", + "description": "Gets document metadata.", + "operationId": "GetDocumentById", + "tags": [ + "Document" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Document" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + }, + "delete": { + "summary": "Deletes a document.", + "description": "Deletes a document.", + "operationId": "DeleteDocumentById", + "tags": [ + "Document" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "description": "Response when the user does not have delete rights to the specified document,\nretention criteria has not been met, or the resource is checked out.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/forbidden", + "title": "Forbidden", + "status": 403, + "detail": "Do not have rights to the specified document, retention criteria has not been met, or the resource is checked out.", + "instance": "/example-resource" + } + } + } + }, + "404": { + "description": "Response when the resource does not exist or the user does not have view rights to the specified document.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/not-found", + "title": "Not Found", + "status": 404, + "detail": "Document does not exist.", + "instance": "/example-resource" + } + } + } + } + } + }, + "patch": { + "summary": "Updates document metadata.", + "description": "Updates document metadata.", + "operationId": "PatchDocumentById", + "tags": [ + "Document" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DocumentPatchRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Document" + } + } + } + }, + "400": { + "description": "Response when the user sends invalid data to modify document metadata.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "Invalid Document Date Format.", + "instance": "/example-resource" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "description": "Response when the user does not have rights to modify document metadata.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/forbidden", + "title": "Forbidden", + "status": 403, + "detail": "Do not have rights to modify the specified document.", + "instance": "/example-resource" + } + } + } + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + }, + "put": { + "summary": "Reindex document.", + "description": "Reindexes a document by first checking if there is a match for rendition or revisions\nand then applies the reindex if there are no matches, the user verifies that it will not\nstore as a new revision or rendition, or if there is no document type change.\nThe keywordCollection requires a keyword GUID. For reindexing, this must come from the source document (/document/{id}/keywords) rather than the target document type.", + "operationId": "PutDocumentById", + "tags": [ + "Document" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DocumentReindexProperties" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + }, + "300": { + "description": "Document(s) are found for which the new document can be added as Revision/Rendition.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MatchedDocumentCollectionResponse" + } + } + } + }, + "400": { + "description": "Response when the user sends invalid data to reindex.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "Invalid Document Date Format.", + "instance": "/example-resource" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "description": "Response when the user does not have rights to reindex.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/forbidden", + "title": "Forbidden", + "status": 403, + "detail": "Do not have rights to reindex the specified document.", + "instance": "/example-resource" + } + } + } + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/documents/{documentId}/keywords": { + "get": { + "summary": "Gets keywords for a document.", + "description": "Gets the keyword values for a document grouped by keyword type group and\nkeyword type.", + "operationId": "GetKeywordCollectionForDocument", + "tags": [ + "Document Keywords" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + }, + { + "in": "query", + "name": "unmask", + "description": "Value determining whether to unmask security masked Keywords. If true and user does not\nhave Access Security Masked Keywords privilege, security masked Keywords will stay masked.\nSetting unmask to false is equivalent to omitting the query string parameter.", + "schema": { + "type": "boolean" + }, + "required": false + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KeywordCollectionResponse" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "description": "Response when the user does not have the document privilege `View Keywords'", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/forbidden", + "title": "Forbidden", + "status": 403, + "detail": "Do not have the document privilege View Keywords", + "instance": "/example-resource" + } + } + } + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + }, + "put": { + "summary": "Sets all keyword values for an indexed document.", + "description": "Sets all keyword values for an indexed document. Existing values will be\nreplaced with the supplied list of keyword values grouped by keyword type\ngroup and keyword type.", + "operationId": "PutKeywordCollectionForDocument", + "tags": [ + "Document Keywords" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KeywordCollectionRequest" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Response when the user sends a empty request body,\ninvalid keyword syntax, invalid keyword data, or missing restricted keyword guid.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "Do not have the document privilege View Keywords", + "instance": "/example-resource" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "description": "Response when the user does not have the document privilege `Modify Keywords',\nthe document is locked by Records Management or Medical records.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/forbidden", + "title": "Forbidden", + "status": 403, + "detail": "Does not have the document privilege Modify Keywords, the document is locked by Records Management or Medical records.", + "instance": "/example-resource" + } + } + } + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/indexing-modifiers": { + "post": { + "summary": "Performs modification of keyword data during indexing processes like Reindex and Archival.", + "description": "During the indexing process, some actions require additional information from the server before indexing can continue. This\nend point provides the ability to perform these actions.\nThis end point is intended to assist with indexing processes that are interactive. Requests to this end point do not persist\nany indexing data on the document.\n# Expand AutoFill Keyword Sets\nAutoFill Keyword Sets can be expanded during the Reindex process and during the Archival process. Information about the desired AutoFill\nto expand is sent to the server to perform the operation and sends back the results based on a Primary Keyword value. Expansion will\noccur if a single Primary Keyword value match is found and will return back the updated Keyword Collection. If multiple matches are\nfound the endpoint will behave differently depending on the following AutoFill Keyword Set configuration.\n\n\n \n \n \n \n \n \n \n \n \n
Expansion Type Behavior
Single Selection A collection of AutoFill Keyword Set Data Sets will be sent\n back that requires a selection of a single AutoFill Keyword Set Data Set Instance Id\n to be passed back for successful expansion.
Multiple Selection A collection of AutoFill Keyword Set Data Sets will be sent back\n that requires a selection of AutoFill Keyword Set Data Set Instance Ids to be passed\n back for successful expansion.
Expand All All matching AutoFill Keyword Set Data Sets will be\n expanded without any other interaction.
", + "operationId": "PostIndexingModifier", + "tags": [ + "Document Keywords" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ReindexAutoFillExpansionModifierProperties" + }, + { + "$ref": "#/components/schemas/ArchivalAutoFillExpansionModifierProperties" + } + ], + "discriminator": { + "propertyName": "objectType", + "mapping": { + "ReindexAutoFillExpansion": "#/components/schemas/ReindexAutoFillExpansionModifierProperties", + "ArchivalAutoFillExpansion": "#/components/schemas/ArchivalAutoFillExpansionModifierProperties" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IndexingModifiersPostResponse" + } + } + } + }, + "300": { + "description": "Multiple AutoFill Keyword Set Data Set items are found matching the Primary Keyword Value.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AutoFillMultipleMatchesResponse" + } + } + } + }, + "400": { + "description": "Response when the user sends an invalid AutoFill Keyword Expansion Properties.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "Invalid Document Type Id.", + "instance": "/example-resource" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "description": "Response when the user does not have the necessary privileges.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/forbidden", + "title": "Forbidden", + "status": 403, + "detail": "No privileges to perform Autofill Expansion", + "instance": "/example-resource" + } + } + } + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/documents/{documentId}/locks": { + "get": { + "summary": "Gets the current locks for the document.", + "description": "Gets the list of locks that are currently placed on a document.", + "operationId": "GetDocumentLocks", + "tags": [ + "Locks" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LockInfoCollection" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + }, + "post": { + "summary": "Create a lock on a document.", + "description": "Creates lock on a document. The type of lock is\nspecified in the required query parameter `lockType`.", + "operationId": "PostDocumentLocks", + "tags": [ + "Locks" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + }, + { + "$ref": "#/components/parameters/lockTypeParam" + } + ], + "responses": { + "204": { + "description": "No Content. Lock created." + }, + "400": { + "description": "Response when lockType is not included.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "Required parameter lockType is not included.", + "instance": "/example-resource" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + }, + "409": { + "$ref": "#/components/responses/409-LOCKING-RESPONSE" + } + } + }, + "delete": { + "summary": "Delete a lock on a document.", + "description": "Deletes a lock on a document. The type of lock is\nspecified in the required query parameter `lockType`.", + "operationId": "DeleteDocumentLock", + "tags": [ + "Locks" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + }, + { + "$ref": "#/components/parameters/lockTypeParam" + } + ], + "responses": { + "204": { + "description": "No content. Document Lock has been removed." + }, + "400": { + "description": "Response when lockType is not included.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "Required parameter lockType is not included.", + "instance": "/example-resource" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/documents/{documentId}/revisions/{revisionId}/notes": { + "get": { + "tags": [ + "Document Notes" + ], + "summary": "Gets a collection of notes for a given document.", + "description": "Gets a collection of notes for a given document.\nUse `latest` to retrieve the most recent revision's notes.", + "operationId": "GetNoteCollectionForDocument", + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + }, + { + "$ref": "#/components/parameters/revisionId" + }, + { + "name": "page", + "in": "query", + "description": "The page of the document to retrieve notes from. A page is one based.\nIf the value is not present then all notes on the document revision will be retrieved.", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NoteCollectionModel" + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + }, + "post": { + "tags": [ + "Document Notes" + ], + "summary": "Create a new note and add it to a given document revision.", + "description": "Create a new note and add it to a given document revision.\nUse `latest` to add to the most recent revision.", + "operationId": "PostNoteOnDocument", + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + }, + { + "$ref": "#/components/parameters/revisionId" + } + ], + "requestBody": { + "description": "Model containing the note metadata to save.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddNotePropertiesModel" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "The note was successfully created.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotesPostResponse" + } + } + }, + "headers": { + "Location": { + "$ref": "#/components/headers/Location" + } + } + }, + "400": { + "description": "The note has an invalid size, the note type of the note is unsupported, or the note\ncould not be created.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "NoteType ID is a staple. Staples and back staples are currently unsupported Note Types.\"", + "instance": "/example-resource" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "description": "The user does not have rights to create notes on this document or\nthe document lock prevents the note from being created.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/forbidden", + "title": "Forbidden", + "status": 403, + "detail": "Do not have rights to create the note.", + "instance": "/example-resource" + } + } + } + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/documents/{documentId}/revisions": { + "get": { + "summary": "Gets a collection of document revisions.", + "description": "Gets collection of document revisions.", + "operationId": "GetRevisionCollectionForDocument", + "tags": [ + "Document Revisions" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RevisionCollection" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "$ref": "#/components/responses/403-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + }, + "post": { + "summary": "Store a new revision", + "description": "Archives or Reindexes the document as a latest revision to the current document.\nKeywords supplied with this request are merged with the existing keywords on the document. For Single Instance keywords, old values are replaced by the new values.", + "operationId": "PostRevisionForDocument", + "tags": [ + "Document Revisions" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/RevisionArchiveProperties" + }, + { + "$ref": "#/components/schemas/RevisionReindexProperties" + } + ], + "discriminator": { + "propertyName": "objectType", + "mapping": { + "RevisionArchive": "#/components/schemas/RevisionArchiveProperties", + "RevisionReindex": "#/components/schemas/RevisionReindexProperties" + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Revision successfully stored.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RevisionsPostResponse" + } + } + }, + "headers": { + "Location": { + "$ref": "#/components/headers/Location" + } + } + }, + "400": { + "description": "A reference to an uploaded file resource is not found, invalid File\nType Id is provided, the document type is not revisable, invalid keywords are supplied or\na comment is not supplied when 'Force Comments' is set to true.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "A reference to an uploaded file resource is not found, invalid File Type Id is provided, or the document type is not revisable", + "instance": "/example-resource" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "description": "Response when the user does not have rights to create or reindex a document, when the user is trying to add/modify keywords with an invalid Keyword Guid, or when the user is trying to add/modify read-only keywords without `Access Restricted Keywords`.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/forbidden", + "title": "Forbidden", + "status": 403, + "detail": "Does not have rights to create revisions.", + "instance": "/example-resource" + } + } + } + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/documents/{documentId}/revisions/{revisionId}": { + "get": { + "summary": "Gets the metadata for a revision.", + "description": "Gets the metadata for a revision.\nUse `latest` to retrieve the most recent revision.\nThe `latest` revision will be available regardless of permission to view revisions.", + "operationId": "GetRevisionByIdForDocument", + "tags": [ + "Document Revisions" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + }, + { + "$ref": "#/components/parameters/revisionId" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Revision" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "$ref": "#/components/responses/403-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/documents/{documentId}/revisions/{revisionId}/renditions": { + "get": { + "summary": "Gets a collection of document renditions.", + "description": "Gets a collection of document renditions.\nUse `latest` to retrieve the most recent revision.\nThe `latest` revision will be available regardless of permission to view revisions.", + "operationId": "GetRenditionCollectionForRevisionOfDocument", + "tags": [ + "Document Renditions" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + }, + { + "$ref": "#/components/parameters/revisionId" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RenditionCollection" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "$ref": "#/components/responses/403-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/documents/{documentId}/revisions/latest/renditions": { + "post": { + "summary": "Store rendition to the latest revision", + "description": "Archives or Reindexes the document as a rendition to the latest revision.\nKeywords supplied with this request are merged with the existing keywords on the document. For Single Instance keywords, old values are replaced by the new values.", + "operationId": "PostRenditionForLatestRevisionOfDocument", + "tags": [ + "Document Renditions" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/RenditionArchiveProperties" + }, + { + "$ref": "#/components/schemas/RenditionReindexProperties" + } + ], + "discriminator": { + "propertyName": "objectType", + "mapping": { + "RenditionArchive": "#/components/schemas/RenditionArchiveProperties", + "RenditionReindex": "#/components/schemas/RenditionReindexProperties" + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Document successfully archived as rendition to the given revision.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RenditionsPostResponse" + } + } + }, + "headers": { + "Location": { + "$ref": "#/components/headers/Location" + } + } + }, + "400": { + "description": "A reference to the uploaded file resource is not found,\nan invalid File Type Id is provided,\nthe document already contains a rendition of the given File Type Id,\nthe document type is not renditionable,\ninvalid keywords are supplied or\na comment is not supplied when 'Force Comments' is set to true.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "An invalid File Type Id is provided", + "instance": "/example-resource" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "description": "Response when the user does not have rights to create or reindex a document, when the user is trying to add/modify keywords with an invalid Keyword Guid, or when the user is trying to add/modify read-only keywords without `Access Restricted Keywords`.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/forbidden", + "title": "Forbidden", + "status": 403, + "detail": "Do not have rights to create a document.", + "instance": "/example-resource" + } + } + } + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/documents/{documentId}/revisions/{revisionId}/renditions/{fileTypeId}": { + "get": { + "summary": "Gets the metadata for a rendition of a revision.", + "description": "Gets the metadata for a rendition of a revision.\nUse `latest` to retrieve the most recent revision.\nThe `latest` revision will be available regardless of permission to view revisions.\nUse `default` to retrieve the default rendition.", + "operationId": "GetRenditionByIdForRevisionOfDocument", + "tags": [ + "Document Renditions" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + }, + { + "$ref": "#/components/parameters/revisionId" + }, + { + "$ref": "#/components/parameters/fileTypeId" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Rendition" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400-GENERIC-RESPONSE" + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "$ref": "#/components/responses/403-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/documents/{documentId}/revisions/{revisionId}/renditions/{fileTypeId}/content": { + "get": { + "summary": "Get document content for a rendition of a revision.", + "description": "To retrieve the default rendition of the latest revision,\nuse 'default' for the fileTypeId and 'latest' for the revisionId.\n\nThe `latest` revision will be available regardless of permission to view revisions.\n\nConsumers can `GET` the content resource by supplying the required\nparameters. A response will be returned\nbased on the result of Content Negotiation. For more detailed information\nregarding how the response content type will be determined, please review\nthe Document Retrieval section of the Programmers Guide.\n\nThe `pages` query parameter can be used to retrieve a single page of the document.\nWhen the `pages` query parameter is provided, the total page count for the document\nwill be included on the response in the `Hyland-Item-Count` header.\nThe Range header can be used to retrieve a specific byte range.\n\nThe `Hyland-Item-Count` header will only be included if the `pages` query parameter is used.\n\nWhen the `pages` query parameter and 'Range' request header is omitted, document content is returned\nin its entirety as a single file with a 200 OK Status code.\n\nThe `context` query parameter can be used to provide additional context of what the\nclient is retrieving the page data for. This will perform client privilege checks and\nlog more appropriate messages to the document history indicating what action the client\nwill be performing.\n\nWhen retrieving a byte range of a document, the response will include an ETag\nrepresenting the specific document that the byte range is from. When retrieving\na second byte range from the same document, this ETag should be included in the\nrequest header If-Match. This will ensure that the second byte range is taken\nfrom the same exact document as the first. If this original document does not\nexist anymore, or if it has been changed in the interim, a status code of 412\nPrecondition Failed will be returned.\n\nWhen requesting a byte range that only represents part of a document, a 206 Partial\nContent response will be returned. If a byte range is requested that includes all of\ndocument's bytes, a 200 OK response will be returned instead.\n\nThe `height` and `width` query parameters can be used to retrieve a smaller scale version\nof the resource with the provided dimensions, in pixels. The `fit` query parameter can also be\nincluded to define how the scaling should occur. When the `fit` query parameter is not provided\nthe default value of `Both` is used.", + "operationId": "GetContentForRenditionOfRevisionOfDocument", + "tags": [ + "Document Content" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + }, + { + "$ref": "#/components/parameters/revisionId" + }, + { + "$ref": "#/components/parameters/fileTypeId" + }, + { + "$ref": "#/components/parameters/pages" + }, + { + "$ref": "#/components/parameters/contentContext" + }, + { + "$ref": "#/components/parameters/height" + }, + { + "$ref": "#/components/parameters/width" + }, + { + "$ref": "#/components/parameters/fit" + }, + { + "$ref": "#/components/parameters/Accept" + }, + { + "$ref": "#/components/parameters/If-Match" + }, + { + "$ref": "#/components/parameters/Range" + } + ], + "responses": { + "200": { + "description": "Response containing document content.", + "headers": { + "Hyland-Item-Count": { + "$ref": "#/components/headers/Hyland-Item-Count" + } + }, + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/Binary-Content" + } + } + } + }, + "206": { + "description": "Response for content that consists of multiple parts.", + "headers": { + "Content-Range": { + "$ref": "#/components/headers/Content-Range" + }, + "Accept-Ranges": { + "$ref": "#/components/headers/Accept-Ranges" + }, + "ETag": { + "$ref": "#/components/headers/ETag" + } + }, + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/Binary-Content" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "$ref": "#/components/responses/403-GENERIC-RESPONSE" + }, + "404": { + "description": "Response for content when the document does not exist a requested\npage does not exist, or the user does not have rights to access.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/not-found", + "title": "Not Found", + "status": 404, + "detail": "The document does not exist, a requested page does not exist, or the user does not have rights to access.", + "instance": "/example-resource" + } + } + } + }, + "406": { + "$ref": "#/components/responses/406-GENERIC-RESPONSE" + }, + "412": { + "description": "Response for when a user is attempting to retrieve a byte range for\nspecific document and that document no longer exists or has been changed.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/precondition-failed", + "title": "Precondition Failed", + "status": 412, + "detail": "The request `ETag: db43771c6daa4f4da95e6d747483452c` is not valid.", + "instance": "/content" + } + } + } + }, + "416": { + "description": "Response for content when the specified range is not valid for the\ncontent or when the specified range requested is for more than one\npart.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/range-not-satisfiable", + "title": "Range not satisfiable", + "status": 416, + "detail": "The request Range bytes=1000-1100 is not valid.", + "instance": "/content" + } + } + } + } + } + }, + "head": { + "summary": "Preview document content.", + "description": "Consumers can make a `HEAD` request to see the response\nheaders without the response body. This allows previewing\nthe result of Content Negotiation with Content-Type,\nand Hyland-Item-Count headers.", + "operationId": "HeadContentForRenditionOfRevisionOfDocument", + "tags": [ + "Document Content" + ], + "parameters": [ + { + "$ref": "#/components/parameters/documentId" + }, + { + "$ref": "#/components/parameters/revisionId" + }, + { + "$ref": "#/components/parameters/fileTypeId" + }, + { + "$ref": "#/components/parameters/pages" + }, + { + "$ref": "#/components/parameters/contentContext" + }, + { + "$ref": "#/components/parameters/height" + }, + { + "$ref": "#/components/parameters/width" + }, + { + "$ref": "#/components/parameters/fit" + }, + { + "$ref": "#/components/parameters/Accept" + }, + { + "$ref": "#/components/parameters/If-Match" + }, + { + "$ref": "#/components/parameters/Range" + } + ], + "responses": { + "200": { + "description": "Preview the result of the document content.", + "headers": { + "Hyland-Item-Count": { + "$ref": "#/components/headers/Hyland-Item-Count" + } + } + }, + "206": { + "description": "Preview the result of content negotiation when the underlying\ncontent consists of multiple parts.", + "headers": { + "Content-Range": { + "$ref": "#/components/headers/Content-Range" + }, + "Accept-Ranges": { + "$ref": "#/components/headers/Accept-Ranges" + }, + "ETag": { + "$ref": "#/components/headers/ETag" + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "$ref": "#/components/responses/403-GENERIC-RESPONSE" + }, + "404": { + "description": "Response for content when the document does not exist, a requested\npage does not exist, or the user does not have rights to access.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/not-found", + "title": "Not Found", + "status": 404, + "detail": "The document does not exist, a requested page does not exist, or the user does not have rights to access.", + "instance": "/example-resource" + } + } + } + }, + "406": { + "$ref": "#/components/responses/406-GENERIC-RESPONSE" + }, + "412": { + "description": "Response for when a user is attempting to retrieve a byte range for\nspecific document and that document no longer exists or has been changed.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/precondition-failed", + "title": "Precondition Failed", + "status": 412, + "detail": "The request ETag: db43771c6daa4f4da95e6d747483452c is not valid.", + "instance": "/content" + } + } + } + }, + "416": { + "description": "Response for content when the specified range is not valid for the\ncontent or when the specified range requested is for more than one\npart.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/range-not-satisfiable", + "title": "Range not satisfiable", + "status": 416, + "detail": "The request Range bytes=1000-1100 is not valid.", + "instance": "/content" + } + } + } + } + } + } + }, + "/documents/queries": { + "post": { + "summary": "Submits a document query, with the provided search constraints.", + "description": "Submits a document query, with the provided search constraints.\n\nIf `Hyland-Include-Item-Count` header is set to true, the estimated number\nof documents that will be returned by the query will be included on the\nresponse in the `Hyland-Item-Count` header.\nEstimated because the number may vary in accuracy based on how the\nquery is formed, any filtering that takes places after the query is run, and\nif there are any external constraints.", + "operationId": "PostDocumentQuery", + "tags": [ + "Document Queries" + ], + "parameters": [ + { + "$ref": "#/components/parameters/Hyland-Include-Item-Count" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QueryInformation" + } + } + } + }, + "responses": { + "201": { + "description": "Query created. The location of the results of the query results\nis given in the Location header.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QueriesPostResponse" + } + } + }, + "headers": { + "Location": { + "$ref": "#/components/headers/Location" + }, + "Hyland-Item-Count": { + "$ref": "#/components/headers/Hyland-Item-Count" + } + } + }, + "400": { + "$ref": "#/components/responses/400-GENERIC-RESPONSE" + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + } + } + } + }, + "/documents/queries/{queryId}/results": { + "get": { + "summary": "Returns the documents results of a query.", + "description": "Returns the documents results of a query.", + "operationId": "GetResultCollectionForDocumentQuery", + "tags": [ + "Document Queries" + ], + "parameters": [ + { + "$ref": "#/components/parameters/queryId" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QueryResults" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/documents/queries/{queryId}/columns": { + "get": { + "summary": "Returns the display column configuration of a query.", + "description": "Returns the display column configuration of a query.", + "operationId": "GetColumnCollectionForDocumentQuery", + "tags": [ + "Document Queries" + ], + "parameters": [ + { + "$ref": "#/components/parameters/queryId" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DisplayColumnConfigurationCollection" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/documents/uploads": { + "post": { + "summary": "Prepare the staging area to start the upload process.", + "description": "Prepares the staging area to start the upload.\nReturns a reference to the file being uploaded.", + "operationId": "PostFileUploadMetadata", + "tags": [ + "File Upload" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadFileMetaData" + } + } + } + }, + "responses": { + "201": { + "description": "Upload staging area created.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadsPostResponse" + } + } + }, + "headers": { + "Location": { + "$ref": "#/components/headers/Location" + } + } + }, + "400": { + "description": "Invalid or missing file metadata. Such as missing extension\nor file or file part size that is less than or equal to zero.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "File Size cannot be negative.", + "instance": "/example-resource" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + } + } + } + }, + "/documents/uploads/{uploadId}": { + "put": { + "summary": "Upload file data.", + "description": "Upload file to a location identified by the unique file reference.\nThis end-point can be called multiple times, to upload multiple files.\nEach file will have it's own unique reference.\nWhen uploading a single file as chunks, upload it to the same file reference.", + "operationId": "PutFileUploadById", + "tags": [ + "File Upload" + ], + "parameters": [ + { + "$ref": "#/components/parameters/uploadId" + }, + { + "$ref": "#/components/parameters/filePart" + } + ], + "requestBody": { + "required": true, + "content": { + "application/octet-stream": { + "schema": { + "$ref": "#/components/schemas/Binary-Content" + } + } + } + }, + "responses": { + "204": { + "description": "Uploaded part has been successfully stored." + }, + "400": { + "description": "File part number is provided in incorrect format", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "File part number is provided in incorrect format", + "instance": "/example-resource" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + }, + "delete": { + "summary": "Delete file corresponding to the given uploadId", + "description": "Deletes an uploaded file corresponding to the given uploadId.\nThis can be used to cancel the upload.", + "operationId": "DeleteFileUploadById", + "tags": [ + "File Upload" + ], + "parameters": [ + { + "$ref": "#/components/parameters/uploadId" + } + ], + "responses": { + "204": { + "description": "File has been deleted." + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + } + } + } + }, + "/notes/{noteId}": { + "get": { + "tags": [ + "Notes" + ], + "summary": "Get the information of a given note.", + "operationId": "GetNoteByNoteId", + "parameters": [ + { + "name": "noteId", + "in": "path", + "description": "The identifier of the note.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NoteModel" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "description": "The user does not have rights to view this note.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/forbidden", + "title": "Forbidden", + "status": 403, + "detail": "Do not have rights to view this note.", + "instance": "/example-resource" + } + } + } + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + }, + "patch": { + "tags": [ + "Notes" + ], + "summary": "Update the information of a given note.", + "operationId": "PatchNoteByNoteId", + "parameters": [ + { + "name": "noteId", + "in": "path", + "description": "Id of the note to update.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Model containing the note data to update.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateNotePropertiesModel" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "The note was updated.", + "content": { + "application/json": { + "schema": { + "properties": { + "noteId": { + "description": "Identifier of the note.", + "type": "string" + } + } + } + } + } + }, + "400": { + "description": "The body of the request is not present or the note type of the note is unsupported.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "NoteType ID is a staple. Staples and back staples are currently unsupported Note Types.\"", + "instance": "/example-resource" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "description": "The user does not have rights to update the note or\nthe document lock prevents the note from being updated or\nthe noteType is not movable.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/forbidden", + "title": "Forbidden", + "status": 403, + "detail": "Do not have rights to update the note.", + "instance": "/example-resource" + } + } + } + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + }, + "422": { + "description": "The note type is not movable.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/unprocessable", + "title": "Unprocessable Request", + "status": 422, + "detail": "Cannot change position, size or page number of Note ID on document ID because the noteType ID is not movable." + } + } + } + } + } + }, + "delete": { + "tags": [ + "Notes" + ], + "summary": "Delete a single note.", + "operationId": "DeleteNoteByNoteId", + "parameters": [ + { + "name": "noteId", + "in": "path", + "description": "Id of the note to delete.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "Note was deleted without any issues." + }, + "400": { + "description": "The note is a note type that is unsupported.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "NoteType ID is a staple. Staples and back staples are currently unsupported Note Types.\"", + "instance": "/example-resource" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "description": "The user does not have rights to delete the note or\nthe document lock prevents the note from being deleted.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/forbidden", + "title": "Forbidden", + "status": 403, + "detail": "Do not have rights to delete the note.", + "instance": "/example-resource" + } + } + } + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + }, + "/note-types": { + "get": { + "summary": "Get note type metadata for all note types.", + "description": "Get the note type metadata for all note types in\nthe system.", + "operationId": "GetNoteTypeCollection", + "tags": [ + "Note Types" + ], + "parameters": [ + { + "in": "query", + "name": "ids", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "The unique identifiers of note types. Multiple values are supported and in a URL should be joined using the \"&\" character. Ex:?id=102&id=103" + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NoteTypeCollectionModel" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400-COLLECTIONPARAMETERS-RESPONSE" + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + } + } + } + }, + "/note-types/{noteTypeId}": { + "get": { + "summary": "Get note type metadata.", + "description": "Get note type metadata for the specified note type id.", + "operationId": "GetNoteTypeById", + "tags": [ + "Note Types" + ], + "parameters": [ + { + "name": "noteTypeId", + "in": "path", + "description": "The identifier of the note type.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Accept-Language" + } + ], + "responses": { + "200": { + "description": "OK", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NoteTypeModel" + } + } + } + }, + "401": { + "$ref": "#/components/responses/401-GENERIC-RESPONSE" + }, + "403": { + "$ref": "#/components/responses/403-GENERIC-RESPONSE" + }, + "404": { + "$ref": "#/components/responses/404-GENERIC-RESPONSE" + } + } + } + } + }, + "components": { + "securitySchemes": { + "Bearer": { + "type": "http", + "scheme": "bearer" + } + }, + "schemas": { + "FileTypeCollection": { + "description": "An array of file types.", + "properties": { + "items": { + "description": "An array of file types.", + "type": "array", + "items": { + "$ref": "#/components/schemas/FileType" + } + } + } + }, + "FileType": { + "description": "File type metadata.", + "properties": { + "id": { + "description": "The unique identifier of the file type.", + "type": "string" + }, + "name": { + "description": "The localized name of the file type.", + "type": "string" + }, + "systemName": { + "description": "The untranslated system name of the file type.\nLocalization is controlled by the Accept-Language header and\nthe language of the response is represented by the Content-Language\nheader.", + "type": "string" + } + } + }, + "DefaultUploadFileType": { + "description": "File type metadata for default upload.", + "properties": { + "id": { + "description": "The unique identifier of the file type.", + "type": "string" + } + } + }, + "KeywordTypeGroupCollectionModel": { + "description": "A representation of keyword type information for a document type", + "properties": { + "keywordOptions": { + "$ref": "#/components/schemas/KeywordOptions" + }, + "items": { + "description": "An array of keyword types grouped by the keyword type group they belong to.", + "type": "array", + "items": { + "$ref": "#/components/schemas/KeywordTypeGroupOnDocumentType" + } + } + } + }, + "KeywordOptions": { + "description": "A group containing keyword type options in relation to the document type\nthey belong to.", + "properties": { + "requiredForArchivalKeywordTypeIds": { + "description": "An array of required keyword type ids for a document to be stored.", + "type": "array", + "items": { + "type": "string" + } + }, + "requiredForRetrievalKeywordTypeIds": { + "description": "An array of required keyword type ids for a document to be retrieved.", + "type": "array", + "items": { + "type": "string" + } + }, + "readOnlyKeywordTypeIds": { + "description": "An array of read only keyword type ids for a document type.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "KeywordTypeGroupOnDocumentType": { + "description": "Keyword type group metadata.", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier of the keyword type group. Omitted id indicates Standalone Keyword Group." + }, + "keywordTypes": { + "description": "An array of keyword types.", + "type": "array", + "items": { + "$ref": "#/components/schemas/KeywordTypeOnDocumentType" + } + } + } + }, + "KeywordTypeOnDocumentType": { + "description": "Keyword type metadata.", + "properties": { + "id": { + "description": "The unique identifier of the keyword type.", + "type": "string" + } + }, + "required": [ + "id" + ] + }, + "QueryInformation": { + "description": "Represents the information required to execute a query.", + "type": "object", + "properties": { + "queryType": { + "description": "An array of query types.", + "type": "array", + "items": { + "$ref": "#/components/schemas/QueryType" + } + }, + "maxResults": { + "description": "Limits the number of results that the execution of\na query can create.", + "type": "integer", + "format": "int32" + }, + "queryKeywordCollection": { + "description": "An array of keywords used to execute a query.", + "type": "array", + "items": { + "$ref": "#/components/schemas/QueryKeyword" + } + }, + "documentDateRangeCollection": { + "description": "An array of date ranges used to execute a query.", + "type": "array", + "items": { + "$ref": "#/components/schemas/DateRange" + } + }, + "userDisplayColumns": { + "description": "An array of user defined display columns. If the\nquery already has display columns defined, the predefined\ndisplay columns will be ignored and the user defined\ndisplay columns will be used.", + "type": "array", + "items": { + "$ref": "#/components/schemas/UserDefinedDisplayColumn" + } + } + } + }, + "QueryType": { + "type": "object", + "required": [ + "type", + "ids" + ], + "description": "The type of query to execute. DocumentType, DocumentTypeGroup, and CustomQuery type queries are supported.\nSee the /custom-queries documentation for which CustomQuery types are supported.", + "properties": { + "type": { + "description": "The type of query to execute. DocumentType, DocumentTypeGroup, and CustomQuery type queries are supported.\nSee the /custom-queries documentation for which CustomQuery types are supported.", + "type": "string", + "enum": [ + "CustomQuery", + "DocumentType", + "DocumentTypeGroup" + ] + }, + "ids": { + "description": "An array of ids for the query", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "QueryKeyword": { + "type": "object", + "required": [ + "typeId", + "value" + ], + "description": "Represents a keyword required to execute a query.", + "properties": { + "typeId": { + "description": "The keyword type Id for the keyword.", + "type": "string" + }, + "value": { + "description": "The keyword value.", + "type": "string" + }, + "operator": { + "description": "Represents the operator for the keyword value of\nthis query keyword. Defaults to Equal if not present.", + "type": "string", + "enum": [ + "Equal", + "LessThan", + "GreaterThan", + "LessThanEqual", + "GreaterThanEqual", + "NotEqual", + "Literal" + ] + }, + "relation": { + "description": "Represents the relation of this query keyword to\nother query keywords. Defaults to And if not present.", + "type": "string", + "enum": [ + "And", + "Or", + "To" + ] + } + } + }, + "UserDefinedDisplayColumn": { + "type": "object", + "description": "Represents a DisplayColumn that the user has defined for this query.", + "properties": { + "keywordTypeId": { + "description": "The keyword type Id for the DisplayColumn. If the\nDisplayColumn is not of type Keyword, this property can be\nomitted and only the displayColumnType is required.", + "type": "string" + }, + "displayColumnType": { + "description": "The attribute type for the Display Column.", + "type": "string", + "enum": [ + "Keyword", + "DocumentId", + "DocumentName", + "DocumentDate", + "ArchivalDate", + "AuthorId", + "Batch", + "DocumentTypeGroup", + "DocumentTypeName" + ] + } + } + }, + "CurrencyFormat": { + "description": "Currency format metadata", + "properties": { + "id": { + "description": "The unique identifier of the currency format. Will be set to 'default' if\ncurrency is configured to use Workstation Regional Settings.", + "type": "string" + }, + "name": { + "description": "The name of the currency format.", + "type": "string" + }, + "currencySymbol": { + "description": "The symbol to represent the currency. For instance, the '$' in $1,000.00.", + "type": "string" + }, + "decimalPlaces": { + "description": "The number of decimal places represented after the decimal point.", + "type": "integer", + "format": "int64" + }, + "decimalSymbol": { + "description": "The symbol to represent the decimal place. For instance, the '.' in 100.00.", + "type": "string" + }, + "groupingDigits": { + "description": "The number of digits by which to group. Also the number of digits between grouping symbols. For instance, in\n$1,000,000.00 the number of grouping digits is 3.", + "type": "integer", + "format": "int64" + }, + "groupingSymbol": { + "description": "The symbol to represent the grouping of digits. For instance, the ',' in 1,000,000.00.", + "type": "string" + }, + "isoCurrencyName": { + "description": "The ISO name for the currency format if one exists.", + "type": "string" + }, + "hasCurrencySymbol": { + "description": "A value indicating whether the format allows for a currency symbols.", + "type": "boolean" + }, + "hasGroupSeparator": { + "description": "A value indicating whether a grouping symbol is specified. Ex. $9,999.00.", + "type": "boolean" + }, + "hasLeadingZero": { + "description": "A value indicating whether there is a leading zero.", + "type": "boolean" + }, + "hasMinusSign": { + "description": "A value indicating whether there is a a minus sign. If true, negativity is indicated with a minus sign \"-\". If\nfalse, negativity is indicated with parentheses around the value.", + "type": "boolean" + }, + "hasWhitespace": { + "description": "A value indicating whether there is a space between the currency symbol and positive values\nEx. $ 9.99\nEx. 9.99 $", + "type": "boolean" + }, + "hasWhitespaceOnNegative": { + "description": "A value indicating whether there is a space between the currency symbol and negative values. Ex. $ -9.99 Ex. -9.99 $.", + "type": "boolean" + }, + "isMinusSignAfter": { + "description": "A value indicating whether the minus is after the number. Only respected if hasMinusSign is also true.", + "type": "boolean" + }, + "isSymbolAfter": { + "description": "A value indicating whether the currency symbol goes after positive values\nEx. 9.99$.", + "type": "boolean" + }, + "isSymbolAfterOnNegative": { + "description": "A value indicating whether the currency symbol comes after the decimal value. Ex. 9.99$-.", + "type": "boolean" + }, + "isSymbolInsideNegative": { + "description": "A value indicating whether the Symbol goes inside the negative If MinusSign is false, SymbolInsideNegative is\nautomatically on. Ex. ($9.99), not $(9.99) If true, the currency symbol will come before the minus sign. Ex.\n9.99$- If false, the currency symbol will go after the minus sign. Ex. 9.99-$", + "type": "boolean" + } + } + }, + "CurrencyFormatCollection": { + "description": "An array of currency formats.", + "properties": { + "items": { + "description": "An array of currency formats.", + "type": "array", + "items": { + "$ref": "#/components/schemas/CurrencyFormat" + } + } + } + }, + "CustomQuery": { + "description": "Custom query metadata", + "properties": { + "id": { + "description": "The unique identifier of the custom query.", + "type": "string" + }, + "name": { + "description": "The localized name of the custom query object", + "type": "string" + }, + "systemName": { + "description": "The untranslated system name of the custom query.\nLocalization is controlled by the Accept-Language header and\nthe language of the response is represented by the Content-Language\nheader.", + "type": "string" + }, + "instructions": { + "description": "Information describing the usage and/or purpose of the custom query", + "type": "string" + }, + "dateOptions": { + "$ref": "#/components/schemas/CustomQueryDateSearchOptions" + }, + "queryType": { + "description": "The type of this custom query.\n\n`DocumentType` is a query configured to limit the results to preselected document type(s).\n\n`DocumentTypeGroup` is a query configured to limit the results to preselected document type group(s).\n\n`Keyword` is a query configured to limit the results to preselected keyword type(s).\n\n`SQL` is a query configured with a SQL statement.", + "type": "string", + "enum": [ + "DocumentType", + "DocumentTypeGroup", + "Keyword", + "SQL" + ] + } + } + }, + "CustomQueryDateSearchOptions": { + "description": "Date options set on a custom query", + "properties": { + "dateSearch": { + "description": "Date search option on the custom query", + "type": "string", + "enum": [ + "NoDate", + "SingleDate", + "DateRange" + ] + }, + "defaultDateRange": { + "$ref": "#/components/schemas/CustomQueryDefaultDateRange" + } + } + }, + "CustomQueryDefaultDateRange": { + "description": "Represents default date set on the custom query. This is set when date search option is set to 'SingleDate' or 'DateRange'.", + "properties": { + "start": { + "description": "Default start date.\nformat - date", + "type": "string", + "format": "date", + "example": "2018-02-21" + }, + "end": { + "description": "Default end date.\nThis is equal to `defaultStartDate` if single date option is set as default.\nformat - date", + "type": "string", + "format": "date", + "example": "2018-02-21" + } + } + }, + "CustomQueryCollection": { + "description": "An array of custom queries.", + "properties": { + "items": { + "description": "An array of custom queries.", + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomQuery" + } + } + } + }, + "CustomQueryKeywordTypeCollection": { + "description": "A lightweight array of keyword types for custom queries.", + "properties": { + "items": { + "description": "An array of keyword type identifiers.", + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomQueryKeywordType" + } + } + } + }, + "CustomQueryKeywordType": { + "description": "Keyword type identifier", + "properties": { + "id": { + "description": "The unique identifier of the keyword type", + "type": "string" + } + } + }, + "DateRange": { + "description": "Represents a range of dates.", + "properties": { + "start": { + "description": "The starting date of the date range.\nIf no start date is present, a default minimum date will be used.", + "type": "string" + }, + "end": { + "description": "The ending date of the date range.\nIf no end date is present, a default maximum date will be used.", + "type": "string" + } + } + }, + "QueriesPostResponse": { + "description": "Query handle information corresponding to a query.", + "type": "object", + "properties": { + "id": { + "description": "Unique handle for the query", + "type": "string" + } + } + }, + "QueryResults": { + "description": "Represents the result of a query", + "properties": { + "items": { + "description": "An array of Documents returned from executing a query.", + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentResult" + } + } + } + }, + "DocumentResult": { + "description": "Document metadata.", + "properties": { + "id": { + "description": "The unique identifier of the document.", + "type": "string" + }, + "displayColumns": { + "description": "An array of Display columns returned from executing a query.", + "type": "array", + "items": { + "$ref": "#/components/schemas/DisplayColumn" + } + } + } + }, + "DisplayColumn": { + "description": "Display column values.", + "properties": { + "index": { + "description": "Index representing the Display column configuration\nassociated with this Display column.", + "type": "string" + }, + "values": { + "description": "An array of values for the Display Column.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "DisplayColumnConfigurationCollection": { + "description": "Represents a collection of configurations of\na Display Column.", + "properties": { + "items": { + "description": "An array of Display Column Configurations returned from executing a query.", + "type": "array", + "items": { + "$ref": "#/components/schemas/DisplayColumnConfiguration" + } + } + } + }, + "DisplayColumnConfiguration": { + "description": "Represents the configuration of a Display Column.", + "properties": { + "index": { + "description": "Index representing the Display column configuration\nassociated with this Display column.", + "type": "integer", + "format": "int32" + }, + "type": { + "description": "Describes the type of Display Column. If the value is `Keyword` than the `keywordTypeId` will be populated as well.", + "type": "string", + "enum": [ + "Keyword", + "Attribute" + ] + }, + "heading": { + "description": "The Header value for the Display Column.", + "type": "string" + }, + "keywordTypeId": { + "description": "The Keyword Type associated with the Display Column.\nOnly necessary if the Display Column Type is \"Keyword\".", + "type": "string" + }, + "dataType": { + "description": "The Data Type of the value of the display column.\nThis is only necessary if the Display Column Type\nis not \"Keyword\". For Keyword Display Columns,\ndata type can be retrieved from the Keyword Type.", + "type": "string", + "enum": [ + "Numeric9", + "Numeric20", + "Alphanumeric", + "Currency", + "SpecificCurrency", + "Date", + "DateTime", + "FloatingPoint" + ] + } + } + }, + "KeywordValueRequest": { + "description": "A list of keyword values. Keyword values are represented as strings\nand blank keyword values are represented by an empty list of values.", + "properties": { + "value": { + "description": "Depending on the underlying keyword type datatype, the specific\nformat of the underlying string adheres to the following formatting\nrules.\n\nValues are normalized and locale specific formatting is not applied.\nFormatting to a specific currency is not applied. Consumers can\napply this formatting through libraries and client locale\npreferences. Determining data type or currency format\nis retrieved from other metadata resources.\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
Data Type Format Example
Numeric9 A whole positive number up to 9 digits, or\n negative number up to 8 digits without commas.123456789
-12345678
Numeric20 A whole number up to 20 digits without\n commas, or a negative number with 19 digits without commas.12345678901234567890
-1234567890123456789
Alphanumeric A string value. ABC 123
Currency Positive or negative numeric value with a\n whole number and decimal portion separated by a period.123456.00
SpecificCurrency Positive or negative numeric value\n with a whole number and decimal portion separated by a\n period. 123456.00
Date ISO-8601\n Date 2018-02-21
DateTime ISO-8601 Date and\n time without time zone. 2018-02-21T21:17:28
FloatingPoint Positive or negative numeric value\n with a whole number and decimal portion separated by a\n period. 123456.091231
", + "type": "string" + }, + "currencyFormatId": { + "description": "The Currency Format Id if the Keyword Type's data type is Specific Currency.", + "type": "string" + } + } + }, + "KeywordValueResponse": { + "allOf": [ + { + "$ref": "#/components/schemas/KeywordValueRequest" + } + ], + "properties": { + "formattedValue": { + "description": "A Keyword Value that has been formatted using locale specific formatting\nand Keyword Masking settings.", + "type": "string" + } + } + }, + "KeywordRequest": { + "description": "A keyword value and information about the related keyword type.", + "properties": { + "typeId": { + "description": "The unique identifier of the keyword type for this keyword value.", + "type": "string" + }, + "values": { + "description": "A List of keyword values that contain various formats of the keyword\nvalue.", + "type": "array", + "items": { + "$ref": "#/components/schemas/KeywordValueRequest" + } + } + } + }, + "KeywordResponse": { + "description": "A keyword value and information about the related keyword type.", + "properties": { + "typeId": { + "description": "The unique identifier of the keyword type for this keyword value.", + "type": "string" + }, + "values": { + "description": "A List of keyword values that contain various formats of the keyword\nvalue.", + "type": "array", + "items": { + "$ref": "#/components/schemas/KeywordValueResponse" + } + } + } + }, + "KeywordGroupRequest": { + "description": "A group of keyword values.", + "properties": { + "typeGroupId": { + "description": "The keyword type group identifier. This field will be omitted when\nnot associated with a `SingleInstance` or `MultiInstance` type\ngroup.", + "type": "string" + }, + "groupId": { + "description": "The identifier for the group of keywords. This field will be omitted\nwhen not associated with a `MultiInstance` type group. This field must be omitted\nif creating a new instance of a `MultiInstance` type group.", + "type": "string" + }, + "instanceId": { + "description": "The identifier used to track restricted keyword data and assign it to\nan instance of a 'MultiInstance' type group. This field is required for\nrestricted keyword values existing on a 'MultiInstance' type group and is provided\nby the GET Keywords on a document or GET Default Keywords response.", + "type": "string" + }, + "keywords": { + "description": "An array of keywords in the group.", + "type": "array", + "items": { + "$ref": "#/components/schemas/KeywordRequest" + } + } + }, + "required": [ + "keywords" + ] + }, + "KeywordGroupResponse": { + "description": "A group of keyword values.", + "properties": { + "typeGroupId": { + "description": "The keyword type group identifier. This field will be omitted when\nnot associated with a `SingleInstance` or `MultiInstance` type\ngroup.", + "type": "string" + }, + "groupId": { + "description": "The identifier for the group of keywords. This field will be omitted\nwhen not associated with a `MultiInstance` type group. This field must be omitted\nif creating a new instance of a `MultiInstance` type group.", + "type": "string" + }, + "instanceId": { + "description": "The identifier used to track restricted keyword data and assign it to\nan instance of a 'MultiInstance' type group.", + "type": "string" + }, + "keywords": { + "description": "An array of keywords in the group.", + "type": "array", + "items": { + "$ref": "#/components/schemas/KeywordResponse" + } + } + }, + "required": [ + "keywords" + ] + }, + "KeywordCollectionRequest": { + "description": "Instance data for keywords on a document", + "properties": { + "keywordGuid": { + "description": "Guid string to ensure integrity of restricted keyword values.", + "type": "string" + }, + "items": { + "description": "An array of keywords grouped by the keyword group they belong to.", + "type": "array", + "items": { + "$ref": "#/components/schemas/KeywordGroupRequest" + } + } + }, + "required": [ + "keywordGuid", + "items" + ] + }, + "KeywordCollectionResponse": { + "description": "Instance data for keywords on a document", + "properties": { + "keywordGuid": { + "description": "Guid string to ensure integrity of restricted keyword values.", + "type": "string" + }, + "items": { + "description": "An array of keywords grouped by the keyword group they belong to.", + "type": "array", + "items": { + "$ref": "#/components/schemas/KeywordGroupResponse" + } + } + }, + "required": [ + "keywordGuid", + "items" + ] + }, + "DocumentCollection": { + "description": "A list of documents.", + "properties": { + "items": { + "description": "An array of documents.", + "type": "array", + "items": { + "$ref": "#/components/schemas/Document" + } + } + } + }, + "Document": { + "description": "Document metadata.", + "properties": { + "id": { + "description": "The unique identifier of the document.", + "type": "string" + }, + "typeId": { + "description": "The unique identifier of the document type for this document.", + "type": "string" + }, + "name": { + "description": "The auto name string for this document.", + "type": "string" + }, + "createdByUserId": { + "description": "The unique identifier of the user that created this document.", + "type": "string" + }, + "storedDate": { + "description": "The date/time this document was stored.\nISO-8601 Date and\n time without time zone.", + "type": "string", + "example": "2018-02-21T21:17:28" + }, + "documentDate": { + "description": "The document date.", + "type": "string", + "format": "date", + "example": "2018-02-21" + }, + "status": { + "description": "The document status of Active, Deleted or Inactive.", + "type": "string", + "enum": [ + "Active", + "Deleted", + "Inactive" + ] + }, + "captureProperties": { + "description": "Meta-data information about the document that was brought in via scanning.", + "$ref": "#/components/schemas/CaptureProperties" + } + } + }, + "DocumentHistory": { + "properties": { + "items": { + "description": "An array of history items.", + "type": "array", + "items": { + "$ref": "#/components/schemas/HistoryItem" + } + } + } + }, + "HistoryItem": { + "properties": { + "action": { + "description": "The action taken on the document", + "type": "string" + }, + "logDate": { + "description": "The date/time this document action was logged.\nISO-8601 Date and\n time with milliseconds and without time zone.", + "type": "string", + "example": "2022-08-01T07:36:13.007" + }, + "message": { + "description": "The logged message.", + "type": "string" + }, + "userId": { + "description": "The user under which action was taken.", + "type": "string" + } + } + }, + "DocumentPatchRequest": { + "description": "Metadata that can be modified on a document.", + "properties": { + "documentDate": { + "description": "The document date.", + "type": "string", + "format": "date", + "example": "2018-02-21" + } + } + }, + "CaptureProperties": { + "description": "Meta-data information about the document that was brought in via scanning.", + "properties": { + "unidentified": { + "description": "Indicates if the document is unidentified.", + "type": "boolean" + }, + "reviewStatus": { + "description": "The review status of NeedsAttention, NeedsRescan or NeedsManagerAttention. This should be used in conjunction with the `unidentified` property.", + "type": "string", + "enum": [ + "NeedsAttention", + "NeedsRescan", + "NeedsManagerAttention" + ] + } + } + }, + "DocumentsPostResponse": { + "description": "Document handle information corresponding to a document.", + "type": "object", + "properties": { + "id": { + "description": "Unique handle for the document", + "type": "string" + } + } + }, + "MatchedDocumentCollectionResponse": { + "description": "List of matching documents along with the options available for each when archiving into a Revisable/Renditionable document type.", + "properties": { + "canAddAsNew": { + "description": "Boolean to indicate if the document can be stored as a new document.\nTo continue adding the document as a new document, a POST request to '/documents' end-point must be made with 'storeAsNew' property set to 'true' in 'DocumentArchiveProperties'.", + "type": "boolean" + }, + "items": { + "description": "List of matching documents along with the options available for each.", + "type": "array", + "items": { + "$ref": "#/components/schemas/MatchedDocument" + } + } + } + }, + "MatchedDocument": { + "description": "A Revisable/Renditionable document that matched along with the options available to archive.", + "type": "object", + "properties": { + "id": { + "description": "Id of the Revisable/Renditionable document that matched.", + "type": "string" + }, + "canAddAsRevision": { + "description": "Boolean indicating if the document can be added as a revision.\nTo add the document as a new revision, a POST request to '/documents/{id}/revisions' must me made.", + "type": "boolean" + }, + "canAddAsRendition": { + "description": "Boolean indicating if the document can be added as a rendition.\nTo add the document as a rendition, a POST request to '/documents/{id}/revisions/latest' must be made.", + "type": "boolean" + } + } + }, + "RevisionsPostResponse": { + "description": "Id of the newly created revision.", + "properties": { + "revisionId": { + "description": "Latest revision Id.", + "type": "string" + } + } + }, + "RenditionsPostResponse": { + "description": "Id of the newly created rendition, which is the File Type Id.", + "properties": { + "fileTypeId": { + "description": "File Type Id.", + "type": "string" + } + } + }, + "DocumentType": { + "description": "Document Type metadata.", + "properties": { + "id": { + "description": "The unique identifier of the document type.", + "type": "string" + }, + "name": { + "description": "The localized name of the document type.", + "type": "string" + }, + "systemName": { + "description": "The untranslated system name of the document type.\nLocalization is controlled by the Accept-Language header and\nthe language of the response is represented by the Content-Language\nheader.", + "type": "string" + }, + "defaultFileTypeId": { + "description": "The unique identifier of the default file format for the document type", + "type": "string" + }, + "documentDateDisplayName": { + "description": "The document date display name setting for the document type", + "type": "string" + }, + "autofillKeywordSetId": { + "description": "The Id of the autofill keyset associated with this document type, if any.", + "type": "string" + }, + "documentTypeGroupId": { + "description": "The id of the document type group the document type is assigned to.", + "type": "string" + }, + "revisionRenditionProperties": { + "description": "Revision/Rendition properties of the document type.", + "type": "object", + "$ref": "#/components/schemas/RevisionRenditionProperties" + } + } + }, + "RevisionRenditionProperties": { + "description": "Revision/Rendition settings of the document type.", + "properties": { + "revisable": { + "description": "Indicates if the document type is revisable", + "type": "boolean" + }, + "renditionable": { + "description": "Indicates if the document is renditionable", + "type": "boolean" + }, + "commentSettings": { + "description": "Comment settings on the document type", + "type": "object", + "$ref": "#/components/schemas/DocumentTypeCommentSettings" + } + } + }, + "DocumentTypeCommentSettings": { + "description": "Settings for comments on the document type.", + "properties": { + "allowComments": { + "description": "True if the document type is set to \"Allow Comments\", false otherwise.", + "type": "boolean" + }, + "forceComment": { + "description": "True if the document type is set to \"Force Comment\".", + "type": "boolean" + }, + "firstRevisionNoComment": { + "description": "True if document type is set to \"Save first revision with no comment\"", + "type": "boolean" + } + } + }, + "DocumentTypeCollection": { + "description": "An array of document types.", + "properties": { + "items": { + "description": "An array of document types.", + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentType" + } + } + } + }, + "DocumentTypeGroup": { + "description": "Document Type Group metadata", + "properties": { + "id": { + "description": "The unique identifier of the document type group.", + "type": "string" + }, + "name": { + "description": "The localized name of the document type group.", + "type": "string" + }, + "systemName": { + "description": "The untranslated system name of the document type group.\nLocalization is controlled by the Accept-Language header and\nthe language of the response is represented by the Content-Language\nheader.", + "type": "string" + } + } + }, + "DocumentTypeGroupCollection": { + "description": "An array of document type groups.", + "properties": { + "items": { + "description": "An array of document type groups", + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTypeGroup" + } + } + } + }, + "DocumentTypeGroupDocumentType": { + "description": "Document type identifier", + "properties": { + "id": { + "description": "The unique identifier of the document type", + "type": "string" + } + } + }, + "DocumentTypeGroupDocumentTypeCollection": { + "description": "A lightweight array of document types associated with a document type group", + "properties": { + "items": { + "description": "An array of document type identifiers.", + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTypeGroupDocumentType" + } + } + } + }, + "DocumentReindexProperties": { + "description": "Metadata that can be modified on a document.", + "properties": { + "targetDocumentTypeId": { + "description": "The document type id to be reindexed into.", + "type": "string" + }, + "targetFileTypeId": { + "description": "The file type id to be reindexed into. This is only necessary if attempting to change the\nfile type ID of the default rendition of the latest revision.", + "type": "string" + }, + "storeAsNew": { + "description": "Boolean indicating if the document should be reindexed as specified.\nThis should be used in conjunction with a Revisable/Renditionable document type to\nindicate that the document should be reindexed as specified regardless of the document type\nsettings for revisions and renditions.\nThis would be considered false by default and if it's a Revisable/Renditionable document type,\nexisting documents are checked to find matching documents for which this new document can be\nadded as a Revision/Rendition.", + "type": "boolean" + }, + "comment": { + "description": "The revision comment that will be saved during reindex if the document is\nrevisiable.", + "type": "string" + }, + "documentDate": { + "description": "The document date.", + "type": "string", + "format": "date", + "example": "2018-02-21" + }, + "keywordCollection": { + "description": "An array of keywords grouped by the keyword group they belong to.", + "type": "object", + "$ref": "#/components/schemas/KeywordCollectionRequest" + } + }, + "required": [ + "keywordCollection", + "targetDocumentTypeId" + ] + }, + "RevisionReindexProperties": { + "description": "Meta-data information required to reindex as revision", + "allOf": [ + { + "$ref": "#/components/schemas/DiscriminatorObject" + }, + { + "type": "object", + "properties": { + "comment": { + "description": "The revision comment that will be saved during reindex if the document\nis revisiable.", + "type": "string" + }, + "sourceDocumentId": { + "description": "The document id of the document that is being reindexed as a new revision.", + "type": "string" + }, + "appendNewRevision": { + "description": "A value indicating whether to append the source document at the end of the target document.\nOnly the image file type supports this option.", + "type": "boolean" + }, + "keywordCollection": { + "description": "An array of keywords grouped by the keyword group they belong to.", + "type": "object", + "$ref": "#/components/schemas/KeywordCollectionRequest" + } + } + } + ], + "required": [ + "sourceDocumentId", + "keywordCollection" + ] + }, + "RenditionReindexProperties": { + "description": "Meta-data information required to reindex as rendition", + "allOf": [ + { + "$ref": "#/components/schemas/DiscriminatorObject" + }, + { + "type": "object", + "properties": { + "comment": { + "description": "The rendition comment that will be saved during reindex if the document\nis revisiable.", + "type": "string" + }, + "targetFileTypeId": { + "description": "The file type id that the document will be reindexed to. If the file type does not need to be changed,\nthen this property does not need to be passed in.", + "type": "string" + }, + "sourceDocumentId": { + "description": "The document id of the document that is being reindexed as a new rendition.", + "type": "string" + }, + "appendPages": { + "description": "A value indicating whether to append the source document at the end of the target document.\nOnly the image file type supports this option.", + "type": "boolean" + }, + "keywordCollection": { + "description": "An array of keywords grouped by the keyword group they belong to.", + "type": "object", + "$ref": "#/components/schemas/KeywordCollectionRequest" + } + } + } + ], + "required": [ + "sourceDocumentId", + "keywordCollection" + ] + }, + "DocumentArchiveProperties": { + "description": "Meta-data information required to upload documents", + "properties": { + "documentTypeId": { + "description": "The Id of the document type to store the document into", + "type": "string" + }, + "fileTypeId": { + "description": "Id of the file Type for the document.", + "type": "string" + }, + "storeAsNew": { + "description": "Boolean indicating if the document should be stored as a new document.\nThis should be used in conjunction with a Revisable/Renditionable document type to indicate that the document should be stored as a new document regardless of the settings.\nThis would be considered false by default and if it's a Revisable/Renditionable document type, existing documents are checked to find matching documents for which this new document can be added as a Revision/Rendition.", + "type": "boolean" + }, + "comment": { + "description": "Comments if the document type is Revisable/Renditionable.", + "type": "string" + }, + "documentDate": { + "description": "Document's date", + "type": "string", + "format": "date", + "example": "2018-08-21" + }, + "uploads": { + "description": "List of references to uploaded files. The order of uploaded file references will be used for the document page order.", + "type": "array", + "items": { + "$ref": "#/components/schemas/Upload" + } + }, + "keywordCollection": { + "description": "An array of keywords grouped by the keyword group they belong to.", + "type": "object", + "$ref": "#/components/schemas/KeywordCollectionRequest" + } + }, + "required": [ + "documentTypeId", + "uploads", + "keywordCollection" + ] + }, + "RevisionArchiveProperties": { + "description": "Meta-data information required to upload revision", + "allOf": [ + { + "$ref": "#/components/schemas/DiscriminatorObject" + }, + { + "type": "object", + "properties": { + "comment": { + "description": "Comment for the revision", + "type": "string" + }, + "fileTypeId": { + "description": "Id of the File Type for the document. If File Type Id is not provided, Document Type's default File Type will be used.", + "type": "string" + }, + "uploads": { + "description": "List of references to uploaded files. The order of uploaded file references will be used for the document page order.", + "type": "array", + "items": { + "$ref": "#/components/schemas/Upload" + } + }, + "keywordCollection": { + "description": "An array of keywords grouped by the keyword group they belong to.", + "type": "object", + "$ref": "#/components/schemas/KeywordCollectionRequest" + } + } + } + ], + "required": [ + "uploads" + ] + }, + "RenditionArchiveProperties": { + "description": "Meta-data information required to upload rendition", + "allOf": [ + { + "$ref": "#/components/schemas/DiscriminatorObject" + }, + { + "type": "object", + "properties": { + "comment": { + "description": "Comment for the rendition", + "type": "string" + }, + "fileTypeId": { + "description": "Id of the File Type for the document. If a File Type Id is not provided, then the Document Type's default File Type ID will be used. If the revision already contains the File Type Id then 400 Bad Request will be returned.", + "type": "string" + }, + "uploads": { + "description": "List of references to uploaded files. The order of uploaded file references will be used for the document page order.", + "type": "array", + "items": { + "$ref": "#/components/schemas/Upload" + } + }, + "keywordCollection": { + "description": "An array of keywords grouped by the keyword group they belong to.", + "type": "object", + "$ref": "#/components/schemas/KeywordCollectionRequest" + } + }, + "required": [ + "uploads" + ] + } + ] + }, + "Upload": { + "description": "Reference to an uploaded file.", + "type": "object", + "properties": { + "id": { + "description": "Unique file reference", + "type": "string" + } + } + }, + "UploadsPostResponse": { + "description": "Unique file reference corresponding to the uploaded file.", + "type": "object", + "properties": { + "id": { + "description": "Unique reference for the uploaded file", + "type": "string" + }, + "filePartSize": { + "description": "Size in bytes of the file parts that will be uploaded. All file parts except the last one must be of size filePartSize. Last part will be either less than or equal to filePartSize.", + "type": "integer", + "format": "int32" + }, + "numberOfParts": { + "description": "Total number of parts a file must be divided into when uploading. This must be used in conjunction with filePartSize.", + "type": "integer", + "format": "int32" + } + } + }, + "UploadFileMetaData": { + "description": "Meta-data information about the file being uploaded.", + "properties": { + "fileExtension": { + "description": "Extension of the file being uploaded. The extension does not need a leading period `.`", + "type": "string" + }, + "fileSize": { + "description": "Size of the file in bytes. Recommended maximum size of a file is 4GB, but the maximum size is only limited by the programming language being used and the host's file system.", + "type": "integer", + "format": "int64" + } + }, + "required": [ + "fileExtension", + "fileSize" + ] + }, + "Revision": { + "description": "Revision metadata", + "properties": { + "id": { + "description": "The unique identifier of the revision.", + "type": "string" + }, + "revisionNumber": { + "description": "The revision number for display purposes and provide ordering of revisions.", + "type": "integer", + "format": "int32" + } + } + }, + "RevisionCollection": { + "description": "An Array of revisions.", + "properties": { + "items": { + "description": "The unique identifier of the revisions.", + "type": "array", + "items": { + "$ref": "#/components/schemas/Revision" + } + } + } + }, + "Rendition": { + "description": "Rendition metadata", + "properties": { + "fileTypeId": { + "description": "The unique identifier of the rendition.", + "type": "string" + }, + "created": { + "description": "The date the rendition was stored.", + "type": "string" + }, + "pageCount": { + "description": "The number of pages in the rendition.", + "type": "integer", + "format": "int32" + }, + "createdByUserId": { + "description": "The user ID of the revision creator.", + "type": "string" + }, + "comment": { + "description": "A comment for the revision.", + "type": "string" + } + } + }, + "RenditionCollection": { + "description": "An Array of renditions.", + "properties": { + "items": { + "description": "An array of Renditions", + "type": "array", + "items": { + "$ref": "#/components/schemas/Rendition" + } + } + } + }, + "Problem-Detail-Locking-Error": { + "type": "object", + "description": "The Problem Detail\nformat defines a way to carry machine-readable details of errors in a\nHTTP response to avoid the need to define new error response formats for\nHTTP APIs.\n\nProblem details can be extended and defined for specific\nproblem types.", + "properties": { + "type": { + "type": "string", + "format": "uri", + "description": "An absolute URI that identifies the problem type. When\ndereferenced, it should provide human-readable documentation\nfor the problem type (e.g., using HTML)." + }, + "title": { + "type": "string", + "description": "A short, human-readable summary of the problem type. It should\nnot change from occurrence to occurrence of the problem." + }, + "status": { + "type": "integer", + "format": "int32", + "description": "The HTTP status code generated by the origin server for this\noccurrence of the problem.", + "minimum": 100, + "maximum": 600, + "exclusiveMaximum": true + }, + "detail": { + "type": "string", + "description": "A human readable explanation specific to this occurrence of the\nproblem." + }, + "instance": { + "type": "string", + "format": "uri", + "description": "A URI reference that identifies the specific occurrence of\nthe problem. It may or may not yield further information\nif dereferenced." + }, + "lockInfo": { + "$ref": "#/components/schemas/LockInfo" + } + } + }, + "LockInfo": { + "type": "object", + "description": "Information on a lock that has been put on an item.", + "properties": { + "lockedByUserId": { + "type": "string", + "description": "A unique identifier of the user that holds the lock on this item." + }, + "lockType": { + "$ref": "#/components/schemas/LockType" + }, + "currentLock": { + "$ref": "#/components/schemas/DocumentLockStatus" + } + }, + "example": { + "lockInfo": { + "lockedByUserId": "103", + "lockType": "Keywords", + "currentLock": "DocumentCheckoutInSameSession" + } + } + }, + "LockInfoCollection": { + "description": "A collection of LockInfo objects.", + "properties": { + "items": { + "description": "A collection of LockInfo objects.", + "type": "array", + "items": { + "$ref": "#/components/schemas/LockInfo" + } + } + } + }, + "DocumentLockStatus": { + "description": "The type of lock which is currently locking the document.", + "type": "string", + "enum": [ + "DocumentLock", + "DocumentCheckout", + "DocumentCheckoutInSameSession", + "Persistent", + "Process" + ] + }, + "LockType": { + "description": "The type of lock to retrieve.\nCurrently, only keyword locks are supported.", + "type": "string", + "enum": [ + "Keywords" + ] + }, + "Binary-Content": { + "description": "Binary content in the request or response for file uploads and downloads.", + "type": "string", + "format": "binary" + }, + "KeywordTypeCollection": { + "description": "An array of keyword types.", + "properties": { + "items": { + "description": "An array of keyword types.", + "type": "array", + "items": { + "$ref": "#/components/schemas/KeywordType" + } + } + } + }, + "KeywordType": { + "description": "Keyword type metadata.", + "properties": { + "id": { + "description": "The unique identifier of the keyword type.", + "type": "string" + }, + "systemName": { + "description": "The untranslated name of the keyword type.\nLocalization is controlled by the Accept-Language header and\nthe language of the response is represented by the Content-Language\nheader.", + "type": "string" + }, + "name": { + "description": "The name of the keyword type. This value is localized.", + "type": "string" + }, + "dataType": { + "description": "Describes the type of data represented by the keyword type.\n\n`Numeric9` represents a number up to 9 digits in length.\n\n`Numeric20` represents a number up to 20 digits in length.\n\n`Alphanumeric` represents any value with letters and/or numbers.\n\n`Currency` represents a monetary value. The currency format used is\n'built-in' to the keyword type.\n\n`SpecificCurrency` represents a monetary value and allows multiple\n currency formats to be used with the same keyword type.\n\n`Date` represents a date.\n\n`DateTime` represents both a date and a time.\n\n`FloatingPoint` represents numeric values that have variable decimal\n point locations.", + "type": "string", + "enum": [ + "Numeric9", + "Numeric20", + "Alphanumeric", + "Currency", + "SpecificCurrency", + "Date", + "DateTime", + "FloatingPoint" + ] + }, + "usedForRetrieval": { + "description": "Classification to determine if the keyword type is used for document retrieval.", + "type": "boolean" + }, + "invisible": { + "description": "Indicates if the keyword type should be made invisible in UI contexts.", + "type": "boolean" + }, + "alphanumericSettings": { + "$ref": "#/components/schemas/AlphanumericSettings" + }, + "currencyFormatId": { + "description": "The Currency Format Id if the Keyword Type's data type is Currency. If Specific Currency, this\nwill be the default currency format id set on the Specific Currency Keyword Type. When Regional Workstation\nSettings is used an id of 'default' is used.", + "type": "string" + }, + "isSecurityMasked": { + "description": "A value indicating whether the keyword type is configured for security masking.\nWhen the value is true and the keyword values have not been unmasked,\nthe corresponding keyword value should be treated as readonly.", + "type": "boolean" + }, + "maskSettings": { + "$ref": "#/components/schemas/KeywordTypeMaskSettings" + } + } + }, + "KeywordTypeMaskSettings": { + "description": "Masking settings for a particular keyword type.", + "properties": { + "fullFieldRequired": { + "description": "A value indicating whether the keyword type required the entire field filled out.", + "type": "boolean" + }, + "maskString": { + "description": "A value that specifies the mask string for a masked keyword type.", + "type": "string" + }, + "staticCharacters": { + "description": "The configured static characters for the keyword mask.", + "type": "string" + }, + "storeMask": { + "description": "A value indicating whether the mask should be stored to the database or stripped.", + "type": "boolean" + } + } + }, + "AlphanumericSettings": { + "description": "Configuration settings for alphanumeric keyword types.", + "properties": { + "caseOptions": { + "description": "The character case option for keyword values of this keyword type.\n\n`Uppercase` keyword values are stored using only uppercase characters.\n\n`MixedCase` keyword values are stored using upper and lower case characters.", + "type": "string", + "enum": [ + "Uppercase", + "MixedCase" + ] + }, + "maximumLength": { + "description": "The maximum number of characters allowed by the keyword type.", + "type": "integer", + "format": "int64" + }, + "storageOptions": { + "description": "The database storage method used for keyword values of this keyword type.\n\n`SingleTable` indicates keyword values are stored in a single database table.\n\n`DualTable` indicates keyword values are stored in two database tables.", + "type": "string", + "enum": [ + "SingleTable", + "DualTable" + ] + } + } + }, + "KeywordTypeGroupCollection": { + "description": "An array of keyword type groups.", + "properties": { + "items": { + "description": "An array of keyword type groups.", + "type": "array", + "items": { + "$ref": "#/components/schemas/KeywordTypeGroup" + } + } + } + }, + "KeywordTypeGroup": { + "description": "Keyword type group metadata.", + "properties": { + "id": { + "description": "The unique identifier of the keyword type group.", + "type": "string" + }, + "systemName": { + "description": "The untranslated name of the keyword type group.\nLocalization is controlled by the Accept-Language header and\nthe language of the response is represented by the Content-Language\nheader.", + "type": "string" + }, + "name": { + "description": "The name of the keyword type group. This value is localized.", + "type": "string" + }, + "storageType": { + "description": "The type of storage for the keyword type group.\n\n`SingleInstance` represents a set of keyword types that will appear\nonly once and are associated with a specific group type. Each single\ninstance keyword type may have only one value instance.\n\n`MultiInstance` represents a set of keyword types that can appear\nmultiple times as a related instance. Each multiple instance keyword\ntype may have only one value instance within a particular group.", + "type": "string", + "enum": [ + "SingleInstance", + "MultiInstance" + ] + } + } + }, + "KeywordTypeGroupKeywordTypeCollection": { + "description": "A list of keyword types grouped by the keyword type group they belong to.", + "properties": { + "items": { + "description": "An array of keyword types.", + "type": "array", + "items": { + "$ref": "#/components/schemas/KeywordTypeGroupKeywordType" + } + } + } + }, + "KeywordTypeGroupKeywordType": { + "description": "Keyword type metadata.", + "properties": { + "id": { + "description": "The unique identifier of the keyword type.", + "type": "string" + } + }, + "required": [ + "id" + ] + }, + "AutoFillKeywordSetCollection": { + "description": "An array of autofill keyword sets.", + "properties": { + "items": { + "description": "An array of autofill keyword sets.", + "type": "array", + "items": { + "$ref": "#/components/schemas/AutoFillKeywordSet" + } + } + } + }, + "AutoFillKeywordSet": { + "description": "Autofill keyword set metadata.", + "properties": { + "id": { + "description": "The unique identifier of the autofill keyword set.", + "type": "string" + }, + "systemName": { + "description": "The untranslated name of the autofill keyword set.\nLocalization is controlled by the Accept-Language header and\nthe language of the response is represented by the Content-Language\nheader.", + "type": "string" + }, + "name": { + "description": "The name of the autofill keyword set. This value is localized.", + "type": "string" + }, + "primaryKeywordTypeId": { + "description": "The keyword type id of the autofill keyword set's primary key.", + "type": "string" + }, + "external": { + "description": "Indicates that the autofill keyword set is external.", + "type": "boolean" + } + } + }, + "AutoFillKeywordSetKeywordTypeCollection": { + "description": "A representation of keyword type information for a autofill type.", + "properties": { + "items": { + "description": "A lightweight array of keyword types associated with an auto fill keyword set.", + "type": "array", + "items": { + "$ref": "#/components/schemas/AutoFillKeywordSetKeywordType" + } + } + } + }, + "AutoFillKeywordSetKeywordType": { + "description": "Keyword type identifier", + "properties": { + "id": { + "description": "The unique identifier of the keyword type.", + "type": "string" + } + } + }, + "KeywordSetDataCollection": { + "description": "An array of autofill keyword set data.", + "properties": { + "items": { + "description": "An array of autofill keyword set data.", + "type": "array", + "items": { + "$ref": "#/components/schemas/KeywordSetData" + } + } + } + }, + "KeywordSetData": { + "description": "A collection of autofill keyword set keyword data.", + "properties": { + "id": { + "description": "The unique identifier of the autofill keyword set data.", + "type": "string" + }, + "keywords": { + "description": "An array of keyword values associated with an auto fill keyword set data object.", + "type": "array", + "items": { + "$ref": "#/components/schemas/AutoFillKeywordSetKeyword" + } + } + } + }, + "AutoFillKeywordSetKeyword": { + "description": "Autofill keyword set keyword data.", + "properties": { + "typeId": { + "description": "The keyword type id.", + "type": "string" + }, + "value": { + "description": "The keyword value.", + "type": "string" + } + } + }, + "AutoFillMultipleMatchesResponse": { + "description": "A collection of AutoFill Keyword Set Data Set items that match a Primary\nKeyword value.", + "properties": { + "autoFillKeywordSetData": { + "description": "An array of autofill keyword set data.", + "type": "array", + "items": { + "$ref": "#/components/schemas/KeywordSetData" + } + }, + "selectMultiple": { + "description": "A value indicating whether multiple AutoFill Keyword Set Data Set selection\nis allowed.", + "type": "boolean" + } + } + }, + "IndexingModifiersPostResponse": { + "description": "Result Keyword Collection.", + "properties": { + "keywordCollection": { + "$ref": "#/components/schemas/KeywordCollectionResponse" + } + } + }, + "ReindexAutoFillExpansionModifierProperties": { + "description": "The properties required to perform AutoFill Keyword Set expansion.", + "allOf": [ + { + "$ref": "#/components/schemas/DiscriminatorObject" + }, + { + "type": "object", + "properties": { + "documentId": { + "description": "The ID of the Document containing the keywords.", + "type": "string" + }, + "targetDocumentTypeId": { + "description": "The ID of the target Document Type containing the keywords being modified. This is the\ntarget Document Type during the Reindex process.", + "type": "string" + } + } + }, + { + "$ref": "#/components/schemas/AutoFillExpansionProperties" + } + ], + "required": [ + "documentId", + "targetDocumentTypeId" + ] + }, + "ArchivalAutoFillExpansionModifierProperties": { + "description": "The properties required to perform AutoFill Keyword Set expansion.", + "allOf": [ + { + "$ref": "#/components/schemas/DiscriminatorObject" + }, + { + "type": "object", + "properties": { + "documentTypeId": { + "description": "The ID of the Document Type containing the keywords being modified. This is the document\ntype that the document is going to be archived into.", + "type": "string" + } + } + }, + { + "$ref": "#/components/schemas/AutoFillExpansionProperties" + } + ], + "required": [ + "documentTypeId" + ] + }, + "AutoFillExpansionProperties": { + "description": "Properties that are required during AutoFill expansion regardless of method of expansion.", + "properties": { + "autoFillKeywordSetDataIds": { + "description": "If selection is required for expansion to occur, the list of AutoFill Keyword\nSet Data Set Ids that the server will expand.", + "type": "array", + "items": { + "description": "AutoFill Keyword Set Data Set Id", + "type": "string" + } + }, + "autoFillKeywordSetPrimaryKeyword": { + "description": "The Primary Keyword value of the AutoFill Keyword Set.", + "$ref": "#/components/schemas/AutoFillKeywordSetKeyword" + }, + "keywordCollection": { + "$ref": "#/components/schemas/KeywordCollectionRequest" + }, + "keywordGroupIndex": { + "description": "If any Keyword Types configured for an AutoFill Keyword Set are part of\na MultiInstance Keyword Group, a index that relates to the order of\nMultiInstance Keyword Group passed in is required. Example. If expanding\ninto a MultiInstance Group that has 3 instances, and the second one is the desired\ngroup to be updated, 2 should be passed in.", + "type": "integer", + "format": "int32" + } + }, + "required": [ + "autoFillKeywordSetPrimaryKeyword", + "keywordCollection" + ] + }, + "AddNotePropertiesModel": { + "properties": { + "noteTypeId": { + "description": "The id of the note type.", + "type": "string" + }, + "page": { + "description": "The page the note is on. This defaults to 1 if not present.", + "type": "integer", + "format": "int64" + }, + "x": { + "description": "This defaults to 0 if not present.", + "$ref": "#/components/schemas/XCoordinate" + }, + "y": { + "description": "This defaults to 0 if not present.", + "$ref": "#/components/schemas/YCoordinate" + }, + "width": { + "description": "This defaults to 0 if not present.", + "$ref": "#/components/schemas/NoteWidth" + }, + "height": { + "description": "This defaults to 0 if not present.", + "$ref": "#/components/schemas/NoteHeight" + }, + "text": { + "description": "The text in the note.\nThis defaults to the configured note type default text if not present.", + "type": "string" + } + }, + "required": [ + "noteTypeId" + ] + }, + "UpdateNotePropertiesModel": { + "properties": { + "position": { + "$ref": "#/components/schemas/UpdateNotePositionModel" + }, + "size": { + "$ref": "#/components/schemas/UpdateNoteSizeModel" + }, + "text": { + "description": "The text in the note.", + "type": "string" + } + } + }, + "NotePrivileges": { + "properties": { + "canModify": { + "type": "boolean", + "description": "A value indicating if the note can be updated." + }, + "canDelete": { + "type": "boolean", + "description": "A value indicating if the note can be deleted." + }, + "canUpdatePrivacyOptions": { + "type": "boolean", + "description": "A value indicating if the user is allowed to update privacy options." + } + } + }, + "NoteCollectionModel": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NoteModel" + } + } + } + }, + "NoteModel": { + "properties": { + "id": { + "description": "The unique id of the note.", + "type": "string" + }, + "noteTypeId": { + "description": "The note type identifier of the note.", + "type": "string" + }, + "title": { + "description": "The title of the note.", + "type": "string" + }, + "text": { + "description": "The text in the note.", + "type": "string" + }, + "createdByUserId": { + "description": "The unique id of the user this note was created by.", + "type": "string" + }, + "created": { + "description": "The date the note was created.", + "type": "string" + }, + "documentId": { + "description": "The unique id of the document the note is affiliated with.", + "type": "string" + }, + "documentRevisionId": { + "description": "The revision number that the note is affiliated with.", + "type": "string" + }, + "page": { + "description": "The page the note is on.", + "type": "integer", + "format": "int64" + }, + "x": { + "$ref": "#/components/schemas/XCoordinate" + }, + "y": { + "$ref": "#/components/schemas/YCoordinate" + }, + "width": { + "$ref": "#/components/schemas/NoteWidth" + }, + "height": { + "$ref": "#/components/schemas/NoteHeight" + }, + "privileges": { + "description": "The user's privileges for the note.", + "$ref": "#/components/schemas/NotePrivileges" + }, + "displayFlags": { + "description": "The flag values set for the note.", + "$ref": "#/components/schemas/NoteDisplayFlags" + } + } + }, + "XCoordinate": { + "description": "The x coordinate of the note.\nThe x coordinate on image documents is given in pixels from the left side.\nThe x coordinate on text documents is given in number of characters from the left side.", + "type": "integer", + "format": "int32" + }, + "YCoordinate": { + "description": "The y coordinate of the note.\nThe y coordinate on image documents is given in pixels from the top.\nThe y coordinate on text documents is given in number of lines from the top.", + "type": "integer", + "format": "int32" + }, + "NoteWidth": { + "description": "The width of the note.\nThe width on image documents is given in pixels.\nThe width on text documents is given in number of characters.", + "type": "integer", + "format": "int32" + }, + "NoteHeight": { + "description": "The height of the note.\nThe height on image documents is given in pixels.\nThe height on text documents is given in number of lines.", + "type": "integer", + "format": "int32" + }, + "NoteTypeCollectionModel": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NoteTypeModel" + } + } + } + }, + "NoteTypeModel": { + "type": "object", + "description": "Note type metadata.", + "properties": { + "color": { + "$ref": "#/components/schemas/Color" + }, + "displayFlags": { + "description": "Note type display metadata.", + "$ref": "#/components/schemas/NoteTypeDisplayFlags" + }, + "flavor": { + "type": "string", + "description": "The style of the note type.", + "enum": [ + "Note", + "Highlight", + "Arrow", + "Ellipse", + "OverlappedText", + "IconStamp" + ] + }, + "fontId": { + "description": "The id of the font used with the note type.", + "type": "string" + }, + "iconId": { + "description": "The id of the icon used with the note type.", + "type": "string" + }, + "id": { + "description": "The id of the note type.", + "type": "string" + }, + "name": { + "description": "The localized name of the note type.", + "type": "string" + }, + "systemName": { + "description": "The untranslated name of the note type.\nLocalization is controlled by the Accept-Language header and\nthe language of the response is represented by the Content-Language\nheader.", + "type": "string" + }, + "userPrivileges": { + "description": "User privileges for the note type.", + "$ref": "#/components/schemas/NoteTypeUserPrivileges" + } + } + }, + "Color": { + "type": "object", + "description": "The color of the note type.", + "properties": { + "r": { + "type": "string", + "description": "The red(R) component of a color." + }, + "g": { + "type": "string", + "description": "The green(G) component of a color." + }, + "b": { + "type": "string", + "description": "The blue(B) component of a color." + }, + "a": { + "type": "string", + "description": "The alpha(A) component of a color." + } + } + }, + "NoteTypeDisplayFlags": { + "type": "object", + "description": "Note type display metadata.", + "properties": { + "allPages": { + "type": "boolean", + "description": "A value indicating whether the note type should be repeated on all pages." + }, + "allRevisions": { + "type": "boolean", + "description": "A value indicating whether the note type should appear on all revisions." + }, + "createOpenNoteWindow": { + "type": "boolean", + "description": "A value indicating whether a note created from this note type should display open when creation is completed." + }, + "deleteWithPage": { + "type": "boolean", + "description": "A value indicating whether a note associated with this note type should be deleted when the page the note is on is deleted." + }, + "floatOnWindow": { + "type": "boolean", + "description": "A value indicating that notes associated with this note type should not be bound to the document." + }, + "hideNoteWindow": { + "type": "boolean", + "description": "A value indicating whether a note created from this note type should be hidden." + }, + "moveable": { + "type": "boolean", + "description": "A value indicating the note associated with this note type should be movable." + }, + "noPrivacyOptions": { + "type": "boolean", + "description": "A value indicating the note type should not allow privacy options to be set on a note." + }, + "open": { + "type": "boolean", + "description": "A value indicating the note associated with this note type should be displayed open." + }, + "privacyNoDelete": { + "type": "boolean", + "description": "A value indicating that only the creator of a note from this note type can delete the note." + }, + "privacyNoModify": { + "type": "boolean", + "description": "A value indicating that only the creator of a note from this note type can modify the note." + }, + "privacyNoView": { + "type": "boolean", + "description": "A value indicating that only the creator of a note from this note type can view the note." + }, + "stampKeepOriginalSize": { + "type": "boolean", + "description": "A value indicating that Icon Stamps cannot have their icons resized." + }, + "stampTransparent": { + "type": "boolean", + "description": "A value indicating that Icon Stamps are transparent." + } + } + }, + "NoteTypeUserPrivileges": { + "type": "object", + "properties": { + "create": { + "type": "boolean", + "description": "A value indicating whether the user can create a note of the note type." + }, + "view": { + "type": "boolean", + "description": "A value indicating whether the user can view a note of the note type." + } + } + }, + "NoteDisplayFlags": { + "type": "object", + "description": "Note display metadata.", + "properties": { + "allowView": { + "type": "boolean", + "description": "A value indicating whether an user other than the creator of the note can view the note." + }, + "allowModify": { + "type": "boolean", + "description": "A value indicating whether an user other than the creator of the note can modify the note." + }, + "allowDelete": { + "type": "boolean", + "description": "A value indicating whether an user other than the creator of the note can delete the note." + } + } + }, + "NotesPostResponse": { + "description": "Id of the newly created note.", + "properties": { + "noteId": { + "description": "Identifier of the note.", + "type": "string" + } + } + }, + "UpdateNotePositionModel": { + "description": "Model containing position metadata to update the note with.", + "required": [ + "x", + "y" + ], + "properties": { + "x": { + "$ref": "#/components/schemas/XCoordinate" + }, + "y": { + "$ref": "#/components/schemas/YCoordinate" + } + } + }, + "UpdateNoteSizeModel": { + "description": "Model containing size metadata to update the note with.", + "required": [ + "width", + "height" + ], + "properties": { + "width": { + "$ref": "#/components/schemas/NoteWidth" + }, + "height": { + "$ref": "#/components/schemas/NoteHeight" + } + } + }, + "Problem-Detail": { + "type": "object", + "description": "The Problem Detail\nformat defines a way to carry machine-readable details of errors in a\nHTTP response to avoid the need to define new error response formats for\nHTTP APIs.\n\nProblem details can be extended and defined for specific\nproblem types.", + "properties": { + "type": { + "type": "string", + "format": "uri", + "description": "An absolute URI that identifies the problem type. When\ndereferenced, it should provide human-readable documentation\nfor the problem type (e.g., using HTML)." + }, + "title": { + "type": "string", + "description": "A short, human-readable summary of the problem type. It should\nnot change from occurrence to occurrence of the problem." + }, + "status": { + "type": "integer", + "format": "int32", + "description": "The HTTP status code generated by the origin server for this\noccurrence of the problem.", + "minimum": 100, + "maximum": 600, + "exclusiveMaximum": true + }, + "detail": { + "type": "string", + "description": "A human readable explanation specific to this occurrence of the\nproblem." + }, + "instance": { + "type": "string", + "format": "uri", + "description": "A URI reference that identifies the specific occurrence of\nthe problem. It may or may not yield further information\nif dereferenced." + } + } + }, + "DiscriminatorObject": { + "type": "object", + "description": "The generic type with a discriminator property, which is an object type name\nthat is used to differentiate between other schemas which may satisfy\nthe payload description.", + "properties": { + "objectType": { + "type": "string", + "description": "The object type of the schema specified for the request body or used for the response payload. " + } + } + } + }, + "parameters": { + "acceptImageHeader": { + "name": "Accept", + "in": "header", + "description": "The Accept request HTTP header advertises which content types,\nexpressed as MIME types, the client is able to understand. Using\ncontent negotiation, the server then selects one of the proposals,\nuses it and informs the client of its choice with the `Content-Type`\nresponse header.", + "required": true, + "schema": { + "type": "string", + "enum": [ + "image/png", + "image/jpeg", + "image/tiff", + "image/svg+xml", + "text/plain", + "text/html", + "application/pdf", + "application/x-isys-docpack" + ] + } + }, + "documentTypeId": { + "description": "The unique identifier of a document type.", + "name": "documentTypeId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + "documentTypeGroupId": { + "description": "The unique identifier of a document type group", + "name": "documentTypeGroupId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + "documentId": { + "name": "documentId", + "in": "path", + "description": "The unique identifier of a document.", + "required": true, + "schema": { + "type": "string" + } + }, + "queryId": { + "name": "queryId", + "in": "path", + "description": "The unique identifier of a query.", + "required": true, + "schema": { + "type": "string" + } + }, + "currencyFormatId": { + "name": "currencyFormatId", + "in": "path", + "description": "The unique identifier of a currency format.", + "required": true, + "schema": { + "type": "string" + } + }, + "customQueryId": { + "name": "customQueryId", + "in": "path", + "description": "The unique identifier of a custom query.", + "required": true, + "schema": { + "type": "string" + } + }, + "fileTypeId": { + "description": "The unique identifier of a file type.", + "name": "fileTypeId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + "lockTypeParam": { + "description": "The type of lock to retrieve.\nCurrently, only keyword locks are supported.", + "schema": { + "type": "string", + "enum": [ + "Keywords" + ] + }, + "name": "lockType", + "in": "query", + "required": true + }, + "pageId": { + "description": "The unique identifier of a document page.", + "name": "pageId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + "pages": { + "description": "The page to be retrieved. Currently only a single\npage number is supported. If a valid page number is provided,\nthe `Hyland-Item-Count` custom header will be included on the response.\n\nIf the page number does not exist or the value provided is not a single\npage number, a 404 Not Found will be returned.\n\nPage number is one-based.", + "name": "pages", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + "contentContext": { + "description": "The context for which the document content is being retrieved. If retrieving the document\nfor client purposes, it is recommended to use the context to verify correct privileges\nand log the most appropriate message in document history.", + "name": "context", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "View", + "Download", + "EmailAttachment" + ] + } + }, + "uploadId": { + "name": "uploadId", + "in": "path", + "description": "The unique reference to the file being uploaded", + "required": true, + "schema": { + "type": "string" + } + }, + "filePart": { + "name": "filePart", + "in": "query", + "required": true, + "description": "part number of the file to upload. Part numbers are sequential and start at 1.", + "schema": { + "type": "integer", + "format": "Int32" + } + }, + "fit": { + "name": "fit", + "in": "query", + "required": false, + "description": "Options for scaling the resource to the provided dimensions.", + "schema": { + "type": "string", + "enum": [ + "Both", + "Height", + "Stretch", + "Width" + ] + } + }, + "height": { + "name": "height", + "in": "query", + "required": false, + "description": "The height of the resource in pixels.", + "schema": { + "type": "integer", + "format": "Int32" + } + }, + "width": { + "name": "width", + "in": "query", + "required": false, + "description": "The width of the resource in pixels.", + "schema": { + "type": "integer", + "format": "Int32" + } + }, + "revisionId": { + "description": "The unique identifier of a document revision.", + "name": "revisionId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + "Hyland-Include-Item-Count": { + "name": "Hyland-Include-Item-Count", + "in": "header", + "description": "The Hyland-Include-Item-Count custom header field can be used by user agents\nto indicate that the item count should be included in the response.", + "required": false, + "schema": { + "type": "boolean" + }, + "example": true + }, + "keywordTypeId": { + "description": "The unique identifier of a keyword type.", + "name": "keywordTypeId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + "keywordTypeGroupId": { + "description": "The unique identifier of a keyword type group.", + "name": "keywordTypeGroupId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + "autoFillKeywordSetId": { + "description": "The unique identifier of a autofill keyword set.", + "name": "autoFillKeywordSetId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + "Accept-Language": { + "name": "Accept-Language", + "in": "header", + "description": "The Accept-Language\nheader field can be used by user agents to\nindicate the set of natural languages that are preferred in the\nresponse. Language tags are defined in RFC 5646. If none of the \nlanguages given are supported, a default language will be returned.", + "required": false, + "schema": { + "type": "string" + }, + "example": "en-US" + }, + "Accept": { + "name": "Accept", + "in": "header", + "description": "The Accept\nheader field can be used by consumers to specify response media types\nthat are preferred.\n\nUpon receiving this header the server will use Proactive\nContent Negotiation to determine the \"best guess\" format for the\nconsumer.\n\nIn cases where the underlying resource can not be returned as a\nrequested format, the server may make a determination of which format\nof the content will be returned.\n\nIn cases where the Accept header is omitted or has the value `*/*`, the\nserver will make a determination of which format of the content will\nbe returned.\n\nConsumers should inspect the `Content-Type` response header to determine\nthe actual format of the content returned.\n\nReview documentation for the specific end point for more detailed information.\non Content Negotiation or how the Accept header.", + "required": false, + "schema": { + "type": "string" + }, + "example": "*/*" + }, + "If-Match": { + "name": "If-Match", + "in": "header", + "description": "The If-Match\nheader field makes the request method conditional on\nthe recipient origin server either having at least one current\nrepresentation of the target resource, when the field-value is \"*\",\nor having a current representation of the target resource that has an\nentity-tag matching a member of the list of entity-tags provided in\nthe field-value.", + "required": false, + "schema": { + "type": "string" + }, + "example": "0731961c81c7403389c42cffd60f209d" + }, + "Range": { + "name": "Range", + "in": "header", + "description": "The Range\nheader field on a request modifies the method semantics to request\ntransfer of only one or more subranges of the selected representation\ndata, rather than the entire selected representation data.\n\nThe Range unit `bytes` is supported to represent byte ranges of the\npage or document of the specific rendition's content. \n\nFor the cases where the Range header is omitted, the full document or page will be \nreturned. ", + "required": false, + "schema": { + "type": "string" + }, + "example": "bytes 1-1000" + } + }, + "headers": { + "Hyland-Item-Count": { + "description": "The Hyland-Item-Count custom\nresponse header indicates the total number of items included in a response.", + "schema": { + "type": "string" + } + }, + "Hyland-Original-Width": { + "description": "Width of original first page before any scaling", + "schema": { + "type": "integer", + "format": "int32" + } + }, + "Hyland-Original-Height": { + "description": "Height of original first page before any scaling", + "schema": { + "type": "integer", + "format": "int32" + } + }, + "Hyland-Original-Orientation": { + "description": "The TIFF orientation of the original first page, before any re-orientation", + "schema": { + "type": "string" + } + }, + "Hyland-Original-Media-Type": { + "description": "The original media type of the first page", + "schema": { + "type": "string" + } + }, + "Hyland-Original-X-DPI": { + "description": "The X dpi of the original first page before any scaling", + "schema": { + "type": "integer", + "format": "int32" + } + }, + "Hyland-Original-Y-DPI": { + "description": "The Y dpi of the original first page before any scaling", + "schema": { + "type": "integer", + "format": "int32" + } + }, + "Hyland-Rendition-Width": { + "description": "The width of the first page rendition", + "schema": { + "type": "integer", + "format": "int32" + } + }, + "Hyland-Rendition-Height": { + "description": "The height of the first page rendition", + "schema": { + "type": "integer", + "format": "int32" + } + }, + "Hyland-Rendition-X-DPI": { + "description": "The X dpi of the first page rendition", + "schema": { + "type": "integer", + "format": "int32" + } + }, + "Hyland-Rendition-Y-DPI": { + "description": "The Y dpi of the first page rendition", + "schema": { + "type": "integer", + "format": "int32" + } + }, + "Hyland-Available-Pages": { + "description": "The total number of available pages in the file", + "schema": { + "type": "integer", + "format": "int32" + } + }, + "Content-Language": { + "description": "The Content-Language header field describes the natural language(s)\nof the intended audience for the representation. Note that this\nmight not be equivalent to all the languages used within the\nrepresentation.\n\nReference: https://tools.ietf.org/html/rfc7231#section-3.1.3.2", + "schema": { + "type": "string" + }, + "example": "en" + }, + "Location": { + "description": "The \"Location\" header field is used in some responses to refer to a\nspecific resource in relation to the response. The type of\nrelationship is defined by the combination of request method and\nstatus code semantics.\nReference: https://tools.ietf.org/html/rfc7231#section-7.1.2", + "schema": { + "type": "string" + }, + "example": "http://www.example.net/index.html" + }, + "Content-Range": { + "description": "The Content-Range header field is sent in a single part 206 (Partial\nContent) response to indicate the partial range of the selected\nrepresentation enclosed as the message payload, sent in each part of a\nmultipart 206 response to indicate the range enclosed within each body\npart, and sent in 416 (Range Not Satisfiable) responses to provide\ninformation about the selected representation.\nReference: https://tools.ietf.org/html/rfc7233#section-4.2", + "schema": { + "type": "string" + }, + "example": "bytes 1-1000/3000" + }, + "Accept-Ranges": { + "description": "The Accept-Ranges header field allows a server to indicate that it\nsupports range requests for the target resource.\nReference: https://tools.ietf.org/html/rfc7233#section-2.3", + "schema": { + "type": "string" + }, + "example": "bytes" + }, + "ETag": { + "description": "The \"ETag\" header field in a response provides the current entity-tag\nfor the selected representation, as determined at the conclusion of\nhandling the request.\nReference: https://tools.ietf.org/html/rfc7232#section-2.3", + "schema": { + "type": "string" + }, + "example": "0731961c81c7403389c42cffd60f209d" + } + }, + "responses": { + "200-rendering-conversion-content": { + "description": "OK", + "content": { + "image/png": { + "schema": { + "$ref": "#/components/schemas/Binary-Content" + } + }, + "image/jpeg": { + "schema": { + "$ref": "#/components/schemas/Binary-Content" + } + }, + "image/tiff": { + "schema": { + "$ref": "#/components/schemas/Binary-Content" + } + }, + "image/svg+xml": { + "schema": { + "$ref": "#/components/schemas/Binary-Content" + } + }, + "text/plain": { + "schema": { + "$ref": "#/components/schemas/Binary-Content" + } + }, + "text/html": { + "schema": { + "$ref": "#/components/schemas/Binary-Content" + } + }, + "application/pdf": { + "schema": { + "$ref": "#/components/schemas/Binary-Content" + } + }, + "application/x-isys-docpack": { + "schema": { + "$ref": "#/components/schemas/Binary-Content" + } + } + }, + "headers": { + "Hyland-Original-Width": { + "$ref": "#/components/headers/Hyland-Original-Width" + }, + "Hyland-Original-Height": { + "$ref": "#/components/headers/Hyland-Original-Height" + }, + "Hyland-Original-Orientation": { + "$ref": "#/components/headers/Hyland-Original-Orientation" + }, + "Hyland-Original-Media-Type": { + "$ref": "#/components/headers/Hyland-Original-Media-Type" + }, + "Hyland-Original-X-DPI": { + "$ref": "#/components/headers/Hyland-Original-X-DPI" + }, + "Hyland-Original-Y-DPI": { + "$ref": "#/components/headers/Hyland-Original-Y-DPI" + }, + "Hyland-Rendition-Width": { + "$ref": "#/components/headers/Hyland-Rendition-Width" + }, + "Hyland-Rendition-Height": { + "$ref": "#/components/headers/Hyland-Rendition-Height" + }, + "Hyland-Rendition-X-DPI": { + "$ref": "#/components/headers/Hyland-Rendition-X-DPI" + }, + "Hyland-Rendition-Y-DPI": { + "$ref": "#/components/headers/Hyland-Rendition-Y-DPI" + }, + "Hyland-Available-Pages": { + "$ref": "#/components/headers/Hyland-Available-Pages" + } + } + }, + "400-COLLECTIONPARAMETERS-RESPONSE": { + "description": "Response when the user tries to combine id and systemName query parameters.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "Cannot combine id and systemName query parameters.", + "instance": "/example-resource" + } + } + } + }, + "409-LOCKING-RESPONSE": { + "description": "Response when the user tries to obtain a lock on an item that is already locked.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail-Locking-Error" + }, + "example": { + "type": "https://example.net/conflict", + "title": "Conflict", + "status": 409, + "detail": "Cannot obtain lock. Item is already locked.", + "instance": "/example-resource", + "lockInfo": { + "lockedByUserId": "2", + "lockType": "Keywords", + "currentLock": "DocumentCheckoutInSameSession" + } + } + } + } + }, + "401-GENERIC-RESPONSE": { + "description": "Response when the user does not supply valid authorization credentials.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/unauthorized", + "title": "Unauthorized", + "status": 401, + "detail": "Not authorized to perform this action.", + "instance": "/example-resource" + } + } + } + }, + "404-GENERIC-RESPONSE": { + "description": "Response when the resource does not exist or the user does not have rights\nto the resource.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/not-found", + "title": "Not Found", + "status": 404, + "detail": "The requested resource was not found.", + "instance": "/example-resource" + } + } + } + }, + "403-GENERIC-RESPONSE": { + "description": "Response when the user does not have permissions to access the resource.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/forbidden", + "title": "Forbidden", + "status": 403, + "detail": "Do not have permissions to access this resource.", + "instance": "/example-resource" + } + } + } + }, + "400-GENERIC-RESPONSE": { + "description": "Response for when a bad request is provided.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "Request is missing required data.", + "instance": "/example-resource" + } + } + } + }, + "406-GENERIC-RESPONSE": { + "description": "Response for when a response matching the list of acceptable values\ndefined in `Accept` cannot be served.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem-Detail" + }, + "example": { + "type": "https://example.net/not-acceptable", + "title": "Not Acceptable", + "status": 406, + "detail": "The requested media type is not acceptable.", + "instance": "/example-resource" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/client/deployment/src/test/resources/openapi/issue-933-security.yaml b/client/deployment/src/test/resources/openapi/issue-933-security.yaml new file mode 100644 index 000000000..a5fd0ee8d --- /dev/null +++ b/client/deployment/src/test/resources/openapi/issue-933-security.yaml @@ -0,0 +1,47 @@ +openapi: 3.0.3 +info: + title: Generated API + version: "1.0" +paths: + /: + post: + operationId: doOperation + security: + - client_id: [ ] + - oauth: [ read, write ] + - bearerAuth: [ ] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/MultiplicationOperation' + responses: + "200": + description: OK +components: + schemas: + MultiplicationOperation: + type: object + securitySchemes: + client_id: + type: apiKey + in: header + name: X-Client-Id + x-key-type: clientId + bearerAuth: + type: http + scheme: bearer + oauth: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: https://example.com/oauth/authorize + tokenUrl: https://example.com/oauth/token + scopes: + read: Grants read access + write: Grants write access + admin: Grants read and write access to administrative information + clientCredentials: + tokenUrl: http://localhost:8382/oauth/token + scopes: + read: read \ No newline at end of file diff --git a/client/integration-tests/additional-properties/pom.xml b/client/integration-tests/additional-properties/pom.xml index 5ab4a4c42..dcf21dff4 100644 --- a/client/integration-tests/additional-properties/pom.xml +++ b/client/integration-tests/additional-properties/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-additional-properties - Quarkus - Openapi Generator - Integration Tests - Client - Additional Properties + Quarkus - OpenAPI Generator - Integration Tests - Client - Additional Properties diff --git a/client/integration-tests/additional-properties/src/main/resources/application.properties b/client/integration-tests/additional-properties/src/main/resources/application.properties index d0997432d..b64974415 100644 --- a/client/integration-tests/additional-properties/src/main/resources/application.properties +++ b/client/integration-tests/additional-properties/src/main/resources/application.properties @@ -5,10 +5,6 @@ quarkus.openapi-generator.codegen.spec.with_additional_properties_as_attr_yaml.a # with additional properties as attribute = FALSE quarkus.openapi-generator.codegen.spec.no_additional_properties_as_attr_yaml.additional-properties-as-attribute=false - - - - - +quarkus.keycloak.devservices.enabled=false diff --git a/client/integration-tests/array-enum/pom.xml b/client/integration-tests/array-enum/pom.xml index 5e42973a5..ccb5fd1c5 100644 --- a/client/integration-tests/array-enum/pom.xml +++ b/client/integration-tests/array-enum/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-array-enum - Quarkus - Openapi Generator - Integration Tests - Client - Array enum + Quarkus - OpenAPI Generator - Integration Tests - Client - Array enum Example project for OpenAPI with array of enums diff --git a/client/integration-tests/array-enum/src/main/resources/application.properties b/client/integration-tests/array-enum/src/main/resources/application.properties index dbf38ebe3..1cb92c2d8 100644 --- a/client/integration-tests/array-enum/src/main/resources/application.properties +++ b/client/integration-tests/array-enum/src/main/resources/application.properties @@ -1 +1,2 @@ -quarkus.rest-client.array_enum_yaml.url=http://localhost:8080 \ No newline at end of file +quarkus.rest-client.array_enum_yaml.url=http://localhost:8080 +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/bean-validation/pom.xml b/client/integration-tests/bean-validation/pom.xml index 1663c3140..021f68a8d 100644 --- a/client/integration-tests/bean-validation/pom.xml +++ b/client/integration-tests/bean-validation/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-bean-validation - Quarkus - Openapi Generator - Integration Tests - Client - Bean Validation + Quarkus - OpenAPI Generator - Integration Tests - Client - Bean Validation Example project for usage of the bean-validation property diff --git a/client/integration-tests/bean-validation/src/main/openapi/bean-validation-true.yaml b/client/integration-tests/bean-validation/src/main/openapi/bean-validation-true.yaml index 68041e319..6da47a50c 100644 --- a/client/integration-tests/bean-validation/src/main/openapi/bean-validation-true.yaml +++ b/client/integration-tests/bean-validation/src/main/openapi/bean-validation-true.yaml @@ -64,4 +64,9 @@ components: size: type: number minimum: 1.0 - maximum: 10.0 \ No newline at end of file + maximum: 10.0 + list: + type: array + items: + type: string + minLength: 1 diff --git a/client/integration-tests/bean-validation/src/main/openapi/issue-976.yaml b/client/integration-tests/bean-validation/src/main/openapi/issue-976.yaml new file mode 100644 index 000000000..4e1d21a8d --- /dev/null +++ b/client/integration-tests/bean-validation/src/main/openapi/issue-976.yaml @@ -0,0 +1,80 @@ +--- +openapi: 3.0.3 +info: + title: Test API + version: "1.0" +paths: + /{pathParam}: + post: + tags: + - ValidatedEndpointIssue976 + operationId: test + parameters: + - name: queryParam + in: query + schema: + $ref: '#/components/schemas/ValidatedObjectIssue976' + - name: pathParam + in: path + description: pathParam description + required: true + schema: + type: string + maxLength: 14 + minLength: 14 + pattern: '^[0-9]{14}$' + example: '19318085994179' + - name: headerParam + in: header + description: 'Header description' + required: false + schema: + maxLength: 32 + minLength: 32 + pattern: '^[a-z0-9]{32}$' + type: string + example: 3cfdad6e03c24d0ab7112dce75cdba35 + - name: cookieParam + in: cookie + required: true + schema: + type: string + minLength: 10 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ValidatedObjectIssue976' + responses: + "200": + description: OK + content: + text/plain: + schema: + type: string +components: + schemas: + ValidatedObjectIssue976: + type: object + description: Some object to be validated + required: + - id + - name + - secondName + - size + properties: + id: + type: integer + minimum: 1 + maximum: 100 + name: + type: string + pattern: "[a-zA-Z]*" + minLength: 1 + maxLength: 10 + secondName: + type: string + size: + type: number + minimum: 1.0 + maximum: 10.0 \ No newline at end of file diff --git a/client/integration-tests/bean-validation/src/main/resources/application.properties b/client/integration-tests/bean-validation/src/main/resources/application.properties index e3cf8e15d..06930b256 100644 --- a/client/integration-tests/bean-validation/src/main/resources/application.properties +++ b/client/integration-tests/bean-validation/src/main/resources/application.properties @@ -1,2 +1,4 @@ quarkus.openapi-generator.codegen.spec.bean_validation_true_yaml.use-bean-validation = true -quarkus.openapi-generator.codegen.spec.bean_validation_false_yaml.use-bean-validation = false \ No newline at end of file +quarkus.openapi-generator.codegen.spec.issue_976_yaml.use-bean-validation = true +quarkus.openapi-generator.codegen.spec.bean_validation_false_yaml.use-bean-validation = false +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/bean-validation/src/test/java/io/quarkiverse/openapi/generator/it/BeanValidationTest.java b/client/integration-tests/bean-validation/src/test/java/io/quarkiverse/openapi/generator/it/BeanValidationTest.java index 4e5d34a43..89ff485a9 100644 --- a/client/integration-tests/bean-validation/src/test/java/io/quarkiverse/openapi/generator/it/BeanValidationTest.java +++ b/client/integration-tests/bean-validation/src/test/java/io/quarkiverse/openapi/generator/it/BeanValidationTest.java @@ -5,6 +5,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.Parameter; import java.util.Arrays; import java.util.stream.Stream; @@ -16,12 +17,15 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.PathParam; import org.junit.jupiter.api.Test; import org.openapi.quarkus.bean_validation_false_yaml.api.UnvalidatedEndpointApi; import org.openapi.quarkus.bean_validation_false_yaml.model.UnvalidatedObject; import org.openapi.quarkus.bean_validation_true_yaml.api.ValidatedEndpointApi; import org.openapi.quarkus.bean_validation_true_yaml.model.ValidatedObject; +import org.openapi.quarkus.issue_976_yaml.api.ValidatedEndpointIssue976Api; import io.quarkus.test.junit.QuarkusTest; @@ -63,6 +67,7 @@ void testValidationAnnotationsAreInPlaceModel() throws Exception { assertThat(name.getAnnotationsByType(Size.class)).hasSize(2); assertThat(name.getAnnotationsByType(Size.class)[0].min()).isEqualTo(1); assertThat(name.getAnnotationsByType(Size.class)[1].max()).isEqualTo(10); + assertThat(name.isAnnotationPresent(Valid.class)).isTrue(); assertThat(size.isAnnotationPresent(DecimalMin.class)).isTrue(); assertThat(size.getAnnotation(DecimalMin.class).value()).isEqualTo("1.0"); @@ -94,7 +99,41 @@ void testValidationAnnotationsAreSkippedModel() throws Exception { assertThat(id.isAnnotationPresent(Max.class)).isFalse(); assertThat(name.isAnnotationPresent(Pattern.class)).isFalse(); assertThat(name.isAnnotationPresent(Size.List.class)).isFalse(); + assertThat(name.isAnnotationPresent(Valid.class)).isFalse(); assertThat(size.isAnnotationPresent(DecimalMin.class)).isFalse(); assertThat(size.isAnnotationPresent(DecimalMax.class)).isFalse(); } + + @Test + void testValidationAnnotationsAreInPlaceApiIssue976() { + Method method = ValidatedEndpointIssue976Api.class.getMethods()[0]; + Annotation[][] annotationsPerParameter = method.getParameterAnnotations(); + + Parameter pathParam = Arrays.stream(method.getParameters()) + .filter(p -> p.getName().equals("pathParam")) + .findFirst().get(); + + Parameter headerParam = Arrays.stream(method.getParameters()) + .filter(p -> p.getName().equals("headerParam")) + .findFirst().get(); + + Boolean validationAnnotationExists = Arrays.stream(annotationsPerParameter) + .allMatch(annotations -> Arrays.stream(annotations) + .filter(a -> a.annotationType().equals(Valid.class)).toList() + .size() == 1); + + assertThat(validationAnnotationExists).isTrue(); + + assertThat(pathParam.isAnnotationPresent(Valid.class)).isTrue(); + assertThat(pathParam.isAnnotationPresent(NotNull.class)).isTrue(); + assertThat(pathParam.isAnnotationPresent(Pattern.class)).isTrue(); + assertThat(pathParam.isAnnotationPresent(Size.List.class)).isTrue(); + assertThat(pathParam.isAnnotationPresent(PathParam.class)).isTrue(); + + assertThat(headerParam.isAnnotationPresent(Valid.class)).isTrue(); + assertThat(headerParam.isAnnotationPresent(NotNull.class)).isFalse(); + assertThat(headerParam.isAnnotationPresent(Size.List.class)).isTrue(); + assertThat(headerParam.isAnnotationPresent(Pattern.class)).isTrue(); + assertThat(headerParam.isAnnotationPresent(HeaderParam.class)).isTrue(); + } } diff --git a/client/integration-tests/beanparam/pom.xml b/client/integration-tests/beanparam/pom.xml index a73d12a8a..7becd9518 100644 --- a/client/integration-tests/beanparam/pom.xml +++ b/client/integration-tests/beanparam/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-beanparam - Quarkus - Openapi Generator - Integration Tests - Client - BeanParam + Quarkus - OpenAPI Generator - Integration Tests - Client - BeanParam Example project for @BeanParam usage @@ -27,8 +27,8 @@ test - com.github.tomakehurst - wiremock-jre8 + org.wiremock + wiremock test diff --git a/client/integration-tests/beanparam/src/main/resources/application.properties b/client/integration-tests/beanparam/src/main/resources/application.properties new file mode 100644 index 000000000..9de819ddd --- /dev/null +++ b/client/integration-tests/beanparam/src/main/resources/application.properties @@ -0,0 +1 @@ +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/change-custom-template-directory/pom.xml b/client/integration-tests/change-custom-template-directory/pom.xml index 9532fb2bc..21ae66723 100644 --- a/client/integration-tests/change-custom-template-directory/pom.xml +++ b/client/integration-tests/change-custom-template-directory/pom.xml @@ -7,7 +7,7 @@ 3.0.0-SNAPSHOT quarkus-openapi-generator-it-change-custom-template-directory - Quarkus - Openapi Generator - Integration Tests - Client - Change custom template directory + Quarkus - OpenAPI Generator - Integration Tests - Client - Change custom template directory @@ -20,8 +20,8 @@ test - com.github.tomakehurst - wiremock-jre8 + org.wiremock + wiremock test diff --git a/client/integration-tests/change-custom-template-directory/src/main/custom/templates/api.qute b/client/integration-tests/change-custom-template-directory/src/main/custom/templates/api.qute index ba096f06d..a6557bf17 100644 --- a/client/integration-tests/change-custom-template-directory/src/main/custom/templates/api.qute +++ b/client/integration-tests/change-custom-template-directory/src/main/custom/templates/api.qute @@ -4,10 +4,10 @@ package {package}; import {imp.import}; {/for} import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; -{#if hasAuthMethods || custom-register-providers.orEmpty.size > 0} +{#if openapi:hasAuthMethods(operations) || custom-register-providers.orEmpty.size > 0} import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; {/if} -{#if hasAuthMethods} +{#if openapi:hasAuthMethods(operations)} import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders; import {package}.auth.CompositeAuthenticationProvider; {#if client-headers-factory is 'default'} @@ -34,10 +34,10 @@ import io.quarkiverse.openapi.generator.annotations.GeneratedParam; * {#if appDescription}

{appDescription}

{/if} */ {/if} -@Path("{#if useAnnotatedBasePath}{contextPath}{/if}{commonPath}") -@RegisterRestClient({#if defaultServerUrl}baseUri="{defaultServerUrl}",{/if}configKey="{configKey}") +@Path("{commonPath}") +@RegisterRestClient({#if defaultServerUrl.or('') != ''}baseUri="{defaultServerUrl}",{/if} configKey="{configKey}") @GeneratedClass(value="{openapi:parseUri(inputSpec)}", tag = "{baseName}") -{#if hasAuthMethods} +{#if openapi:hasAuthMethods(operations)} @RegisterProvider(CompositeAuthenticationProvider.class) {#when client-headers-factory} {#is 'default'} @@ -106,13 +106,13 @@ public interface {classname} { {#if is-resteasy-reactive} @jakarta.ws.rs.BeanParam {op.operationIdCamelCase}MultipartForm multipartForm{#if op.hasPathParams},{/if}{! !}{#for p in op.pathParams}{#include templates.path_params param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasQueryParams},{/if}{! - !}{#for p in op.queryParams}{#include templates.query_params param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParams},{/if}{! + !}{#for p in op.queryParams}{#include templates.query_params param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParam},{/if}{! !}{#for p in op.bodyParams}{#include bodyParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasHeaderParams},{/if}{! !}{#for p in op.headerParams}{#include headerParams.qute param=p/}{#if p_hasNext}, {/if}{/for} {#else} @org.jboss.resteasy.annotations.providers.multipart.MultipartForm {op.operationIdCamelCase}MultipartForm multipartForm{#if op.hasPathParams},{/if}{! !}{#for p in op.pathParams}{#include pathParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasQueryParams},{/if}{! - !}{#for p in op.queryParams}{#include queryParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParams},{/if}{! + !}{#for p in op.queryParams}{#include queryParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParam},{/if}{! !}{#for p in op.bodyParams}{#include bodyParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasHeaderParams},{/if}{! !}{#for p in op.headerParams}{#include headerParams.qute param=p/}{#if p_hasNext}, {/if}{/for} {/if} diff --git a/client/integration-tests/change-custom-template-directory/src/main/resources/application.properties b/client/integration-tests/change-custom-template-directory/src/main/resources/application.properties index 1d8f83320..fd3f253cc 100644 --- a/client/integration-tests/change-custom-template-directory/src/main/resources/application.properties +++ b/client/integration-tests/change-custom-template-directory/src/main/resources/application.properties @@ -1,3 +1,4 @@ quarkus.openapi-generator.codegen.template-base-dir=custom/templates quarkus.openapi-generator.codegen.spec.simple_openapi_yaml.base-package=org.simple.openapi -quarkus.rest-client.simple_openapi_yaml.uri=http://localhost:8080 \ No newline at end of file +quarkus.rest-client.simple_openapi_yaml.uri=http://localhost:8080 +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/change-directory/pom.xml b/client/integration-tests/change-directory/pom.xml index 49bfa265e..6ec30ed95 100644 --- a/client/integration-tests/change-directory/pom.xml +++ b/client/integration-tests/change-directory/pom.xml @@ -7,7 +7,7 @@ 3.0.0-SNAPSHOT quarkus-openapi-generator-it-change-directory - Quarkus - Openapi Generator - Integration Tests - Client - Change directory + Quarkus - OpenAPI Generator - Integration Tests - Client - Change directory @@ -20,8 +20,8 @@ test - com.github.tomakehurst - wiremock-jre8 + org.wiremock + wiremock test diff --git a/client/integration-tests/change-directory/src/main/resources/application.properties b/client/integration-tests/change-directory/src/main/resources/application.properties index e28f97b63..a483204b1 100644 --- a/client/integration-tests/change-directory/src/main/resources/application.properties +++ b/client/integration-tests/change-directory/src/main/resources/application.properties @@ -1,3 +1,4 @@ quarkus.openapi-generator.codegen.input-base-dir=openapi-definitions quarkus.openapi-generator.codegen.spec.simple_openapi_yaml.base-package=org.simple.openapi -quarkus.rest-client.simple_openapi_yaml.uri=http://localhost:8080 \ No newline at end of file +quarkus.rest-client.simple_openapi_yaml.uri=http://localhost:8080 +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/circuit-breaker/pom.xml b/client/integration-tests/circuit-breaker/pom.xml index 194046520..bc691350c 100644 --- a/client/integration-tests/circuit-breaker/pom.xml +++ b/client/integration-tests/circuit-breaker/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-circuit-breaker - Quarkus - Openapi Generator - Integration Tests - Client - Circuit Breaker + Quarkus - OpenAPI Generator - Integration Tests - Client - Circuit Breaker diff --git a/client/integration-tests/circuit-breaker/src/main/resources/application.properties b/client/integration-tests/circuit-breaker/src/main/resources/application.properties index a5ac61d5c..334e83247 100644 --- a/client/integration-tests/circuit-breaker/src/main/resources/application.properties +++ b/client/integration-tests/circuit-breaker/src/main/resources/application.properties @@ -7,3 +7,6 @@ org.acme.openapi.simple.api.DefaultApi/byeGet/CircuitBreaker/requestVolumeThresh org.acme.openapi.simple.api.DefaultApi/byeGet/CircuitBreaker/failureRatio = 3.14 org.acme.openapi.simple.api.DefaultApi/byeGet/CircuitBreaker/successThreshold = 22 +quarkus.keycloak.devservices.enabled=false + +quarkus.openapi-generator.codegen.spec.simple_openapi_json.base-package=org.acme.openapi.simple diff --git a/client/integration-tests/circuit-breaker/src/test/java/io/quarkiverse/openapi/generator/it/circuit/breaker/SimpleOpenApiTest.java b/client/integration-tests/circuit-breaker/src/test/java/io/quarkiverse/openapi/generator/it/circuit/breaker/SimpleOpenApiTest.java index 8980f481e..a0a5cd9ff 100644 --- a/client/integration-tests/circuit-breaker/src/test/java/io/quarkiverse/openapi/generator/it/circuit/breaker/SimpleOpenApiTest.java +++ b/client/integration-tests/circuit-breaker/src/test/java/io/quarkiverse/openapi/generator/it/circuit/breaker/SimpleOpenApiTest.java @@ -24,8 +24,8 @@ class SimpleOpenApiTest { @Test void circuitBreaker() throws IOException { - Path generatedRestClient = Paths.get("target", "generated-sources", "open-api-json", "org", "openapi", - "quarkus", "simple_openapi_json", "api", "DefaultApi.java"); + Path generatedRestClient = Paths.get("target", "generated-sources", "open-api", "org", "acme", + "openapi", "simple", "api", "DefaultApi.java"); assertThat(generatedRestClient) .exists() diff --git a/client/integration-tests/config-key/pom.xml b/client/integration-tests/config-key/pom.xml index 3c4c46104..22c590c25 100644 --- a/client/integration-tests/config-key/pom.xml +++ b/client/integration-tests/config-key/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-config-key - Quarkus - Openapi Generator - Integration Tests - Client - Config Key + Quarkus - OpenAPI Generator - Integration Tests - Client - Config Key diff --git a/client/integration-tests/config-key/src/main/openapi/config-key-with-dash.yaml b/client/integration-tests/config-key/src/main/openapi/config-key-with-dash.yaml new file mode 100644 index 000000000..4a853fe0b --- /dev/null +++ b/client/integration-tests/config-key/src/main/openapi/config-key-with-dash.yaml @@ -0,0 +1,274 @@ +--- +openapi: 3.0.3 +info: + title: greeting-flow API + version: "1.0" +paths: + /: + post: + requestBody: + content: + '*/*': + schema: + $ref: '#/components/schemas/CloudEvent' + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Response' + /hello: + get: + tags: + - Reactive Greeting Resource + operationId: hello + responses: + "200": + description: OK + content: + text/plain: + schema: + type: string + /messaging/topics: + get: + tags: + - Quarkus Topics Information Resource + responses: + "200": + description: OK +components: + schemas: + CloudEvent: + type: object + properties: + specVersion: + $ref: '#/components/schemas/SpecVersion' + id: + type: string + type: + type: string + source: + format: uri + type: string + dataContentType: + type: string + dataSchema: + format: uri + type: string + subject: + type: string + time: + format: date-time + type: string + attributeNames: + uniqueItems: true + type: array + items: + type: string + extensionNames: + uniqueItems: true + type: array + items: + type: string + data: + $ref: '#/components/schemas/CloudEventData' + CloudEventData: + type: object + EntityTag: + type: object + properties: + value: + type: string + weak: + type: boolean + Family: + enum: + - INFORMATIONAL + - SUCCESSFUL + - REDIRECTION + - CLIENT_ERROR + - SERVER_ERROR + - OTHER + type: string + Link: + type: object + properties: + uri: + format: uri + type: string + uriBuilder: + $ref: '#/components/schemas/UriBuilder' + rel: + type: string + rels: + type: array + items: + type: string + title: + type: string + type: + type: string + params: + type: object + additionalProperties: + type: string + Locale: + type: object + properties: + language: + type: string + script: + type: string + country: + type: string + variant: + type: string + extensionKeys: + uniqueItems: true + type: array + items: + format: byte + type: string + unicodeLocaleAttributes: + uniqueItems: true + type: array + items: + type: string + unicodeLocaleKeys: + uniqueItems: true + type: array + items: + type: string + iSO3Language: + type: string + iSO3Country: + type: string + displayLanguage: + type: string + displayScript: + type: string + displayCountry: + type: string + displayVariant: + type: string + displayName: + type: string + MediaType: + type: object + properties: + type: + type: string + subtype: + type: string + parameters: + type: object + additionalProperties: + type: string + wildcardType: + type: boolean + wildcardSubtype: + type: boolean + MultivaluedMapStringObject: + type: object + additionalProperties: + type: array + items: + type: object + MultivaluedMapStringString: + type: object + additionalProperties: + type: array + items: + type: string + NewCookie: + type: object + properties: + name: + type: string + value: + type: string + version: + format: int32 + type: integer + path: + type: string + domain: + type: string + comment: + type: string + maxAge: + format: int32 + type: integer + expiry: + format: date + type: string + secure: + type: boolean + httpOnly: + type: boolean + Response: + type: object + properties: + status: + format: int32 + type: integer + statusInfo: + $ref: '#/components/schemas/StatusType' + entity: + type: object + mediaType: + $ref: '#/components/schemas/MediaType' + language: + $ref: '#/components/schemas/Locale' + length: + format: int32 + type: integer + allowedMethods: + uniqueItems: true + type: array + items: + type: string + cookies: + type: object + additionalProperties: + $ref: '#/components/schemas/NewCookie' + entityTag: + $ref: '#/components/schemas/EntityTag' + date: + format: date + type: string + lastModified: + format: date + type: string + location: + format: uri + type: string + links: + uniqueItems: true + type: array + items: + $ref: '#/components/schemas/Link' + metadata: + $ref: '#/components/schemas/MultivaluedMapStringObject' + headers: + $ref: '#/components/schemas/MultivaluedMapStringObject' + stringHeaders: + $ref: '#/components/schemas/MultivaluedMapStringString' + SpecVersion: + enum: + - V03 + - V1 + type: string + StatusType: + type: object + properties: + statusCode: + format: int32 + type: integer + family: + $ref: '#/components/schemas/Family' + reasonPhrase: + type: string + UriBuilder: + type: object diff --git a/client/integration-tests/config-key/src/main/resources/application.properties b/client/integration-tests/config-key/src/main/resources/application.properties index de078f4ae..8db83ec60 100644 --- a/client/integration-tests/config-key/src/main/resources/application.properties +++ b/client/integration-tests/config-key/src/main/resources/application.properties @@ -14,3 +14,9 @@ quarkus.openapi-generator.codegen.spec.empty_config_key_yaml.config-key= quarkus.openapi-generator.codegen.spec.empty_config_key_yaml.additional-api-type-annotations=@io.quarkiverse.openapi.generator.configkey.CustomAnnotation quarkus.rest-client.empty_config_key_yaml.url=http://localhost:8080 + +quarkus.keycloak.devservices.enabled=false + +# config-key with `-` +quarkus.openapi-generator.codegen.spec.config_key_with_dash_yaml.config-key=my-api +quarkus.rest-client.my-api.url=http://localhost:8080 diff --git a/client/integration-tests/config-key/src/test/java/io/quarkiverse/openapi/generator/configkey/QuarkusConfigKeyWithDashOpenApiTest.java b/client/integration-tests/config-key/src/test/java/io/quarkiverse/openapi/generator/configkey/QuarkusConfigKeyWithDashOpenApiTest.java new file mode 100644 index 000000000..4efa7e4e9 --- /dev/null +++ b/client/integration-tests/config-key/src/test/java/io/quarkiverse/openapi/generator/configkey/QuarkusConfigKeyWithDashOpenApiTest.java @@ -0,0 +1,31 @@ +package io.quarkiverse.openapi.generator.configkey; + +import static org.assertj.core.api.Assertions.assertThat; + +import jakarta.inject.Inject; + +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.junit.jupiter.api.Test; +import org.openapi.quarkus.config_key_with_dash_yaml.api.DefaultApi; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class QuarkusConfigKeyWithDashOpenApiTest { + + @RestClient + @Inject + DefaultApi defaultApi; + + @Test + void apiIsBeingGenerated() { + assertThat(defaultApi).isNotNull(); + } + + @Test + void config_key_should_have_dash() { + assertThat(DefaultApi.class.getAnnotation(RegisterRestClient.class)).isNotNull(); + assertThat(DefaultApi.class.getAnnotation(RegisterRestClient.class).configKey()).isEqualTo("my-api"); + } +} diff --git a/client/integration-tests/cookie-authentication/pom.xml b/client/integration-tests/cookie-authentication/pom.xml index f5a5a9831..944bc43aa 100644 --- a/client/integration-tests/cookie-authentication/pom.xml +++ b/client/integration-tests/cookie-authentication/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-cookie-authentication - Quarkus - Openapi Generator - Integration Tests - Client - Cookie Authentication + Quarkus - OpenAPI Generator - Integration Tests - Client - Cookie Authentication Use cookie authentication @@ -28,8 +28,8 @@ - com.github.tomakehurst - wiremock-jre8 + org.wiremock + wiremock test diff --git a/client/integration-tests/cookie-authentication/src/main/resources/application.properties b/client/integration-tests/cookie-authentication/src/main/resources/application.properties index 517251c08..2cd30cc3d 100644 --- a/client/integration-tests/cookie-authentication/src/main/resources/application.properties +++ b/client/integration-tests/cookie-authentication/src/main/resources/application.properties @@ -1,2 +1,4 @@ quarkus.openapi-generator.codegen.spec.cookie_authentication_json.mutiny=true quarkus.openapi-generator.cookie_authentication_json.auth.cookie.api-key=Quarkus + +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/custom-templates/pom.xml b/client/integration-tests/custom-templates/pom.xml index 1fb5edcb1..be6e5e381 100644 --- a/client/integration-tests/custom-templates/pom.xml +++ b/client/integration-tests/custom-templates/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-custom-templates - Quarkus - Openapi Generator - Integration Tests - Client - Custom Templates + Quarkus - OpenAPI Generator - Integration Tests - Client - Custom Templates Example project with custom templates diff --git a/client/integration-tests/custom-templates/src/main/resources/application.properties b/client/integration-tests/custom-templates/src/main/resources/application.properties index a2f16a854..07439c101 100644 --- a/client/integration-tests/custom-templates/src/main/resources/application.properties +++ b/client/integration-tests/custom-templates/src/main/resources/application.properties @@ -1 +1,3 @@ -quarkus.rest-client.quarkus_simple_openapi_yaml.url=http://localhost:8080 \ No newline at end of file +quarkus.rest-client.quarkus_simple_openapi_yaml.url=http://localhost:8080 + +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/custom-templates/src/main/resources/templates/api.qute b/client/integration-tests/custom-templates/src/main/resources/templates/api.qute index ba096f06d..b02877f0b 100644 --- a/client/integration-tests/custom-templates/src/main/resources/templates/api.qute +++ b/client/integration-tests/custom-templates/src/main/resources/templates/api.qute @@ -4,10 +4,10 @@ package {package}; import {imp.import}; {/for} import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; -{#if hasAuthMethods || custom-register-providers.orEmpty.size > 0} +{#if openapi:hasAuthMethods(operations) || custom-register-providers.orEmpty.size > 0} import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; {/if} -{#if hasAuthMethods} +{#if openapi:hasAuthMethods(operations)} import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders; import {package}.auth.CompositeAuthenticationProvider; {#if client-headers-factory is 'default'} @@ -34,10 +34,10 @@ import io.quarkiverse.openapi.generator.annotations.GeneratedParam; * {#if appDescription}

{appDescription}

{/if} */ {/if} -@Path("{#if useAnnotatedBasePath}{contextPath}{/if}{commonPath}") -@RegisterRestClient({#if defaultServerUrl}baseUri="{defaultServerUrl}",{/if}configKey="{configKey}") +@Path("{commonPath}") +@RegisterRestClient({#if defaultServerUrl.or('') != ''}baseUri="{defaultServerUrl}", {/if}configKey="{configKey}") @GeneratedClass(value="{openapi:parseUri(inputSpec)}", tag = "{baseName}") -{#if hasAuthMethods} +{#if openapi:hasAuthMethods(operations)} @RegisterProvider(CompositeAuthenticationProvider.class) {#when client-headers-factory} {#is 'default'} @@ -106,13 +106,13 @@ public interface {classname} { {#if is-resteasy-reactive} @jakarta.ws.rs.BeanParam {op.operationIdCamelCase}MultipartForm multipartForm{#if op.hasPathParams},{/if}{! !}{#for p in op.pathParams}{#include templates.path_params param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasQueryParams},{/if}{! - !}{#for p in op.queryParams}{#include templates.query_params param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParams},{/if}{! + !}{#for p in op.queryParams}{#include templates.query_params param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParam},{/if}{! !}{#for p in op.bodyParams}{#include bodyParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasHeaderParams},{/if}{! !}{#for p in op.headerParams}{#include headerParams.qute param=p/}{#if p_hasNext}, {/if}{/for} {#else} @org.jboss.resteasy.annotations.providers.multipart.MultipartForm {op.operationIdCamelCase}MultipartForm multipartForm{#if op.hasPathParams},{/if}{! !}{#for p in op.pathParams}{#include pathParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasQueryParams},{/if}{! - !}{#for p in op.queryParams}{#include queryParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParams},{/if}{! + !}{#for p in op.queryParams}{#include queryParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParam},{/if}{! !}{#for p in op.bodyParams}{#include bodyParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasHeaderParams},{/if}{! !}{#for p in op.headerParams}{#include headerParams.qute param=p/}{#if p_hasNext}, {/if}{/for} {/if} diff --git a/client/integration-tests/custom-templates/src/main/resources/templates/pojoQueryParam.qute b/client/integration-tests/custom-templates/src/main/resources/templates/pojoQueryParam.qute index 74d2d1646..6beb12f1e 100644 --- a/client/integration-tests/custom-templates/src/main/resources/templates/pojoQueryParam.qute +++ b/client/integration-tests/custom-templates/src/main/resources/templates/pojoQueryParam.qute @@ -1,21 +1,11 @@ - {#if withXml} - @XmlAccessorType(XmlAccessType.FIELD) - {#if m.hasVars}@XmlType(name = "{m.classname}", propOrder = - { {#for var in m.vars}"{var.name}"{#if var_hasNext}, {/if}{/for} - }){#else} - @XmlType(name = "{m.classname}") - {/if} - {#if !m.parent || m.parent.isEmpty}@XmlRootElement(name = "{m.classname}"){/if} - {#else} @JsonIgnoreProperties(ignoreUnknown = true) - {/if} {#if m.description} /** * {m.description} **/ {/if} {#include additionalModelTypeAnnotations.qute m=m/} - public static class {m.classname}QueryParam {#if m.parent}extends {m.parent}{/if}{#if m.serializableModel} implements Serializable{/if} { + public static class {m.classname}QueryParam {#if m.parent}extends {m.parent}{/if}{#if serializableModel} implements Serializable{/if} { public final int myCustomQueryParamAttribute = 42; @@ -28,9 +18,6 @@ {#include enumClass.qute e=v/}{/if} {/if} - {#if withXml} - @XmlElement(name="{v.basename}"{#if v.required}, required = {v.required}{/if}) - {/if} {#if m.description} /** * {m.description} @@ -63,13 +50,11 @@ {/if} * @return {v.name} **/ - {#if !withXml} @JsonProperty("{v.baseName}") - {/if} {#for ext in v.vendorExtensions.x-extra-annotation.orEmpty} {ext} {/for} - {#if v.useBeanValidation}{#include beanValidation.qute p=v/}{/if} + {#if use-bean-validation}{#include beanValidation.qute p=v/}{/if} {#if v.isEnum && v.isContainer}public {v.datatypeWithEnum} {v.getter}(){ return {v.name}; }{#else if v.isEnum && !v.isArray && !v.isMap}public {v.datatypeWithEnum} {v.getter}() { diff --git a/client/integration-tests/enum-property/pom.xml b/client/integration-tests/enum-property/pom.xml index d96a13f83..b6afadfc2 100644 --- a/client/integration-tests/enum-property/pom.xml +++ b/client/integration-tests/enum-property/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-enum-property - Quarkus - Openapi Generator - Integration Tests - Client - Enum Property + Quarkus - OpenAPI Generator - Integration Tests - Client - Enum Property Example project for OpenAPI with enum property @@ -27,8 +27,8 @@ test
- com.github.tomakehurst - wiremock-jre8 + org.wiremock + wiremock test
diff --git a/client/integration-tests/enum-property/src/main/resources/application.properties b/client/integration-tests/enum-property/src/main/resources/application.properties new file mode 100644 index 000000000..9de819ddd --- /dev/null +++ b/client/integration-tests/enum-property/src/main/resources/application.properties @@ -0,0 +1 @@ +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/enum-property/src/test/java/io/quarkiverse/openapi/generator/it/EnumPropertyTest.java b/client/integration-tests/enum-property/src/test/java/io/quarkiverse/openapi/generator/it/EnumPropertyTest.java index b3f79f406..38a70da7a 100644 --- a/client/integration-tests/enum-property/src/test/java/io/quarkiverse/openapi/generator/it/EnumPropertyTest.java +++ b/client/integration-tests/enum-property/src/test/java/io/quarkiverse/openapi/generator/it/EnumPropertyTest.java @@ -20,7 +20,6 @@ import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer; import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; @@ -67,8 +66,7 @@ public Map start() { } private void configureWiremockServer() { - var wireMockConfiguration = WireMockConfiguration.wireMockConfig() - .extensions(new ResponseTemplateTransformer(false)).dynamicPort(); + var wireMockConfiguration = WireMockConfiguration.wireMockConfig().dynamicPort(); wireMockServer = new WireMockServer(wireMockConfiguration); wireMockServer.start(); diff --git a/client/integration-tests/enum-unexpected/pom.xml b/client/integration-tests/enum-unexpected/pom.xml index 34d9bd4ec..65ca3ddcf 100644 --- a/client/integration-tests/enum-unexpected/pom.xml +++ b/client/integration-tests/enum-unexpected/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-enum-unexpected - Quarkus - Openapi Generator - Integration Tests - Enum Unexpected + Quarkus - OpenAPI Generator - Integration Tests - Client - Enum Unexpected Example project for OpenAPI with enum unexpected value @@ -27,8 +27,8 @@ test
- com.github.tomakehurst - wiremock-jre8 + org.wiremock + wiremock test
diff --git a/client/integration-tests/enum-unexpected/src/main/resources/application.properties b/client/integration-tests/enum-unexpected/src/main/resources/application.properties index 04ab946c7..f41961686 100644 --- a/client/integration-tests/enum-unexpected/src/main/resources/application.properties +++ b/client/integration-tests/enum-unexpected/src/main/resources/application.properties @@ -1,4 +1,5 @@ quarkus.openapi-generator.codegen.spec.with_enum_unexpected_yaml.additional-enum-type-unexpected-member=true - quarkus.openapi-generator.codegen.spec.without_enum_unexpected_yaml.additional-enum-type-unexpected-member=false + +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/enum-unexpected/src/test/java/io/quarkiverse/openapi/generator/it/EnumUnexpectedTest.java b/client/integration-tests/enum-unexpected/src/test/java/io/quarkiverse/openapi/generator/it/EnumUnexpectedTest.java index 1a0225723..e72f2c830 100644 --- a/client/integration-tests/enum-unexpected/src/test/java/io/quarkiverse/openapi/generator/it/EnumUnexpectedTest.java +++ b/client/integration-tests/enum-unexpected/src/test/java/io/quarkiverse/openapi/generator/it/EnumUnexpectedTest.java @@ -22,7 +22,6 @@ import com.fasterxml.jackson.databind.exc.ValueInstantiationException; import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer; import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; @@ -35,6 +34,8 @@ class EnumUnexpectedTest { @RestClient @Inject org.openapi.quarkus.with_enum_unexpected_yaml.api.DefaultApi api; + @Inject + ObjectMapper objectMapper; @Test void apiIsBeingGenerated() { @@ -45,9 +46,6 @@ void apiIsBeingGenerated() { .isEqualTo(org.openapi.quarkus.with_enum_unexpected_yaml.model.Echo.MsgTypeEnum.UNEXPECTED); } - @Inject - ObjectMapper objectMapper; - @Test void when_additional_enum_type_unexpected_member_is_true_should_have_extra_member() { AssertionsForClassTypes.assertThat(org.openapi.quarkus.with_enum_unexpected_yaml.model.Echo.MsgTypeEnum.class) @@ -89,8 +87,7 @@ public Map start() { } private void configureWiremockServer() { - var wireMockConfiguration = WireMockConfiguration.wireMockConfig() - .extensions(new ResponseTemplateTransformer(false)).dynamicPort(); + var wireMockConfiguration = WireMockConfiguration.wireMockConfig().dynamicPort(); wireMockServer = new WireMockServer(wireMockConfiguration); wireMockServer.start(); diff --git a/client/integration-tests/equals-hashcode/pom.xml b/client/integration-tests/equals-hashcode/pom.xml new file mode 100644 index 000000000..b9d0bf03e --- /dev/null +++ b/client/integration-tests/equals-hashcode/pom.xml @@ -0,0 +1,93 @@ + + + + quarkus-openapi-generator-integration-tests + io.quarkiverse.openapi.generator + 3.0.0-SNAPSHOT + + 4.0.0 + + quarkus-openapi-generator-it-equals-hashcode + Quarkus - OpenAPI Generator - Integration Tests - Client - Equals hashcode + Example project for general usage + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator + + + org.assertj + assertj-core + test + + + io.quarkus + quarkus-junit5 + test + + + + + + io.quarkus + quarkus-maven-plugin + true + + + + build + generate-code + generate-code-tests + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + + ${project.build.directory}/${project.build.finalName}-runner + + org.jboss.logmanager.LogManager + + ${maven.home} + + + + + + + + + native + + + + + \ No newline at end of file diff --git a/client/integration-tests/equals-hashcode/src/main/openapi/quarkus-equals-hashcode-openapi.yaml b/client/integration-tests/equals-hashcode/src/main/openapi/quarkus-equals-hashcode-openapi.yaml new file mode 100644 index 000000000..ec431be5e --- /dev/null +++ b/client/integration-tests/equals-hashcode/src/main/openapi/quarkus-equals-hashcode-openapi.yaml @@ -0,0 +1,125 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Animals - OpenAPI 3.0", + "version": "1.0.5" + }, + "servers": [ + { + "url": "/api/v3" + } + ], + "tags": [ + { + "name": "primate", + "description": "Everything about Primates" + } + ], + "paths": { + "/primate/{id}": { + "get": { + "tags": [ + "primate" + ], + "summary": "Find primate by ID", + "description": "Returns a single primate", + "operationId": "getPrimateById", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of primate to return", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Primate" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Primate not found" + } + } + } + } + }, + "components": { + "schemas": { + "Animal": { + "type": "object", + "properties": { + "born": { + "type": "string", + "description": "Dated Base extension.", + "format": "date-time" + }, + "deceased": { + "type": "string", + "description": "Dated Base extension.", + "format": "date-time" + } + }, + "xml": { + "name": "animal" + } + }, + "Mammal": { + "type": "object", + "allOf": [ { + "$ref": "#/components/schemas/Animal" + } ], + "properties": { + "gender": { + "type": "string", + "enum": [ + "female", + "male" + ] + } + }, + "xml": { + "name": "mammal" + } + }, + "Primate": { + "required": [ + "name" + ], + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/Mammal" + } + ], + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "name": { + "type": "string", + "example": "jane doe" + } + }, + "xml": { + "name": "primate" + } + } + } + } +} diff --git a/client/integration-tests/equals-hashcode/src/main/openapi/quarkus-non-equals-hashcode-openapi.yaml b/client/integration-tests/equals-hashcode/src/main/openapi/quarkus-non-equals-hashcode-openapi.yaml new file mode 100644 index 000000000..ec431be5e --- /dev/null +++ b/client/integration-tests/equals-hashcode/src/main/openapi/quarkus-non-equals-hashcode-openapi.yaml @@ -0,0 +1,125 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Animals - OpenAPI 3.0", + "version": "1.0.5" + }, + "servers": [ + { + "url": "/api/v3" + } + ], + "tags": [ + { + "name": "primate", + "description": "Everything about Primates" + } + ], + "paths": { + "/primate/{id}": { + "get": { + "tags": [ + "primate" + ], + "summary": "Find primate by ID", + "description": "Returns a single primate", + "operationId": "getPrimateById", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of primate to return", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Primate" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Primate not found" + } + } + } + } + }, + "components": { + "schemas": { + "Animal": { + "type": "object", + "properties": { + "born": { + "type": "string", + "description": "Dated Base extension.", + "format": "date-time" + }, + "deceased": { + "type": "string", + "description": "Dated Base extension.", + "format": "date-time" + } + }, + "xml": { + "name": "animal" + } + }, + "Mammal": { + "type": "object", + "allOf": [ { + "$ref": "#/components/schemas/Animal" + } ], + "properties": { + "gender": { + "type": "string", + "enum": [ + "female", + "male" + ] + } + }, + "xml": { + "name": "mammal" + } + }, + "Primate": { + "required": [ + "name" + ], + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/Mammal" + } + ], + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "name": { + "type": "string", + "example": "jane doe" + } + }, + "xml": { + "name": "primate" + } + } + } + } +} diff --git a/client/integration-tests/equals-hashcode/src/main/resources/application.properties b/client/integration-tests/equals-hashcode/src/main/resources/application.properties new file mode 100644 index 000000000..929c6beeb --- /dev/null +++ b/client/integration-tests/equals-hashcode/src/main/resources/application.properties @@ -0,0 +1,6 @@ +quarkus.openapi-generator.codegen.spec.quarkus_equals_hashcode_openapi_yaml.base-package=org.acme.equals.hashcode + +quarkus.openapi-generator.codegen.spec.quarkus_non_equals_hashcode_openapi_yaml.base-package=org.acme.non.equals.hashcode +quarkus.openapi-generator.codegen.spec.quarkus_non_equals_hashcode_openapi_yaml.equals-hashcode=false + +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/equals-hashcode/src/test/java/io/quarkiverse/openapi/generator/it/EqualsHashcodeTest.java b/client/integration-tests/equals-hashcode/src/test/java/io/quarkiverse/openapi/generator/it/EqualsHashcodeTest.java new file mode 100644 index 000000000..c9bf43842 --- /dev/null +++ b/client/integration-tests/equals-hashcode/src/test/java/io/quarkiverse/openapi/generator/it/EqualsHashcodeTest.java @@ -0,0 +1,52 @@ +package io.quarkiverse.openapi.generator.it; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import java.time.OffsetDateTime; + +import org.acme.equals.hashcode.model.Animal; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class EqualsHashcodeTest { + + @Test + void verifyModelNotEquals() { + var object1 = new Animal(); + object1.setDeceased(OffsetDateTime.now().minusHours(2)); + + var object2 = new Animal(); + object2.setBorn(OffsetDateTime.now().minusYears(1)); + + assertNotEquals(object1, object2); + } + + @Test + void verifyModelEquals() { + var offset = OffsetDateTime.now().minusHours(2); + + var object1 = new Animal(); + object1.setDeceased(offset); + + var object2 = new Animal(); + object2.setDeceased(offset); + + assertEquals(object1, object2); + } + + @Test + void verifyModelHasHashCode() { + var offset = OffsetDateTime.now().minusHours(2); + + var object1 = new Animal(); + object1.setDeceased(offset); + + var object2 = new Animal(); + object2.setDeceased(offset); + + assertEquals(object1.hashCode(), object2.hashCode()); + } +} diff --git a/client/integration-tests/equals-hashcode/src/test/java/io/quarkiverse/openapi/generator/it/VerifyGenerationTest.java b/client/integration-tests/equals-hashcode/src/test/java/io/quarkiverse/openapi/generator/it/VerifyGenerationTest.java new file mode 100644 index 000000000..30224b75a --- /dev/null +++ b/client/integration-tests/equals-hashcode/src/test/java/io/quarkiverse/openapi/generator/it/VerifyGenerationTest.java @@ -0,0 +1,42 @@ +package io.quarkiverse.openapi.generator.it; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import java.lang.reflect.Method; +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class VerifyGenerationTest { + + @Test + void verifyThatGeneratedModelDoesHaveMethods() { + var equalsMethod = getMethod(org.acme.equals.hashcode.model.Animal.class, "equals"); + var hashCodeMethod = getMethod(org.acme.equals.hashcode.model.Animal.class, "hashCode"); + + assertEquals(equalsMethod.getDeclaringClass(), org.acme.equals.hashcode.model.Animal.class); + assertEquals(hashCodeMethod.getDeclaringClass(), org.acme.equals.hashcode.model.Animal.class); + } + + @Test + void verifyThatGeneratedModelDoesntHaveMethods() { + var equalsMethod = getMethod(org.acme.non.equals.hashcode.model.Animal.class, "equals"); + var hashCodeMethod = getMethod(org.acme.non.equals.hashcode.model.Animal.class, "hashCode"); + + assertNotEquals(equalsMethod.getDeclaringClass(), org.acme.equals.hashcode.model.Animal.class); + assertNotEquals(hashCodeMethod.getDeclaringClass(), org.acme.equals.hashcode.model.Animal.class); + } + + private static Method getMethod(Class clazz, String methodName) { + var methods = clazz.getMethods(); + + return Arrays.stream(methods) + .filter(method -> method.getName().equals(methodName)) + .findAny() + .orElseThrow(); + } +} diff --git a/client/integration-tests/exclude/pom.xml b/client/integration-tests/exclude/pom.xml index a105cb359..39667b7b5 100644 --- a/client/integration-tests/exclude/pom.xml +++ b/client/integration-tests/exclude/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-exclude - Quarkus - Openapi Generator - Integration Tests - Client - Exclude + Quarkus - OpenAPI Generator - Integration Tests - Client - Exclude Example project with OpenAPI documents that should be excluded diff --git a/client/integration-tests/exclude/src/main/resources/application.properties b/client/integration-tests/exclude/src/main/resources/application.properties index a5f55c31d..14b0b7b70 100644 --- a/client/integration-tests/exclude/src/main/resources/application.properties +++ b/client/integration-tests/exclude/src/main/resources/application.properties @@ -1 +1,2 @@ quarkus.openapi-generator.codegen.exclude=exclude-openapi.yaml +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/generate-flags/pom.xml b/client/integration-tests/generate-flags/pom.xml new file mode 100644 index 000000000..96c1a7d09 --- /dev/null +++ b/client/integration-tests/generate-flags/pom.xml @@ -0,0 +1,97 @@ + + + + quarkus-openapi-generator-integration-tests + io.quarkiverse.openapi.generator + 3.0.0-SNAPSHOT + + 4.0.0 + + quarkus-openapi-generator-it-generate-flags + Quarkus - OpenAPI Generator - Integration Tests - Client - Generate Flags + Example project for usage of the generate-models and generate-apis properties + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator + + + org.assertj + assertj-core + test + + + io.quarkus + quarkus-junit5 + test + + + io.quarkus + quarkus-hibernate-validator + + + + + + io.quarkus + quarkus-maven-plugin + true + + + + build + generate-code + generate-code-tests + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + + ${project.build.directory}/${project.build.finalName}-runner + + org.jboss.logmanager.LogManager + + ${maven.home} + + + + + + + + + native + + + + + \ No newline at end of file diff --git a/client/integration-tests/generate-flags/src/main/openapi/generate_apis_false.yaml b/client/integration-tests/generate-flags/src/main/openapi/generate_apis_false.yaml new file mode 100644 index 000000000..e7e1b2247 --- /dev/null +++ b/client/integration-tests/generate-flags/src/main/openapi/generate_apis_false.yaml @@ -0,0 +1,60 @@ +--- +openapi: 3.0.3 +info: + title: Test API + version: "1.0" +paths: + /{pathParam}: + get: + tags: + - NonExistentEndpoint + operationId: test + parameters: + - name: pathParam + in: path + required: true + schema: + type: string + minimum: 5 + - name: headerParam + in: header + required: false + schema: + type: integer + maximum: 5 + - name: cookieParam + in: cookie + required: true + schema: + type: string + minLength: 10 + responses: + "200": + description: OK + content: + text/plain: + schema: + type: string +components: + schemas: + ExistentObject: + type: object + description: Some object not to be validated + required: + - id + - name + - size + properties: + id: + type: integer + minimum: 1 + maximum: 100 + name: + type: string + pattern: "[a-zA-Z]*" + minLength: 1 + maxLength: 10 + size: + type: number + minimum: 1.0 + maximum: 10.0 \ No newline at end of file diff --git a/client/integration-tests/generate-flags/src/main/openapi/generate_models_false.yaml b/client/integration-tests/generate-flags/src/main/openapi/generate_models_false.yaml new file mode 100644 index 000000000..c2b29f207 --- /dev/null +++ b/client/integration-tests/generate-flags/src/main/openapi/generate_models_false.yaml @@ -0,0 +1,60 @@ +--- +openapi: 3.0.3 +info: + title: Test API + version: "1.0" +paths: + /{pathParam}: + get: + tags: + - ExistentEndpoint + operationId: test + parameters: + - name: pathParam + in: path + required: true + schema: + type: string + minimum: 5 + - name: headerParam + in: header + required: false + schema: + type: integer + maximum: 5 + - name: cookieParam + in: cookie + required: true + schema: + type: string + minLength: 10 + responses: + "200": + description: OK + content: + text/plain: + schema: + type: string +components: + schemas: + NonExistentObject: + type: object + description: Some object not to be validated + required: + - id + - name + - size + properties: + id: + type: integer + minimum: 1 + maximum: 100 + name: + type: string + pattern: "[a-zA-Z]*" + minLength: 1 + maxLength: 10 + size: + type: number + minimum: 1.0 + maximum: 10.0 \ No newline at end of file diff --git a/client/integration-tests/generate-flags/src/main/resources/application.properties b/client/integration-tests/generate-flags/src/main/resources/application.properties new file mode 100644 index 000000000..40183424f --- /dev/null +++ b/client/integration-tests/generate-flags/src/main/resources/application.properties @@ -0,0 +1,2 @@ +quarkus.openapi-generator.codegen.spec.generate_models_false_yaml.generate-models = false +quarkus.openapi-generator.codegen.spec.generate_apis_false_yaml.generate-apis = false \ No newline at end of file diff --git a/client/integration-tests/generate-flags/src/test/java/io/quarkiverse/openapi/generator/it/GenerateFlagsTest.java b/client/integration-tests/generate-flags/src/test/java/io/quarkiverse/openapi/generator/it/GenerateFlagsTest.java new file mode 100644 index 000000000..58bab06ff --- /dev/null +++ b/client/integration-tests/generate-flags/src/test/java/io/quarkiverse/openapi/generator/it/GenerateFlagsTest.java @@ -0,0 +1,35 @@ +package io.quarkiverse.openapi.generator.it; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class GenerateFlagsTest { + + @Test + void testEndpointIsInPlace() throws ClassNotFoundException { + Class.forName("org.openapi.quarkus.generate_models_false_yaml.api.ExistentEndpointApi"); + } + + @Test + void testModelIsInPlace() throws ClassNotFoundException { + Class.forName("org.openapi.quarkus.generate_apis_false_yaml.model.ExistentObject"); + } + + @Test + void testEndpointIsNotInPlace() { + Assertions.assertThatThrownBy(() -> { + Class.forName("org.openapi.quarkus.generate_apis_false_yaml.model.NonExistentEndpointApi"); + }).isInstanceOf(ClassNotFoundException.class); + } + + @Test + void testModelIsNotInPlace() { + Assertions.assertThatThrownBy(() -> { + Class.forName("org.openapi.quarkus.generate_models_false_yaml.api.NonExistentObject"); + }).isInstanceOf(ClassNotFoundException.class); + } + +} diff --git a/client/integration-tests/generation-input/pom.xml b/client/integration-tests/generation-input/pom.xml index 05fa23821..f8c9fd9c7 100644 --- a/client/integration-tests/generation-input/pom.xml +++ b/client/integration-tests/generation-input/pom.xml @@ -7,7 +7,7 @@ 3.0.0-SNAPSHOT quarkus-openapi-generator-it-generation-input - Quarkus - Openapi Generator - Integration Tests - Client - Generation Input + Quarkus - OpenAPI Generator - Integration Tests - Client - Generation Input Example library that implements the OpenApiSpecInputProvider interface diff --git a/client/integration-tests/generation-input/src/main/resources/application.properties b/client/integration-tests/generation-input/src/main/resources/application.properties new file mode 100644 index 000000000..9de819ddd --- /dev/null +++ b/client/integration-tests/generation-input/src/main/resources/application.properties @@ -0,0 +1 @@ +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/generation-tests/pom.xml b/client/integration-tests/generation-tests/pom.xml index e845744ce..605dd880f 100644 --- a/client/integration-tests/generation-tests/pom.xml +++ b/client/integration-tests/generation-tests/pom.xml @@ -7,7 +7,7 @@ 3.0.0-SNAPSHOT quarkus-openapi-generator-it-generation-tests - Quarkus - Openapi Generator - Integration Tests - Client - Generation Tests + Quarkus - OpenAPI Generator - Integration Tests - Client - Generation Tests Example Project that uses the OpenApiSpecProvider (via input Stream) to provide the OpenAPI Spec input. See the module "generation-input", which has this implementation. @@ -15,6 +15,10 @@ io.quarkiverse.openapi.generator quarkus-openapi-generator
+ + io.quarkiverse.openapi.generator + quarkus-openapi-generator-oidc + io.quarkiverse.openapi.generator quarkus-openapi-generator-it-generation-input @@ -25,8 +29,8 @@ test - com.github.tomakehurst - wiremock-jre8 + org.wiremock + wiremock test @@ -92,5 +96,26 @@ native + + resteasy-classic + + true + + + + io.quarkus + quarkus-resteasy-client-oidc-filter + + + + + resteasy-reactive + + + io.quarkus + quarkus-rest-client-oidc-filter + + + diff --git a/client/integration-tests/generation-tests/src/main/resources/application.properties b/client/integration-tests/generation-tests/src/main/resources/application.properties index 6a8f163ae..a312f6f35 100644 --- a/client/integration-tests/generation-tests/src/main/resources/application.properties +++ b/client/integration-tests/generation-tests/src/main/resources/application.properties @@ -14,3 +14,5 @@ quarkus.oidc-client.petstore_auth.grant.type=password quarkus.oidc-client.petstore_auth.grant-options.password.username=alice quarkus.oidc-client.petstore_auth.grant-options.password.password=alice quarkus.oidc-client.petstore_auth.client-id=petstore-app + +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/github/pom.xml b/client/integration-tests/github/pom.xml index fd731b3ec..237a370f6 100644 --- a/client/integration-tests/github/pom.xml +++ b/client/integration-tests/github/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-ghub - Quarkus - Openapi Generator - Integration Tests - Client - GitHub spec + Quarkus - OpenAPI Generator - Integration Tests - Client - GitHub spec Example project for general usage diff --git a/client/integration-tests/github/src/main/resources/application.properties b/client/integration-tests/github/src/main/resources/application.properties new file mode 100644 index 000000000..9de819ddd --- /dev/null +++ b/client/integration-tests/github/src/main/resources/application.properties @@ -0,0 +1 @@ +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/include/pom.xml b/client/integration-tests/include/pom.xml index 1407cc93d..9f5162425 100644 --- a/client/integration-tests/include/pom.xml +++ b/client/integration-tests/include/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-include - Quarkus - Openapi Generator - Integration Tests - Client - Include + Quarkus - OpenAPI Generator - Integration Tests - Client - Include Example project with OpenAPI documents that should be included diff --git a/client/integration-tests/include/src/main/resources/application.properties b/client/integration-tests/include/src/main/resources/application.properties index 0d10e8524..207a30aef 100644 --- a/client/integration-tests/include/src/main/resources/application.properties +++ b/client/integration-tests/include/src/main/resources/application.properties @@ -1 +1,2 @@ quarkus.openapi-generator.codegen.include=include-openapi.yaml +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/initialize-empty-collections/pom.xml b/client/integration-tests/initialize-empty-collections/pom.xml new file mode 100644 index 000000000..5e96d575f --- /dev/null +++ b/client/integration-tests/initialize-empty-collections/pom.xml @@ -0,0 +1,89 @@ + + + + quarkus-openapi-generator-integration-tests + io.quarkiverse.openapi.generator + 3.0.0-SNAPSHOT + + 4.0.0 + + quarkus-openapi-generator-it-initialize-empty-collections + Quarkus - OpenAPI Generator - Integration Tests - Client - Initialize Empty Collections + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator + + + org.assertj + assertj-core + test + + + io.quarkus + quarkus-junit5 + test + + + + + + io.quarkus + quarkus-maven-plugin + true + + + + build + generate-code + generate-code-tests + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + + + \ No newline at end of file diff --git a/client/integration-tests/initialize-empty-collections/src/main/openapi/quarkus-simple-openapi-empty-collections.yaml b/client/integration-tests/initialize-empty-collections/src/main/openapi/quarkus-simple-openapi-empty-collections.yaml new file mode 100644 index 000000000..559d04451 --- /dev/null +++ b/client/integration-tests/initialize-empty-collections/src/main/openapi/quarkus-simple-openapi-empty-collections.yaml @@ -0,0 +1,282 @@ +--- +openapi: 3.0.3 +info: + title: greeting-flow API + version: "1.0" +paths: + /: + post: + requestBody: + content: + '*/*': + schema: + $ref: '#/components/schemas/CloudEvent' + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Response' + /hello: + get: + tags: + - Reactive Greeting Resource + operationId: hello + responses: + "200": + description: OK + content: + text/plain: + schema: + type: string + /messaging/topics: + get: + tags: + - Quarkus Topics Information Resource + responses: + "200": + description: OK +components: + schemas: + Type: + type: object + properties: + tags: + uniqueItems: true + type: array + items: + type: string + CloudEvent: + type: object + properties: + specVersion: + $ref: '#/components/schemas/SpecVersion' + id: + type: string + type: + type: string + source: + format: uri + type: string + dataContentType: + type: string + dataSchema: + format: uri + type: string + subject: + type: string + time: + format: date-time + type: string + attributeNames: + uniqueItems: true + type: array + items: + type: string + extensionNames: + uniqueItems: true + type: array + items: + type: string + data: + $ref: '#/components/schemas/CloudEventData' + CloudEventData: + type: object + EntityTag: + type: object + properties: + value: + type: string + weak: + type: boolean + Family: + enum: + - INFORMATIONAL + - SUCCESSFUL + - REDIRECTION + - CLIENT_ERROR + - SERVER_ERROR + - OTHER + type: string + Link: + type: object + properties: + uri: + format: uri + type: string + uriBuilder: + $ref: '#/components/schemas/UriBuilder' + rel: + type: string + rels: + type: array + items: + type: string + title: + type: string + type: + type: string + params: + type: object + additionalProperties: + type: string + Locale: + type: object + properties: + language: + type: string + script: + type: string + country: + type: string + variant: + type: string + extensionKeys: + uniqueItems: true + type: array + items: + format: byte + type: string + unicodeLocaleAttributes: + uniqueItems: true + type: array + items: + type: string + unicodeLocaleKeys: + uniqueItems: true + type: array + items: + type: string + iSO3Language: + type: string + iSO3Country: + type: string + displayLanguage: + type: string + displayScript: + type: string + displayCountry: + type: string + displayVariant: + type: string + displayName: + type: string + MediaType: + type: object + properties: + type: + type: string + subtype: + type: string + parameters: + type: object + additionalProperties: + type: string + wildcardType: + type: boolean + wildcardSubtype: + type: boolean + MultivaluedMapStringObject: + type: object + additionalProperties: + type: array + items: + type: object + MultivaluedMapStringString: + type: object + additionalProperties: + type: array + items: + type: string + NewCookie: + type: object + properties: + name: + type: string + value: + type: string + version: + format: int32 + type: integer + path: + type: string + domain: + type: string + comment: + type: string + maxAge: + format: int32 + type: integer + expiry: + format: date + type: string + secure: + type: boolean + httpOnly: + type: boolean + Response: + type: object + properties: + status: + format: int32 + type: integer + statusInfo: + $ref: '#/components/schemas/StatusType' + entity: + type: object + mediaType: + $ref: '#/components/schemas/MediaType' + language: + $ref: '#/components/schemas/Locale' + length: + format: int32 + type: integer + allowedMethods: + uniqueItems: true + type: array + items: + type: string + cookies: + type: object + additionalProperties: + $ref: '#/components/schemas/NewCookie' + entityTag: + $ref: '#/components/schemas/EntityTag' + date: + format: date + type: string + lastModified: + format: date + type: string + location: + format: uri + type: string + links: + uniqueItems: true + type: array + items: + $ref: '#/components/schemas/Link' + metadata: + $ref: '#/components/schemas/MultivaluedMapStringObject' + headers: + $ref: '#/components/schemas/MultivaluedMapStringObject' + stringHeaders: + $ref: '#/components/schemas/MultivaluedMapStringString' + SpecVersion: + enum: + - V03 + - V1 + type: string + StatusType: + type: object + properties: + statusCode: + format: int32 + type: integer + family: + $ref: '#/components/schemas/Family' + reasonPhrase: + type: string + UriBuilder: + type: object diff --git a/client/integration-tests/initialize-empty-collections/src/main/openapi/quarkus-simple-openapi-null-collections.yaml b/client/integration-tests/initialize-empty-collections/src/main/openapi/quarkus-simple-openapi-null-collections.yaml new file mode 100644 index 000000000..559d04451 --- /dev/null +++ b/client/integration-tests/initialize-empty-collections/src/main/openapi/quarkus-simple-openapi-null-collections.yaml @@ -0,0 +1,282 @@ +--- +openapi: 3.0.3 +info: + title: greeting-flow API + version: "1.0" +paths: + /: + post: + requestBody: + content: + '*/*': + schema: + $ref: '#/components/schemas/CloudEvent' + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Response' + /hello: + get: + tags: + - Reactive Greeting Resource + operationId: hello + responses: + "200": + description: OK + content: + text/plain: + schema: + type: string + /messaging/topics: + get: + tags: + - Quarkus Topics Information Resource + responses: + "200": + description: OK +components: + schemas: + Type: + type: object + properties: + tags: + uniqueItems: true + type: array + items: + type: string + CloudEvent: + type: object + properties: + specVersion: + $ref: '#/components/schemas/SpecVersion' + id: + type: string + type: + type: string + source: + format: uri + type: string + dataContentType: + type: string + dataSchema: + format: uri + type: string + subject: + type: string + time: + format: date-time + type: string + attributeNames: + uniqueItems: true + type: array + items: + type: string + extensionNames: + uniqueItems: true + type: array + items: + type: string + data: + $ref: '#/components/schemas/CloudEventData' + CloudEventData: + type: object + EntityTag: + type: object + properties: + value: + type: string + weak: + type: boolean + Family: + enum: + - INFORMATIONAL + - SUCCESSFUL + - REDIRECTION + - CLIENT_ERROR + - SERVER_ERROR + - OTHER + type: string + Link: + type: object + properties: + uri: + format: uri + type: string + uriBuilder: + $ref: '#/components/schemas/UriBuilder' + rel: + type: string + rels: + type: array + items: + type: string + title: + type: string + type: + type: string + params: + type: object + additionalProperties: + type: string + Locale: + type: object + properties: + language: + type: string + script: + type: string + country: + type: string + variant: + type: string + extensionKeys: + uniqueItems: true + type: array + items: + format: byte + type: string + unicodeLocaleAttributes: + uniqueItems: true + type: array + items: + type: string + unicodeLocaleKeys: + uniqueItems: true + type: array + items: + type: string + iSO3Language: + type: string + iSO3Country: + type: string + displayLanguage: + type: string + displayScript: + type: string + displayCountry: + type: string + displayVariant: + type: string + displayName: + type: string + MediaType: + type: object + properties: + type: + type: string + subtype: + type: string + parameters: + type: object + additionalProperties: + type: string + wildcardType: + type: boolean + wildcardSubtype: + type: boolean + MultivaluedMapStringObject: + type: object + additionalProperties: + type: array + items: + type: object + MultivaluedMapStringString: + type: object + additionalProperties: + type: array + items: + type: string + NewCookie: + type: object + properties: + name: + type: string + value: + type: string + version: + format: int32 + type: integer + path: + type: string + domain: + type: string + comment: + type: string + maxAge: + format: int32 + type: integer + expiry: + format: date + type: string + secure: + type: boolean + httpOnly: + type: boolean + Response: + type: object + properties: + status: + format: int32 + type: integer + statusInfo: + $ref: '#/components/schemas/StatusType' + entity: + type: object + mediaType: + $ref: '#/components/schemas/MediaType' + language: + $ref: '#/components/schemas/Locale' + length: + format: int32 + type: integer + allowedMethods: + uniqueItems: true + type: array + items: + type: string + cookies: + type: object + additionalProperties: + $ref: '#/components/schemas/NewCookie' + entityTag: + $ref: '#/components/schemas/EntityTag' + date: + format: date + type: string + lastModified: + format: date + type: string + location: + format: uri + type: string + links: + uniqueItems: true + type: array + items: + $ref: '#/components/schemas/Link' + metadata: + $ref: '#/components/schemas/MultivaluedMapStringObject' + headers: + $ref: '#/components/schemas/MultivaluedMapStringObject' + stringHeaders: + $ref: '#/components/schemas/MultivaluedMapStringString' + SpecVersion: + enum: + - V03 + - V1 + type: string + StatusType: + type: object + properties: + statusCode: + format: int32 + type: integer + family: + $ref: '#/components/schemas/Family' + reasonPhrase: + type: string + UriBuilder: + type: object diff --git a/client/integration-tests/initialize-empty-collections/src/main/resources/application.properties b/client/integration-tests/initialize-empty-collections/src/main/resources/application.properties new file mode 100644 index 000000000..fb4e9b409 --- /dev/null +++ b/client/integration-tests/initialize-empty-collections/src/main/resources/application.properties @@ -0,0 +1,5 @@ +# with initialise empty collections = TRUE +quarkus.openapi-generator.codegen.spec.quarkus_simple_openapi_empty_collections_yaml.initialize-empty-collections=true +# with initialise empty collections = TRUE +quarkus.openapi-generator.codegen.spec.quarkus_simple_openapi_null_collections_yaml.initialize-empty-collections=false +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/initialize-empty-collections/src/test/java/io/quarkiverse/openapi/generator/initialiseEmptyCollections/QuarkusInitialiseEmptyCollectionWhenFalseTest.java b/client/integration-tests/initialize-empty-collections/src/test/java/io/quarkiverse/openapi/generator/initialiseEmptyCollections/QuarkusInitialiseEmptyCollectionWhenFalseTest.java new file mode 100644 index 000000000..86bc197c3 --- /dev/null +++ b/client/integration-tests/initialize-empty-collections/src/test/java/io/quarkiverse/openapi/generator/initialiseEmptyCollections/QuarkusInitialiseEmptyCollectionWhenFalseTest.java @@ -0,0 +1,35 @@ +package io.quarkiverse.openapi.generator.initialiseEmptyCollections; + +import jakarta.inject.Inject; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.openapi.quarkus.quarkus_simple_openapi_null_collections_yaml.model.CloudEvent; +import org.openapi.quarkus.quarkus_simple_openapi_null_collections_yaml.model.Link; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class QuarkusInitialiseEmptyCollectionWhenFalseTest { + + @Inject + ObjectMapper objectMapper; + + @Test + void when_initialize_empty_collections_is_false_then_should_not_initialize_collections() { + var cloudEvent = new CloudEvent(); + Assertions.assertThat(cloudEvent.getAttributeNames()).isNull(); + var link = new Link(); + Assertions.assertThat(link.getRels()).isNull(); + } + + @Test + void when_initialize_empty_collections_is_false_then_jackson_should_map_collections_correctly() + throws JsonProcessingException { + Assertions.assertThat(objectMapper.writeValueAsString(new Link())) + .isEqualTo("{}"); + } +} diff --git a/client/integration-tests/initialize-empty-collections/src/test/java/io/quarkiverse/openapi/generator/initialiseEmptyCollections/QuarkusInitialiseEmptyCollectionWhenTrueTest.java b/client/integration-tests/initialize-empty-collections/src/test/java/io/quarkiverse/openapi/generator/initialiseEmptyCollections/QuarkusInitialiseEmptyCollectionWhenTrueTest.java new file mode 100644 index 000000000..ba98762f5 --- /dev/null +++ b/client/integration-tests/initialize-empty-collections/src/test/java/io/quarkiverse/openapi/generator/initialiseEmptyCollections/QuarkusInitialiseEmptyCollectionWhenTrueTest.java @@ -0,0 +1,40 @@ +package io.quarkiverse.openapi.generator.initialiseEmptyCollections; + +import java.util.ArrayList; +import java.util.LinkedHashSet; + +import jakarta.inject.Inject; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.openapi.quarkus.quarkus_simple_openapi_empty_collections_yaml.model.CloudEvent; +import org.openapi.quarkus.quarkus_simple_openapi_empty_collections_yaml.model.Link; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class QuarkusInitialiseEmptyCollectionWhenTrueTest { + + @Inject + ObjectMapper objectMapper; + + @Test + void when_initialize_empty_collections_is_true_then_should_initialize_collections() { + var cloudEvent = new CloudEvent(); + Assertions.assertThat(cloudEvent.getAttributeNames()).isNotNull().isEmpty(); + Assertions.assertThat(cloudEvent.getAttributeNames()).isInstanceOf(LinkedHashSet.class); + var link = new Link(); + Assertions.assertThat(link.getRels()).isNotNull().isEmpty(); + Assertions.assertThat(link.getRels()).isInstanceOf(ArrayList.class); + } + + @Test + void when_initialize_empty_collections_is_true_then_jackson_should_map_collections_correctly() + throws JsonProcessingException { + Assertions.assertThat(objectMapper.writeValueAsString(new Link())) + .isEqualTo("{\"rels\":[],\"params\":{}}"); + } +} diff --git a/client/integration-tests/multipart-request/pom.xml b/client/integration-tests/multipart-request/pom.xml index 56283cfe0..32cdab012 100644 --- a/client/integration-tests/multipart-request/pom.xml +++ b/client/integration-tests/multipart-request/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-multipart-request - Quarkus - Openapi Generator - Integration Tests - Client - Multipart Request + Quarkus - OpenAPI Generator - Integration Tests - Client - Multipart Request @@ -21,8 +21,8 @@ test - com.github.tomakehurst - wiremock-jre8 + org.wiremock + wiremock test diff --git a/client/integration-tests/multipart-request/src/main/resources/application.properties b/client/integration-tests/multipart-request/src/main/resources/application.properties index 3ccb9c794..fa64e2ec1 100644 --- a/client/integration-tests/multipart-request/src/main/resources/application.properties +++ b/client/integration-tests/multipart-request/src/main/resources/application.properties @@ -2,3 +2,5 @@ quarkus.openapi-generator.codegen.spec.multipart_requests_yml.base-package=org.a quarkus.openapi-generator.codegen.spec.multipart_requests_yml.additional-model-type-annotations=@io.quarkus.runtime.annotations.RegisterForReflection # By default the openapi-generator doesn't generate models for multipart requests quarkus.openapi-generator.codegen.spec.multipart_requests_yml.skip-form-model=false + +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/mutiny-return-response/pom.xml b/client/integration-tests/mutiny-return-response/pom.xml index fcc4660d8..74108384d 100644 --- a/client/integration-tests/mutiny-return-response/pom.xml +++ b/client/integration-tests/mutiny-return-response/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-mutiny-return-response - Quarkus - Openapi Generator - Integration Tests - Client - Mutiny Return Response + Quarkus - OpenAPI Generator - Integration Tests - Client - Mutiny Return Response Example project for usage of the mutiny-return-response property diff --git a/client/integration-tests/mutiny-return-response/src/main/resources/application.properties b/client/integration-tests/mutiny-return-response/src/main/resources/application.properties index 826dba648..ce35b884b 100644 --- a/client/integration-tests/mutiny-return-response/src/main/resources/application.properties +++ b/client/integration-tests/mutiny-return-response/src/main/resources/application.properties @@ -34,3 +34,5 @@ quarkus.openapi-generator.codegen.spec.mutiny_return_response_true_void_simple_o quarkus.openapi-generator.codegen.spec.mutiny_multi_return_response_true_void_simple_openapi_yaml.mutiny = true quarkus.openapi-generator.codegen.spec.mutiny_multi_return_response_true_void_simple_openapi_yaml.mutiny.return-response = true quarkus.openapi-generator.codegen.spec.mutiny_multi_return_response_true_void_simple_openapi_yaml.mutiny.operation-ids.hello = Multi + +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/mutiny/pom.xml b/client/integration-tests/mutiny/pom.xml index a6e4450d4..c0bc3c05d 100644 --- a/client/integration-tests/mutiny/pom.xml +++ b/client/integration-tests/mutiny/pom.xml @@ -8,7 +8,7 @@ quarkus-openapi-generator-it-mutiny - Quarkus - Openapi Generator - Integration Tests - Client - Mutiny + Quarkus - OpenAPI Generator - Integration Tests - Client - Mutiny Example project for general usage with Mutiny diff --git a/client/integration-tests/mutiny/src/main/resources/application.properties b/client/integration-tests/mutiny/src/main/resources/application.properties index b1900ad13..7fdc33f2c 100644 --- a/client/integration-tests/mutiny/src/main/resources/application.properties +++ b/client/integration-tests/mutiny/src/main/resources/application.properties @@ -8,4 +8,6 @@ quarkus.openapi-generator.codegen.spec.quarkus_multiple_endpoints_openapi_yaml.m quarkus.openapi-generator.codegen.spec.quarkus_multiple_endpoints_wrong_configuration_openapi_yaml.mutiny=true quarkus.openapi-generator.codegen.spec.quarkus_multiple_endpoints_wrong_configuration_openapi_yaml.mutiny.operation-ids.testEndpoint1=Multi quarkus.openapi-generator.codegen.spec.quarkus_multiple_endpoints_wrong_configuration_openapi_yaml.mutiny.operation-ids.testEndpoint2=Uni -quarkus.openapi-generator.codegen.spec.quarkus_multiple_endpoints_wrong_configuration_openapi_yaml.mutiny.operation-ids.testEndpoint3=Umi \ No newline at end of file +quarkus.openapi-generator.codegen.spec.quarkus_multiple_endpoints_wrong_configuration_openapi_yaml.mutiny.operation-ids.testEndpoint3=Umi + +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/open-api-normalizer/pom.xml b/client/integration-tests/open-api-normalizer/pom.xml index a8272f284..eb71dd670 100644 --- a/client/integration-tests/open-api-normalizer/pom.xml +++ b/client/integration-tests/open-api-normalizer/pom.xml @@ -8,7 +8,7 @@ quarkus-openapi-generator-it-open-api-normalizer - Quarkus - Openapi Generator - Integration Tests - Client - OpenAPI Normalizer + Quarkus - OpenAPI Generator - Integration Tests - Client - OpenAPI Normalizer @@ -26,8 +26,8 @@ test - com.github.tomakehurst - wiremock-jre8 + org.wiremock + wiremock test diff --git a/client/integration-tests/open-api-normalizer/src/main/resources/application.properties b/client/integration-tests/open-api-normalizer/src/main/resources/application.properties index d80d6c847..9a6d0d6a3 100644 --- a/client/integration-tests/open-api-normalizer/src/main/resources/application.properties +++ b/client/integration-tests/open-api-normalizer/src/main/resources/application.properties @@ -1,4 +1,6 @@ quarkus.openapi-generator.codegen.spec.open_api_normalizer_json.base-package=org.acme.openapi.animals quarkus.openapi-generator.codegen.spec.open_api_normalizer_json.type-mappings.DateTime=Instant quarkus.openapi-generator.codegen.spec.open_api_normalizer_json.import-mappings.Instant=java.time.Instant -quarkus.openapi-generator.codegen.spec.open_api_normalizer_json.open-api-normalizer.REF_AS_PARENT_IN_ALLOF=true \ No newline at end of file +quarkus.openapi-generator.codegen.spec.open_api_normalizer_json.open-api-normalizer.REF_AS_PARENT_IN_ALLOF=true + +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/part-filename/pom.xml b/client/integration-tests/part-filename/pom.xml index aedbe92eb..878b31e21 100644 --- a/client/integration-tests/part-filename/pom.xml +++ b/client/integration-tests/part-filename/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-part-filename - Quarkus - Openapi Generator - Integration Tests - Client - PartFilename + Quarkus - OpenAPI Generator - Integration Tests - Client - PartFilename diff --git a/client/integration-tests/part-filename/src/main/resources/application.properties b/client/integration-tests/part-filename/src/main/resources/application.properties index b48826d72..9327f700a 100644 --- a/client/integration-tests/part-filename/src/main/resources/application.properties +++ b/client/integration-tests/part-filename/src/main/resources/application.properties @@ -23,3 +23,5 @@ quarkus.openapi-generator.codegen.spec.do_not_use_field_name_in_part_filename_ym quarkus.openapi-generator.codegen.spec.do_not_use_field_name_in_part_filename_yml.generate-part-filename=true quarkus.openapi-generator.codegen.spec.do_not_use_field_name_in_part_filename_yml.part-filename-value=test.pdf quarkus.openapi-generator.codegen.spec.do_not_use_field_name_in_part_filename_yml.use-field-name-in-part-filename=false + +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/path/pom.xml b/client/integration-tests/path/pom.xml new file mode 100644 index 000000000..efa483146 --- /dev/null +++ b/client/integration-tests/path/pom.xml @@ -0,0 +1,91 @@ + + + 4.0.0 + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-integration-tests + 3.0.0-SNAPSHOT + + + quarkus-openapi-generator-it-path + Quarkus - OpenAPI Generator - Integration Tests - Client - path + Test from path + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator + + + org.assertj + assertj-core + test + + + io.quarkus + quarkus-junit5 + test + + + + + + + io.quarkus + quarkus-maven-plugin + true + + + + build + generate-code + generate-code-tests + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + + + \ No newline at end of file diff --git a/client/integration-tests/path/src/main/openapi/openapi-path-with-forward-slash.yaml b/client/integration-tests/path/src/main/openapi/openapi-path-with-forward-slash.yaml new file mode 100644 index 000000000..50a12a951 --- /dev/null +++ b/client/integration-tests/path/src/main/openapi/openapi-path-with-forward-slash.yaml @@ -0,0 +1,108 @@ +--- +openapi: 3.0.3 +info: + title: code-with-quarkus API + version: 1.0.0-SNAPSHOT +servers: +- url: http://localhost:8080 + description: Auto generated value +- url: http://0.0.0.0:8080 + description: Auto generated value +paths: + /users/: + get: + tags: + - User Resource + description: Find All + operationId: UserResource_findAll + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/User" + post: + tags: + - User Resource + description: Add + operationId: UserResource_add + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/User" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/User" + /users/{id}: + get: + tags: + - User Resource + description: Find + operationId: UserResource_find + parameters: + - name: id + in: path + required: true + schema: + format: int32 + type: integer + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/User" + put: + tags: + - User Resource + description: Update + operationId: UserResource_update + parameters: + - name: id + in: path + required: true + schema: + format: int32 + type: integer + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/User" + responses: + "204": + description: No Content + delete: + tags: + - User Resource + description: Delete + operationId: UserResource_delete + parameters: + - name: id + in: path + required: true + schema: + format: int32 + type: integer + responses: + "204": + description: No Content +components: + schemas: + User: + type: object + properties: + id: + format: int32 + type: integer + name: + type: string diff --git a/client/integration-tests/path/src/main/openapi/openapi-path.yaml b/client/integration-tests/path/src/main/openapi/openapi-path.yaml new file mode 100644 index 000000000..270c78d9c --- /dev/null +++ b/client/integration-tests/path/src/main/openapi/openapi-path.yaml @@ -0,0 +1,108 @@ +--- +openapi: 3.0.3 +info: + title: code-with-quarkus API + version: 1.0.0-SNAPSHOT +servers: +- url: http://localhost:8080 + description: Auto generated value +- url: http://0.0.0.0:8080 + description: Auto generated value +paths: + /users: + get: + tags: + - User Resource + description: Find All + operationId: UserResource_findAll + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/User" + post: + tags: + - User Resource + description: Add + operationId: UserResource_add + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/User" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/User" + /users/{id}: + get: + tags: + - User Resource + description: Find + operationId: UserResource_find + parameters: + - name: id + in: path + required: true + schema: + format: int32 + type: integer + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/User" + put: + tags: + - User Resource + description: Update + operationId: UserResource_update + parameters: + - name: id + in: path + required: true + schema: + format: int32 + type: integer + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/User" + responses: + "204": + description: No Content + delete: + tags: + - User Resource + description: Delete + operationId: UserResource_delete + parameters: + - name: id + in: path + required: true + schema: + format: int32 + type: integer + responses: + "204": + description: No Content +components: + schemas: + User: + type: object + properties: + id: + format: int32 + type: integer + name: + type: string diff --git a/client/integration-tests/path/src/main/resources/application.properties b/client/integration-tests/path/src/main/resources/application.properties new file mode 100644 index 000000000..11a6c1a9f --- /dev/null +++ b/client/integration-tests/path/src/main/resources/application.properties @@ -0,0 +1 @@ +quarkus.rest-client.quarkus_simple_openapi_yaml.url=http://localhost:8080 diff --git a/client/integration-tests/path/src/test/java/io/quarkiverse/openapi/generator/it/PathTest.java b/client/integration-tests/path/src/test/java/io/quarkiverse/openapi/generator/it/PathTest.java new file mode 100644 index 000000000..b65b22015 --- /dev/null +++ b/client/integration-tests/path/src/test/java/io/quarkiverse/openapi/generator/it/PathTest.java @@ -0,0 +1,106 @@ +package io.quarkiverse.openapi.generator.it; + +import static org.assertj.core.api.Assertions.assertThat; + +import jakarta.ws.rs.Path; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class PathTest { + + @Test + void testPathGenerated() throws ClassNotFoundException, NoSuchMethodException { + String apiClassName = "org.openapi.quarkus.openapi_path_yaml.api.UserResourceApi"; + String modelClassName = "org.openapi.quarkus.openapi_path_yaml.model.User"; + + assertThat( + Class.forName(apiClassName) + .getAnnotation(Path.class) + .value()) + .isEqualTo("/users"); + + assertThat( + Class.forName(apiClassName) + .getMethod("userResourceFind", Integer.class) + .getAnnotation(Path.class) + .value()) + .isEqualTo("/{id}"); + + assertThat( + Class.forName(apiClassName) + .getMethod("userResourceFindAll") + .getAnnotation(Path.class)) + .isNull(); + + assertThat( + Class.forName(apiClassName) + .getMethod("userResourceAdd", Class.forName(modelClassName)) + .getAnnotation(Path.class)) + .isNull(); + + assertThat( + Class.forName(apiClassName) + .getMethod("userResourceUpdate", Integer.class, Class.forName(modelClassName)) + .getAnnotation(Path.class) + .value()) + .isEqualTo("/{id}"); + + assertThat( + Class.forName(apiClassName) + .getMethod("userResourceDelete", Integer.class) + .getAnnotation(Path.class) + .value()) + .isEqualTo("/{id}"); + } + + @Test + void testPathWithForwardSlashGenerated() throws ClassNotFoundException, NoSuchMethodException { + String apiClassName = "org.openapi.quarkus.openapi_path_with_forward_slash_yaml.api.UserResourceApi"; + String modelClassName = "org.openapi.quarkus.openapi_path_with_forward_slash_yaml.model.User"; + + assertThat( + Class.forName(apiClassName) + .getAnnotation(Path.class) + .value()) + .isEqualTo("/users"); + + assertThat( + Class.forName(apiClassName) + .getMethod("userResourceFind", Integer.class) + .getAnnotation(Path.class) + .value()) + .isEqualTo("/{id}"); + + assertThat( + Class.forName(apiClassName) + .getMethod("userResourceFindAll") + .getAnnotation(Path.class) + .value()) + .isEqualTo("/"); + + assertThat( + Class.forName(apiClassName) + .getMethod("userResourceAdd", Class.forName(modelClassName)) + .getAnnotation(Path.class) + .value()) + .isEqualTo("/"); + + assertThat( + Class.forName(apiClassName) + .getMethod("userResourceUpdate", Integer.class, Class.forName(modelClassName)) + .getAnnotation(Path.class) + .value()) + .isEqualTo("/{id}"); + + assertThat( + Class.forName(apiClassName) + .getMethod("userResourceDelete", Integer.class) + .getAnnotation(Path.class) + .value()) + .isEqualTo("/{id}"); + } + +} diff --git a/client/integration-tests/polymorphism/pom.xml b/client/integration-tests/polymorphism/pom.xml index fe79e4555..bc8e14ecb 100644 --- a/client/integration-tests/polymorphism/pom.xml +++ b/client/integration-tests/polymorphism/pom.xml @@ -1,6 +1,5 @@ - + quarkus-openapi-generator-integration-tests io.quarkiverse.openapi.generator @@ -9,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-polymorphism - Quarkus - Openapi Generator - Integration Tests - Polymorphism + Quarkus - OpenAPI Generator - Integration Tests - Client - Polymorphism @@ -22,8 +21,8 @@ test - com.github.tomakehurst - wiremock-jre8 + org.wiremock + wiremock test diff --git a/client/integration-tests/polymorphism/src/main/resources/application.properties b/client/integration-tests/polymorphism/src/main/resources/application.properties new file mode 100644 index 000000000..9de819ddd --- /dev/null +++ b/client/integration-tests/polymorphism/src/main/resources/application.properties @@ -0,0 +1 @@ +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/polymorphism/src/test/java/io/quarkiverse/openapi/generator/it/PolymorphismTest.java b/client/integration-tests/polymorphism/src/test/java/io/quarkiverse/openapi/generator/it/PolymorphismTest.java index fc527cd23..edc8c50de 100644 --- a/client/integration-tests/polymorphism/src/test/java/io/quarkiverse/openapi/generator/it/PolymorphismTest.java +++ b/client/integration-tests/polymorphism/src/test/java/io/quarkiverse/openapi/generator/it/PolymorphismTest.java @@ -15,7 +15,6 @@ import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer; import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; @@ -52,8 +51,7 @@ public Map start() { } private void configureWiremockServer() { - var wireMockConfiguration = WireMockConfiguration.wireMockConfig() - .extensions(new ResponseTemplateTransformer(false)).dynamicPort(); + var wireMockConfiguration = WireMockConfiguration.wireMockConfig().dynamicPort(); wireMockServer = new WireMockServer(wireMockConfiguration); wireMockServer.start(); diff --git a/client/integration-tests/pom.xml b/client/integration-tests/pom.xml index 8c2d96ba8..b2a36db4a 100644 --- a/client/integration-tests/pom.xml +++ b/client/integration-tests/pom.xml @@ -8,7 +8,7 @@ ../pom.xml quarkus-openapi-generator-integration-tests - Quarkus - Openapi Generator - Client - Integration Tests + Quarkus - OpenAPI Generator - Client - Integration Tests pom additional-properties @@ -23,16 +23,20 @@ enum-property enum-unexpected exclude + generate-flags generation-input generation-tests include + initialize-empty-collections multipart-request mutiny mutiny-return-response open-api-normalizer part-filename + path polymorphism return-response + remove-operationid-prefix security simple skip-validation @@ -40,13 +44,16 @@ type-mapping config-key github + without-oidc + serializable-model + equals-hashcode - com.github.tomakehurst - wiremock-jre8 - ${version.com.github.tomakehurst} + org.wiremock + wiremock + ${version.org.wiremock} test @@ -54,6 +61,11 @@ quarkus-openapi-generator-it-generation-input ${project.version} + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-oidc + ${project.version} + @@ -68,12 +80,8 @@ quarkus-resteasy-client-jackson - io.quarkus - quarkus-resteasy-client-oidc-filter - - - io.quarkus - quarkus-resteasy-multipart + org.jboss.resteasy + resteasy-multipart-provider @@ -102,10 +110,6 @@ io.quarkus quarkus-rest-client-jackson - - io.quarkus - quarkus-rest-client-oidc-filter - jakarta.validation jakarta.validation-api diff --git a/client/integration-tests/remove-operationid-prefix/pom.xml b/client/integration-tests/remove-operationid-prefix/pom.xml new file mode 100644 index 000000000..184df406a --- /dev/null +++ b/client/integration-tests/remove-operationid-prefix/pom.xml @@ -0,0 +1,91 @@ + + + 4.0.0 + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-integration-tests + 3.0.0-SNAPSHOT + + + quarkus-openapi-generator-it-remove-operationid-prefix + Quarkus - OpenAPI Generator - Integration Tests - Client - remove operation id prefix + Example project for general usage with remove operation id prefix + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator + + + org.assertj + assertj-core + test + + + io.quarkus + quarkus-junit5 + test + + + + + + + io.quarkus + quarkus-maven-plugin + true + + + + build + generate-code + generate-code-tests + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + + + \ No newline at end of file diff --git a/client/integration-tests/remove-operationid-prefix/src/main/openapi/openapi-prefix-count.yaml b/client/integration-tests/remove-operationid-prefix/src/main/openapi/openapi-prefix-count.yaml new file mode 100644 index 000000000..0759f940e --- /dev/null +++ b/client/integration-tests/remove-operationid-prefix/src/main/openapi/openapi-prefix-count.yaml @@ -0,0 +1,108 @@ +--- +openapi: 3.0.3 +info: + title: code-with-quarkus API + version: 1.0.0-SNAPSHOT +servers: +- url: http://localhost:8080 + description: Auto generated value +- url: http://0.0.0.0:8080 + description: Auto generated value +paths: + /users: + get: + tags: + - User Resource + description: Find All + operationId: org.acme.UserResource.findAll + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/User" + post: + tags: + - User Resource + description: Add + operationId: org.acme.UserResource.add + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/User" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/User" + /users/{id}: + get: + tags: + - User Resource + description: Find + operationId: org.acme.UserResource.find + parameters: + - name: id + in: path + required: true + schema: + format: int32 + type: integer + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/User" + put: + tags: + - User Resource + description: Update + operationId: org.acme.UserResource.update + parameters: + - name: id + in: path + required: true + schema: + format: int32 + type: integer + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/User" + responses: + "204": + description: No Content + delete: + tags: + - User Resource + description: Delete + operationId: org.acme.UserResource.delete + parameters: + - name: id + in: path + required: true + schema: + format: int32 + type: integer + responses: + "204": + description: No Content +components: + schemas: + User: + type: object + properties: + id: + format: int32 + type: integer + name: + type: string diff --git a/client/integration-tests/remove-operationid-prefix/src/main/openapi/openapi-prefix-delimiter.yaml b/client/integration-tests/remove-operationid-prefix/src/main/openapi/openapi-prefix-delimiter.yaml new file mode 100644 index 000000000..a6ac8abb9 --- /dev/null +++ b/client/integration-tests/remove-operationid-prefix/src/main/openapi/openapi-prefix-delimiter.yaml @@ -0,0 +1,108 @@ +--- +openapi: 3.0.3 +info: + title: code-with-quarkus API + version: 1.0.0-SNAPSHOT +servers: +- url: http://localhost:8080 + description: Auto generated value +- url: http://0.0.0.0:8080 + description: Auto generated value +paths: + /users: + get: + tags: + - User Resource + description: Find All + operationId: UserResource.findAll + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/User" + post: + tags: + - User Resource + description: Add + operationId: UserResource.add + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/User" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/User" + /users/{id}: + get: + tags: + - User Resource + description: Find + operationId: UserResource.find + parameters: + - name: id + in: path + required: true + schema: + format: int32 + type: integer + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/User" + put: + tags: + - User Resource + description: Update + operationId: UserResource.update + parameters: + - name: id + in: path + required: true + schema: + format: int32 + type: integer + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/User" + responses: + "204": + description: No Content + delete: + tags: + - User Resource + description: Delete + operationId: UserResource.delete + parameters: + - name: id + in: path + required: true + schema: + format: int32 + type: integer + responses: + "204": + description: No Content +components: + schemas: + User: + type: object + properties: + id: + format: int32 + type: integer + name: + type: string diff --git a/client/integration-tests/remove-operationid-prefix/src/main/openapi/openapi-remove-operation-id-prefix.yaml b/client/integration-tests/remove-operationid-prefix/src/main/openapi/openapi-remove-operation-id-prefix.yaml new file mode 100644 index 000000000..270c78d9c --- /dev/null +++ b/client/integration-tests/remove-operationid-prefix/src/main/openapi/openapi-remove-operation-id-prefix.yaml @@ -0,0 +1,108 @@ +--- +openapi: 3.0.3 +info: + title: code-with-quarkus API + version: 1.0.0-SNAPSHOT +servers: +- url: http://localhost:8080 + description: Auto generated value +- url: http://0.0.0.0:8080 + description: Auto generated value +paths: + /users: + get: + tags: + - User Resource + description: Find All + operationId: UserResource_findAll + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/User" + post: + tags: + - User Resource + description: Add + operationId: UserResource_add + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/User" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/User" + /users/{id}: + get: + tags: + - User Resource + description: Find + operationId: UserResource_find + parameters: + - name: id + in: path + required: true + schema: + format: int32 + type: integer + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/User" + put: + tags: + - User Resource + description: Update + operationId: UserResource_update + parameters: + - name: id + in: path + required: true + schema: + format: int32 + type: integer + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/User" + responses: + "204": + description: No Content + delete: + tags: + - User Resource + description: Delete + operationId: UserResource_delete + parameters: + - name: id + in: path + required: true + schema: + format: int32 + type: integer + responses: + "204": + description: No Content +components: + schemas: + User: + type: object + properties: + id: + format: int32 + type: integer + name: + type: string diff --git a/client/integration-tests/remove-operationid-prefix/src/main/resources/application.properties b/client/integration-tests/remove-operationid-prefix/src/main/resources/application.properties new file mode 100644 index 000000000..ccde9f8d2 --- /dev/null +++ b/client/integration-tests/remove-operationid-prefix/src/main/resources/application.properties @@ -0,0 +1,10 @@ +quarkus.rest-client.quarkus_simple_openapi_yaml.url=http://localhost:8080 + +quarkus.openapi-generator.codegen.spec.openapi_remove_operation_id_prefix_yaml.remove-operation-id-prefix=true + +quarkus.openapi-generator.codegen.spec.openapi_prefix_delimiter_yaml.remove-operation-id-prefix=true +quarkus.openapi-generator.codegen.spec.openapi_prefix_delimiter_yaml.remove-operation-id-prefix-delimiter=. + +quarkus.openapi-generator.codegen.spec.openapi_prefix_count_yaml.remove-operation-id-prefix=true +quarkus.openapi-generator.codegen.spec.openapi_prefix_count_yaml.remove-operation-id-prefix-delimiter=. +quarkus.openapi-generator.codegen.spec.openapi_prefix_count_yaml.remove-operation-id-prefix-count=3 diff --git a/client/integration-tests/remove-operationid-prefix/src/test/java/io/quarkiverse/openapi/generator/it/RemoveOperationIdPrefixTest.java b/client/integration-tests/remove-operationid-prefix/src/test/java/io/quarkiverse/openapi/generator/it/RemoveOperationIdPrefixTest.java new file mode 100644 index 000000000..e108f8791 --- /dev/null +++ b/client/integration-tests/remove-operationid-prefix/src/test/java/io/quarkiverse/openapi/generator/it/RemoveOperationIdPrefixTest.java @@ -0,0 +1,77 @@ +package io.quarkiverse.openapi.generator.it; + +import static org.assertj.core.api.Assertions.assertThatCode; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class RemoveOperationIdPrefixTest { + + @Test + void removeOperationIdPrefixTest() { + String apiClassName = "org.openapi.quarkus.openapi_remove_operation_id_prefix_yaml.api.UserResourceApi"; + String modelClassName = "org.openapi.quarkus.openapi_remove_operation_id_prefix_yaml.model.User"; + + assertThatCode(() -> Class.forName(apiClassName).getMethod("find", Integer.class)) + .doesNotThrowAnyException(); + + assertThatCode(() -> Class.forName(apiClassName).getMethod("findAll")) + .doesNotThrowAnyException(); + + assertThatCode(() -> Class.forName(apiClassName).getMethod("add", Class.forName(modelClassName))) + .doesNotThrowAnyException(); + + assertThatCode(() -> Class.forName(apiClassName).getMethod("update", Integer.class, Class.forName(modelClassName))) + .doesNotThrowAnyException(); + + assertThatCode(() -> Class.forName(apiClassName).getMethod("delete", Integer.class)) + .doesNotThrowAnyException(); + + } + + @Test + void removeOperationIdPrefixDelimiterTest() { + String apiClassName = "org.openapi.quarkus.openapi_prefix_delimiter_yaml.api.UserResourceApi"; + String modelClassName = "org.openapi.quarkus.openapi_prefix_delimiter_yaml.model.User"; + + assertThatCode(() -> Class.forName(apiClassName).getMethod("find", Integer.class)) + .doesNotThrowAnyException(); + + assertThatCode(() -> Class.forName(apiClassName).getMethod("findAll")) + .doesNotThrowAnyException(); + + assertThatCode(() -> Class.forName(apiClassName).getMethod("add", Class.forName(modelClassName))) + .doesNotThrowAnyException(); + + assertThatCode(() -> Class.forName(apiClassName).getMethod("update", Integer.class, Class.forName(modelClassName))) + .doesNotThrowAnyException(); + + assertThatCode(() -> Class.forName(apiClassName).getMethod("delete", Integer.class)) + .doesNotThrowAnyException(); + + } + + @Test + void removeOperationIdPrefixCountTest() { + String apiClassName = "org.openapi.quarkus.openapi_prefix_count_yaml.api.UserResourceApi"; + String modelClassName = "org.openapi.quarkus.openapi_prefix_count_yaml.model.User"; + + assertThatCode(() -> Class.forName(apiClassName).getMethod("find", Integer.class)) + .doesNotThrowAnyException(); + + assertThatCode(() -> Class.forName(apiClassName).getMethod("findAll")) + .doesNotThrowAnyException(); + + assertThatCode(() -> Class.forName(apiClassName).getMethod("add", Class.forName(modelClassName))) + .doesNotThrowAnyException(); + + assertThatCode(() -> Class.forName(apiClassName).getMethod("update", Integer.class, Class.forName(modelClassName))) + .doesNotThrowAnyException(); + + assertThatCode(() -> Class.forName(apiClassName).getMethod("delete", Integer.class)) + .doesNotThrowAnyException(); + + } +} diff --git a/client/integration-tests/return-response/pom.xml b/client/integration-tests/return-response/pom.xml index 3a86a104f..fa996369c 100644 --- a/client/integration-tests/return-response/pom.xml +++ b/client/integration-tests/return-response/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-return-response - Quarkus - Openapi Generator - Integration Tests - Client - return-response + Quarkus - OpenAPI Generator - Integration Tests - Client - return-response Example project for usage of the return-response property diff --git a/client/integration-tests/return-response/src/main/resources/application.properties b/client/integration-tests/return-response/src/main/resources/application.properties index a8e2cac17..ea03ea095 100644 --- a/client/integration-tests/return-response/src/main/resources/application.properties +++ b/client/integration-tests/return-response/src/main/resources/application.properties @@ -11,4 +11,6 @@ quarkus.openapi-generator.codegen.spec.return_response_true_void_simple_openapi_ quarkus.openapi-generator.codegen.spec.return_response_false_string_simple_openapi_yaml.mutiny = false quarkus.openapi-generator.codegen.spec.return_response_false_void_simple_openapi_yaml.mutiny = false quarkus.openapi-generator.codegen.spec.return_response_true_string_simple_openapi_yaml.mutiny = false -quarkus.openapi-generator.codegen.spec.return_response_true_void_simple_openapi_yaml.mutiny = false \ No newline at end of file +quarkus.openapi-generator.codegen.spec.return_response_true_void_simple_openapi_yaml.mutiny = false + +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/security/pom.xml b/client/integration-tests/security/pom.xml index 11035298f..be31c56f8 100644 --- a/client/integration-tests/security/pom.xml +++ b/client/integration-tests/security/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-security - Quarkus - Openapi Generator - Integration Tests - Client - Security + Quarkus - OpenAPI Generator - Integration Tests - Client - Security A few use cases that relies on security use cases with the OpenAPI Generator @@ -16,14 +16,18 @@ io.quarkiverse.openapi.generator quarkus-openapi-generator + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-oidc + io.quarkus quarkus-junit5 test - com.github.tomakehurst - wiremock-jre8 + org.wiremock + wiremock test @@ -91,5 +95,30 @@ native + + resteasy-reactive + + + io.quarkus + quarkus-rest-client-oidc-filter + + + + + resteasy-classic + + true + + + + io.quarkus + quarkus-resteasy-client-oidc-filter + + + io.quarkus + quarkus-resteasy-multipart + + + \ No newline at end of file diff --git a/client/integration-tests/security/src/main/java/io/quarkiverse/openapi/generator/it/security/auth/DummyApiKeyAuthenticationProvider.java b/client/integration-tests/security/src/main/java/io/quarkiverse/openapi/generator/it/security/auth/DummyApiKeyAuthenticationProvider.java index 3e353d93d..1096cca18 100644 --- a/client/integration-tests/security/src/main/java/io/quarkiverse/openapi/generator/it/security/auth/DummyApiKeyAuthenticationProvider.java +++ b/client/integration-tests/security/src/main/java/io/quarkiverse/openapi/generator/it/security/auth/DummyApiKeyAuthenticationProvider.java @@ -5,13 +5,10 @@ import jakarta.annotation.PostConstruct; import jakarta.annotation.Priority; -import jakarta.inject.Inject; import jakarta.ws.rs.Priorities; import jakarta.ws.rs.client.ClientRequestContext; import jakarta.ws.rs.client.ClientRequestFilter; -import io.quarkiverse.openapi.generator.OpenApiGeneratorConfig; -import io.quarkiverse.openapi.generator.SpecItemConfig; import io.quarkiverse.openapi.generator.providers.ApiKeyAuthenticationProvider; import io.quarkiverse.openapi.generator.providers.ApiKeyIn; import io.quarkiverse.openapi.generator.providers.AuthProvider; @@ -19,16 +16,11 @@ @Priority(Priorities.AUTHENTICATION) public class DummyApiKeyAuthenticationProvider implements ClientRequestFilter { - @Inject - OpenApiGeneratorConfig generatorConfig; - private AuthProvider authProvider; @PostConstruct public void init() { authProvider = new ApiKeyAuthenticationProvider("open_weather_custom_security_yaml", "app_id", ApiKeyIn.query, "appid", - generatorConfig.getItemConfig("open_weather_custom_security_yaml") - .flatMap(SpecItemConfig::getAuth).flatMap(x -> x.getItemConfig("app_id")).orElse(null), List.of()); } diff --git a/client/integration-tests/security/src/main/resources/application.properties b/client/integration-tests/security/src/main/resources/application.properties index 03188f952..5422a5e76 100644 --- a/client/integration-tests/security/src/main/resources/application.properties +++ b/client/integration-tests/security/src/main/resources/application.properties @@ -76,4 +76,6 @@ quarkus.oidc-client.service5_oauth2.discovery-enabled=false quarkus.oidc-client.service5_oauth2.client-id=kogito-app quarkus.oidc-client.service5_oauth2.grant.type=client quarkus.oidc-client.service5_oauth2.credentials.client-secret.method=basic -quarkus.oidc-client.service5_oauth2.credentials.client-secret.value=secret \ No newline at end of file +quarkus.oidc-client.service5_oauth2.credentials.client-secret.value=secret + +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationExternalServicesMock.java b/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationExternalServicesMock.java index a29ab6b40..43b6289e3 100644 --- a/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationExternalServicesMock.java +++ b/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationExternalServicesMock.java @@ -14,6 +14,9 @@ import jakarta.ws.rs.core.HttpHeaders; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.github.tomakehurst.wiremock.WireMockServer; import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; @@ -25,18 +28,25 @@ public class TokenPropagationExternalServicesMock implements QuarkusTestResource public static final String SERVICE3_AUTHORIZATION_TOKEN = "SERVICE3_AUTHORIZATION_TOKEN"; public static final String SERVICE4_HEADER_TO_PROPAGATE = "SERVICE4_HEADER_TO_PROPAGATE"; public static final String SERVICE4_AUTHORIZATION_TOKEN = "SERVICE4_AUTHORIZATION_TOKEN"; - - private static final String BEARER = "Bearer "; - public static final String TOKEN_PROPAGATION_EXTERNAL_SERVICE_MOCK_URL = "propagation-external-service-mock.url"; - + private static final String BEARER = "Bearer "; + private static final Logger LOGGER = LoggerFactory.getLogger(TokenPropagationExternalServicesMock.class); private WireMockServer wireMockServer; + private static void stubForExternalService(String tokenPropagationExternalServiceUrl, String authorizationToken) { + stubFor(post(tokenPropagationExternalServiceUrl) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo(BEARER + authorizationToken)) + .willReturn(aResponse() + .withHeader(CONTENT_TYPE, APPLICATION_JSON) + .withBody("{}"))); + } + @Override public Map start() { wireMockServer = new WireMockServer(options().dynamicPort()); wireMockServer.start(); configureFor(wireMockServer.port()); + LOGGER.info("Mocked Server started at {}", wireMockServer.baseUrl()); // stub the token-propagation-external-service1 invocation with the expected token stubForExternalService("/token-propagation-external-service1/executeQuery1", AUTHORIZATION_TOKEN); @@ -58,14 +68,6 @@ public Map start() { return Map.of(TOKEN_PROPAGATION_EXTERNAL_SERVICE_MOCK_URL, wireMockServer.baseUrl()); } - private static void stubForExternalService(String tokenPropagationExternalServiceUrl, String authorizationToken) { - stubFor(post(tokenPropagationExternalServiceUrl) - .withHeader(HttpHeaders.AUTHORIZATION, equalTo(BEARER + authorizationToken)) - .willReturn(aResponse() - .withHeader(CONTENT_TYPE, APPLICATION_JSON) - .withBody("{}"))); - } - @Override public void stop() { if (wireMockServer != null) { diff --git a/client/integration-tests/serializable-model/pom.xml b/client/integration-tests/serializable-model/pom.xml new file mode 100644 index 000000000..4b2c58ce4 --- /dev/null +++ b/client/integration-tests/serializable-model/pom.xml @@ -0,0 +1,93 @@ + + + + quarkus-openapi-generator-integration-tests + io.quarkiverse.openapi.generator + 3.0.0-SNAPSHOT + + 4.0.0 + + quarkus-openapi-generator-it-serializable-model + Quarkus - OpenAPI Generator - Integration Tests - Client - Serializable model + Example project for general usage + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator + + + org.assertj + assertj-core + test + + + io.quarkus + quarkus-junit5 + test + + + + + + io.quarkus + quarkus-maven-plugin + true + + + + build + generate-code + generate-code-tests + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + + ${project.build.directory}/${project.build.finalName}-runner + + org.jboss.logmanager.LogManager + + ${maven.home} + + + + + + + + + native + + + + + \ No newline at end of file diff --git a/client/integration-tests/serializable-model/src/main/openapi/quarkus-non-serializable-openapi.yaml b/client/integration-tests/serializable-model/src/main/openapi/quarkus-non-serializable-openapi.yaml new file mode 100644 index 000000000..ec431be5e --- /dev/null +++ b/client/integration-tests/serializable-model/src/main/openapi/quarkus-non-serializable-openapi.yaml @@ -0,0 +1,125 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Animals - OpenAPI 3.0", + "version": "1.0.5" + }, + "servers": [ + { + "url": "/api/v3" + } + ], + "tags": [ + { + "name": "primate", + "description": "Everything about Primates" + } + ], + "paths": { + "/primate/{id}": { + "get": { + "tags": [ + "primate" + ], + "summary": "Find primate by ID", + "description": "Returns a single primate", + "operationId": "getPrimateById", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of primate to return", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Primate" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Primate not found" + } + } + } + } + }, + "components": { + "schemas": { + "Animal": { + "type": "object", + "properties": { + "born": { + "type": "string", + "description": "Dated Base extension.", + "format": "date-time" + }, + "deceased": { + "type": "string", + "description": "Dated Base extension.", + "format": "date-time" + } + }, + "xml": { + "name": "animal" + } + }, + "Mammal": { + "type": "object", + "allOf": [ { + "$ref": "#/components/schemas/Animal" + } ], + "properties": { + "gender": { + "type": "string", + "enum": [ + "female", + "male" + ] + } + }, + "xml": { + "name": "mammal" + } + }, + "Primate": { + "required": [ + "name" + ], + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/Mammal" + } + ], + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "name": { + "type": "string", + "example": "jane doe" + } + }, + "xml": { + "name": "primate" + } + } + } + } +} diff --git a/client/integration-tests/serializable-model/src/main/openapi/quarkus-serializable-openapi.yaml b/client/integration-tests/serializable-model/src/main/openapi/quarkus-serializable-openapi.yaml new file mode 100644 index 000000000..ec431be5e --- /dev/null +++ b/client/integration-tests/serializable-model/src/main/openapi/quarkus-serializable-openapi.yaml @@ -0,0 +1,125 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Animals - OpenAPI 3.0", + "version": "1.0.5" + }, + "servers": [ + { + "url": "/api/v3" + } + ], + "tags": [ + { + "name": "primate", + "description": "Everything about Primates" + } + ], + "paths": { + "/primate/{id}": { + "get": { + "tags": [ + "primate" + ], + "summary": "Find primate by ID", + "description": "Returns a single primate", + "operationId": "getPrimateById", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of primate to return", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Primate" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Primate not found" + } + } + } + } + }, + "components": { + "schemas": { + "Animal": { + "type": "object", + "properties": { + "born": { + "type": "string", + "description": "Dated Base extension.", + "format": "date-time" + }, + "deceased": { + "type": "string", + "description": "Dated Base extension.", + "format": "date-time" + } + }, + "xml": { + "name": "animal" + } + }, + "Mammal": { + "type": "object", + "allOf": [ { + "$ref": "#/components/schemas/Animal" + } ], + "properties": { + "gender": { + "type": "string", + "enum": [ + "female", + "male" + ] + } + }, + "xml": { + "name": "mammal" + } + }, + "Primate": { + "required": [ + "name" + ], + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/Mammal" + } + ], + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "name": { + "type": "string", + "example": "jane doe" + } + }, + "xml": { + "name": "primate" + } + } + } + } +} diff --git a/client/integration-tests/serializable-model/src/main/resources/application.properties b/client/integration-tests/serializable-model/src/main/resources/application.properties new file mode 100644 index 000000000..30fc60530 --- /dev/null +++ b/client/integration-tests/serializable-model/src/main/resources/application.properties @@ -0,0 +1,6 @@ +quarkus.openapi-generator.codegen.spec.quarkus_serializable_openapi_yaml.serializable-model=true +quarkus.openapi-generator.codegen.spec.quarkus_serializable_openapi_yaml.base-package=org.acme.serializable + +quarkus.openapi-generator.codegen.spec.quarkus_non_serializable_openapi_yaml.base-package=org.acme.non.serializable + +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/serializable-model/src/test/java/io/quarkiverse/openapi/generator/it/SerializableModelTest.java b/client/integration-tests/serializable-model/src/test/java/io/quarkiverse/openapi/generator/it/SerializableModelTest.java new file mode 100644 index 000000000..4f1937c15 --- /dev/null +++ b/client/integration-tests/serializable-model/src/test/java/io/quarkiverse/openapi/generator/it/SerializableModelTest.java @@ -0,0 +1,27 @@ +package io.quarkiverse.openapi.generator.it; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.Serializable; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class SerializableModelTest { + + @Test + void verifySerializableIsEnabled() { + var interfaces = org.acme.serializable.model.Animal.class.getInterfaces(); + + assertThat(interfaces).contains(Serializable.class); + } + + @Test + void verifySerializableIsNotEnabled() { + var interfaces = org.acme.non.serializable.model.Animal.class.getInterfaces(); + + assertThat(interfaces).doesNotContain(Serializable.class); + } +} diff --git a/client/integration-tests/simple/pom.xml b/client/integration-tests/simple/pom.xml index af06edbec..dbc79ac92 100644 --- a/client/integration-tests/simple/pom.xml +++ b/client/integration-tests/simple/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-simple - Quarkus - Openapi Generator - Integration Tests - Client - Simple + Quarkus - OpenAPI Generator - Integration Tests - Client - Simple Example project for general usage diff --git a/client/integration-tests/simple/src/main/resources/application.properties b/client/integration-tests/simple/src/main/resources/application.properties index a2f16a854..07439c101 100644 --- a/client/integration-tests/simple/src/main/resources/application.properties +++ b/client/integration-tests/simple/src/main/resources/application.properties @@ -1 +1,3 @@ -quarkus.rest-client.quarkus_simple_openapi_yaml.url=http://localhost:8080 \ No newline at end of file +quarkus.rest-client.quarkus_simple_openapi_yaml.url=http://localhost:8080 + +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/skip-validation/pom.xml b/client/integration-tests/skip-validation/pom.xml index 8cedef1bf..5b678a044 100644 --- a/client/integration-tests/skip-validation/pom.xml +++ b/client/integration-tests/skip-validation/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-skip-validation - Quarkus - Openapi Generator - Integration Tests - Client - Skip Spec Validation + Quarkus - OpenAPI Generator - Integration Tests - Client - Skip Spec Validation Skip OpenAPI Spec Validation @@ -22,8 +22,8 @@ test - com.github.tomakehurst - wiremock-jre8 + org.wiremock + wiremock test
diff --git a/client/integration-tests/skip-validation/src/main/resources/application.properties b/client/integration-tests/skip-validation/src/main/resources/application.properties index 054894535..8cccb79fe 100644 --- a/client/integration-tests/skip-validation/src/main/resources/application.properties +++ b/client/integration-tests/skip-validation/src/main/resources/application.properties @@ -1 +1,2 @@ -quarkus.openapi-generator.codegen.validateSpec=false \ No newline at end of file +quarkus.openapi-generator.codegen.validateSpec=false +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/suffix-prefix/pom.xml b/client/integration-tests/suffix-prefix/pom.xml index a7130832b..1b90d9ae9 100644 --- a/client/integration-tests/suffix-prefix/pom.xml +++ b/client/integration-tests/suffix-prefix/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-suffix-prefix - Quarkus - Openapi Generator - Integration Tests - Client - Suffix / Prefix + Quarkus - OpenAPI Generator - Integration Tests - Client - Suffix / Prefix diff --git a/client/integration-tests/suffix-prefix/src/main/resources/application.properties b/client/integration-tests/suffix-prefix/src/main/resources/application.properties index 2655ddeec..3e88e88d6 100644 --- a/client/integration-tests/suffix-prefix/src/main/resources/application.properties +++ b/client/integration-tests/suffix-prefix/src/main/resources/application.properties @@ -1,4 +1,6 @@ quarkus.rest-client.quarkus_suffix_prefix_openapi_yaml.url=http://localhost:8080 quarkus.openapi-generator.codegen.spec.quarkus_suffix_prefix_openapi_yaml.api-name-suffix=CustomApiSuffix quarkus.openapi-generator.codegen.spec.quarkus_suffix_prefix_openapi_yaml.model-name-suffix=CustomModelSuffix -quarkus.openapi-generator.codegen.spec.quarkus_suffix_prefix_openapi_yaml.model-name-prefix=CustomModelPrefix \ No newline at end of file +quarkus.openapi-generator.codegen.spec.quarkus_suffix_prefix_openapi_yaml.model-name-prefix=CustomModelPrefix + +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/type-mapping/pom.xml b/client/integration-tests/type-mapping/pom.xml index 78c5b6372..7d258890f 100644 --- a/client/integration-tests/type-mapping/pom.xml +++ b/client/integration-tests/type-mapping/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-type-mapping - Quarkus - Openapi Generator - Integration Tests - Client - Type Mapping + Quarkus - OpenAPI Generator - Integration Tests - Client - Type Mapping @@ -21,8 +21,8 @@ test - com.github.tomakehurst - wiremock-jre8 + org.wiremock + wiremock test diff --git a/client/integration-tests/type-mapping/src/main/openapi/type-mappings-testing.yml b/client/integration-tests/type-mapping/src/main/openapi/type-mappings-testing.yml index d48b71c3e..0a54c5c9c 100644 --- a/client/integration-tests/type-mapping/src/main/openapi/type-mappings-testing.yml +++ b/client/integration-tests/type-mapping/src/main/openapi/type-mappings-testing.yml @@ -36,6 +36,23 @@ components: UserId: type: string format: uuid + YearMonth: + type: object + properties: + year: + format: int32 + type: integer + month: + format: int32 + type: integer + prolepticMonth: + format: int64 + type: integer + monthValue: + format: int32 + type: integer + leapYear: + type: boolean MultipartRequestBody: type: object @@ -46,5 +63,7 @@ components: $ref: '#/components/schemas/SomeDateTime' binaryStringFile: $ref: '#/components/schemas/BinaryStringFile' + yearMonth: + $ref: '#/components/schemas/YearMonth' diff --git a/client/integration-tests/type-mapping/src/main/resources/application.properties b/client/integration-tests/type-mapping/src/main/resources/application.properties index e7665f407..dbaee9734 100644 --- a/client/integration-tests/type-mapping/src/main/resources/application.properties +++ b/client/integration-tests/type-mapping/src/main/resources/application.properties @@ -1,5 +1,8 @@ quarkus.openapi-generator.codegen.spec.type_mappings_testing_yml.type-mappings.UUID=String quarkus.openapi-generator.codegen.spec.type_mappings_testing_yml.type-mappings.File=InputStream quarkus.openapi-generator.codegen.spec.type_mappings_testing_yml.import-mappings.File=java.io.InputStream +quarkus.openapi-generator.codegen.spec.type_mappings_testing_yml.schema-mappings.YearMonth=java.time.YearMonth quarkus.openapi-generator.codegen.spec.type_mappings_testing_yml.base-package=org.acme.openapi.typemapping -quarkus.openapi-generator.codegen.spec.type_mappings_testing_yml.additional-api-type-annotations=@org.eclipse.microprofile.rest.client.annotation.RegisterProvider(io.quarkiverse.openapi.generator.it.type.mapping.OffsetDateTimeParamConverterProvider.class) \ No newline at end of file +quarkus.openapi-generator.codegen.spec.type_mappings_testing_yml.additional-api-type-annotations=@org.eclipse.microprofile.rest.client.annotation.RegisterProvider(io.quarkiverse.openapi.generator.it.type.mapping.OffsetDateTimeParamConverterProvider.class) + +quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyClassicTest.java b/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyClassicTest.java index 89d21966e..77ee7be17 100644 --- a/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyClassicTest.java +++ b/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyClassicTest.java @@ -9,6 +9,7 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.time.OffsetDateTime; +import java.time.YearMonth; import java.time.ZoneOffset; import jakarta.inject.Inject; @@ -41,10 +42,12 @@ class TypeAndImportMappingRestEasyClassicTest { public void canMapTypesAndImportToDifferentValues() { final String testUuid = "00112233-4455-6677-8899-aabbccddeeff"; final InputStream testFile = new ByteArrayInputStream("Content of the file".getBytes(StandardCharsets.UTF_8)); + final YearMonth testYearMonth = YearMonth.parse("2024-06"); TypeMappingApi.PostTheDataMultipartForm requestBody = new TypeMappingApi.PostTheDataMultipartForm(); requestBody.id = testUuid; // String instead of UUID requestBody.binaryStringFile = testFile; // InputStream instead of File + requestBody.yearMonth = testYearMonth; // YearMonth instead of String // dateTime remains OffsetDateTime (as is default) requestBody.dateTime = OffsetDateTime.of(2000, 2, 13, 4, 5, 6, 0, ZoneOffset.UTC); @@ -63,6 +66,10 @@ public void canMapTypesAndImportToDifferentValues() { .withName("binaryStringFile") .withHeader("Content-Disposition", containing("filename=")) .withHeader(ContentTypeHeader.KEY, equalTo(MediaType.APPLICATION_OCTET_STREAM)) - .withBody(equalTo("Content of the file")).build())); + .withBody(equalTo("Content of the file")).build()) + .withRequestBodyPart(new MultipartValuePatternBuilder() + .withName("yearMonth") + .withHeader(ContentTypeHeader.KEY, equalTo(MediaType.APPLICATION_JSON)) + .withBody(equalTo("\"2024-06\"")).build())); } } diff --git a/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyReactiveTest.java b/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyReactiveTest.java index f18fc10f7..6909b89fb 100644 --- a/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyReactiveTest.java +++ b/client/integration-tests/type-mapping/src/test/java/io/quarkiverse/openapi/generator/it/type/mapping/TypeAndImportMappingRestEasyReactiveTest.java @@ -5,6 +5,7 @@ import java.io.*; import java.nio.charset.StandardCharsets; import java.time.OffsetDateTime; +import java.time.YearMonth; import java.time.ZoneOffset; import jakarta.inject.Inject; @@ -37,10 +38,12 @@ public class TypeAndImportMappingRestEasyReactiveTest { public void canMapTypesAndImportToDifferentValues() { final String testUuid = "00112233-4455-6677-8899-aabbccddeeff"; final InputStream testFile = new ByteArrayInputStream("Content of the file".getBytes(StandardCharsets.UTF_8)); + final YearMonth testYearMonth = YearMonth.parse("2024-06"); TypeMappingApi.PostTheDataMultipartForm requestBody = new TypeMappingApi.PostTheDataMultipartForm(); requestBody.id = testUuid; // String instead of UUID requestBody.binaryStringFile = testFile; // InputStream instead of File + requestBody.yearMonth = testYearMonth; // YearMonth instead of String // dateTime remains OffsetDateTime (as is default) requestBody.dateTime = OffsetDateTime.of(2000, 2, 13, 4, 5, 6, 0, ZoneOffset.UTC); @@ -52,6 +55,12 @@ public void canMapTypesAndImportToDifferentValues() { .withHeader(ContentTypeHeader.KEY, equalTo(MediaType.TEXT_PLAIN + "; charset=UTF-8")) .withBody(equalTo(testUuid)).build())); + typeMappingServer.verify(postRequestedFor(urlEqualTo("/type-mapping")) + .withRequestBodyPart(new MultipartValuePatternBuilder() + .withName("yearMonth") + .withHeader(ContentTypeHeader.KEY, equalTo(MediaType.APPLICATION_JSON)) + .withBody(equalTo("\"2024-06\"")).build())); + typeMappingServer.verify(postRequestedFor(urlEqualTo("/type-mapping")) .withRequestBodyPart(new MultipartValuePatternBuilder() .withName("dateTime") diff --git a/client/integration-tests/without-oidc/pom.xml b/client/integration-tests/without-oidc/pom.xml new file mode 100644 index 000000000..752f4dee3 --- /dev/null +++ b/client/integration-tests/without-oidc/pom.xml @@ -0,0 +1,94 @@ + + + + quarkus-openapi-generator-integration-tests + io.quarkiverse.openapi.generator + 3.0.0-SNAPSHOT + + 4.0.0 + + quarkus-openapi-generator-it-without-oidc + Quarkus - OpenAPI Generator - Integration Tests - Client - Without OIDC + Example project for general usage + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator + + + io.quarkus + quarkus-hibernate-validator + + + org.assertj + assertj-core + test + + + io.quarkus + quarkus-junit5 + test + + + + + + io.quarkus + quarkus-maven-plugin + true + + + + build + generate-code + generate-code-tests + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + + + \ No newline at end of file diff --git a/client/integration-tests/without-oidc/src/main/openapi/quarkus-simple-openapi.yaml b/client/integration-tests/without-oidc/src/main/openapi/quarkus-simple-openapi.yaml new file mode 100644 index 000000000..559d04451 --- /dev/null +++ b/client/integration-tests/without-oidc/src/main/openapi/quarkus-simple-openapi.yaml @@ -0,0 +1,282 @@ +--- +openapi: 3.0.3 +info: + title: greeting-flow API + version: "1.0" +paths: + /: + post: + requestBody: + content: + '*/*': + schema: + $ref: '#/components/schemas/CloudEvent' + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Response' + /hello: + get: + tags: + - Reactive Greeting Resource + operationId: hello + responses: + "200": + description: OK + content: + text/plain: + schema: + type: string + /messaging/topics: + get: + tags: + - Quarkus Topics Information Resource + responses: + "200": + description: OK +components: + schemas: + Type: + type: object + properties: + tags: + uniqueItems: true + type: array + items: + type: string + CloudEvent: + type: object + properties: + specVersion: + $ref: '#/components/schemas/SpecVersion' + id: + type: string + type: + type: string + source: + format: uri + type: string + dataContentType: + type: string + dataSchema: + format: uri + type: string + subject: + type: string + time: + format: date-time + type: string + attributeNames: + uniqueItems: true + type: array + items: + type: string + extensionNames: + uniqueItems: true + type: array + items: + type: string + data: + $ref: '#/components/schemas/CloudEventData' + CloudEventData: + type: object + EntityTag: + type: object + properties: + value: + type: string + weak: + type: boolean + Family: + enum: + - INFORMATIONAL + - SUCCESSFUL + - REDIRECTION + - CLIENT_ERROR + - SERVER_ERROR + - OTHER + type: string + Link: + type: object + properties: + uri: + format: uri + type: string + uriBuilder: + $ref: '#/components/schemas/UriBuilder' + rel: + type: string + rels: + type: array + items: + type: string + title: + type: string + type: + type: string + params: + type: object + additionalProperties: + type: string + Locale: + type: object + properties: + language: + type: string + script: + type: string + country: + type: string + variant: + type: string + extensionKeys: + uniqueItems: true + type: array + items: + format: byte + type: string + unicodeLocaleAttributes: + uniqueItems: true + type: array + items: + type: string + unicodeLocaleKeys: + uniqueItems: true + type: array + items: + type: string + iSO3Language: + type: string + iSO3Country: + type: string + displayLanguage: + type: string + displayScript: + type: string + displayCountry: + type: string + displayVariant: + type: string + displayName: + type: string + MediaType: + type: object + properties: + type: + type: string + subtype: + type: string + parameters: + type: object + additionalProperties: + type: string + wildcardType: + type: boolean + wildcardSubtype: + type: boolean + MultivaluedMapStringObject: + type: object + additionalProperties: + type: array + items: + type: object + MultivaluedMapStringString: + type: object + additionalProperties: + type: array + items: + type: string + NewCookie: + type: object + properties: + name: + type: string + value: + type: string + version: + format: int32 + type: integer + path: + type: string + domain: + type: string + comment: + type: string + maxAge: + format: int32 + type: integer + expiry: + format: date + type: string + secure: + type: boolean + httpOnly: + type: boolean + Response: + type: object + properties: + status: + format: int32 + type: integer + statusInfo: + $ref: '#/components/schemas/StatusType' + entity: + type: object + mediaType: + $ref: '#/components/schemas/MediaType' + language: + $ref: '#/components/schemas/Locale' + length: + format: int32 + type: integer + allowedMethods: + uniqueItems: true + type: array + items: + type: string + cookies: + type: object + additionalProperties: + $ref: '#/components/schemas/NewCookie' + entityTag: + $ref: '#/components/schemas/EntityTag' + date: + format: date + type: string + lastModified: + format: date + type: string + location: + format: uri + type: string + links: + uniqueItems: true + type: array + items: + $ref: '#/components/schemas/Link' + metadata: + $ref: '#/components/schemas/MultivaluedMapStringObject' + headers: + $ref: '#/components/schemas/MultivaluedMapStringObject' + stringHeaders: + $ref: '#/components/schemas/MultivaluedMapStringString' + SpecVersion: + enum: + - V03 + - V1 + type: string + StatusType: + type: object + properties: + statusCode: + format: int32 + type: integer + family: + $ref: '#/components/schemas/Family' + reasonPhrase: + type: string + UriBuilder: + type: object diff --git a/client/integration-tests/without-oidc/src/main/resources/application.properties b/client/integration-tests/without-oidc/src/main/resources/application.properties new file mode 100644 index 000000000..11a6c1a9f --- /dev/null +++ b/client/integration-tests/without-oidc/src/main/resources/application.properties @@ -0,0 +1 @@ +quarkus.rest-client.quarkus_simple_openapi_yaml.url=http://localhost:8080 diff --git a/client/integration-tests/without-oidc/src/test/java/io/quarkiverse/openapi/generator/it/QuarkusSimpleOpenApiTest.java b/client/integration-tests/without-oidc/src/test/java/io/quarkiverse/openapi/generator/it/QuarkusSimpleOpenApiTest.java new file mode 100644 index 000000000..f055063c6 --- /dev/null +++ b/client/integration-tests/without-oidc/src/test/java/io/quarkiverse/openapi/generator/it/QuarkusSimpleOpenApiTest.java @@ -0,0 +1,24 @@ +package io.quarkiverse.openapi.generator.it; + +import static org.assertj.core.api.Assertions.assertThat; + +import jakarta.inject.Inject; + +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.junit.jupiter.api.Test; +import org.openapi.quarkus.quarkus_simple_openapi_yaml.api.ReactiveGreetingResourceApi; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class QuarkusSimpleOpenApiTest { + + @RestClient + @Inject + ReactiveGreetingResourceApi api; + + @Test + void apiIsBeingGenerated() { + assertThat(api).isNotNull(); + } +} diff --git a/client/oidc/pom.xml b/client/oidc/pom.xml new file mode 100644 index 000000000..0dfc506b4 --- /dev/null +++ b/client/oidc/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-client-parent + 3.0.0-SNAPSHOT + + + quarkus-openapi-generator-oidc + Quarkus - OpenAPI Generator - Client - OIDC + OIDC Capabilities for Runtime + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator + ${project.version} + + + io.quarkus + quarkus-rest-client-oidc-filter + provided + + + io.quarkus + quarkus-resteasy-client-oidc-filter + provided + + + + \ No newline at end of file diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/ClassicOidcClientRequestFilterDelegate.java b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/ClassicOidcClientRequestFilterDelegate.java similarity index 82% rename from client/runtime/src/main/java/io/quarkiverse/openapi/generator/ClassicOidcClientRequestFilterDelegate.java rename to client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/ClassicOidcClientRequestFilterDelegate.java index 9f25d1057..f37895c8a 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/ClassicOidcClientRequestFilterDelegate.java +++ b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/ClassicOidcClientRequestFilterDelegate.java @@ -1,4 +1,4 @@ -package io.quarkiverse.openapi.generator; +package io.quarkiverse.openapi.generator.oidc; import java.io.IOException; @@ -10,7 +10,9 @@ import org.jboss.logging.Logger; -import io.quarkiverse.openapi.generator.providers.OAuth2AuthenticationProvider; +import io.quarkiverse.openapi.generator.OidcClient; +import io.quarkiverse.openapi.generator.OpenApiGeneratorConfig; +import io.quarkiverse.openapi.generator.oidc.providers.OAuth2AuthenticationProvider; import io.quarkus.oidc.client.runtime.AbstractTokensProducer; import io.quarkus.oidc.client.runtime.DisabledOidcClientException; @@ -19,15 +21,13 @@ public class ClassicOidcClientRequestFilterDelegate extends AbstractTokensProducer implements ClientRequestFilter, OAuth2AuthenticationProvider.OidcClientRequestFilterDelegate { - private static final Logger LOG = Logger - .getLogger(ClassicOidcClientRequestFilterDelegate.class); + private static final Logger LOG = Logger.getLogger(ClassicOidcClientRequestFilterDelegate.class); final String clientId; ClassicOidcClientRequestFilterDelegate(InjectionPoint injectionPoint) { OidcClient annotation = (OidcClient) injectionPoint.getQualifiers().stream() - .filter(x -> x.annotationType().equals(OidcClient.class)) - .findFirst().orElseThrow(); + .filter(x -> x.annotationType().equals(OidcClient.class)).findFirst().orElseThrow(); this.clientId = OpenApiGeneratorConfig.getSanitizedSecuritySchemeName(annotation.name()); } diff --git a/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/OidcAuthenticationRecorder.java b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/OidcAuthenticationRecorder.java new file mode 100644 index 000000000..2ed758540 --- /dev/null +++ b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/OidcAuthenticationRecorder.java @@ -0,0 +1,25 @@ +package io.quarkiverse.openapi.generator.oidc; + +import java.util.List; +import java.util.function.Function; + +import io.quarkiverse.openapi.generator.OidcClient; +import io.quarkiverse.openapi.generator.oidc.providers.OAuth2AuthenticationProvider; +import io.quarkiverse.openapi.generator.providers.AuthProvider; +import io.quarkiverse.openapi.generator.providers.OperationAuthInfo; +import io.quarkus.arc.SyntheticCreationalContext; +import io.quarkus.runtime.annotations.Recorder; + +@Recorder +public class OidcAuthenticationRecorder { + + public Function, AuthProvider> recordOauthAuthProvider( + String name, + String openApiSpecId, + List operations) { + return context -> new OAuth2AuthenticationProvider(name, openApiSpecId, + context.getInjectedReference(OAuth2AuthenticationProvider.OidcClientRequestFilterDelegate.class, + new OidcClient.Literal(name)), + operations); + } +} diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/ReactiveOidcClientRequestFilterDelegate.java b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/ReactiveOidcClientRequestFilterDelegate.java similarity index 62% rename from client/runtime/src/main/java/io/quarkiverse/openapi/generator/ReactiveOidcClientRequestFilterDelegate.java rename to client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/ReactiveOidcClientRequestFilterDelegate.java index d4fb3f4df..cdbd57961 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/ReactiveOidcClientRequestFilterDelegate.java +++ b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/ReactiveOidcClientRequestFilterDelegate.java @@ -1,7 +1,6 @@ -package io.quarkiverse.openapi.generator; +package io.quarkiverse.openapi.generator.oidc; import java.io.IOException; -import java.util.function.Consumer; import jakarta.annotation.Priority; import jakarta.enterprise.inject.spi.InjectionPoint; @@ -13,8 +12,9 @@ import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestContext; import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestFilter; -import io.quarkiverse.openapi.generator.providers.OAuth2AuthenticationProvider; -import io.quarkus.oidc.client.Tokens; +import io.quarkiverse.openapi.generator.OidcClient; +import io.quarkiverse.openapi.generator.OpenApiGeneratorConfig; +import io.quarkiverse.openapi.generator.oidc.providers.OAuth2AuthenticationProvider; import io.quarkus.oidc.client.runtime.AbstractTokensProducer; import io.quarkus.oidc.client.runtime.DisabledOidcClientException; import io.quarkus.oidc.common.runtime.OidcConstants; @@ -24,16 +24,14 @@ public class ReactiveOidcClientRequestFilterDelegate extends AbstractTokensProducer implements ResteasyReactiveClientRequestFilter, OAuth2AuthenticationProvider.OidcClientRequestFilterDelegate { - private static final Logger LOG = Logger - .getLogger(ReactiveOidcClientRequestFilterDelegate.class); + private static final Logger LOG = Logger.getLogger(ReactiveOidcClientRequestFilterDelegate.class); private static final String BEARER_SCHEME_WITH_SPACE = OidcConstants.BEARER_SCHEME + " "; final String clientId; ReactiveOidcClientRequestFilterDelegate(InjectionPoint injectionPoint) { OidcClient annotation = (OidcClient) injectionPoint.getQualifiers().stream() - .filter(x -> x.annotationType().equals(OidcClient.class)) - .findFirst().orElseThrow(); + .filter(x -> x.annotationType().equals(OidcClient.class)).findFirst().orElseThrow(); this.clientId = OpenApiGeneratorConfig.getSanitizedSecuritySchemeName(annotation.name()); } @@ -58,23 +56,17 @@ public void filter(ClientRequestContext requestContext) throws IOException { public void filter(ResteasyReactiveClientRequestContext requestContext) { requestContext.suspend(); - super.getTokens().subscribe().with(new Consumer<>() { - @Override - public void accept(Tokens tokens) { - requestContext.getHeaders().putSingle(HttpHeaders.AUTHORIZATION, - BEARER_SCHEME_WITH_SPACE + tokens.getAccessToken()); + super.getTokens().subscribe().with(tokens -> { + requestContext.getHeaders().putSingle(HttpHeaders.AUTHORIZATION, + BEARER_SCHEME_WITH_SPACE + tokens.getAccessToken()); + requestContext.resume(); + }, t -> { + if (t instanceof DisabledOidcClientException) { + LOG.debug("Client is disabled, acquiring and propagating the token is not necessary"); requestContext.resume(); - } - }, new Consumer<>() { - @Override - public void accept(Throwable t) { - if (t instanceof DisabledOidcClientException) { - LOG.debug("Client is disabled, acquiring and propagating the token is not necessary"); - requestContext.resume(); - } else { - LOG.debugf("Access token is not available, cause: %s, aborting the request", t.getMessage()); - requestContext.resume((t instanceof RuntimeException) ? t : new RuntimeException(t)); - } + } else { + LOG.debugf("Access token is not available, cause: %s, aborting the request", t.getMessage()); + requestContext.resume((t instanceof RuntimeException) ? t : new RuntimeException(t)); } }); } diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/OAuth2AuthenticationProvider.java b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/providers/OAuth2AuthenticationProvider.java similarity index 81% rename from client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/OAuth2AuthenticationProvider.java rename to client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/providers/OAuth2AuthenticationProvider.java index ff5368ced..f4f7009ed 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/OAuth2AuthenticationProvider.java +++ b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/providers/OAuth2AuthenticationProvider.java @@ -1,4 +1,4 @@ -package io.quarkiverse.openapi.generator.providers; +package io.quarkiverse.openapi.generator.oidc.providers; import static io.quarkiverse.openapi.generator.AuthConfig.TOKEN_PROPAGATION; @@ -11,7 +11,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.quarkiverse.openapi.generator.AuthConfig; +import io.quarkiverse.openapi.generator.providers.AbstractAuthProvider; +import io.quarkiverse.openapi.generator.providers.ConfigCredentialsProvider; +import io.quarkiverse.openapi.generator.providers.OperationAuthInfo; import io.quarkus.oidc.common.runtime.OidcConstants; public class OAuth2AuthenticationProvider extends AbstractAuthProvider { @@ -20,9 +22,9 @@ public class OAuth2AuthenticationProvider extends AbstractAuthProvider { private final OidcClientRequestFilterDelegate delegate; - public OAuth2AuthenticationProvider(final AuthConfig authConfig, String name, + public OAuth2AuthenticationProvider(String name, String openApiSpecId, OidcClientRequestFilterDelegate delegate, List operations) { - super(authConfig, name, openApiSpecId, operations); + super(name, openApiSpecId, operations, new ConfigCredentialsProvider()); this.delegate = delegate; validateConfig(); } diff --git a/client/pom.xml b/client/pom.xml index c82086b00..9c394590e 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -8,12 +8,13 @@ 4.0.0 quarkus-openapi-generator-client-parent - Quarkus - Openapi Generator - Client - Parent + Quarkus - OpenAPI Generator - Client - Parent pom deployment runtime test-utils + oidc \ No newline at end of file diff --git a/client/runtime/pom.xml b/client/runtime/pom.xml index b0b2ab4aa..67a5e03aa 100644 --- a/client/runtime/pom.xml +++ b/client/runtime/pom.xml @@ -8,7 +8,7 @@ ../pom.xml quarkus-openapi-generator - Quarkus - Openapi Generator - Client - Runtime + Quarkus - OpenAPI Generator - Client - Runtime Generation of Rest Clients based on OpenAPI specification files @@ -37,16 +37,6 @@ provided - - io.quarkus - quarkus-rest-client-oidc-filter - true - - - io.quarkus - quarkus-resteasy-client-oidc-filter - true - org.assertj assertj-core @@ -67,6 +57,11 @@ quarkus-junit5 test + + org.jboss.resteasy + resteasy-core + test + @@ -99,9 +94,6 @@ ${quarkus.version} - - -AlegacyConfigRoot=true - diff --git a/client/runtime/src/main/codestarts/quarkus/openapi-generator-codestart/codestart.yml b/client/runtime/src/main/codestarts/quarkus/openapi-generator-codestart/codestart.yml index cc83535aa..29eabb85d 100644 --- a/client/runtime/src/main/codestarts/quarkus/openapi-generator-codestart/codestart.yml +++ b/client/runtime/src/main/codestarts/quarkus/openapi-generator-codestart/codestart.yml @@ -3,6 +3,6 @@ ref: openapi-generator type: code tags: extension-codestart metadata: - title: OpenAPI Generator Codestart - description: Start to code with the OpenAPI Generator extension. - related-guide-section: https://docs.quarkiverse.io/quarkus-openapi-generator/dev/index.html \ No newline at end of file + title: OpenAPI Generator Client Codestart + description: Start to code with the OpenAPI Generator Client extension. + related-guide-section: https://docs.quarkiverse.io/quarkus-openapi-generator/dev/client.html \ No newline at end of file diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthConfig.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthConfig.java index 5a1e57987..0169d1524 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthConfig.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthConfig.java @@ -1,23 +1,18 @@ package io.quarkiverse.openapi.generator; -import java.util.Map; import java.util.Optional; -import io.quarkiverse.openapi.generator.providers.ApiKeyAuthenticationProvider; -import io.quarkiverse.openapi.generator.providers.BasicAuthenticationProvider; -import io.quarkiverse.openapi.generator.providers.BearerAuthenticationProvider; import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; /** * This class represents the runtime authentication related configuration for an individual securityScheme present * on an OpenApi spec definition, i.e. the provided files. */ @ConfigGroup -public class AuthConfig { +public interface AuthConfig { - public static final String TOKEN_PROPAGATION = "token-propagation"; - public static final String HEADER_NAME = "header-name"; + String TOKEN_PROPAGATION = "token-propagation"; + String HEADER_NAME = "header-name"; /** * Enables the authentication token propagation for this particular securityScheme. @@ -31,8 +26,7 @@ public class AuthConfig { * @see SpecItemConfig * @see OpenApiGeneratorConfig */ - @ConfigItem(defaultValue = "false") - public Optional tokenPropagation; + Optional tokenPropagation(); /** * Configures a particular http header attribute from were to take the security token from when the token propagation @@ -47,48 +41,91 @@ public class AuthConfig { * @see SpecItemConfig * @see OpenApiGeneratorConfig */ - @ConfigItem - public Optional headerName; + Optional headerName(); /** - * Configures a particular parameter value to be used by any of the different internal authentication filters - * that processes the different securityScheme definitions. - *

+ * Sets the Basic Authentication username for a given OpenAPI securityScheme. + *

* For example, given a file named petstore.json with a securityScheme named "petstore-basic-auth", that is of - * http basic authentication type, the following configuration can establish the user and password to be used. - * must be used. - *

+ * http basic authentication type, the following configuration can establish the user to be used. + *

* quarkus.openapi-generator.petstore_json.auth.petstore_basic_auth.username=MyUserName - * quarkus.openapi-generator.petstore_json.auth.petstore_basic_auth.password=MyPassword * - * @see AuthsConfig - * @see SpecItemConfig - * @see OpenApiGeneratorConfig - * @see BasicAuthenticationProvider - * @see BearerAuthenticationProvider - * @see ApiKeyAuthenticationProvider + * @return the username portion for Basic Authentication + * @see AuthConfig#password() + * @see 4.8.27.2.1 Basic Authentication + * Sample */ - @ConfigItem(name = ConfigItem.PARENT) - public Map authConfigParams; + Optional username(); - public Optional getTokenPropagation() { - return tokenPropagation; - } + /** + * Sets the Basic Authentication password for a given OpenAPI securityScheme. + *

+ * For example, given a file named petstore.json with a securityScheme named "petstore-basic-auth", that is of + * http basic authentication type, the following configuration can establish the password to be used. + *

+ * quarkus.openapi-generator.petstore_json.auth.petstore_basic_auth.password=MyPassword + *

+ * Ignored if the given securityScheme is not Basic Authentication + * + * @return the password portion for Basic Authentication + * @see AuthConfig#username() + * @see 4.8.27.2.1 Basic Authentication + * Sample + */ + Optional password(); - public Optional getHeaderName() { - return headerName; - } + /** + * Sets the Bearer Token for a given OpenAPI securityScheme. + *

+ * For example, given a file named petstore.json with a securityScheme named "petstore-bearer-auth", that is of + * bearer authentication type, the following configuration can establish the token to be used. + *

+ * quarkus.openapi-generator.petstore_json.auth.petstore_bearer_auth.token=1234567890 + *

+ * Ignored if the given securityScheme is not Bearer Token Authentication + * + * @return the token + * @see 4.8.27.2.3 JWT Bearer Sample + */ + Optional bearerToken(); - public Optional getConfigParam(String paramName) { - return Optional.ofNullable(authConfigParams.get(paramName)); - } + /** + * Sets the API Key for a given OpenAPI securityScheme. + *

+ * For example, given a file named petstore.json with a securityScheme named "petstore-apikey-auth", that is of + * API Key authentication type, the following configuration can establish the API Key to be used. + *

+ * quarkus.openapi-generator.petstore_json.auth.petstore_apikey_auth.api-key=${MY_SECRET_KEY_IN_AN_ENV_VAR} + *

+ * Ignored if the given securityScheme is not API Key Authentication + * + * @return the token + * @see 4.8.27.2.2 API Key Samplee + */ + Optional apiKey(); - @Override - public String toString() { - return "AuthConfig{" + - "tokenPropagation=" + tokenPropagation + - ", headerName=" + headerName + - ", authConfigParams=" + authConfigParams + - '}'; - } + /** + * Only valid for API Key Authentication. + *

+ * When to add the `Authorization` value to the API Key in the authentication header. + *

+ * For example, if this property is set to `true`, the API Key will be sent to the server in the header along with + * `Authorization`: + *

+ * [source] + * --- + * Authentication: Authorization MY-API-KEY + * --- + *

+ * If set to `false`, the header should be: + *

+ * [source] + * --- + * Authentication: MY-API-KEY + * --- + * + * @return whether to use the prefix `Authorization` when sending an API Key using headers. + */ + Optional useAuthorizationHeaderValue(); } diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthenticationRecorder.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthenticationRecorder.java index c94127edc..74ec86904 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthenticationRecorder.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthenticationRecorder.java @@ -1,7 +1,6 @@ package io.quarkiverse.openapi.generator; import java.util.List; -import java.util.Objects; import java.util.function.Function; import jakarta.enterprise.inject.Instance; @@ -11,11 +10,10 @@ import io.quarkiverse.openapi.generator.providers.ApiKeyAuthenticationProvider; import io.quarkiverse.openapi.generator.providers.ApiKeyIn; import io.quarkiverse.openapi.generator.providers.AuthProvider; +import io.quarkiverse.openapi.generator.providers.BaseCompositeAuthenticationProvider; import io.quarkiverse.openapi.generator.providers.BasicAuthenticationProvider; import io.quarkiverse.openapi.generator.providers.BearerAuthenticationProvider; -import io.quarkiverse.openapi.generator.providers.CompositeAuthenticationProvider; -import io.quarkiverse.openapi.generator.providers.OAuth2AuthenticationProvider; -import io.quarkiverse.openapi.generator.providers.OAuth2AuthenticationProvider.OidcClientRequestFilterDelegate; +import io.quarkiverse.openapi.generator.providers.CredentialsProvider; import io.quarkiverse.openapi.generator.providers.OperationAuthInfo; import io.quarkus.arc.SyntheticCreationalContext; import io.quarkus.runtime.annotations.Recorder; @@ -23,18 +21,12 @@ @Recorder public class AuthenticationRecorder { - final OpenApiGeneratorConfig generatorConfig; - - public AuthenticationRecorder(OpenApiGeneratorConfig generatorConfig) { - this.generatorConfig = generatorConfig; - } - - public Function, CompositeAuthenticationProvider> recordCompositeProvider( + public Function, BaseCompositeAuthenticationProvider> recordCompositeProvider( String openApiSpec) { return ctx -> { List providers = ctx.getInjectedReference(new TypeLiteral>() { }, new Literal(openApiSpec)).stream().toList(); - return new CompositeAuthenticationProvider(providers); + return new BaseCompositeAuthenticationProvider(providers); }; } @@ -44,9 +36,8 @@ public Function, AuthProvider> recordAp ApiKeyIn apiKeyIn, String apiKeyName, List operations) { - return context -> new ApiKeyAuthenticationProvider(openApiSpecId, name, apiKeyIn, apiKeyName, - getAuthConfig(openApiSpecId, name), - operations); + return context -> new ApiKeyAuthenticationProvider(openApiSpecId, name, apiKeyIn, + apiKeyName, operations, context.getInjectedReference(CredentialsProvider.class)); } public Function, AuthProvider> recordBearerAuthProvider( @@ -54,30 +45,18 @@ public Function, AuthProvider> recordBe String scheme, String openApiSpecId, List operations) { - return context -> new BearerAuthenticationProvider(openApiSpecId, name, scheme, getAuthConfig(openApiSpecId, name), - operations); + return context -> new BearerAuthenticationProvider(openApiSpecId, name, scheme, + operations, context.getInjectedReference(CredentialsProvider.class)); } public Function, AuthProvider> recordBasicAuthProvider( String name, String openApiSpecId, List operations) { - return context -> new BasicAuthenticationProvider(openApiSpecId, name, getAuthConfig(openApiSpecId, name), operations); - } - public Function, AuthProvider> recordOauthAuthProvider( - String name, - String openApiSpecId, - List operations) { - return context -> new OAuth2AuthenticationProvider(getAuthConfig(openApiSpecId, name), name, openApiSpecId, - context.getInjectedReference(OidcClientRequestFilterDelegate.class, new OidcClient.Literal(name)), operations); - } + return context -> new BasicAuthenticationProvider(openApiSpecId, name, + operations, context.getInjectedReference(CredentialsProvider.class)); - AuthConfig getAuthConfig(String openApiSpecId, String name) { - return Objects.requireNonNull(generatorConfig, "generatorConfig can't be null.") - .getItemConfig(openApiSpecId) - .flatMap(SpecItemConfig::getAuth) - .flatMap(authsConfig -> authsConfig.getItemConfig(name)) - .orElse(null); } + } diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthsConfig.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthsConfig.java index a02f00c11..ce90f077b 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthsConfig.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthsConfig.java @@ -3,11 +3,9 @@ import java.util.Map; import java.util.Optional; -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; +import io.smallrye.config.WithParentName; -@ConfigGroup -public class AuthsConfig { +public interface AuthsConfig { /** * Configurations for the individual securitySchemes present on a given OpenApi spec definition file. @@ -21,17 +19,10 @@ public class AuthsConfig { * @see SpecItemConfig * @see AuthConfig */ - @ConfigItem(name = ConfigItem.PARENT) - public Map authConfigs; + @WithParentName() + Map authConfigs(); - public Optional getItemConfig(String authConfig) { - return Optional.ofNullable(authConfigs.get(authConfig)); - } - - @Override - public String toString() { - return "AuthsConfig{" + - "authConfigs=" + authConfigs + - '}'; + default Optional getItemConfig(String authConfig) { + return Optional.ofNullable(authConfigs().get(authConfig)); } } diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/OpenApiGeneratorConfig.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/OpenApiGeneratorConfig.java index 88454d1fa..9fe301a93 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/OpenApiGeneratorConfig.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/OpenApiGeneratorConfig.java @@ -3,18 +3,20 @@ import java.util.Map; import java.util.Optional; -import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithParentName; import io.smallrye.config.common.utils.StringUtil; /** * This class represents the runtime configurations for the openapi-generator extension. */ -@ConfigRoot(name = OpenApiGeneratorConfig.RUNTIME_TIME_CONFIG_PREFIX, phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED) -public class OpenApiGeneratorConfig { +@ConfigMapping(prefix = "quarkus." + OpenApiGeneratorConfig.RUNTIME_TIME_CONFIG_PREFIX) +@ConfigRoot(phase = ConfigPhase.RUN_TIME) +public interface OpenApiGeneratorConfig { - public static final String RUNTIME_TIME_CONFIG_PREFIX = "openapi-generator"; + String RUNTIME_TIME_CONFIG_PREFIX = "openapi-generator"; /** * Configurations of the individual OpenApi spec definitions, i.e. the provided files. @@ -23,21 +25,14 @@ public class OpenApiGeneratorConfig { * For example, a file named petstore.json is sanitized into the name petstore_json, and thus the specific * configurations this file must start with the prefix quarkus.openapi-generator.petstore_json */ - @ConfigItem(name = ConfigItem.PARENT) - public Map itemConfigs; + @WithParentName + Map itemConfigs(); - public Optional getItemConfig(String specItem) { - return Optional.ofNullable(itemConfigs.get(specItem)); + default Optional getItemConfig(String specItem) { + return Optional.ofNullable(itemConfigs().get(specItem)); } - @Override - public String toString() { - return "OpenApiGeneratorConfig{" + - "itemConfigs=" + itemConfigs + - '}'; - } - - public static String getSanitizedSecuritySchemeName(final String securitySchemeName) { + static String getSanitizedSecuritySchemeName(final String securitySchemeName) { return StringUtil.replaceNonAlphanumericByUnderscores(securitySchemeName); } } diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/SpecItemConfig.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/SpecItemConfig.java index 91c79b656..da942e1fd 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/SpecItemConfig.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/SpecItemConfig.java @@ -2,15 +2,11 @@ import java.util.Optional; -import io.quarkus.runtime.annotations.ConfigGroup; -import io.quarkus.runtime.annotations.ConfigItem; - /** * This class represents the runtime authentication related configurations for the individual OpenApi spec definitions, * i.e. the provided files. */ -@ConfigGroup -public class SpecItemConfig { +public interface SpecItemConfig { /** * Authentication related configurations for the different securitySchemes present on a given OpenApi spec @@ -21,17 +17,9 @@ public class SpecItemConfig { * * @see AuthsConfig */ - @ConfigItem - public AuthsConfig auth; - - public Optional getAuth() { - return Optional.ofNullable(auth); - } + AuthsConfig auth(); - @Override - public String toString() { - return "SpecItemConfig{" + - "auth=" + auth + - '}'; + default Optional getAuth() { + return Optional.ofNullable(auth()); } -} \ No newline at end of file +} diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AbstractAuthProvider.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AbstractAuthProvider.java index 1ede697f4..d5a776fc1 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AbstractAuthProvider.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AbstractAuthProvider.java @@ -10,25 +10,44 @@ import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MultivaluedMap; +import org.eclipse.microprofile.config.ConfigProvider; + import io.quarkiverse.openapi.generator.AuthConfig; public abstract class AbstractAuthProvider implements AuthProvider { + CredentialsProvider credentialsProvider; + private static final String BEARER_WITH_SPACE = "Bearer "; - private static final String CANONICAL_AUTH_CONFIG_PROPERTY_NAME = "quarkus." + - RUNTIME_TIME_CONFIG_PREFIX + ".%s.auth.%s.%s"; + private static final String BASIC_WITH_SPACE = "Basic "; + + private static final String CANONICAL_AUTH_CONFIG_PROPERTY_NAME = "quarkus." + RUNTIME_TIME_CONFIG_PREFIX + + ".%s.auth.%s.%s"; private final String openApiSpecId; private final String name; - private final AuthConfig authConfig; private final List applyToOperations = new ArrayList<>(); - protected AbstractAuthProvider(AuthConfig authConfig, String name, String openApiSpecId, - List operations) { + protected AbstractAuthProvider(String name, String openApiSpecId, List operations, + CredentialsProvider credentialsProvider) { this.name = name; this.openApiSpecId = openApiSpecId; - this.authConfig = authConfig; this.applyToOperations.addAll(operations); + this.credentialsProvider = credentialsProvider; + } + + protected static String sanitizeBearerToken(String token) { + if (token != null && token.toLowerCase().startsWith(BEARER_WITH_SPACE.toLowerCase())) { + return token.substring(BEARER_WITH_SPACE.length()); + } + return token; + } + + protected static String sanitizeBasicToken(String token) { + if (token != null && token.toLowerCase().startsWith(BASIC_WITH_SPACE.toLowerCase())) { + return token.substring(BASIC_WITH_SPACE.length()); + } + return token; } public String getOpenApiSpecId() { @@ -41,7 +60,9 @@ public String getName() { } public boolean isTokenPropagation() { - return authConfig != null && authConfig.getTokenPropagation().orElse(false); + return ConfigProvider.getConfig() + .getOptionalValue(getCanonicalAuthConfigPropertyName(AuthConfig.TOKEN_PROPAGATION), Boolean.class) + .orElse(false); } public String getTokenForPropagation(MultivaluedMap httpHeaders) { @@ -51,10 +72,8 @@ public String getTokenForPropagation(MultivaluedMap httpHeaders) } public String getHeaderName() { - if (authConfig != null) { - return authConfig.getHeaderName().orElse(null); - } - return null; + return ConfigProvider.getConfig() + .getOptionalValue(getCanonicalAuthConfigPropertyName(AuthConfig.HEADER_NAME), String.class).orElse(null); } @Override @@ -62,21 +81,11 @@ public List operationsToFilter() { return applyToOperations; } - public String getAuthConfigParam(String paramName, String defaultValue) { - if (authConfig != null) { - return authConfig.getConfigParam(paramName).orElse(defaultValue); - } - return defaultValue; - } - - protected static String sanitizeBearerToken(String token) { - if (token != null && token.toLowerCase().startsWith(BEARER_WITH_SPACE.toLowerCase())) { - return token.substring(BEARER_WITH_SPACE.length()); - } - return token; + public final String getCanonicalAuthConfigPropertyName(String authPropertyName) { + return getCanonicalAuthConfigPropertyName(authPropertyName, getOpenApiSpecId(), getName()); } - protected String getCanonicalAuthConfigPropertyName(String authPropertyName) { - return String.format(CANONICAL_AUTH_CONFIG_PROPERTY_NAME, getOpenApiSpecId(), getName(), authPropertyName); + public static String getCanonicalAuthConfigPropertyName(String authPropertyName, String openApiSpecId, String authName) { + return String.format(CANONICAL_AUTH_CONFIG_PROPERTY_NAME, openApiSpecId, authName, authPropertyName); } } diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AbstractAuthenticationPropagationHeadersFactory.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AbstractAuthenticationPropagationHeadersFactory.java index 586e14c72..2f9c1a04e 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AbstractAuthenticationPropagationHeadersFactory.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AbstractAuthenticationPropagationHeadersFactory.java @@ -19,11 +19,11 @@ public abstract class AbstractAuthenticationPropagationHeadersFactory implements private static final String HEADER_NAME_PREFIX_FOR_TOKEN_PROPAGATION = "QCG_%s"; private static final String HEADER_NAME_FOR_TOKEN_PROPAGATION = "QCG_%s_%s_%s"; - protected CompositeAuthenticationProvider compositeProvider; + protected BaseCompositeAuthenticationProvider compositeProvider; protected OpenApiGeneratorConfig generatorConfig; protected HeadersProvider headersProvider; - protected AbstractAuthenticationPropagationHeadersFactory(CompositeAuthenticationProvider compositeProvider, + protected AbstractAuthenticationPropagationHeadersFactory(BaseCompositeAuthenticationProvider compositeProvider, OpenApiGeneratorConfig generatorConfig, HeadersProvider headersProvider) { this.compositeProvider = compositeProvider; diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProvider.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProvider.java index 03a55f6e5..2f3db8393 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProvider.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProvider.java @@ -10,10 +10,8 @@ import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.UriBuilder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.eclipse.microprofile.config.ConfigProvider; -import io.quarkiverse.openapi.generator.AuthConfig; import io.quarkiverse.openapi.generator.OpenApiGeneratorException; /** @@ -21,31 +19,33 @@ */ public class ApiKeyAuthenticationProvider extends AbstractAuthProvider { - private static final Logger LOGGER = LoggerFactory.getLogger(ApiKeyAuthenticationProvider.class); - - static final String API_KEY = "api-key"; static final String USE_AUTHORIZATION_HEADER_VALUE = "use-authorization-header-value"; - private final ApiKeyIn apiKeyIn; private final String apiKeyName; public ApiKeyAuthenticationProvider(final String openApiSpecId, final String name, final ApiKeyIn apiKeyIn, - final String apiKeyName, - final AuthConfig authConfig, List operations) { - super(authConfig, name, openApiSpecId, operations); + final String apiKeyName, List operations, CredentialsProvider credentialsProvider) { + super(name, openApiSpecId, operations, credentialsProvider); this.apiKeyIn = apiKeyIn; this.apiKeyName = apiKeyName; validateConfig(); } + public ApiKeyAuthenticationProvider(final String openApiSpecId, final String name, final ApiKeyIn apiKeyIn, + final String apiKeyName, List operations) { + this(openApiSpecId, name, apiKeyIn, apiKeyName, operations, new ConfigCredentialsProvider()); + } + @Override public void filter(ClientRequestContext requestContext) throws IOException { switch (apiKeyIn) { case query: - requestContext.setUri(UriBuilder.fromUri(requestContext.getUri()).queryParam(apiKeyName, getApiKey()).build()); + requestContext.setUri( + UriBuilder.fromUri(requestContext.getUri()).queryParam(apiKeyName, getApiKey(requestContext)).build()); break; case cookie: - requestContext.getHeaders().add(HttpHeaders.COOKIE, new Cookie.Builder(apiKeyName).value(getApiKey()).build()); + requestContext.getHeaders().add(HttpHeaders.COOKIE, + new Cookie.Builder(apiKeyName).value(getApiKey(requestContext)).build()); break; case header: if (requestContext.getHeaderString("Authorization") != null @@ -53,22 +53,19 @@ public void filter(ClientRequestContext requestContext) throws IOException { && isUseAuthorizationHeaderValue()) { requestContext.getHeaders().putSingle(apiKeyName, requestContext.getHeaderString("Authorization")); } else - requestContext.getHeaders().putSingle(apiKeyName, getApiKey()); + requestContext.getHeaders().putSingle(apiKeyName, getApiKey(requestContext)); break; } } - private String getApiKey() { - final String key = getAuthConfigParam(API_KEY, ""); - if (key.isEmpty()) { - LOGGER.warn("configured " + API_KEY + " property (see application.properties) is empty. hint: configure it."); - } - return key; + private String getApiKey(ClientRequestContext requestContext) { + return credentialsProvider.getApiKey(requestContext, getOpenApiSpecId(), getName()); } private boolean isUseAuthorizationHeaderValue() { - final String value = getAuthConfigParam(USE_AUTHORIZATION_HEADER_VALUE, "true"); - return "true".equals(value); + return ConfigProvider.getConfig() + .getOptionalValue(getCanonicalAuthConfigPropertyName(USE_AUTHORIZATION_HEADER_VALUE), Boolean.class) + .orElse(true); } private void validateConfig() { diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AuthUtils.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AuthUtils.java index 3baed3bca..2948e0108 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AuthUtils.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AuthUtils.java @@ -10,10 +10,14 @@ public final class AuthUtils { private AuthUtils() { } - public static String basicAuthAccessToken(final String username, final String password) { + public static String basicAuthAccessTokenWithoutPrefix(final String username, final String password) { + return Base64.getEncoder().encodeToString(String.format("%s:%s", username, password).getBytes()); + } + + public static String basicAuthAccessToken(final String basicToken) { return String.format("%s %s", BASIC_HEADER_PREFIX, - Base64.getEncoder().encodeToString(String.format("%s:%s", username, password).getBytes())); + basicToken); } public static String authTokenOrBearer(final String scheme, final String token) { diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/CompositeAuthenticationProvider.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BaseCompositeAuthenticationProvider.java similarity index 94% rename from client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/CompositeAuthenticationProvider.java rename to client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BaseCompositeAuthenticationProvider.java index 3df16cb19..a6d705f49 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/CompositeAuthenticationProvider.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BaseCompositeAuthenticationProvider.java @@ -16,11 +16,11 @@ * Composition of supported {@link ClientRequestFilter} defined by a given OpenAPI interface. * This class is used as the base class of generated code. */ -public class CompositeAuthenticationProvider implements ClientRequestFilter { +public class BaseCompositeAuthenticationProvider implements ClientRequestFilter { private final List authProviders; - public CompositeAuthenticationProvider(List authProviders) { + public BaseCompositeAuthenticationProvider(List authProviders) { this.authProviders = List.copyOf(authProviders); } diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BasicAuthenticationProvider.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BasicAuthenticationProvider.java index 219828b33..10011a9b3 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BasicAuthenticationProvider.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BasicAuthenticationProvider.java @@ -1,15 +1,13 @@ package io.quarkiverse.openapi.generator.providers; -import static io.quarkiverse.openapi.generator.AuthConfig.TOKEN_PROPAGATION; - import java.io.IOException; import java.util.List; import jakarta.ws.rs.client.ClientRequestContext; import jakarta.ws.rs.core.HttpHeaders; -import io.quarkiverse.openapi.generator.AuthConfig; -import io.quarkiverse.openapi.generator.OpenApiGeneratorException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Provider for Basic Authentication. @@ -17,38 +15,39 @@ * during build time. */ public class BasicAuthenticationProvider extends AbstractAuthProvider { + private static final Logger LOGGER = LoggerFactory.getLogger(BasicAuthenticationProvider.class); - static final String USER_NAME = "username"; - static final String PASSWORD = "password"; + public BasicAuthenticationProvider(final String openApiSpecId, String name, List operations, + CredentialsProvider credentialsProvider) { + super(name, openApiSpecId, operations, credentialsProvider); + } - public BasicAuthenticationProvider(final String openApiSpecId, String name, final AuthConfig authConfig, - List operations) { - super(authConfig, name, openApiSpecId, operations); - validateConfig(); + public BasicAuthenticationProvider(final String openApiSpecId, String name, List operations) { + this(openApiSpecId, name, operations, new ConfigCredentialsProvider()); } - private String getUsername() { - return getAuthConfigParam(USER_NAME, ""); + private String getUsername(ClientRequestContext requestContext) { + return credentialsProvider.getBasicUsername(requestContext, getOpenApiSpecId(), getName()); } - private String getPassword() { - return getAuthConfigParam(PASSWORD, ""); + private String getPassword(ClientRequestContext requestContext) { + return credentialsProvider.getBasicPassword(requestContext, getOpenApiSpecId(), getName()); } @Override public void filter(ClientRequestContext requestContext) throws IOException { - requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, - AuthUtils.basicAuthAccessToken(getUsername(), getPassword())); - } + String basicToken = AuthUtils.basicAuthAccessTokenWithoutPrefix(getUsername(requestContext), + getPassword(requestContext)); - private void validateConfig() { if (isTokenPropagation()) { - throw new OpenApiGeneratorException( - "Token propagation is not admitted for the OpenApi securitySchemes of \"type\": \"http\", \"scheme\": \"basic\"." - + - " A potential source of the problem might be that the configuration property " + - getCanonicalAuthConfigPropertyName(TOKEN_PROPAGATION) + - " was set with the value true in your application, please check your configuration."); + LOGGER.warn("Token propagation enabled for BasicAuthentication"); + basicToken = sanitizeBasicToken(getTokenForPropagation(requestContext.getHeaders())); } + + if (!basicToken.isBlank()) { + requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, + AuthUtils.basicAuthAccessToken(basicToken)); + } + } } diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BearerAuthenticationProvider.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BearerAuthenticationProvider.java index 0e80ab2a5..5454b1808 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BearerAuthenticationProvider.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BearerAuthenticationProvider.java @@ -6,8 +6,6 @@ import jakarta.ws.rs.client.ClientRequestContext; import jakarta.ws.rs.core.HttpHeaders; -import io.quarkiverse.openapi.generator.AuthConfig; - /** * Provides bearer token authentication or any other valid scheme. * @@ -15,32 +13,33 @@ */ public class BearerAuthenticationProvider extends AbstractAuthProvider { - static final String BEARER_TOKEN = "bearer-token"; - private final String scheme; public BearerAuthenticationProvider(final String openApiSpecId, final String name, final String scheme, - final AuthConfig authConfig, List operations) { - super(authConfig, name, openApiSpecId, operations); + List operations, CredentialsProvider credentialsProvider) { + super(name, openApiSpecId, operations, credentialsProvider); this.scheme = scheme; } + public BearerAuthenticationProvider(final String openApiSpecId, final String name, final String scheme, + List operations) { + this(openApiSpecId, name, scheme, operations, new ConfigCredentialsProvider()); + } + @Override public void filter(ClientRequestContext requestContext) throws IOException { - String bearerToken; + String bearerToken = getBearerToken(requestContext); + if (isTokenPropagation()) { - bearerToken = getTokenForPropagation(requestContext.getHeaders()); - bearerToken = sanitizeBearerToken(bearerToken); - } else { - bearerToken = getBearerToken(); + bearerToken = sanitizeBearerToken(getTokenForPropagation(requestContext.getHeaders())); } + if (!bearerToken.isBlank()) { - requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, - AuthUtils.authTokenOrBearer(this.scheme, bearerToken)); + requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, AuthUtils.authTokenOrBearer(this.scheme, bearerToken)); } } - private String getBearerToken() { - return getAuthConfigParam(BEARER_TOKEN, ""); + private String getBearerToken(ClientRequestContext requestContext) { + return credentialsProvider.getBearerToken(requestContext, getOpenApiSpecId(), getName()); } } diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ConfigCredentialsProvider.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ConfigCredentialsProvider.java new file mode 100644 index 000000000..8f78ee1e8 --- /dev/null +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ConfigCredentialsProvider.java @@ -0,0 +1,66 @@ +package io.quarkiverse.openapi.generator.providers; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Alternative; +import jakarta.ws.rs.client.ClientRequestContext; + +import org.eclipse.microprofile.config.ConfigProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Dependent +@Alternative +@Priority(100) +public class ConfigCredentialsProvider implements CredentialsProvider { + + static final String USER_NAME = "username"; + static final String PASSWORD = "password"; + static final String BEARER_TOKEN = "bearer-token"; + static final String API_KEY = "api-key"; + + private static final Logger LOGGER = LoggerFactory.getLogger(ConfigCredentialsProvider.class); + + public ConfigCredentialsProvider() { + + } + + @Override + public String getApiKey(ClientRequestContext requestContext, String openApiSpecId, String authName) { + final String key = ConfigProvider.getConfig() + .getOptionalValue(AbstractAuthProvider.getCanonicalAuthConfigPropertyName(API_KEY, openApiSpecId, authName), + String.class) + .orElse(""); + if (key.isEmpty()) { + LOGGER.warn("configured {} property (see application.properties) is empty. hint: configure it.", + AbstractAuthProvider.getCanonicalAuthConfigPropertyName(API_KEY, openApiSpecId, authName)); + } + return key; + } + + @Override + public String getBasicUsername(ClientRequestContext requestContext, String openApiSpecId, String authName) { + return ConfigProvider.getConfig() + .getOptionalValue(AbstractAuthProvider.getCanonicalAuthConfigPropertyName(USER_NAME, openApiSpecId, authName), + String.class) + .orElse(""); + } + + @Override + public String getBasicPassword(ClientRequestContext requestContext, String openApiSpecId, String authName) { + return ConfigProvider.getConfig() + .getOptionalValue(AbstractAuthProvider.getCanonicalAuthConfigPropertyName(PASSWORD, openApiSpecId, authName), + String.class) + .orElse(""); + } + + @Override + public String getBearerToken(ClientRequestContext requestContext, String openApiSpecId, String authName) { + return ConfigProvider.getConfig() + .getOptionalValue( + AbstractAuthProvider.getCanonicalAuthConfigPropertyName(BEARER_TOKEN, openApiSpecId, authName), + String.class) + .orElse(""); + } + +} diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/CredentialsProvider.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/CredentialsProvider.java new file mode 100644 index 000000000..3f15dd67c --- /dev/null +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/CredentialsProvider.java @@ -0,0 +1,47 @@ +package io.quarkiverse.openapi.generator.providers; + +import jakarta.ws.rs.client.ClientRequestContext; + +/** + * Provider for security credentials. Clients can implement this interface to control how to provide security credentials in + * runtime. + * Annotate your bean with @RequestScope (or @Dependant) and @Priority(1). + */ +public interface CredentialsProvider { + + /** + * Gets the API Key given the OpenAPI definition and security schema + * + * @param openApiSpecId the OpenAPI Spec identification as defined by the OpenAPI Extension + * @param authName The security schema for this API Key definition + * @return the API Key to use when filtering the request + */ + String getApiKey(ClientRequestContext requestContext, String openApiSpecId, String authName); + + /** + * Gets the username given the OpenAPI definition and security schema + * + * @param openApiSpecId the OpenAPI Spec identification as defined by the OpenAPI Extension + * @param authName The security schema for this Basic Authorization definition + * @return the username to use when filtering the request + */ + String getBasicUsername(ClientRequestContext requestContext, String openApiSpecId, String authName); + + /** + * Gets the password given the OpenAPI definition and security schema + * + * @param openApiSpecId the OpenAPI Spec identification as defined by the OpenAPI Extension + * @param authName The security schema for this Basic Authorization definition + * @return the password to use when filtering the request + */ + String getBasicPassword(ClientRequestContext requestContext, String openApiSpecId, String authName); + + /** + * Gets the Bearer Token given the OpenAPI definition and security schema + * + * @param openApiSpecId the OpenAPI Spec identification as defined by the OpenAPI Extension + * @param authName The security schema for this Bearer Token definition + * @return the Bearer Token to use when filtering the request + */ + String getBearerToken(ClientRequestContext requestContext, String openApiSpecId, String authName); +} diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/UrlPatternMatcher.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/UrlPatternMatcher.java index 819a85a65..a80451c25 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/UrlPatternMatcher.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/UrlPatternMatcher.java @@ -1,102 +1,57 @@ /* Original Copyright Headers - This file has been modified, but copied from - https://github.com/RestExpress/RestExpress/blob/master/core/src/main/java/org/restexpress/url/UrlPattern.java + This file include a substantially simplified version of + https://github.com/wilkincheung/URI-Template-Pattern-Matcher/blob/master/src/main/java/com/prodigi/service/UriTemplateValidator.java */ /** - * Copyright 2010, Strategic Gains, Inc. - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.quarkiverse.openapi.generator.providers; +The MIT License (MIT) +Copyright (c) 2015 Wilkin Cheung -import java.util.regex.Pattern; +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -/** - * PathPatternMatcher leverages Regex Pattern to represent a parameterized URL. Parameters within the URL are - * denoted by curly braces '{}' with the parameter name contained within (e.g. '{userid}'). - *

- *

- * Parameter names must be formed of word characters (e.g. A-Z, a-z, 0-9, '_'). - *

- * An optional format parameter following a dot ('.') may be added to the end. While it could be named any valid parameter name, - * RestExpress offers special handling (e.g. within the Request, etc.) if it's named 'format'. - *

- * Note that the format specifier allows only word characters and percent-encoded characters. - *

- *

- * URL Pattern examples: - *

    - *
  • /api/search.{format}
  • - *
  • /api/search/users/{userid}.{format}
  • - *
  • /api/{version}/search/users/{userid}
  • - *
- *

- * RestExpress parses URI paths which is described in the URI Generic Syntax IETF RFC 3986 specification, - * section 3.3 (http://tools.ietf.org/html/rfc3986#section-3.3). RestExpress parses paths into segments - * separated by slashes ("/"), the segments of which are composed of unreserved, percent encoded, - * sub-delimiters, colon (":") or ampersand ("@"), each of which are defined below (from the spec): - *

- * pct-encoded = "%" HEXDIG HEXDIG - *

- * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
- * reserved = gen-delims / sub-delims
- * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
- * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" * - *

- * In other words, RestExpress accepts path segments containing: [A-Z] [a-z] [0-9] % - . _ ~ ! $ & ' ( ) * + , ; = : @ - *

- * RestExpress also accepts square brackets ('[' and ']'), but this is deprecated and not recommended. - * - * @author toddf - * @see http://www.ietf.org/rfc/rfc3986.txt - * @since Apr 28, 2010 - */ -public class UrlPatternMatcher { - // Finds parameters in the URL pattern string. - private static final String URL_PARAM_REGEX = "\\{(\\S*?)\\}"; +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - // Replaces parameter names in the URL pattern string to match parameters in URLs. - private static final String URL_PARAM_MATCH_REGEX = "\\([%\\\\w-.\\\\~!\\$&'\\\\(\\\\)\\\\*\\\\+,;=:\\\\[\\\\]@]+?\\)"; +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +package io.quarkiverse.openapi.generator.providers; - // Finds the 'format' portion of the URL pattern string. - private static final String URL_FORMAT_REGEX = "(?:\\.\\{format\\})$"; +import java.util.regex.Matcher; +import java.util.regex.Pattern; - // Replaces the format parameter name in the URL pattern string to match the format specifier in URLs. Appended to the end of the regex string - // when a URL pattern contains a format parameter. - private static final String URL_FORMAT_MATCH_REGEX = "(?:\\\\.\\([\\\\w%]+?\\))?"; +public class UrlPatternMatcher { - // Finds the query string portion within a URL. Appended to the end of the built-up regex string. + // For each pattern {keyName} replaces it with (.*) + private static final Pattern LEVEL_ONE_PATTERN = Pattern.compile("\\{([^/]+?)\\}"); + // Replaces each {keyName} with (.*) + private static final String REPLACES_WITH = "(.*)"; private static final String URL_QUERY_STRING_REGEX = "(?:\\?.*?)?$"; - /** - * The URL pattern describing the URL layout and any parameters. - */ - private final String urlPattern; - - /** - * A compiled regex created from the urlPattern, above. - */ - private Pattern compiledUrl; + private final Pattern pattern; - /** - * @param pattern - */ - public UrlPatternMatcher(String pattern) { - this.urlPattern = pattern; - String parsedPattern = this.urlPattern.replaceFirst(URL_FORMAT_REGEX, URL_FORMAT_MATCH_REGEX); - parsedPattern = parsedPattern.replaceAll(URL_PARAM_REGEX, URL_PARAM_MATCH_REGEX); - this.compiledUrl = Pattern.compile(parsedPattern + URL_QUERY_STRING_REGEX); + public UrlPatternMatcher(String uriTemplate) { + StringBuilder patternBuilder = new StringBuilder(); + Matcher m = LEVEL_ONE_PATTERN.matcher(uriTemplate); + int end = 0; + while (m.find()) { + // In each loop, find next pattern in URI that is "{keyName}" + // If found,append the substring to patternBuilder. + patternBuilder.append(Pattern.quote(uriTemplate.substring(end, m.start()))).append(REPLACES_WITH); + end = m.end(); + } + patternBuilder.append(Pattern.quote(uriTemplate.substring(end, uriTemplate.length()))); + this.pattern = Pattern.compile(patternBuilder + URL_QUERY_STRING_REGEX); } /** @@ -107,6 +62,10 @@ public UrlPatternMatcher(String pattern) { * @return true if the given URL matches the underlying pattern. Otherwise false. */ public boolean matches(String url) { - return compiledUrl.matcher(url).matches(); + return pattern.matcher(url).matches(); + } + + public String toString() { + return pattern.toString(); } -} \ No newline at end of file +} diff --git a/client/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/client/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 179992546..5b091677d 100644 --- a/client/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/client/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -8,6 +8,8 @@ metadata: - "rest-client" categories: - "web" + guide: "https://docs.quarkiverse.io/quarkus-openapi-generator/dev/index.html" + icon-url: "https://raw.githubusercontent.com/quarkiverse/quarkus-openapi-generator/main/docs/modules/ROOT/assets/images/openapi.svg" status: "preview" codestart: name: "openapi-generator" diff --git a/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/AbstractOpenApiSpecProviderTest.java b/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/AbstractOpenApiSpecProviderTest.java index 2387cff63..c303ace93 100644 --- a/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/AbstractOpenApiSpecProviderTest.java +++ b/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/AbstractOpenApiSpecProviderTest.java @@ -1,69 +1,39 @@ package io.quarkiverse.openapi.generator.providers; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.lenient; - -import java.util.HashMap; -import java.util.Optional; - import jakarta.ws.rs.client.ClientRequestContext; import jakarta.ws.rs.core.MultivaluedHashMap; import jakarta.ws.rs.core.MultivaluedMap; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; -import io.quarkiverse.openapi.generator.AuthConfig; -import io.quarkiverse.openapi.generator.AuthsConfig; -import io.quarkiverse.openapi.generator.OpenApiGeneratorConfig; -import io.quarkiverse.openapi.generator.SpecItemConfig; - @ExtendWith(MockitoExtension.class) abstract class AbstractOpenApiSpecProviderTest { protected static final String OPEN_API_FILE_SPEC_ID = "open_api_file_spec_id_json"; protected static final String AUTH_SCHEME_NAME = "auth_scheme_name"; - protected OpenApiGeneratorConfig generatorConfig; - - protected AuthConfig authConfig; - @Mock protected ClientRequestContext requestContext; - protected MultivaluedMap headers; protected T provider; @BeforeEach void setUp() { - createConfiguration(); - provider = createProvider(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, authConfig); - } - - protected abstract T createProvider(String openApiSpecId, String authSchemeName, - AuthConfig authConfig); - - protected void createConfiguration() { - generatorConfig = new OpenApiGeneratorConfig(); - generatorConfig.itemConfigs = new HashMap<>(); - SpecItemConfig specItemConfig = new SpecItemConfig(); - specItemConfig.auth = new AuthsConfig(); - specItemConfig.auth.authConfigs = new HashMap<>(); - authConfig = new AuthConfig(); - authConfig.headerName = Optional.empty(); - authConfig.tokenPropagation = Optional.of(false); - authConfig.authConfigParams = new HashMap<>(); - specItemConfig.auth.authConfigs.put(AUTH_SCHEME_NAME, authConfig); - generatorConfig.itemConfigs.put(OPEN_API_FILE_SPEC_ID, specItemConfig); headers = new MultivaluedHashMap<>(); - lenient().doReturn(headers).when(requestContext).getHeaders(); + Mockito.lenient().doReturn(headers).when(requestContext).getHeaders(); + provider = createProvider(); } + protected abstract T createProvider(); + protected void assertHeader(MultivaluedMap headers, String headerName, String value) { - assertThat(headers.getFirst(headerName)) + Assertions.assertThat(headers.getFirst(headerName)) .isNotNull() .isEqualTo(value); } diff --git a/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/ApiKeyOpenApiSpecProviderTest.java b/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/ApiKeyOpenApiSpecProviderTest.java index 89f1d9758..d6ffd6fab 100644 --- a/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/ApiKeyOpenApiSpecProviderTest.java +++ b/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/ApiKeyOpenApiSpecProviderTest.java @@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.io.IOException; import java.net.URI; @@ -16,10 +17,14 @@ import jakarta.ws.rs.core.MultivaluedMap; import org.assertj.core.api.InstanceOfAssertFactories; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; import org.jboss.resteasy.specimpl.MultivaluedTreeMap; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; +import org.mockito.MockedStatic; +import org.mockito.Mockito; import io.quarkiverse.openapi.generator.AuthConfig; @@ -35,16 +40,9 @@ class ApiKeyOpenApiSpecProviderTest extends AbstractOpenApiSpecProviderTest uriCaptor; @Override - protected void createConfiguration() { - super.createConfiguration(); - authConfig.authConfigParams.put(ApiKeyAuthenticationProvider.API_KEY, API_KEY_VALUE); - } - - @Override - protected ApiKeyAuthenticationProvider createProvider(String openApiSpecId, String authSchemeName, - AuthConfig authConfig) { - return new ApiKeyAuthenticationProvider(openApiSpecId, authSchemeName, ApiKeyIn.header, API_KEY_NAME, - authConfig, List.of()); + protected ApiKeyAuthenticationProvider createProvider() { + return new ApiKeyAuthenticationProvider(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, ApiKeyIn.header, API_KEY_NAME, + List.of()); } @Test @@ -56,20 +54,28 @@ void filterHeaderFromAuthorizationHeaderDefaultCase() throws IOException { @Test void filterHeaderFromAuthorizationHeaderCase() throws IOException { - authConfig.authConfigParams.put(ApiKeyAuthenticationProvider.USE_AUTHORIZATION_HEADER_VALUE, "true"); doReturn(API_KEY_AUTH_HEADER_VALUE).when(requestContext).getHeaderString("Authorization"); provider.filter(requestContext); assertHeader(headers, API_KEY_NAME, API_KEY_AUTH_HEADER_VALUE); - authConfig.authConfigParams.remove(ApiKeyAuthenticationProvider.USE_AUTHORIZATION_HEADER_VALUE); } @Test void filterHeaderNotFromAuthorizationHeaderCase() throws IOException { - authConfig.authConfigParams.put(ApiKeyAuthenticationProvider.USE_AUTHORIZATION_HEADER_VALUE, "false"); - doReturn(API_KEY_AUTH_HEADER_VALUE).when(requestContext).getHeaderString("Authorization"); - provider.filter(requestContext); - assertHeader(headers, API_KEY_NAME, API_KEY_VALUE); - authConfig.authConfigParams.remove(ApiKeyAuthenticationProvider.USE_AUTHORIZATION_HEADER_VALUE); + try (MockedStatic configProviderMocked = Mockito.mockStatic(ConfigProvider.class)) { + Config mockedConfig = Mockito.mock(Config.class); + configProviderMocked.when(ConfigProvider::getConfig).thenReturn(mockedConfig); + + when(mockedConfig.getOptionalValue( + provider.getCanonicalAuthConfigPropertyName(ApiKeyAuthenticationProvider.USE_AUTHORIZATION_HEADER_VALUE), + Boolean.class)).thenReturn(Optional.of(false)); + when(mockedConfig.getOptionalValue( + provider.getCanonicalAuthConfigPropertyName(ConfigCredentialsProvider.API_KEY), String.class)) + .thenReturn(Optional.of(API_KEY_VALUE)); + doReturn(API_KEY_AUTH_HEADER_VALUE).when(requestContext).getHeaderString("Authorization"); + + provider.filter(requestContext); + assertHeader(headers, API_KEY_NAME, API_KEY_VALUE); + } } @Test @@ -82,12 +88,10 @@ void filterHeaderCase() throws IOException { void filterQueryCase() throws IOException { doReturn(INVOKED_URI).when(requestContext).getUri(); provider = new ApiKeyAuthenticationProvider(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, ApiKeyIn.query, API_KEY_NAME, - authConfig, List.of()); + List.of()); provider.filter(requestContext); verify(requestContext).setUri(uriCaptor.capture()); - assertThat(uriCaptor.getValue()) - .isNotNull() - .hasParameter(API_KEY_NAME, API_KEY_VALUE); + assertThat(uriCaptor.getValue()).isNotNull().hasParameter(API_KEY_NAME, API_KEY_VALUE); } @Test @@ -95,12 +99,10 @@ void filterCookieCaseEmpty() throws IOException { final MultivaluedMap headers = new MultivaluedTreeMap<>(); doReturn(headers).when(requestContext).getHeaders(); provider = new ApiKeyAuthenticationProvider(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, ApiKeyIn.cookie, API_KEY_NAME, - authConfig, List.of()); + List.of()); provider.filter(requestContext); final List cookies = headers.get(HttpHeaders.COOKIE); - assertThat(cookies) - .singleElement() - .satisfies(cookie -> assertCookie(cookie, API_KEY_NAME, API_KEY_VALUE)); + assertThat(cookies).singleElement().satisfies(cookie -> assertCookie(cookie, API_KEY_NAME, API_KEY_VALUE)); } @Test @@ -108,34 +110,33 @@ void filterCookieCaseExisting() throws IOException { final MultivaluedMap headers = new MultivaluedTreeMap<>(); final String existingCookieName = "quarkus"; final String existingCookieValue = "rocks"; - final Cookie existingCookie = new Cookie.Builder(existingCookieName) - .value(existingCookieValue) - .build(); + final Cookie existingCookie = new Cookie.Builder(existingCookieName).value(existingCookieValue).build(); headers.add(HttpHeaders.COOKIE, existingCookie); doReturn(headers).when(requestContext).getHeaders(); provider = new ApiKeyAuthenticationProvider(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, ApiKeyIn.cookie, API_KEY_NAME, - authConfig, List.of()); + List.of()); provider.filter(requestContext); final List cookies = headers.get(HttpHeaders.COOKIE); - assertThat(cookies) - .satisfiesExactlyInAnyOrder( - cookie -> assertCookie(cookie, existingCookieName, existingCookieValue), - cookie -> assertCookie(cookie, API_KEY_NAME, API_KEY_VALUE)); + assertThat(cookies).satisfiesExactlyInAnyOrder(cookie -> assertCookie(cookie, existingCookieName, existingCookieValue), + cookie -> assertCookie(cookie, API_KEY_NAME, API_KEY_VALUE)); } @Test void tokenPropagationNotSupported() { - authConfig.tokenPropagation = Optional.of(true); - assertThatThrownBy(() -> new ApiKeyAuthenticationProvider(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, ApiKeyIn.header, - API_KEY_NAME, authConfig, List.of())) - .hasMessageContaining("quarkus.openapi-generator.%s.auth.%s.token-propagation", OPEN_API_FILE_SPEC_ID, - AUTH_SCHEME_NAME); + try (MockedStatic configProviderMocked = Mockito.mockStatic(ConfigProvider.class)) { + Config mockedConfig = Mockito.mock(Config.class); + configProviderMocked.when(ConfigProvider::getConfig).thenReturn(mockedConfig); + when(mockedConfig.getOptionalValue(provider.getCanonicalAuthConfigPropertyName(AuthConfig.TOKEN_PROPAGATION), + Boolean.class)).thenReturn(Optional.of(true)); + + assertThatThrownBy(() -> new ApiKeyAuthenticationProvider(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, ApiKeyIn.header, + API_KEY_NAME, List.of())).hasMessageContaining("quarkus.openapi-generator.%s.auth.%s.token-propagation", + OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME); + } } private void assertCookie(final Object cookie, final String name, final String value) { - assertThat(cookie) - .asInstanceOf(InstanceOfAssertFactories.type(Cookie.class)) - .matches(c -> Objects.equals(c.getName(), name)) - .matches(c -> Objects.equals(c.getValue(), value)); + assertThat(cookie).asInstanceOf(InstanceOfAssertFactories.type(Cookie.class)) + .matches(c -> Objects.equals(c.getName(), name)).matches(c -> Objects.equals(c.getValue(), value)); } } diff --git a/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/BasicOpenApiSpecProviderTest.java b/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/BasicOpenApiSpecProviderTest.java index 479f5a71f..20a26d577 100644 --- a/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/BasicOpenApiSpecProviderTest.java +++ b/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/BasicOpenApiSpecProviderTest.java @@ -1,46 +1,84 @@ package io.quarkiverse.openapi.generator.providers; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static io.quarkiverse.openapi.generator.providers.AbstractAuthenticationPropagationHeadersFactory.propagationHeaderName; +import static org.mockito.Mockito.when; import java.io.IOException; import java.util.Base64; import java.util.List; import java.util.Optional; +import java.util.stream.Stream; import jakarta.ws.rs.core.HttpHeaders; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.MockedStatic; +import org.mockito.Mockito; import io.quarkiverse.openapi.generator.AuthConfig; class BasicOpenApiSpecProviderTest extends AbstractOpenApiSpecProviderTest { + private static final String PROPAGATED_TOKEN = "PROPAGATED_TOKEN"; private static final String USER = "USER"; private static final String PASSWORD = "PASSWORD"; + private static final String USER_PROP = "username"; + private static final String PASSWORD_PROP = "password"; + + private static final String CUSTOM_SCHEMA = "custom_scheme"; + private static final String HEADER_NAME = "HEADER_NAME"; + private static final String EXPECTED_BASIC_TOKEN = "Basic " + Base64.getEncoder().encodeToString((USER + ":" + PASSWORD).getBytes()); @Override - protected BasicAuthenticationProvider createProvider(String openApiSpecId, String authSchemeName, - AuthConfig authConfig) { - return new BasicAuthenticationProvider(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, authConfig, List.of()); + protected BasicAuthenticationProvider createProvider() { + return new BasicAuthenticationProvider(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, List.of()); } @Test void filter() throws IOException { - authConfig.authConfigParams.put(BasicAuthenticationProvider.USER_NAME, USER); - authConfig.authConfigParams.put(BasicAuthenticationProvider.PASSWORD, PASSWORD); + filter(EXPECTED_BASIC_TOKEN); + } + + private void filter(String expectedAuthorizationHeader) throws IOException { provider.filter(requestContext); - assertHeader(requestContext.getHeaders(), HttpHeaders.AUTHORIZATION, EXPECTED_BASIC_TOKEN); + assertHeader(requestContext.getHeaders(), HttpHeaders.AUTHORIZATION, expectedAuthorizationHeader); } - @Test - void tokenPropagationNotSupported() { - authConfig.tokenPropagation = Optional.of(true); - assertThatThrownBy( - () -> new BasicAuthenticationProvider(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, authConfig, List.of())) - .hasMessageContaining("quarkus.openapi-generator.%s.auth.%s.token-propagation", OPEN_API_FILE_SPEC_ID, - AUTH_SCHEME_NAME); + @ParameterizedTest + @MethodSource("filterWithPropagationTestValues") + void filterWithPropagation(String headerName, + String expectedAuthorizationHeader) throws IOException { + String propagatedHeaderName = headerName == null + ? propagationHeaderName(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, HttpHeaders.AUTHORIZATION) + : propagationHeaderName(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, HEADER_NAME); + try (MockedStatic configProviderMocked = Mockito.mockStatic(ConfigProvider.class)) { + Config mockedConfig = Mockito.mock(Config.class); + configProviderMocked.when(ConfigProvider::getConfig).thenReturn(mockedConfig); + + when(mockedConfig.getOptionalValue(provider.getCanonicalAuthConfigPropertyName(AuthConfig.TOKEN_PROPAGATION), + Boolean.class)).thenReturn(Optional.of(true)); + when(mockedConfig.getOptionalValue(provider.getCanonicalAuthConfigPropertyName(AuthConfig.HEADER_NAME), + String.class)).thenReturn(Optional.of(headerName == null ? HttpHeaders.AUTHORIZATION : headerName)); + when(mockedConfig.getOptionalValue(provider.getCanonicalAuthConfigPropertyName(USER_PROP), + String.class)).thenReturn(Optional.of(USER)); + when(mockedConfig.getOptionalValue(provider.getCanonicalAuthConfigPropertyName(PASSWORD_PROP), + String.class)).thenReturn(Optional.of(PASSWORD)); + headers.putSingle(propagatedHeaderName, PROPAGATED_TOKEN); + filter(expectedAuthorizationHeader); + } + } + + static Stream filterWithPropagationTestValues() { + return Stream.of( + Arguments.of(null, "Basic " + PROPAGATED_TOKEN), + Arguments.of(HEADER_NAME, "Basic " + PROPAGATED_TOKEN)); } } diff --git a/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/BearerOpenApiSpecProviderTest.java b/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/BearerOpenApiSpecProviderTest.java index 1e39c96a9..0321b99da 100644 --- a/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/BearerOpenApiSpecProviderTest.java +++ b/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/BearerOpenApiSpecProviderTest.java @@ -1,6 +1,7 @@ package io.quarkiverse.openapi.generator.providers; import static io.quarkiverse.openapi.generator.providers.AbstractAuthenticationPropagationHeadersFactory.propagationHeaderName; +import static org.mockito.Mockito.when; import java.io.IOException; import java.util.List; @@ -9,48 +10,61 @@ import jakarta.ws.rs.core.HttpHeaders; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.MockedStatic; +import org.mockito.Mockito; import io.quarkiverse.openapi.generator.AuthConfig; class BearerOpenApiSpecProviderTest extends AbstractOpenApiSpecProviderTest { - private static final String TOKEN = "TOKEN"; private static final String INCOMING_TOKEN = "INCOMING_TOKEN"; + private static final String PROPAGATED_TOKEN = "PROPAGATED_TOKEN"; + private static final String BEARER_SCHEMA = "bearer"; + + private static final String BEARER_TOKEN = "bearer-token"; private static final String CUSTOM_SCHEMA = "custom_scheme"; private static final String HEADER_NAME = "HEADER_NAME"; + static Stream filterWithPropagationTestValues() { + return Stream.of( + Arguments.of(null, "bearer", "Bearer " + PROPAGATED_TOKEN), + Arguments.of(null, CUSTOM_SCHEMA, CUSTOM_SCHEMA + " " + PROPAGATED_TOKEN), + Arguments.of(HEADER_NAME, "bearer", "Bearer " + PROPAGATED_TOKEN), + Arguments.of(HEADER_NAME, CUSTOM_SCHEMA, CUSTOM_SCHEMA + " " + PROPAGATED_TOKEN)); + } + @Override - protected BearerAuthenticationProvider createProvider(String openApiSpecId, String authSchemeName, - AuthConfig authConfig) { - return new BearerAuthenticationProvider(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, null, authConfig, + protected BearerAuthenticationProvider createProvider() { + return new BearerAuthenticationProvider(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, null, List.of()); } @Test void filterNoSchemaCase() throws IOException { - filter(null, TOKEN, TOKEN); + filter(null, INCOMING_TOKEN); } @Test void filterBearerSchemaCase() throws IOException { - filter(BEARER_SCHEMA, TOKEN, "Bearer " + TOKEN); + filter(BEARER_SCHEMA, "Bearer " + INCOMING_TOKEN); } @Test void filterCustomSchemaCase() throws IOException { - filter(CUSTOM_SCHEMA, TOKEN, CUSTOM_SCHEMA + " " + TOKEN); + filter(CUSTOM_SCHEMA, CUSTOM_SCHEMA + " " + INCOMING_TOKEN); } - private void filter(String bearerScheme, String currentToken, String expectedAuthorizationHeader) throws IOException { - provider = new BearerAuthenticationProvider(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, bearerScheme, authConfig, + private void filter(String bearerScheme, String expectedAuthorizationHeader) throws IOException { + provider = new BearerAuthenticationProvider(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, bearerScheme, List.of()); - authConfig.authConfigParams.put(BearerAuthenticationProvider.BEARER_TOKEN, currentToken); provider.filter(requestContext); assertHeader(headers, HttpHeaders.AUTHORIZATION, expectedAuthorizationHeader); } @@ -58,7 +72,6 @@ private void filter(String bearerScheme, String currentToken, String expectedAut @ParameterizedTest @MethodSource("filterWithPropagationTestValues") void filterWithPropagation(String headerName, - String currentToken, String bearerScheme, String expectedAuthorizationHeader) throws IOException { String propagatedHeaderName; @@ -69,17 +82,19 @@ void filterWithPropagation(String headerName, propagatedHeaderName = propagationHeaderName(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME, HEADER_NAME); } - headers.putSingle(propagatedHeaderName, INCOMING_TOKEN); - authConfig.tokenPropagation = Optional.of(true); - authConfig.headerName = Optional.ofNullable(headerName); - filter(bearerScheme, currentToken, expectedAuthorizationHeader); - } + try (MockedStatic configProviderMocked = Mockito.mockStatic(ConfigProvider.class)) { + Config mockedConfig = Mockito.mock(Config.class); + configProviderMocked.when(ConfigProvider::getConfig).thenReturn(mockedConfig); - static Stream filterWithPropagationTestValues() { - return Stream.of( - Arguments.of(null, INCOMING_TOKEN, "bearer", "Bearer " + INCOMING_TOKEN), - Arguments.of(null, INCOMING_TOKEN, CUSTOM_SCHEMA, CUSTOM_SCHEMA + " " + INCOMING_TOKEN), - Arguments.of(HEADER_NAME, INCOMING_TOKEN, "bearer", "Bearer " + INCOMING_TOKEN), - Arguments.of(HEADER_NAME, INCOMING_TOKEN, CUSTOM_SCHEMA, CUSTOM_SCHEMA + " " + INCOMING_TOKEN)); + when(mockedConfig.getOptionalValue(provider.getCanonicalAuthConfigPropertyName(AuthConfig.TOKEN_PROPAGATION), + Boolean.class)).thenReturn(Optional.of(true)); + when(mockedConfig.getOptionalValue(provider.getCanonicalAuthConfigPropertyName(AuthConfig.HEADER_NAME), + String.class)).thenReturn(Optional.of(headerName == null ? HttpHeaders.AUTHORIZATION : headerName)); + when(mockedConfig.getOptionalValue(provider.getCanonicalAuthConfigPropertyName(BEARER_TOKEN), + String.class)).thenReturn(Optional.of(INCOMING_TOKEN)); + + headers.putSingle(propagatedHeaderName, PROPAGATED_TOKEN); + filter(bearerScheme, expectedAuthorizationHeader); + } } } diff --git a/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/UrlPatternMatcherTest.java b/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/UrlPatternMatcherTest.java index 9610f7bfd..4c7a7d9f8 100644 --- a/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/UrlPatternMatcherTest.java +++ b/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/UrlPatternMatcherTest.java @@ -16,6 +16,13 @@ void verifyPathsMatch(final String pathPattern, final String requestPath) { Assertions.assertTrue(pattern.matches(requestPath)); } + @ParameterizedTest + @MethodSource("providePathsThatNotMatch") + void verifyPathsNotMatch(final String pathPattern, final String requestPath) { + UrlPatternMatcher pattern = new UrlPatternMatcher(pathPattern); + Assertions.assertFalse(pattern.matches(requestPath)); + } + private static Stream providePathsThatMatch() { return Stream.of( Arguments.of("/pets/{id}", "/pets/1"), @@ -32,7 +39,16 @@ private static Stream providePathsThatMatch() { Arguments.of("/{id}/{foo}/{id2}", "/1/2/3?q=1&q2=2"), Arguments.of("/{id}/{foo}/{id2}", "/1/2/3"), Arguments.of("/v2/pets/{id}", "/v2/pets/1"), - Arguments.of("/pets/{pet-id}/types/{type-id}", "/pets/1/types/2")); + Arguments.of("/pets/{pet-id}/types/{type-id}", "/pets/1/types/2"), + Arguments.of("/repos/{ref}", "/repos/prefixed/cool.sw"), + Arguments.of("/repos/{ref}/pepe", "/repos/prefixed/cool.sw/pepe"), + Arguments.of("pepe/pepa/pepu", "pepe/pepa/pepu")); + } + + private static Stream providePathsThatNotMatch() { + return Stream.of( + Arguments.of("/pets/{id}", "/pes/1"), + Arguments.of("/{id}/pepe", "/1/2/pep")); } } \ No newline at end of file diff --git a/client/runtime/src/test/resources/application.properties b/client/runtime/src/test/resources/application.properties new file mode 100644 index 000000000..9863c1aa8 --- /dev/null +++ b/client/runtime/src/test/resources/application.properties @@ -0,0 +1,4 @@ +quarkus.openapi-generator.open_api_file_spec_id_json.auth.auth_scheme_name.api-key=API_KEY_VALUE +quarkus.openapi-generator.open_api_file_spec_id_json.auth.auth_scheme_name.username=USER +quarkus.openapi-generator.open_api_file_spec_id_json.auth.auth_scheme_name.password=PASSWORD +quarkus.openapi-generator.open_api_file_spec_id_json.auth.auth_scheme_name.bearer-token=INCOMING_TOKEN \ No newline at end of file diff --git a/client/test-utils/pom.xml b/client/test-utils/pom.xml index 354e59fa5..e72a863c7 100644 --- a/client/test-utils/pom.xml +++ b/client/test-utils/pom.xml @@ -8,7 +8,7 @@ ../pom.xml quarkus-openapi-generator-test-utils - Quarkus - Openapi Generator - Client - Test Utils + Quarkus - OpenAPI Generator - Client - Test Utils @@ -22,8 +22,8 @@ compile - com.github.tomakehurst - wiremock-jre8 + org.wiremock + wiremock io.quarkus diff --git a/docs/antora.yml b/docs/antora.yml index de97e7734..b26c140a1 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -1,5 +1,5 @@ name: quarkus-openapi-generator -title: Openapi Generator +title: OpenAPI Generator version: dev nav: - modules/ROOT/nav.adoc diff --git a/docs/modules/ROOT/assets/images/moqu-devui-card-framework.png b/docs/modules/ROOT/assets/images/moqu-devui-card-framework.png new file mode 100644 index 000000000..e9b7d0e40 Binary files /dev/null and b/docs/modules/ROOT/assets/images/moqu-devui-card-framework.png differ diff --git a/docs/modules/ROOT/assets/images/openapi.svg b/docs/modules/ROOT/assets/images/openapi.svg new file mode 100644 index 000000000..50ee1e39f --- /dev/null +++ b/docs/modules/ROOT/assets/images/openapi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/modules/ROOT/assets/images/table-wiremock.png b/docs/modules/ROOT/assets/images/table-wiremock.png new file mode 100644 index 000000000..7113b186b Binary files /dev/null and b/docs/modules/ROOT/assets/images/table-wiremock.png differ diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 57338522b..5fdf5662d 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -1,2 +1,3 @@ * xref:client.adoc[Client Generator] -* xref:server.adoc[Server Generator] \ No newline at end of file +* xref:server.adoc[Server Generator] +* xref:moqu.adoc[Moqu Wiremock Generator] \ No newline at end of file diff --git a/docs/modules/ROOT/pages/client.adoc b/docs/modules/ROOT/pages/client.adoc index 95ad693fc..605a1b1a5 100644 --- a/docs/modules/ROOT/pages/client.adoc +++ b/docs/modules/ROOT/pages/client.adoc @@ -42,6 +42,11 @@ include::./includes/authentication-support.adoc[leveloffset=+1, opts=optional] include::./includes/authorization-token-propagation.adoc[leveloffset=+1, opts=optional] +[[custom-credentials-provider]] +== Custom Credentials Provider + +include::./includes/custom-auth-provider.adoc[leveloffset=+1, opts=optional] + [[circuit-breaker]] == Circuit Breaker @@ -115,7 +120,7 @@ See the module `integration-tests/register-provider` for an example of how to us Use the property key `quarkus.openapi-generator.codegen.validateSpec=false` to disable validating the input specification file before code generation. By default, invalid specifications will result in an error. -== Type and import mappings +== Type, schema and import mappings It's possible to remap types in the generated files. For example, instead of a `File` you can configure the code generator to use `InputStream` for all file upload parts of multipart request, or you could change all `UUID` types to `String`. You can configure this in your `application.properties` using the following configuration keys: @@ -129,6 +134,9 @@ It's possible to remap types in the generated files. For example, instead of a ` |Import Mapping |`quarkus.openapi-generator.codegen.spec.[filename].import-mappings.[type]` |`quarkus.openapi-generator.codegen.spec.my_spec_yml.import-mappings.File=java.io.InputStream` will replace the default `import java.io.File` with `import java.io.InputStream` +|Schema Mapping +|`quarkus.openapi-generator.codegen.spec.[filename].schema-mappings.[type]` +|`quarkus.openapi-generator.codegen.spec.my_spec_yml.schema-mappings.YearMonth=java.time.YearMonth` will use `java.time.YearMonth` as type for all schemas of the chosen type in the file. |=== Note that these configuration properties are maps. For the type-mapping the keys are OAS data types and the values are Java types. @@ -141,7 +149,7 @@ quarkus.openapi-generator.codegen.spec.my_spec_yml.type-mappings.DateTime=Instan quarkus.openapi-generator.codegen.spec.my_spec_yml.import-mappings.Instant=java.time.Instant ---- -It's also possible to only use a type mapping with a fully qualified name, for instance `quarkus.openapi-generator.codegen.spec.my_spec_yml.type-mappings.File=java.io.InputStream`. For more information and a list of all types see the OpenAPI generator documentation on https://openapi-generator.tech/docs/usage/#type-mappings-and-import-mappings[Type Mappings and Import Mappings]. +It's also possible to only use a type mapping with a fully qualified name, for instance `quarkus.openapi-generator.codegen.spec.my_spec_yml.type-mappings.File=java.io.InputStream`. For more information and a list of all types see the OpenAPI generator documentation on https://openapi-generator.tech/docs/usage/#type-mappings-and-import-mappings[Type Mappings and Import Mappings] and https://openapi-generator.tech/docs/customization#schema-mapping[Schema mapping]. See the module https://github.com/quarkiverse/quarkus-openapi-generator/tree/main/integration-tests/type-mapping[type-mapping] for an example of how to use this feature. @@ -170,6 +178,42 @@ include::./includes/additional-request-args.adoc[leveloffset=+1, opts=optional] include::./includes/bean-validation.adoc[leveloffset=+1, opts=optional] +[[generate-apis]] +== Generate APIs + +include::./includes/generate-apis.adoc[leveloffset=+1, opts=optional] + +[[generate-models]] +== Generate Models + +include::./includes/generate-models.adoc[leveloffset=+1, opts=optional] + +[[generate-serilazble-models]] +== Generate Serilazable Models + +If you need to have the generated models to implement `java.io.Serializable`-interface then set the `serializable-model` +to true: + +[source,properties] +---- +quarkus.openapi-generator.codegen.spec.my_openapi_yaml.serializable-model=true +---- + +[[initialize-empty-collections]] +== Initialize Empty Collections + +include::./includes/initialize-empty-collections.adoc[leveloffset=+1, opts=optional] + +[[equals-hashcode]] +== Equals and hashcode + +include::./includes/equals-hashcode.adoc[leveloffset=+1, opts=optional] + +[[dynamic-url]] +== Dynamic base URLs + +include::./includes/dynamic-url.adoc[leveloffset=+1, opts=optional] + == Known Limitations === Supported Arguments @@ -196,4 +240,8 @@ These are the known limitations of this pre-release version: * Only Jackson support -We will work in the next few releases to address these use cases, until there please provide feedback for the current state of this extension. We also love contributions icon:heart[1x,role=red]. \ No newline at end of file +We will work in the next few releases to address these use cases, until there please provide feedback for the current state of this extension. We also love contributions icon:heart[1x,role=red]. + +== Configuration Properties + +include::./includes/quarkus-openapi-generator_quarkus.openapi-generator.adoc[opts=optional, leveloffset=+1] diff --git a/docs/modules/ROOT/pages/includes/attributes.adoc b/docs/modules/ROOT/pages/includes/attributes.adoc index 132b09b8f..5533dbd74 100644 --- a/docs/modules/ROOT/pages/includes/attributes.adoc +++ b/docs/modules/ROOT/pages/includes/attributes.adoc @@ -1,3 +1,3 @@ -:project-version: 2.5.0 +:project-version: 2.10.0 :examples-dir: ./../examples/ diff --git a/docs/modules/ROOT/pages/includes/authentication-support.adoc b/docs/modules/ROOT/pages/includes/authentication-support.adoc index a51e9bc1d..be9d0c519 100644 --- a/docs/modules/ROOT/pages/includes/authentication-support.adoc +++ b/docs/modules/ROOT/pages/includes/authentication-support.adoc @@ -44,7 +44,7 @@ If the OpenAPI specification file has `securitySchemes` definitions, but no http |`quarkus.openapi-generator.codegen.default-security-scheme=api_key` |=== -See the module https://github.com/quarkiverse/quarkus-openapi-generator/tree/main/integration-tests/security[security] for an example of how to use this feature. +See the module https://github.com/quarkiverse/quarkus-openapi-generator/tree/main/client/integration-tests/security[security] for an example of how to use this feature. == Basic HTTP Authentication @@ -134,12 +134,18 @@ quarkus.oidc-client.petstore_auth.client-id=petstore-app The configuration suffix `quarkus.oidc-client.petstore_auth` is exclusive for the schema defined in the specification file and the `schemaName` is sanitized by applying the rules described above. -For this to work you **must** add https://quarkus.io/guides/security-openid-connect-client#oidc-client-filter[Quarkus OIDC Client Filter Extension] to your project: +For this to work you **must** add https://quarkus.io/guides/security-openid-connect-client#oidc-client-filter[Quarkus OIDC Client Filter Extension] to your project. + +IMPORTANT: From version 2.7.0 and onwards you must also add the `quarkus-openapi-generator-oidc` additional dependency. Please see the details below. RESTEasy Classic: [source ,xml] ---- + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-oidc + io.quarkus quarkus-oidc-client-filter @@ -150,6 +156,10 @@ RESTEasy Reactive: [source ,xml] ---- + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-oidc + io.quarkus quarkus-oidc-client-reactive-filter diff --git a/docs/modules/ROOT/pages/includes/authorization-token-propagation.adoc b/docs/modules/ROOT/pages/includes/authorization-token-propagation.adoc index 58726989b..656d39d46 100644 --- a/docs/modules/ROOT/pages/includes/authorization-token-propagation.adoc +++ b/docs/modules/ROOT/pages/includes/authorization-token-propagation.adoc @@ -78,7 +78,7 @@ WARNING: When configured, the token propagation applies to all the operations se === Propagation flow configuration -The token propagation can be used with type "oauth2" or "bearer" security schemes. Finally, considering that a given security scheme might be configured on a set of operations in the same specification file when configured, it'll apply to all these operations. +The token propagation can be used with type "oauth2", "bearer" or "basic" security schemes. Finally, considering that a given security scheme might be configured on a set of operations in the same specification file when configured, it'll apply to all these operations. [%autowidth] |=== diff --git a/docs/modules/ROOT/pages/includes/custom-auth-provider.adoc b/docs/modules/ROOT/pages/includes/custom-auth-provider.adoc new file mode 100644 index 000000000..6d2c5b5a2 --- /dev/null +++ b/docs/modules/ROOT/pages/includes/custom-auth-provider.adoc @@ -0,0 +1,78 @@ + +Instead of relying solely on application properties to provide runtime credentials, you can implement the `io.quarkiverse.openapi.generator.providers.CredentialsProvider` interface. This approach lets you override the default behavior of looking up configuration values and instead supply credentials dynamically at runtime. + +== Default Behavior + +By default, the extension searches for pre-configured values. For instance, to provide a `username` and `password` for a specific OpenAPI security schema definition, you might add these properties to your configuration: + +[source,properties] +---- +quarkus.openapi-generator.myopenapi_yaml.auth.mybasicsecscheme.username=alice +quarkus.openapi-generator.myopenapi_yaml.auth.mybasicsecscheme.password=${SECRET} +---- + +== Overriding with a Custom Implementation + +In some cases, you might need to determine credentials at runtime—for example, when the credentials depend on the server's current URL or other request-specific details. To do this, you can implement the `CredentialsProvider` interface. + +For example: + +[source,java] +---- +import jakarta.ws.rs.client.ClientRequestContext; +import jakarta.enterprise.context.RequestScoped; +import jakarta.annotation.Priority; +import jakarta.enterprise.inject.Alternative; +import io.quarkiverse.openapi.generator.providers.CredentialsProvider; + +@RequestScoped +@Alternative +@Priority(10) // A higher priority than the default provider. +public class RuntimeCredentialsProvider implements CredentialsProvider { + + @Override + public String getApiKey(ClientRequestContext requestContext, String openApiSpecId, String authName) { + // Example: return an API key dynamically based on the current request. + // You could inspect requestContext to decide which API key to return. + return "runtimeApiKey"; + } + + @Override + public String getBasicUsername(ClientRequestContext requestContext, String openApiSpecId, String authName) { + // Use requestContext to obtain dynamic request data (like URL or headers) + // for your custom lookup logic. + return "runtimeUser"; + } + + @Override + public String getBasicPassword(ClientRequestContext requestContext, String openApiSpecId, String authName) { + // Return the password dynamically, potentially using details from requestContext. + return "runtimePassword"; + } + + @Override + public String getBearerToken(ClientRequestContext requestContext, String openApiSpecId, String authName) { + // Dynamically compute or look up the bearer token using data in requestContext. + return "runtimeBearerToken"; + } +} +---- + +== How It Works + +* `openApiSpecId` – Identifies the specific OpenAPI specification file. +* `authName` – Refers to the name of the security schema defined in your OpenAPI file. +* `ClientRequestContext` – Provides access to information about the current request (for example, the URL and headers). This data can be crucial if credential resolution depends on runtime request details, such as when different servers or endpoints require different authentication credentials. + +When you implement the provider, the extension will pass the current `ClientRequestContext` along with the identifiers. Your custom code can then use any available request information to dynamically look up or compute the appropriate credentials before they are applied by the authentication filter. + +== Summary + +By implementing the `CredentialsProvider` interface, you gain the following benefits: + +* **Dynamic Credential Resolution:** Obtain credentials at runtime, which is useful if they vary based on the current request context. +* **Custom Lookup Logic:** Use request-specific data (via `ClientRequestContext`) such as URL or headers to determine the correct authentication values. +* **Seamless Integration:** Your custom provider integrates into the authentication filter, replacing static configuration with dynamic behavior as needed. + +This design provides maximum flexibility and control over how authentication credentials are supplied in your application. + diff --git a/docs/modules/ROOT/pages/includes/dynamic-url.adoc b/docs/modules/ROOT/pages/includes/dynamic-url.adoc new file mode 100644 index 000000000..f4b36df11 --- /dev/null +++ b/docs/modules/ROOT/pages/includes/dynamic-url.adoc @@ -0,0 +1,16 @@ + +If you need the generated `RestClient` to target different server URLs at runtime—rather than relying solely on the static URL from the application configuration—you can enable dynamic base URL support. + +To do so, set the following property in your configuration: + +[source,properties] +---- +quarkus.openapi-generator.codegen.spec.my_openapi_yaml.use-dynamic-url=true +---- + +When this property is enabled and `quarkus-rest-client` is present on the classpath, the generator will include a method parameter annotated with `@io.quarkus.rest.client.reactive.Url`. This allows your application to supply the target URL dynamically at runtime. + +This feature is particularly useful when integrating with multiple instances of the same API or switching endpoints based on contextual information. + +For more details, refer to the official Quarkus documentation: +https://quarkus.io/version/3.20/guides/rest-client#dynamic-base-urls diff --git a/docs/modules/ROOT/pages/includes/equals-hashcode.adoc b/docs/modules/ROOT/pages/includes/equals-hashcode.adoc new file mode 100644 index 000000000..b916c4c8f --- /dev/null +++ b/docs/modules/ROOT/pages/includes/equals-hashcode.adoc @@ -0,0 +1,8 @@ +By default, `hashcode` and `equals` methods are automatically generated for all models. These methods are based on all the variables in the model and can not be customized. + +To disable set `equals-hashcode` to false: + +[source,properties] +---- +quarkus.openapi-generator.codegen.spec.my_openapi_yaml.equals-hashcode=false +---- \ No newline at end of file diff --git a/docs/modules/ROOT/pages/includes/generate-apis.adoc b/docs/modules/ROOT/pages/includes/generate-apis.adoc new file mode 100644 index 000000000..54d2c2611 --- /dev/null +++ b/docs/modules/ROOT/pages/includes/generate-apis.adoc @@ -0,0 +1,6 @@ +APIs are generated by default. To disable them set `generate-apis` to false: + +[source,properties] +---- +quarkus.openapi-generator.codegen.spec.my_openapi_yaml.generate-apis=false +---- \ No newline at end of file diff --git a/docs/modules/ROOT/pages/includes/generate-models.adoc b/docs/modules/ROOT/pages/includes/generate-models.adoc new file mode 100644 index 000000000..336071a55 --- /dev/null +++ b/docs/modules/ROOT/pages/includes/generate-models.adoc @@ -0,0 +1,6 @@ +Models are generated by default. To disable them set `generate-models` to false: + +[source,properties] +---- +quarkus.openapi-generator.codegen.spec.my_openapi_yaml.generate-models=false +---- \ No newline at end of file diff --git a/docs/modules/ROOT/pages/includes/getting-started.adoc b/docs/modules/ROOT/pages/includes/getting-started.adoc index e1cf60fa9..494c454c1 100644 --- a/docs/modules/ROOT/pages/includes/getting-started.adoc +++ b/docs/modules/ROOT/pages/includes/getting-started.adoc @@ -88,6 +88,27 @@ quarkus.openapi-generator.codegen.spec.petstore_json.model-name-suffix=CustomMod quarkus.openapi-generator.codegen.spec.petstore_json.model-name-prefix=CustomModelPrefix ---- +You can remove operationId prefix (e.g. User_findAll=> findAll). To do that, you must define the following properties: + +[source,properties] +---- +quarkus.openapi-generator.codegen.spec.petstore_json.remove-operation-id-prefix=true +---- + +Character to use as a delimiter for the prefix. Default is '_'.You can define the prefix delimiter (e.g. User.findAll=> findAll): + +[source,properties] +---- +quarkus.openapi-generator.codegen.spec.petstore_json.remove-operation-id-prefix-delimiter=. +---- + +You can define count of delimiter for the prefix (e.g. org.acme.UserResource.findAll=> findAll). Use -1 for last Default: + +[source,properties] +---- +quarkus.openapi-generator.codegen.spec.petstore_json.remove-operation-id-prefix-count=3 +---- + 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: [source,properties] diff --git a/docs/modules/ROOT/pages/includes/initialize-empty-collections.adoc b/docs/modules/ROOT/pages/includes/initialize-empty-collections.adoc new file mode 100644 index 000000000..dd34d6568 --- /dev/null +++ b/docs/modules/ROOT/pages/includes/initialize-empty-collections.adoc @@ -0,0 +1,9 @@ +In the default configuration, the generated RestClient is designed to set container types (e.g., lists, maps, or other collections) to null when no data is provided by the API. + +However, if you prefer container types to default to empty values instead (e.g., an empty list or map), you can override the default behavior by setting the following property in your configuration: + +[source,properties] +quarkus.openapi-generator.codegen.spec.my_openapi_yaml.initialize-empty-collections=true + +When this property is set to true, the generator will ensure that container types are initialized with empty values instead of null, allowing you to avoid null-checks and directly work with populated collections. +This feature is particularly useful when you expect the generated code to follow a defensive programming approach, or when dealing with APIs that always expect empty containers instead of null values. \ No newline at end of file diff --git a/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock.adoc b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock.adoc new file mode 100644 index 000000000..fe7a5ce9e --- /dev/null +++ b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock.adoc @@ -0,0 +1,32 @@ +[.configuration-legend] +icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime +[.configuration-reference.searchable, cols="80,.^10,.^10"] +|=== + +h|[.header-title]##Configuration property## +h|Type +h|Default + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator-moqu-wiremock_quarkus-openapi-generator-moqu-resource-dir]] [.property-path]##link:#quarkus-openapi-generator-moqu-wiremock_quarkus-openapi-generator-moqu-resource-dir[`quarkus.openapi-generator.moqu.resource-dir`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.moqu.resource-dir+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Path to the Moqu (relative to the project). + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_MOQU_RESOURCE_DIR+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_MOQU_RESOURCE_DIR+++` +endif::add-copy-button-to-env-var[] +-- +|string +|`openapi` + +|=== + diff --git a/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock_quarkus.adoc b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock_quarkus.adoc new file mode 100644 index 000000000..7a6b1b4da --- /dev/null +++ b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock_quarkus.adoc @@ -0,0 +1,32 @@ +[.configuration-legend] +icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime +[.configuration-reference.searchable, cols="80,.^10,.^10"] +|=== + +h|[.header-title]##Configuration property## +h|Type +h|Default + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator-moqu-wiremock_quarkus-resource-dir]] [.property-path]##link:#quarkus-openapi-generator-moqu-wiremock_quarkus-resource-dir[`quarkus.resource-dir`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.resource-dir+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Path to the Moqu (relative to the project). + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_RESOURCE_DIR+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_RESOURCE_DIR+++` +endif::add-copy-button-to-env-var[] +-- +|string +|`openapi` + +|=== + diff --git a/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock_quarkus.openapi-generator.adoc b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock_quarkus.openapi-generator.adoc new file mode 100644 index 000000000..fe7a5ce9e --- /dev/null +++ b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock_quarkus.openapi-generator.adoc @@ -0,0 +1,32 @@ +[.configuration-legend] +icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime +[.configuration-reference.searchable, cols="80,.^10,.^10"] +|=== + +h|[.header-title]##Configuration property## +h|Type +h|Default + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator-moqu-wiremock_quarkus-openapi-generator-moqu-resource-dir]] [.property-path]##link:#quarkus-openapi-generator-moqu-wiremock_quarkus-openapi-generator-moqu-resource-dir[`quarkus.openapi-generator.moqu.resource-dir`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.moqu.resource-dir+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Path to the Moqu (relative to the project). + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_MOQU_RESOURCE_DIR+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_MOQU_RESOURCE_DIR+++` +endif::add-copy-button-to-env-var[] +-- +|string +|`openapi` + +|=== + diff --git a/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-server.adoc b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-server.adoc new file mode 100644 index 000000000..9955fd047 --- /dev/null +++ b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-server.adoc @@ -0,0 +1,95 @@ +[.configuration-legend] +icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime +[.configuration-reference.searchable, cols="80,.^10,.^10"] +|=== + +h|[.header-title]##Configuration property## +h|Type +h|Default + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator-server_quarkus-openapi-generator-spec]] [.property-path]##link:#quarkus-openapi-generator-server_quarkus-openapi-generator-spec[`quarkus.openapi.generator.spec`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi.generator.spec+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The OpenAPI specification filename. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_SPEC+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_SPEC+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator-server_quarkus-openapi-generator-input-base-dir]] [.property-path]##link:#quarkus-openapi-generator-server_quarkus-openapi-generator-input-base-dir[`quarkus.openapi.generator.input-base-dir`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi.generator.input-base-dir+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The input base dir where the OpenAPI specification is. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_INPUT_BASE_DIR+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_INPUT_BASE_DIR+++` +endif::add-copy-button-to-env-var[] +-- +|string +|`src/main/resources/openapi` + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator-server_quarkus-openapi-generator-reactive]] [.property-path]##link:#quarkus-openapi-generator-server_quarkus-openapi-generator-reactive[`quarkus.openapi.generator.reactive`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi.generator.reactive+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether it must generate with reactive code. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_REACTIVE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_REACTIVE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +|`false` + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator-server_quarkus-openapi-generator-base-package]] [.property-path]##link:#quarkus-openapi-generator-server_quarkus-openapi-generator-base-package[`quarkus.openapi.generator.base-package`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi.generator.base-package+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The base package to be used to generated sources. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_BASE_PACKAGE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_BASE_PACKAGE+++` +endif::add-copy-button-to-env-var[] +-- +|string +|`io.apicurio.api` + +|=== + diff --git a/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-server_quarkus.openapi.adoc b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-server_quarkus.openapi.adoc new file mode 100644 index 000000000..9955fd047 --- /dev/null +++ b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-server_quarkus.openapi.adoc @@ -0,0 +1,95 @@ +[.configuration-legend] +icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime +[.configuration-reference.searchable, cols="80,.^10,.^10"] +|=== + +h|[.header-title]##Configuration property## +h|Type +h|Default + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator-server_quarkus-openapi-generator-spec]] [.property-path]##link:#quarkus-openapi-generator-server_quarkus-openapi-generator-spec[`quarkus.openapi.generator.spec`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi.generator.spec+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The OpenAPI specification filename. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_SPEC+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_SPEC+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator-server_quarkus-openapi-generator-input-base-dir]] [.property-path]##link:#quarkus-openapi-generator-server_quarkus-openapi-generator-input-base-dir[`quarkus.openapi.generator.input-base-dir`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi.generator.input-base-dir+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The input base dir where the OpenAPI specification is. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_INPUT_BASE_DIR+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_INPUT_BASE_DIR+++` +endif::add-copy-button-to-env-var[] +-- +|string +|`src/main/resources/openapi` + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator-server_quarkus-openapi-generator-reactive]] [.property-path]##link:#quarkus-openapi-generator-server_quarkus-openapi-generator-reactive[`quarkus.openapi.generator.reactive`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi.generator.reactive+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether it must generate with reactive code. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_REACTIVE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_REACTIVE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +|`false` + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator-server_quarkus-openapi-generator-base-package]] [.property-path]##link:#quarkus-openapi-generator-server_quarkus-openapi-generator-base-package[`quarkus.openapi.generator.base-package`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi.generator.base-package+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The base package to be used to generated sources. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_BASE_PACKAGE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_BASE_PACKAGE+++` +endif::add-copy-button-to-env-var[] +-- +|string +|`io.apicurio.api` + +|=== + diff --git a/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-server_quarkus.quarkus.adoc b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-server_quarkus.quarkus.adoc new file mode 100644 index 000000000..8410f0a8c --- /dev/null +++ b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-server_quarkus.quarkus.adoc @@ -0,0 +1,13 @@ +[.configuration-legend] +icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime +[.configuration-reference.searchable, cols="80,.^10,.^10"] +|=== + +h|[.header-title]##Configuration property## +h|Type +h|Default + +3+|No configuration properties found. + +|=== + diff --git a/docs/modules/ROOT/pages/includes/quarkus-openapi-generator.adoc b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator.adoc index c0650ac1b..41d33043d 100644 --- a/docs/modules/ROOT/pages/includes/quarkus-openapi-generator.adoc +++ b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator.adoc @@ -1,16 +1,1351 @@ +[.configuration-legend] +icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime +[.configuration-reference.searchable, cols="80,.^10,.^10"] +|=== + +h|[.header-title]##Configuration property## +h|Type +h|Default + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-skip-form-model]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-skip-form-model[`quarkus.openapi-generator.codegen.skip-form-model`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.skip-form-model+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether to skip the generation of models for form parameters + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SKIP_FORM_MODEL+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SKIP_FORM_MODEL+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-type-mappings-type-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-type-mappings-type-mappings[`quarkus.openapi-generator.codegen.type-mappings."type-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.type-mappings."type-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +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) + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_TYPE_MAPPINGS__TYPE_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_TYPE_MAPPINGS__TYPE_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-import-mappings-import-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-import-mappings-import-mappings[`quarkus.openapi-generator.codegen.import-mappings."import-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.import-mappings."import-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +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 + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_IMPORT_MAPPINGS__IMPORT_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_IMPORT_MAPPINGS__IMPORT_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-schema-mappings-schema-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-schema-mappings-schema-mappings[`quarkus.openapi-generator.codegen.schema-mappings."schema-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.schema-mappings."schema-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Schema Mapping is an OpenAPI Generator configuration specifying which Java types (the values) should be imported when a given schema type (the keys of this map) is used + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SCHEMA_MAPPINGS__SCHEMA_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SCHEMA_MAPPINGS__SCHEMA_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-model-type-annotations]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-model-type-annotations[`quarkus.openapi-generator.codegen.additional-model-type-annotations`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.additional-model-type-annotations+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The specified annotations will be added to the generated model files + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_MODEL_TYPE_ANNOTATIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_MODEL_TYPE_ANNOTATIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-enum-type-unexpected-member]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-enum-type-unexpected-member[`quarkus.openapi-generator.codegen.additional-enum-type-unexpected-member`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.additional-enum-type-unexpected-member+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if the enums should have an `UNEXPECTED` member to convey values that cannot be parsed. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-api-type-annotations]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-api-type-annotations[`quarkus.openapi-generator.codegen.additional-api-type-annotations`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.additional-api-type-annotations+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The specified annotations will be added to the generated api files + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_API_TYPE_ANNOTATIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_API_TYPE_ANNOTATIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-request-args]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-request-args[`quarkus.openapi-generator.codegen.additional-request-args`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.additional-request-args+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Add custom/additional HTTP Headers or other args to every request + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_REQUEST_ARGS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_REQUEST_ARGS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-return-response]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-return-response[`quarkus.openapi-generator.codegen.return-response`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.return-response+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if the methods should return `jakarta.ws.rs.core.Response` or a model. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_RETURN_RESPONSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_RETURN_RESPONSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-enable-security-generation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-enable-security-generation[`quarkus.openapi-generator.codegen.enable-security-generation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.enable-security-generation+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if security support classes should be generated + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ENABLE_SECURITY_GENERATION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ENABLE_SECURITY_GENERATION+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-open-api-normalizer-normalizer]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-open-api-normalizer-normalizer[`quarkus.openapi-generator.codegen.open-api-normalizer."normalizer"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.open-api-normalizer."normalizer"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines the normalizer options. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_OPEN_API_NORMALIZER__NORMALIZER_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_OPEN_API_NORMALIZER__NORMALIZER_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-mutiny]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-mutiny[`quarkus.openapi-generator.codegen.mutiny`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.mutiny+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable SmallRye Mutiny support. If you set this to `true`, all return types will be wrapped in `io.smallrye.mutiny.Uni`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_MUTINY+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_MUTINY+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-mutiny-return-response]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-mutiny-return-response[`quarkus.openapi-generator.codegen.mutiny.return-response`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.mutiny.return-response+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines with SmallRye Mutiny enabled if methods should return `jakarta.ws.rs.core.Response` or a model. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_MUTINY_RETURN_RESPONSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_MUTINY_RETURN_RESPONSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-mutiny-operation-ids-mutiny-multi-operation-ids]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-mutiny-operation-ids-mutiny-multi-operation-ids[`quarkus.openapi-generator.codegen.mutiny.operation-ids."mutiny-multi-operation-ids"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.mutiny.operation-ids."mutiny-multi-operation-ids"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Handles the return type for each operation, depending on the configuration. The following cases are supported: + +1. If `mutiny` is enabled and the operation ID is specified to return `Multi`: - The return type will be wrapped in `io.smallrye.mutiny.Multi`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Multi`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Multi`. - Otherwise, it will return `io.smallrye.mutiny.Multi`. + +2. If `mutiny` is enabled and the operation ID is specified to return `Uni`: - The return type will be wrapped in `io.smallrye.mutiny.Uni`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Uni`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Uni`. - Otherwise, it will return `io.smallrye.mutiny.Uni`. + +3. If `mutiny` is enabled but no specific operation ID is configured for `Multi` or `Uni`: - The return type defaults to `Uni`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Uni`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Uni`. - Otherwise, it will return `io.smallrye.mutiny.Uni``. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_MUTINY_OPERATION_IDS__MUTINY_MULTI_OPERATION_IDS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_MUTINY_OPERATION_IDS__MUTINY_MULTI_OPERATION_IDS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-generate-part-filename]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-generate-part-filename[`quarkus.openapi-generator.codegen.generate-part-filename`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.generate-part-filename+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines, whether the `PartFilename` (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) annotation should be generated for MultipartForm POJOs. By setting to `false`, the annotation will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_GENERATE_PART_FILENAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_GENERATE_PART_FILENAME+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-part-filename-value]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-part-filename-value[`quarkus.openapi-generator.codegen.part-filename-value`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.part-filename-value+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines the filename for a part in case the `PartFilename` annotation (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) is generated. In case no value is set, the default one is `File` or `file`, depending on the `CommonItemConfig++#++useFieldNameInPartFilename` configuration. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_PART_FILENAME_VALUE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_PART_FILENAME_VALUE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-use-field-name-in-part-filename]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-use-field-name-in-part-filename[`quarkus.openapi-generator.codegen.use-field-name-in-part-filename`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.use-field-name-in-part-filename+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines, whether the filename should also include the property name in case the `PartFilename` annotation (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) is generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_USE_FIELD_NAME_IN_PART_FILENAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_USE_FIELD_NAME_IN_PART_FILENAME+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-use-bean-validation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-use-bean-validation[`quarkus.openapi-generator.codegen.use-bean-validation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.use-bean-validation+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable bean validation. If you set this to `true`, validation annotations are added to generated sources E.g. `@Size`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_USE_BEAN_VALIDATION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_USE_BEAN_VALIDATION+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-generate-apis]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-generate-apis[`quarkus.openapi-generator.codegen.generate-apis`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.generate-apis+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of APIs. If you set this to `false`, APIs will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_GENERATE_APIS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_GENERATE_APIS+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-generate-models]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-generate-models[`quarkus.openapi-generator.codegen.generate-models`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.generate-models+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of models. If you set this to `false`, models will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_GENERATE_MODELS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_GENERATE_MODELS+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-equals-hashcode]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-equals-hashcode[`quarkus.openapi-generator.codegen.equals-hashcode`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.equals-hashcode+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of equals and hashcode in models. If you set this to `false`, the models will not have equals and hashcode. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_EQUALS_HASHCODE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_EQUALS_HASHCODE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-properties-as-attribute]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-properties-as-attribute[`quarkus.openapi-generator.codegen.additional-properties-as-attribute`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.additional-properties-as-attribute+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Add additional properties as attribute. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_PROPERTIES_AS_ATTRIBUTE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_PROPERTIES_AS_ATTRIBUTE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-initialize-empty-collections]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-initialize-empty-collections[`quarkus.openapi-generator.codegen.initialize-empty-collections`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.initialize-empty-collections+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Initialise collections as empty instead of null + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_INITIALIZE_EMPTY_COLLECTIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_INITIALIZE_EMPTY_COLLECTIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-verbose]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-verbose[`quarkus.openapi-generator.codegen.verbose`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.verbose+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether to log the internal generator codegen process in the default output or not. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_VERBOSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_VERBOSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +|`false` + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-input-base-dir]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-input-base-dir[`quarkus.openapi-generator.codegen.input-base-dir`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.input-base-dir+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Option to change the directory where OpenAPI files must be found. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_INPUT_BASE_DIR+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_INPUT_BASE_DIR+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-template-base-dir]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-template-base-dir[`quarkus.openapi-generator.codegen.template-base-dir`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.template-base-dir+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Option to change the directory where template files must be found. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_TEMPLATE_BASE_DIR+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_TEMPLATE_BASE_DIR+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-validatespec]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-validatespec[`quarkus.openapi-generator.codegen.validateSpec`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.validateSpec+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether or not to skip validating the input spec prior to generation. By default, invalid specifications will result in an error. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_VALIDATESPEC+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_VALIDATESPEC+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +|`true` + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-include]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-include[`quarkus.openapi-generator.codegen.include`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.include+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Option to specify files for which generation should be executed only + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_INCLUDE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_INCLUDE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-exclude]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-exclude[`quarkus.openapi-generator.codegen.exclude`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.exclude+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Option to exclude file from generation + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_EXCLUDE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_EXCLUDE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-default-security-scheme]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-default-security-scheme[`quarkus.openapi-generator.codegen.default-security-scheme`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.default-security-scheme+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Create security for the referenced security scheme + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_DEFAULT_SECURITY_SCHEME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_DEFAULT_SECURITY_SCHEME+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-skip-form-model]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-skip-form-model[`quarkus.openapi-generator.codegen.spec."spec-item".skip-form-model`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".skip-form-model+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether to skip the generation of models for form parameters + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__SKIP_FORM_MODEL+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__SKIP_FORM_MODEL+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-type-mappings-type-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-type-mappings-type-mappings[`quarkus.openapi-generator.codegen.spec."spec-item".type-mappings."type-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".type-mappings."type-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +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) + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__TYPE_MAPPINGS__TYPE_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__TYPE_MAPPINGS__TYPE_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-import-mappings-import-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-import-mappings-import-mappings[`quarkus.openapi-generator.codegen.spec."spec-item".import-mappings."import-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".import-mappings."import-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +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 + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__IMPORT_MAPPINGS__IMPORT_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__IMPORT_MAPPINGS__IMPORT_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-schema-mappings-schema-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-schema-mappings-schema-mappings[`quarkus.openapi-generator.codegen.spec."spec-item".schema-mappings."schema-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".schema-mappings."schema-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Schema Mapping is an OpenAPI Generator configuration specifying which Java types (the values) should be imported when a given schema type (the keys of this map) is used + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__SCHEMA_MAPPINGS__SCHEMA_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__SCHEMA_MAPPINGS__SCHEMA_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-model-type-annotations]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-model-type-annotations[`quarkus.openapi-generator.codegen.spec."spec-item".additional-model-type-annotations`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".additional-model-type-annotations+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The specified annotations will be added to the generated model files + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_MODEL_TYPE_ANNOTATIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_MODEL_TYPE_ANNOTATIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-enum-type-unexpected-member]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-enum-type-unexpected-member[`quarkus.openapi-generator.codegen.spec."spec-item".additional-enum-type-unexpected-member`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".additional-enum-type-unexpected-member+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if the enums should have an `UNEXPECTED` member to convey values that cannot be parsed. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-api-type-annotations]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-api-type-annotations[`quarkus.openapi-generator.codegen.spec."spec-item".additional-api-type-annotations`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".additional-api-type-annotations+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The specified annotations will be added to the generated api files + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_API_TYPE_ANNOTATIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_API_TYPE_ANNOTATIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-request-args]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-request-args[`quarkus.openapi-generator.codegen.spec."spec-item".additional-request-args`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".additional-request-args+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Add custom/additional HTTP Headers or other args to every request + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_REQUEST_ARGS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_REQUEST_ARGS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-return-response]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-return-response[`quarkus.openapi-generator.codegen.spec."spec-item".return-response`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".return-response+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if the methods should return `jakarta.ws.rs.core.Response` or a model. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__RETURN_RESPONSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__RETURN_RESPONSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-enable-security-generation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-enable-security-generation[`quarkus.openapi-generator.codegen.spec."spec-item".enable-security-generation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".enable-security-generation+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if security support classes should be generated + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ENABLE_SECURITY_GENERATION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ENABLE_SECURITY_GENERATION+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-open-api-normalizer-normalizer]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-open-api-normalizer-normalizer[`quarkus.openapi-generator.codegen.spec."spec-item".open-api-normalizer."normalizer"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".open-api-normalizer."normalizer"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines the normalizer options. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__OPEN_API_NORMALIZER__NORMALIZER_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__OPEN_API_NORMALIZER__NORMALIZER_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-mutiny]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-mutiny[`quarkus.openapi-generator.codegen.spec."spec-item".mutiny`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".mutiny+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable SmallRye Mutiny support. If you set this to `true`, all return types will be wrapped in `io.smallrye.mutiny.Uni`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MUTINY+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MUTINY+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-mutiny-return-response]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-mutiny-return-response[`quarkus.openapi-generator.codegen.spec."spec-item".mutiny.return-response`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".mutiny.return-response+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines with SmallRye Mutiny enabled if methods should return `jakarta.ws.rs.core.Response` or a model. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MUTINY_RETURN_RESPONSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MUTINY_RETURN_RESPONSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-mutiny-operation-ids-mutiny-multi-operation-ids]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-mutiny-operation-ids-mutiny-multi-operation-ids[`quarkus.openapi-generator.codegen.spec."spec-item".mutiny.operation-ids."mutiny-multi-operation-ids"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".mutiny.operation-ids."mutiny-multi-operation-ids"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Handles the return type for each operation, depending on the configuration. The following cases are supported: + +1. If `mutiny` is enabled and the operation ID is specified to return `Multi`: - The return type will be wrapped in `io.smallrye.mutiny.Multi`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Multi`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Multi`. - Otherwise, it will return `io.smallrye.mutiny.Multi`. + +2. If `mutiny` is enabled and the operation ID is specified to return `Uni`: - The return type will be wrapped in `io.smallrye.mutiny.Uni`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Uni`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Uni`. - Otherwise, it will return `io.smallrye.mutiny.Uni`. + +3. If `mutiny` is enabled but no specific operation ID is configured for `Multi` or `Uni`: - The return type defaults to `Uni`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Uni`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Uni`. - Otherwise, it will return `io.smallrye.mutiny.Uni``. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MUTINY_OPERATION_IDS__MUTINY_MULTI_OPERATION_IDS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MUTINY_OPERATION_IDS__MUTINY_MULTI_OPERATION_IDS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-generate-part-filename]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-generate-part-filename[`quarkus.openapi-generator.codegen.spec."spec-item".generate-part-filename`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".generate-part-filename+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines, whether the `PartFilename` (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) annotation should be generated for MultipartForm POJOs. By setting to `false`, the annotation will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__GENERATE_PART_FILENAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__GENERATE_PART_FILENAME+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-part-filename-value]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-part-filename-value[`quarkus.openapi-generator.codegen.spec."spec-item".part-filename-value`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".part-filename-value+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines the filename for a part in case the `PartFilename` annotation (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) is generated. In case no value is set, the default one is `File` or `file`, depending on the `CommonItemConfig++#++useFieldNameInPartFilename` configuration. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__PART_FILENAME_VALUE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__PART_FILENAME_VALUE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-use-field-name-in-part-filename]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-use-field-name-in-part-filename[`quarkus.openapi-generator.codegen.spec."spec-item".use-field-name-in-part-filename`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".use-field-name-in-part-filename+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines, whether the filename should also include the property name in case the `PartFilename` annotation (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) is generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__USE_FIELD_NAME_IN_PART_FILENAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__USE_FIELD_NAME_IN_PART_FILENAME+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-use-bean-validation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-use-bean-validation[`quarkus.openapi-generator.codegen.spec."spec-item".use-bean-validation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".use-bean-validation+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable bean validation. If you set this to `true`, validation annotations are added to generated sources E.g. `@Size`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__USE_BEAN_VALIDATION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__USE_BEAN_VALIDATION+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-generate-apis]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-generate-apis[`quarkus.openapi-generator.codegen.spec."spec-item".generate-apis`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".generate-apis+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of APIs. If you set this to `false`, APIs will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__GENERATE_APIS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__GENERATE_APIS+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-generate-models]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-generate-models[`quarkus.openapi-generator.codegen.spec."spec-item".generate-models`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".generate-models+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of models. If you set this to `false`, models will not be generated. -:summaryTableId: quarkus-openapi-generator -[.configuration-legend] -icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime -[.configuration-reference.searchable, cols="80,.^10,.^10"] -|=== -h|[[quarkus-openapi-generator_configuration]]link:#quarkus-openapi-generator_configuration[Configuration property] +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__GENERATE_MODELS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__GENERATE_MODELS+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-equals-hashcode]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-equals-hashcode[`quarkus.openapi-generator.codegen.spec."spec-item".equals-hashcode`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".equals-hashcode+++[] +endif::add-copy-button-to-config-props[] -h|Type -h|Default -a| [[quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-token-propagation]]`link:#quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-token-propagation[quarkus.openapi-generator."item-configs".auth."auth-configs".token-propagation]` +[.description] +-- +Enable the generation of equals and hashcode in models. If you set this to `false`, the models will not have equals and hashcode. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__EQUALS_HASHCODE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__EQUALS_HASHCODE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-properties-as-attribute]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-properties-as-attribute[`quarkus.openapi-generator.codegen.spec."spec-item".additional-properties-as-attribute`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".additional-properties-as-attribute+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Add additional properties as attribute. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_PROPERTIES_AS_ATTRIBUTE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_PROPERTIES_AS_ATTRIBUTE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-initialize-empty-collections]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-initialize-empty-collections[`quarkus.openapi-generator.codegen.spec."spec-item".initialize-empty-collections`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".initialize-empty-collections+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Initialise collections as empty instead of null + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__INITIALIZE_EMPTY_COLLECTIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__INITIALIZE_EMPTY_COLLECTIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-base-package]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-base-package[`quarkus.openapi-generator.codegen.spec."spec-item".base-package`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".base-package+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Base package for where the generated code for the given OpenAPI specification will be added. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__BASE_PACKAGE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__BASE_PACKAGE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-config-key]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-config-key[`quarkus.openapi-generator.codegen.spec."spec-item".config-key`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".config-key+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Custom config key to use in place of the openapi spec file + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__CONFIG_KEY+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__CONFIG_KEY+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-api-name-suffix]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-api-name-suffix[`quarkus.openapi-generator.codegen.spec."spec-item".api-name-suffix`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".api-name-suffix+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Suffix name for generated api classes + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__API_NAME_SUFFIX+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__API_NAME_SUFFIX+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-model-name-suffix]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-model-name-suffix[`quarkus.openapi-generator.codegen.spec."spec-item".model-name-suffix`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".model-name-suffix+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Suffix name for generated model classes + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MODEL_NAME_SUFFIX+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MODEL_NAME_SUFFIX+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-model-name-prefix]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-model-name-prefix[`quarkus.openapi-generator.codegen.spec."spec-item".model-name-prefix`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".model-name-prefix+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Prefix name for generated model classes + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MODEL_NAME_PREFIX+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MODEL_NAME_PREFIX+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-remove-operation-id-prefix]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-remove-operation-id-prefix[`quarkus.openapi-generator.codegen.spec."spec-item".remove-operation-id-prefix`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".remove-operation-id-prefix+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Remove operation id prefix + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-remove-operation-id-prefix-delimiter]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-remove-operation-id-prefix-delimiter[`quarkus.openapi-generator.codegen.spec."spec-item".remove-operation-id-prefix-delimiter`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".remove-operation-id-prefix-delimiter+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Remove operation id prefix + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX_DELIMITER+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX_DELIMITER+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-remove-operation-id-prefix-count]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-remove-operation-id-prefix-count[`quarkus.openapi-generator.codegen.spec."spec-item".remove-operation-id-prefix-count`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".remove-operation-id-prefix-count+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Remove operation id prefix + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX_COUNT+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX_COUNT+++` +endif::add-copy-button-to-env-var[] +-- +|int +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-serializable-model]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-serializable-model[`quarkus.openapi-generator.codegen.spec."spec-item".serializable-model`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".serializable-model+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Set serializable model + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__SERIALIZABLE_MODEL+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__SERIALIZABLE_MODEL+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-use-dynamic-url]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-use-dynamic-url[`quarkus.openapi-generator.codegen.spec."spec-item".use-dynamic-url`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".use-dynamic-url+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether to enable Dynamic URLs on APIs methods. By enabling this property every method on `RestClients` will be annotated with `io.quarkus.rest.client.reactive.Url`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__USE_DYNAMIC_URL+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__USE_DYNAMIC_URL+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +|`false` + +a| [[quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-token-propagation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-token-propagation[`quarkus.openapi-generator."item-configs".auth."auth-configs".token-propagation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator."item-configs".auth."auth-configs".token-propagation+++[] +endif::add-copy-button-to-config-props[] [.description] @@ -21,17 +1356,21 @@ For example, given a file named petstore.json with a securityScheme named "petst quarkus.openapi-generator.petstore_json.auth.petstore_auth.token-propagation=true + ifdef::add-copy-button-to-env-var[] Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__TOKEN_PROPAGATION+++[] endif::add-copy-button-to-env-var[] ifndef::add-copy-button-to-env-var[] Environment variable: `+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__TOKEN_PROPAGATION+++` endif::add-copy-button-to-env-var[] ---|boolean -|`false` - +-- +|boolean +| -a| [[quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-header-name]]`link:#quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-header-name[quarkus.openapi-generator."item-configs".auth."auth-configs".header-name]` +a| [[quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-header-name]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-header-name[`quarkus.openapi-generator."item-configs".auth."auth-configs".header-name`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator."item-configs".auth."auth-configs".header-name+++[] +endif::add-copy-button-to-config-props[] [.description] @@ -42,35 +1381,153 @@ For example, given a file named petstore.json with a securityScheme named "petst quarkus.openapi-generator.petstore_json.auth.petstore_auth.header-name=MyParticularHttpHeaderName + ifdef::add-copy-button-to-env-var[] Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__HEADER_NAME+++[] endif::add-copy-button-to-env-var[] ifndef::add-copy-button-to-env-var[] Environment variable: `+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__HEADER_NAME+++` endif::add-copy-button-to-env-var[] ---|string +-- +|string +| + +a| [[quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-username]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-username[`quarkus.openapi-generator."item-configs".auth."auth-configs".username`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator."item-configs".auth."auth-configs".username+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Sets the Basic Authentication username for a given OpenAPI securityScheme. + +For example, given a file named petstore.json with a securityScheme named "petstore-basic-auth", that is of http basic authentication type, the following configuration can establish the user to be used. + +quarkus.openapi-generator.petstore_json.auth.petstore_basic_auth.username=MyUserName + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__USERNAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__USERNAME+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a| [[quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-password]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-password[`quarkus.openapi-generator."item-configs".auth."auth-configs".password`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator."item-configs".auth."auth-configs".password+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Sets the Basic Authentication password for a given OpenAPI securityScheme. + +For example, given a file named petstore.json with a securityScheme named "petstore-basic-auth", that is of http basic authentication type, the following configuration can establish the password to be used. + +quarkus.openapi-generator.petstore_json.auth.petstore_basic_auth.password=MyPassword + +Ignored if the given securityScheme is not Basic Authentication + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__PASSWORD+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__PASSWORD+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a| [[quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-bearer-token]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-bearer-token[`quarkus.openapi-generator."item-configs".auth."auth-configs".bearer-token`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator."item-configs".auth."auth-configs".bearer-token+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Sets the Bearer Token for a given OpenAPI securityScheme. + +For example, given a file named petstore.json with a securityScheme named "petstore-bearer-auth", that is of bearer authentication type, the following configuration can establish the token to be used. + +quarkus.openapi-generator.petstore_json.auth.petstore_bearer_auth.token=1234567890 + +Ignored if the given securityScheme is not Bearer Token Authentication + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__BEARER_TOKEN+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__BEARER_TOKEN+++` +endif::add-copy-button-to-env-var[] +-- +|string | +a| [[quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-api-key]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-api-key[`quarkus.openapi-generator."item-configs".auth."auth-configs".api-key`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator."item-configs".auth."auth-configs".api-key+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Sets the API Key for a given OpenAPI securityScheme. + +For example, given a file named petstore.json with a securityScheme named "petstore-apikey-auth", that is of API Key authentication type, the following configuration can establish the API Key to be used. + +quarkus.openapi-generator.petstore_json.auth.petstore_apikey_auth.api-key=$++{++MY_SECRET_KEY_IN_AN_ENV_VAR++}++ -a| [[quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-auth-config-params]]`link:#quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-auth-config-params[quarkus.openapi-generator."item-configs".auth."auth-configs"."auth-config-params"]` +Ignored if the given securityScheme is not API Key Authentication + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__API_KEY+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__API_KEY+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a| [[quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-use-authorization-header-value]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-use-authorization-header-value[`quarkus.openapi-generator."item-configs".auth."auth-configs".use-authorization-header-value`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator."item-configs".auth."auth-configs".use-authorization-header-value+++[] +endif::add-copy-button-to-config-props[] [.description] -- -Configures a particular parameter value to be used by any of the different internal authentication filters that processes the different securityScheme definitions. +Only valid for API Key Authentication. + +When to add the `Authorization` value to the API Key in the authentication header. + +For example, if this property is set to `true`, the API Key will be sent to the server in the header along with `Authorization`: + +++[++source++]++ --- Authentication: Authorization MY-API-KEY --- -For example, given a file named petstore.json with a securityScheme named "petstore-basic-auth", that is of http basic authentication type, the following configuration can establish the user and password to be used. must be used. +If set to `false`, the header should be: + +++[++source++]++ --- Authentication: MY-API-KEY --- -quarkus.openapi-generator.petstore_json.auth.petstore_basic_auth.username=MyUserName quarkus.openapi-generator.petstore_json.auth.petstore_basic_auth.password=MyPassword ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS___AUTH_CONFIG_PARAMS_+++[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__USE_AUTHORIZATION_HEADER_VALUE+++[] endif::add-copy-button-to-env-var[] ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS___AUTH_CONFIG_PARAMS_+++` +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__USE_AUTHORIZATION_HEADER_VALUE+++` endif::add-copy-button-to-env-var[] ---|link:https://docs.oracle.com/javase/8/docs/api/java/lang/String.html[String] - +-- +|boolean | -|=== \ No newline at end of file +|=== + diff --git a/docs/modules/ROOT/pages/includes/quarkus-openapi-generator_quarkus.adoc b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator_quarkus.adoc new file mode 100644 index 000000000..73cce0732 --- /dev/null +++ b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator_quarkus.adoc @@ -0,0 +1,1199 @@ +[.configuration-legend] +icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime +[.configuration-reference.searchable, cols="80,.^10,.^10"] +|=== + +h|[.header-title]##Configuration property## +h|Type +h|Default + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-skip-form-model]] [.property-path]##link:#quarkus-openapi-generator_quarkus-skip-form-model[`quarkus.skip-form-model`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.skip-form-model+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether to skip the generation of models for form parameters + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SKIP_FORM_MODEL+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SKIP_FORM_MODEL+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-type-mappings-type-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-type-mappings-type-mappings[`quarkus.type-mappings."type-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.type-mappings."type-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +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) + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_TYPE_MAPPINGS__TYPE_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_TYPE_MAPPINGS__TYPE_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-import-mappings-import-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-import-mappings-import-mappings[`quarkus.import-mappings."import-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.import-mappings."import-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +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 + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_IMPORT_MAPPINGS__IMPORT_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_IMPORT_MAPPINGS__IMPORT_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-schema-mappings-schema-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-schema-mappings-schema-mappings[`quarkus.schema-mappings."schema-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.schema-mappings."schema-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Schema Mapping is an OpenAPI Generator configuration specifying which Java types (the values) should be imported when a given schema type (the keys of this map) is used + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SCHEMA_MAPPINGS__SCHEMA_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SCHEMA_MAPPINGS__SCHEMA_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-additional-model-type-annotations]] [.property-path]##link:#quarkus-openapi-generator_quarkus-additional-model-type-annotations[`quarkus.additional-model-type-annotations`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.additional-model-type-annotations+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The specified annotations will be added to the generated model files + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_ADDITIONAL_MODEL_TYPE_ANNOTATIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_ADDITIONAL_MODEL_TYPE_ANNOTATIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-additional-enum-type-unexpected-member]] [.property-path]##link:#quarkus-openapi-generator_quarkus-additional-enum-type-unexpected-member[`quarkus.additional-enum-type-unexpected-member`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.additional-enum-type-unexpected-member+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if the enums should have an `UNEXPECTED` member to convey values that cannot be parsed. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-additional-api-type-annotations]] [.property-path]##link:#quarkus-openapi-generator_quarkus-additional-api-type-annotations[`quarkus.additional-api-type-annotations`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.additional-api-type-annotations+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The specified annotations will be added to the generated api files + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_ADDITIONAL_API_TYPE_ANNOTATIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_ADDITIONAL_API_TYPE_ANNOTATIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-additional-request-args]] [.property-path]##link:#quarkus-openapi-generator_quarkus-additional-request-args[`quarkus.additional-request-args`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.additional-request-args+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Add custom/additional HTTP Headers or other args to every request + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_ADDITIONAL_REQUEST_ARGS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_ADDITIONAL_REQUEST_ARGS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-return-response]] [.property-path]##link:#quarkus-openapi-generator_quarkus-return-response[`quarkus.return-response`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.return-response+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if the methods should return `jakarta.ws.rs.core.Response` or a model. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_RETURN_RESPONSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_RETURN_RESPONSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-enable-security-generation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-enable-security-generation[`quarkus.enable-security-generation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.enable-security-generation+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if security support classes should be generated + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_ENABLE_SECURITY_GENERATION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_ENABLE_SECURITY_GENERATION+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-open-api-normalizer-normalizer]] [.property-path]##link:#quarkus-openapi-generator_quarkus-open-api-normalizer-normalizer[`quarkus.open-api-normalizer."normalizer"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.open-api-normalizer."normalizer"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines the normalizer options. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPEN_API_NORMALIZER__NORMALIZER_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPEN_API_NORMALIZER__NORMALIZER_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-mutiny]] [.property-path]##link:#quarkus-openapi-generator_quarkus-mutiny[`quarkus.mutiny`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.mutiny+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable SmallRye Mutiny support. If you set this to `true`, all return types will be wrapped in `io.smallrye.mutiny.Uni`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_MUTINY+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_MUTINY+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-mutiny-return-response]] [.property-path]##link:#quarkus-openapi-generator_quarkus-mutiny-return-response[`quarkus.mutiny.return-response`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.mutiny.return-response+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines with SmallRye Mutiny enabled if methods should return `jakarta.ws.rs.core.Response` or a model. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_MUTINY_RETURN_RESPONSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_MUTINY_RETURN_RESPONSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-mutiny-operation-ids-mutiny-multi-operation-ids]] [.property-path]##link:#quarkus-openapi-generator_quarkus-mutiny-operation-ids-mutiny-multi-operation-ids[`quarkus.mutiny.operation-ids."mutiny-multi-operation-ids"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.mutiny.operation-ids."mutiny-multi-operation-ids"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Handles the return type for each operation, depending on the configuration. The following cases are supported: + +1. If `mutiny` is enabled and the operation ID is specified to return `Multi`: - The return type will be wrapped in `io.smallrye.mutiny.Multi`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Multi`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Multi`. - Otherwise, it will return `io.smallrye.mutiny.Multi`. + +2. If `mutiny` is enabled and the operation ID is specified to return `Uni`: - The return type will be wrapped in `io.smallrye.mutiny.Uni`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Uni`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Uni`. - Otherwise, it will return `io.smallrye.mutiny.Uni`. + +3. If `mutiny` is enabled but no specific operation ID is configured for `Multi` or `Uni`: - The return type defaults to `Uni`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Uni`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Uni`. - Otherwise, it will return `io.smallrye.mutiny.Uni``. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_MUTINY_OPERATION_IDS__MUTINY_MULTI_OPERATION_IDS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_MUTINY_OPERATION_IDS__MUTINY_MULTI_OPERATION_IDS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-generate-part-filename]] [.property-path]##link:#quarkus-openapi-generator_quarkus-generate-part-filename[`quarkus.generate-part-filename`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.generate-part-filename+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines, whether the `PartFilename` (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) annotation should be generated for MultipartForm POJOs. By setting to `false`, the annotation will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_GENERATE_PART_FILENAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_GENERATE_PART_FILENAME+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-part-filename-value]] [.property-path]##link:#quarkus-openapi-generator_quarkus-part-filename-value[`quarkus.part-filename-value`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.part-filename-value+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines the filename for a part in case the `PartFilename` annotation (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) is generated. In case no value is set, the default one is `File` or `file`, depending on the `CommonItemConfig++#++useFieldNameInPartFilename` configuration. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_PART_FILENAME_VALUE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_PART_FILENAME_VALUE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-use-field-name-in-part-filename]] [.property-path]##link:#quarkus-openapi-generator_quarkus-use-field-name-in-part-filename[`quarkus.use-field-name-in-part-filename`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.use-field-name-in-part-filename+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines, whether the filename should also include the property name in case the `PartFilename` annotation (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) is generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_USE_FIELD_NAME_IN_PART_FILENAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_USE_FIELD_NAME_IN_PART_FILENAME+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-use-bean-validation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-use-bean-validation[`quarkus.use-bean-validation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.use-bean-validation+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable bean validation. If you set this to `true`, validation annotations are added to generated sources E.g. `@Size`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_USE_BEAN_VALIDATION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_USE_BEAN_VALIDATION+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-generate-apis]] [.property-path]##link:#quarkus-openapi-generator_quarkus-generate-apis[`quarkus.generate-apis`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.generate-apis+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of APIs. If you set this to `false`, APIs will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_GENERATE_APIS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_GENERATE_APIS+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-generate-models]] [.property-path]##link:#quarkus-openapi-generator_quarkus-generate-models[`quarkus.generate-models`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.generate-models+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of models. If you set this to `false`, models will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_GENERATE_MODELS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_GENERATE_MODELS+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-equals-hashcode]] [.property-path]##link:#quarkus-openapi-generator_quarkus-equals-hashcode[`quarkus.equals-hashcode`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.equals-hashcode+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of equals and hashcode in models. If you set this to `false`, the models will not have equals and hashcode. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_EQUALS_HASHCODE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_EQUALS_HASHCODE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-verbose]] [.property-path]##link:#quarkus-openapi-generator_quarkus-verbose[`quarkus.verbose`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.verbose+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether to log the internal generator codegen process in the default output or not. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_VERBOSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_VERBOSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +|`false` + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-input-base-dir]] [.property-path]##link:#quarkus-openapi-generator_quarkus-input-base-dir[`quarkus.input-base-dir`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.input-base-dir+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Option to change the directory where OpenAPI files must be found. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_INPUT_BASE_DIR+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_INPUT_BASE_DIR+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-template-base-dir]] [.property-path]##link:#quarkus-openapi-generator_quarkus-template-base-dir[`quarkus.template-base-dir`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.template-base-dir+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Option to change the directory where template files must be found. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_TEMPLATE_BASE_DIR+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_TEMPLATE_BASE_DIR+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-validatespec]] [.property-path]##link:#quarkus-openapi-generator_quarkus-validatespec[`quarkus.validateSpec`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.validateSpec+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether or not to skip validating the input spec prior to generation. By default, invalid specifications will result in an error. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_VALIDATESPEC+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_VALIDATESPEC+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +|`true` + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-include]] [.property-path]##link:#quarkus-openapi-generator_quarkus-include[`quarkus.include`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.include+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Option to specify files for which generation should be executed only + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_INCLUDE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_INCLUDE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-exclude]] [.property-path]##link:#quarkus-openapi-generator_quarkus-exclude[`quarkus.exclude`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.exclude+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Option to exclude file from generation + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_EXCLUDE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_EXCLUDE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-default-security-scheme]] [.property-path]##link:#quarkus-openapi-generator_quarkus-default-security-scheme[`quarkus.default-security-scheme`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.default-security-scheme+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Create security for the referenced security scheme + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_DEFAULT_SECURITY_SCHEME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_DEFAULT_SECURITY_SCHEME+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-skip-form-model]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-skip-form-model[`quarkus.spec."spec-item".skip-form-model`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".skip-form-model+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether to skip the generation of models for form parameters + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__SKIP_FORM_MODEL+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__SKIP_FORM_MODEL+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-type-mappings-type-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-type-mappings-type-mappings[`quarkus.spec."spec-item".type-mappings."type-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".type-mappings."type-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +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) + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__TYPE_MAPPINGS__TYPE_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__TYPE_MAPPINGS__TYPE_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-import-mappings-import-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-import-mappings-import-mappings[`quarkus.spec."spec-item".import-mappings."import-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".import-mappings."import-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +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 + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__IMPORT_MAPPINGS__IMPORT_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__IMPORT_MAPPINGS__IMPORT_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-schema-mappings-schema-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-schema-mappings-schema-mappings[`quarkus.spec."spec-item".schema-mappings."schema-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".schema-mappings."schema-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Schema Mapping is an OpenAPI Generator configuration specifying which Java types (the values) should be imported when a given schema type (the keys of this map) is used + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__SCHEMA_MAPPINGS__SCHEMA_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__SCHEMA_MAPPINGS__SCHEMA_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-additional-model-type-annotations]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-additional-model-type-annotations[`quarkus.spec."spec-item".additional-model-type-annotations`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".additional-model-type-annotations+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The specified annotations will be added to the generated model files + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__ADDITIONAL_MODEL_TYPE_ANNOTATIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__ADDITIONAL_MODEL_TYPE_ANNOTATIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-additional-enum-type-unexpected-member]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-additional-enum-type-unexpected-member[`quarkus.spec."spec-item".additional-enum-type-unexpected-member`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".additional-enum-type-unexpected-member+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if the enums should have an `UNEXPECTED` member to convey values that cannot be parsed. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-additional-api-type-annotations]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-additional-api-type-annotations[`quarkus.spec."spec-item".additional-api-type-annotations`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".additional-api-type-annotations+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The specified annotations will be added to the generated api files + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__ADDITIONAL_API_TYPE_ANNOTATIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__ADDITIONAL_API_TYPE_ANNOTATIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-additional-request-args]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-additional-request-args[`quarkus.spec."spec-item".additional-request-args`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".additional-request-args+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Add custom/additional HTTP Headers or other args to every request + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__ADDITIONAL_REQUEST_ARGS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__ADDITIONAL_REQUEST_ARGS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-return-response]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-return-response[`quarkus.spec."spec-item".return-response`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".return-response+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if the methods should return `jakarta.ws.rs.core.Response` or a model. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__RETURN_RESPONSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__RETURN_RESPONSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-enable-security-generation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-enable-security-generation[`quarkus.spec."spec-item".enable-security-generation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".enable-security-generation+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if security support classes should be generated + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__ENABLE_SECURITY_GENERATION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__ENABLE_SECURITY_GENERATION+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-open-api-normalizer-normalizer]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-open-api-normalizer-normalizer[`quarkus.spec."spec-item".open-api-normalizer."normalizer"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".open-api-normalizer."normalizer"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines the normalizer options. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__OPEN_API_NORMALIZER__NORMALIZER_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__OPEN_API_NORMALIZER__NORMALIZER_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-mutiny]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-mutiny[`quarkus.spec."spec-item".mutiny`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".mutiny+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable SmallRye Mutiny support. If you set this to `true`, all return types will be wrapped in `io.smallrye.mutiny.Uni`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__MUTINY+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__MUTINY+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-mutiny-return-response]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-mutiny-return-response[`quarkus.spec."spec-item".mutiny.return-response`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".mutiny.return-response+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines with SmallRye Mutiny enabled if methods should return `jakarta.ws.rs.core.Response` or a model. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__MUTINY_RETURN_RESPONSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__MUTINY_RETURN_RESPONSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-mutiny-operation-ids-mutiny-multi-operation-ids]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-mutiny-operation-ids-mutiny-multi-operation-ids[`quarkus.spec."spec-item".mutiny.operation-ids."mutiny-multi-operation-ids"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".mutiny.operation-ids."mutiny-multi-operation-ids"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Handles the return type for each operation, depending on the configuration. The following cases are supported: + +1. If `mutiny` is enabled and the operation ID is specified to return `Multi`: - The return type will be wrapped in `io.smallrye.mutiny.Multi`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Multi`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Multi`. - Otherwise, it will return `io.smallrye.mutiny.Multi`. + +2. If `mutiny` is enabled and the operation ID is specified to return `Uni`: - The return type will be wrapped in `io.smallrye.mutiny.Uni`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Uni`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Uni`. - Otherwise, it will return `io.smallrye.mutiny.Uni`. + +3. If `mutiny` is enabled but no specific operation ID is configured for `Multi` or `Uni`: - The return type defaults to `Uni`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Uni`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Uni`. - Otherwise, it will return `io.smallrye.mutiny.Uni``. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__MUTINY_OPERATION_IDS__MUTINY_MULTI_OPERATION_IDS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__MUTINY_OPERATION_IDS__MUTINY_MULTI_OPERATION_IDS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-generate-part-filename]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-generate-part-filename[`quarkus.spec."spec-item".generate-part-filename`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".generate-part-filename+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines, whether the `PartFilename` (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) annotation should be generated for MultipartForm POJOs. By setting to `false`, the annotation will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__GENERATE_PART_FILENAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__GENERATE_PART_FILENAME+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-part-filename-value]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-part-filename-value[`quarkus.spec."spec-item".part-filename-value`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".part-filename-value+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines the filename for a part in case the `PartFilename` annotation (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) is generated. In case no value is set, the default one is `File` or `file`, depending on the `CommonItemConfig++#++useFieldNameInPartFilename` configuration. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__PART_FILENAME_VALUE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__PART_FILENAME_VALUE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-use-field-name-in-part-filename]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-use-field-name-in-part-filename[`quarkus.spec."spec-item".use-field-name-in-part-filename`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".use-field-name-in-part-filename+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines, whether the filename should also include the property name in case the `PartFilename` annotation (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) is generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__USE_FIELD_NAME_IN_PART_FILENAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__USE_FIELD_NAME_IN_PART_FILENAME+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-use-bean-validation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-use-bean-validation[`quarkus.spec."spec-item".use-bean-validation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".use-bean-validation+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable bean validation. If you set this to `true`, validation annotations are added to generated sources E.g. `@Size`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__USE_BEAN_VALIDATION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__USE_BEAN_VALIDATION+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-generate-apis]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-generate-apis[`quarkus.spec."spec-item".generate-apis`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".generate-apis+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of APIs. If you set this to `false`, APIs will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__GENERATE_APIS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__GENERATE_APIS+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-generate-models]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-generate-models[`quarkus.spec."spec-item".generate-models`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".generate-models+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of models. If you set this to `false`, models will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__GENERATE_MODELS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__GENERATE_MODELS+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-equals-hashcode]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-equals-hashcode[`quarkus.spec."spec-item".equals-hashcode`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".equals-hashcode+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of equals and hashcode in models. If you set this to `false`, the models will not have equals and hashcode. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__EQUALS_HASHCODE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__EQUALS_HASHCODE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-base-package]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-base-package[`quarkus.spec."spec-item".base-package`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".base-package+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Base package for where the generated code for the given OpenAPI specification will be added. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__BASE_PACKAGE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__BASE_PACKAGE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-api-name-suffix]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-api-name-suffix[`quarkus.spec."spec-item".api-name-suffix`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".api-name-suffix+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Suffix name for generated api classes + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__API_NAME_SUFFIX+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__API_NAME_SUFFIX+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-model-name-suffix]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-model-name-suffix[`quarkus.spec."spec-item".model-name-suffix`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".model-name-suffix+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Suffix name for generated model classes + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__MODEL_NAME_SUFFIX+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__MODEL_NAME_SUFFIX+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-model-name-prefix]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-model-name-prefix[`quarkus.spec."spec-item".model-name-prefix`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".model-name-prefix+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Prefix name for generated model classes + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__MODEL_NAME_PREFIX+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__MODEL_NAME_PREFIX+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-remove-operation-id-prefix]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-remove-operation-id-prefix[`quarkus.spec."spec-item".remove-operation-id-prefix`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".remove-operation-id-prefix+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Remove operation id prefix + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-remove-operation-id-prefix-delimiter]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-remove-operation-id-prefix-delimiter[`quarkus.spec."spec-item".remove-operation-id-prefix-delimiter`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".remove-operation-id-prefix-delimiter+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Remove operation id prefix + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX_DELIMITER+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX_DELIMITER+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-remove-operation-id-prefix-count]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-remove-operation-id-prefix-count[`quarkus.spec."spec-item".remove-operation-id-prefix-count`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".remove-operation-id-prefix-count+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Remove operation id prefix + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX_COUNT+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX_COUNT+++` +endif::add-copy-button-to-env-var[] +-- +|int +| + +|=== + diff --git a/docs/modules/ROOT/pages/includes/quarkus-openapi-generator_quarkus.openapi-generator.adoc b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator_quarkus.openapi-generator.adoc new file mode 100644 index 000000000..41d33043d --- /dev/null +++ b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator_quarkus.openapi-generator.adoc @@ -0,0 +1,1533 @@ +[.configuration-legend] +icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime +[.configuration-reference.searchable, cols="80,.^10,.^10"] +|=== + +h|[.header-title]##Configuration property## +h|Type +h|Default + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-skip-form-model]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-skip-form-model[`quarkus.openapi-generator.codegen.skip-form-model`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.skip-form-model+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether to skip the generation of models for form parameters + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SKIP_FORM_MODEL+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SKIP_FORM_MODEL+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-type-mappings-type-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-type-mappings-type-mappings[`quarkus.openapi-generator.codegen.type-mappings."type-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.type-mappings."type-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +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) + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_TYPE_MAPPINGS__TYPE_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_TYPE_MAPPINGS__TYPE_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-import-mappings-import-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-import-mappings-import-mappings[`quarkus.openapi-generator.codegen.import-mappings."import-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.import-mappings."import-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +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 + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_IMPORT_MAPPINGS__IMPORT_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_IMPORT_MAPPINGS__IMPORT_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-schema-mappings-schema-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-schema-mappings-schema-mappings[`quarkus.openapi-generator.codegen.schema-mappings."schema-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.schema-mappings."schema-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Schema Mapping is an OpenAPI Generator configuration specifying which Java types (the values) should be imported when a given schema type (the keys of this map) is used + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SCHEMA_MAPPINGS__SCHEMA_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SCHEMA_MAPPINGS__SCHEMA_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-model-type-annotations]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-model-type-annotations[`quarkus.openapi-generator.codegen.additional-model-type-annotations`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.additional-model-type-annotations+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The specified annotations will be added to the generated model files + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_MODEL_TYPE_ANNOTATIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_MODEL_TYPE_ANNOTATIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-enum-type-unexpected-member]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-enum-type-unexpected-member[`quarkus.openapi-generator.codegen.additional-enum-type-unexpected-member`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.additional-enum-type-unexpected-member+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if the enums should have an `UNEXPECTED` member to convey values that cannot be parsed. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-api-type-annotations]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-api-type-annotations[`quarkus.openapi-generator.codegen.additional-api-type-annotations`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.additional-api-type-annotations+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The specified annotations will be added to the generated api files + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_API_TYPE_ANNOTATIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_API_TYPE_ANNOTATIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-request-args]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-request-args[`quarkus.openapi-generator.codegen.additional-request-args`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.additional-request-args+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Add custom/additional HTTP Headers or other args to every request + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_REQUEST_ARGS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_REQUEST_ARGS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-return-response]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-return-response[`quarkus.openapi-generator.codegen.return-response`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.return-response+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if the methods should return `jakarta.ws.rs.core.Response` or a model. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_RETURN_RESPONSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_RETURN_RESPONSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-enable-security-generation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-enable-security-generation[`quarkus.openapi-generator.codegen.enable-security-generation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.enable-security-generation+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if security support classes should be generated + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ENABLE_SECURITY_GENERATION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ENABLE_SECURITY_GENERATION+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-open-api-normalizer-normalizer]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-open-api-normalizer-normalizer[`quarkus.openapi-generator.codegen.open-api-normalizer."normalizer"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.open-api-normalizer."normalizer"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines the normalizer options. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_OPEN_API_NORMALIZER__NORMALIZER_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_OPEN_API_NORMALIZER__NORMALIZER_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-mutiny]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-mutiny[`quarkus.openapi-generator.codegen.mutiny`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.mutiny+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable SmallRye Mutiny support. If you set this to `true`, all return types will be wrapped in `io.smallrye.mutiny.Uni`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_MUTINY+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_MUTINY+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-mutiny-return-response]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-mutiny-return-response[`quarkus.openapi-generator.codegen.mutiny.return-response`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.mutiny.return-response+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines with SmallRye Mutiny enabled if methods should return `jakarta.ws.rs.core.Response` or a model. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_MUTINY_RETURN_RESPONSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_MUTINY_RETURN_RESPONSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-mutiny-operation-ids-mutiny-multi-operation-ids]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-mutiny-operation-ids-mutiny-multi-operation-ids[`quarkus.openapi-generator.codegen.mutiny.operation-ids."mutiny-multi-operation-ids"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.mutiny.operation-ids."mutiny-multi-operation-ids"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Handles the return type for each operation, depending on the configuration. The following cases are supported: + +1. If `mutiny` is enabled and the operation ID is specified to return `Multi`: - The return type will be wrapped in `io.smallrye.mutiny.Multi`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Multi`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Multi`. - Otherwise, it will return `io.smallrye.mutiny.Multi`. + +2. If `mutiny` is enabled and the operation ID is specified to return `Uni`: - The return type will be wrapped in `io.smallrye.mutiny.Uni`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Uni`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Uni`. - Otherwise, it will return `io.smallrye.mutiny.Uni`. + +3. If `mutiny` is enabled but no specific operation ID is configured for `Multi` or `Uni`: - The return type defaults to `Uni`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Uni`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Uni`. - Otherwise, it will return `io.smallrye.mutiny.Uni``. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_MUTINY_OPERATION_IDS__MUTINY_MULTI_OPERATION_IDS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_MUTINY_OPERATION_IDS__MUTINY_MULTI_OPERATION_IDS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-generate-part-filename]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-generate-part-filename[`quarkus.openapi-generator.codegen.generate-part-filename`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.generate-part-filename+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines, whether the `PartFilename` (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) annotation should be generated for MultipartForm POJOs. By setting to `false`, the annotation will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_GENERATE_PART_FILENAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_GENERATE_PART_FILENAME+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-part-filename-value]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-part-filename-value[`quarkus.openapi-generator.codegen.part-filename-value`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.part-filename-value+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines the filename for a part in case the `PartFilename` annotation (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) is generated. In case no value is set, the default one is `File` or `file`, depending on the `CommonItemConfig++#++useFieldNameInPartFilename` configuration. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_PART_FILENAME_VALUE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_PART_FILENAME_VALUE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-use-field-name-in-part-filename]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-use-field-name-in-part-filename[`quarkus.openapi-generator.codegen.use-field-name-in-part-filename`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.use-field-name-in-part-filename+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines, whether the filename should also include the property name in case the `PartFilename` annotation (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) is generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_USE_FIELD_NAME_IN_PART_FILENAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_USE_FIELD_NAME_IN_PART_FILENAME+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-use-bean-validation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-use-bean-validation[`quarkus.openapi-generator.codegen.use-bean-validation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.use-bean-validation+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable bean validation. If you set this to `true`, validation annotations are added to generated sources E.g. `@Size`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_USE_BEAN_VALIDATION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_USE_BEAN_VALIDATION+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-generate-apis]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-generate-apis[`quarkus.openapi-generator.codegen.generate-apis`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.generate-apis+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of APIs. If you set this to `false`, APIs will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_GENERATE_APIS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_GENERATE_APIS+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-generate-models]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-generate-models[`quarkus.openapi-generator.codegen.generate-models`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.generate-models+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of models. If you set this to `false`, models will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_GENERATE_MODELS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_GENERATE_MODELS+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-equals-hashcode]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-equals-hashcode[`quarkus.openapi-generator.codegen.equals-hashcode`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.equals-hashcode+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of equals and hashcode in models. If you set this to `false`, the models will not have equals and hashcode. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_EQUALS_HASHCODE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_EQUALS_HASHCODE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-properties-as-attribute]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-additional-properties-as-attribute[`quarkus.openapi-generator.codegen.additional-properties-as-attribute`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.additional-properties-as-attribute+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Add additional properties as attribute. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_PROPERTIES_AS_ATTRIBUTE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_ADDITIONAL_PROPERTIES_AS_ATTRIBUTE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-initialize-empty-collections]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-initialize-empty-collections[`quarkus.openapi-generator.codegen.initialize-empty-collections`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.initialize-empty-collections+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Initialise collections as empty instead of null + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_INITIALIZE_EMPTY_COLLECTIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_INITIALIZE_EMPTY_COLLECTIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-verbose]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-verbose[`quarkus.openapi-generator.codegen.verbose`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.verbose+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether to log the internal generator codegen process in the default output or not. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_VERBOSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_VERBOSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +|`false` + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-input-base-dir]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-input-base-dir[`quarkus.openapi-generator.codegen.input-base-dir`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.input-base-dir+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Option to change the directory where OpenAPI files must be found. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_INPUT_BASE_DIR+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_INPUT_BASE_DIR+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-template-base-dir]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-template-base-dir[`quarkus.openapi-generator.codegen.template-base-dir`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.template-base-dir+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Option to change the directory where template files must be found. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_TEMPLATE_BASE_DIR+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_TEMPLATE_BASE_DIR+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-validatespec]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-validatespec[`quarkus.openapi-generator.codegen.validateSpec`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.validateSpec+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether or not to skip validating the input spec prior to generation. By default, invalid specifications will result in an error. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_VALIDATESPEC+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_VALIDATESPEC+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +|`true` + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-include]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-include[`quarkus.openapi-generator.codegen.include`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.include+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Option to specify files for which generation should be executed only + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_INCLUDE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_INCLUDE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-exclude]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-exclude[`quarkus.openapi-generator.codegen.exclude`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.exclude+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Option to exclude file from generation + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_EXCLUDE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_EXCLUDE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-default-security-scheme]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-default-security-scheme[`quarkus.openapi-generator.codegen.default-security-scheme`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.default-security-scheme+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Create security for the referenced security scheme + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_DEFAULT_SECURITY_SCHEME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_DEFAULT_SECURITY_SCHEME+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-skip-form-model]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-skip-form-model[`quarkus.openapi-generator.codegen.spec."spec-item".skip-form-model`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".skip-form-model+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether to skip the generation of models for form parameters + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__SKIP_FORM_MODEL+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__SKIP_FORM_MODEL+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-type-mappings-type-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-type-mappings-type-mappings[`quarkus.openapi-generator.codegen.spec."spec-item".type-mappings."type-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".type-mappings."type-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +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) + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__TYPE_MAPPINGS__TYPE_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__TYPE_MAPPINGS__TYPE_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-import-mappings-import-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-import-mappings-import-mappings[`quarkus.openapi-generator.codegen.spec."spec-item".import-mappings."import-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".import-mappings."import-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +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 + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__IMPORT_MAPPINGS__IMPORT_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__IMPORT_MAPPINGS__IMPORT_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-schema-mappings-schema-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-schema-mappings-schema-mappings[`quarkus.openapi-generator.codegen.spec."spec-item".schema-mappings."schema-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".schema-mappings."schema-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Schema Mapping is an OpenAPI Generator configuration specifying which Java types (the values) should be imported when a given schema type (the keys of this map) is used + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__SCHEMA_MAPPINGS__SCHEMA_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__SCHEMA_MAPPINGS__SCHEMA_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-model-type-annotations]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-model-type-annotations[`quarkus.openapi-generator.codegen.spec."spec-item".additional-model-type-annotations`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".additional-model-type-annotations+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The specified annotations will be added to the generated model files + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_MODEL_TYPE_ANNOTATIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_MODEL_TYPE_ANNOTATIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-enum-type-unexpected-member]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-enum-type-unexpected-member[`quarkus.openapi-generator.codegen.spec."spec-item".additional-enum-type-unexpected-member`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".additional-enum-type-unexpected-member+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if the enums should have an `UNEXPECTED` member to convey values that cannot be parsed. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-api-type-annotations]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-api-type-annotations[`quarkus.openapi-generator.codegen.spec."spec-item".additional-api-type-annotations`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".additional-api-type-annotations+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The specified annotations will be added to the generated api files + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_API_TYPE_ANNOTATIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_API_TYPE_ANNOTATIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-request-args]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-request-args[`quarkus.openapi-generator.codegen.spec."spec-item".additional-request-args`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".additional-request-args+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Add custom/additional HTTP Headers or other args to every request + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_REQUEST_ARGS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_REQUEST_ARGS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-return-response]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-return-response[`quarkus.openapi-generator.codegen.spec."spec-item".return-response`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".return-response+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if the methods should return `jakarta.ws.rs.core.Response` or a model. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__RETURN_RESPONSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__RETURN_RESPONSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-enable-security-generation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-enable-security-generation[`quarkus.openapi-generator.codegen.spec."spec-item".enable-security-generation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".enable-security-generation+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if security support classes should be generated + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ENABLE_SECURITY_GENERATION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ENABLE_SECURITY_GENERATION+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-open-api-normalizer-normalizer]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-open-api-normalizer-normalizer[`quarkus.openapi-generator.codegen.spec."spec-item".open-api-normalizer."normalizer"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".open-api-normalizer."normalizer"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines the normalizer options. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__OPEN_API_NORMALIZER__NORMALIZER_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__OPEN_API_NORMALIZER__NORMALIZER_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-mutiny]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-mutiny[`quarkus.openapi-generator.codegen.spec."spec-item".mutiny`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".mutiny+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable SmallRye Mutiny support. If you set this to `true`, all return types will be wrapped in `io.smallrye.mutiny.Uni`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MUTINY+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MUTINY+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-mutiny-return-response]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-mutiny-return-response[`quarkus.openapi-generator.codegen.spec."spec-item".mutiny.return-response`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".mutiny.return-response+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines with SmallRye Mutiny enabled if methods should return `jakarta.ws.rs.core.Response` or a model. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MUTINY_RETURN_RESPONSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MUTINY_RETURN_RESPONSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-mutiny-operation-ids-mutiny-multi-operation-ids]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-mutiny-operation-ids-mutiny-multi-operation-ids[`quarkus.openapi-generator.codegen.spec."spec-item".mutiny.operation-ids."mutiny-multi-operation-ids"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".mutiny.operation-ids."mutiny-multi-operation-ids"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Handles the return type for each operation, depending on the configuration. The following cases are supported: + +1. If `mutiny` is enabled and the operation ID is specified to return `Multi`: - The return type will be wrapped in `io.smallrye.mutiny.Multi`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Multi`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Multi`. - Otherwise, it will return `io.smallrye.mutiny.Multi`. + +2. If `mutiny` is enabled and the operation ID is specified to return `Uni`: - The return type will be wrapped in `io.smallrye.mutiny.Uni`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Uni`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Uni`. - Otherwise, it will return `io.smallrye.mutiny.Uni`. + +3. If `mutiny` is enabled but no specific operation ID is configured for `Multi` or `Uni`: - The return type defaults to `Uni`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Uni`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Uni`. - Otherwise, it will return `io.smallrye.mutiny.Uni``. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MUTINY_OPERATION_IDS__MUTINY_MULTI_OPERATION_IDS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MUTINY_OPERATION_IDS__MUTINY_MULTI_OPERATION_IDS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-generate-part-filename]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-generate-part-filename[`quarkus.openapi-generator.codegen.spec."spec-item".generate-part-filename`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".generate-part-filename+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines, whether the `PartFilename` (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) annotation should be generated for MultipartForm POJOs. By setting to `false`, the annotation will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__GENERATE_PART_FILENAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__GENERATE_PART_FILENAME+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-part-filename-value]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-part-filename-value[`quarkus.openapi-generator.codegen.spec."spec-item".part-filename-value`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".part-filename-value+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines the filename for a part in case the `PartFilename` annotation (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) is generated. In case no value is set, the default one is `File` or `file`, depending on the `CommonItemConfig++#++useFieldNameInPartFilename` configuration. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__PART_FILENAME_VALUE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__PART_FILENAME_VALUE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-use-field-name-in-part-filename]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-use-field-name-in-part-filename[`quarkus.openapi-generator.codegen.spec."spec-item".use-field-name-in-part-filename`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".use-field-name-in-part-filename+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines, whether the filename should also include the property name in case the `PartFilename` annotation (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) is generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__USE_FIELD_NAME_IN_PART_FILENAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__USE_FIELD_NAME_IN_PART_FILENAME+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-use-bean-validation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-use-bean-validation[`quarkus.openapi-generator.codegen.spec."spec-item".use-bean-validation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".use-bean-validation+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable bean validation. If you set this to `true`, validation annotations are added to generated sources E.g. `@Size`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__USE_BEAN_VALIDATION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__USE_BEAN_VALIDATION+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-generate-apis]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-generate-apis[`quarkus.openapi-generator.codegen.spec."spec-item".generate-apis`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".generate-apis+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of APIs. If you set this to `false`, APIs will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__GENERATE_APIS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__GENERATE_APIS+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-generate-models]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-generate-models[`quarkus.openapi-generator.codegen.spec."spec-item".generate-models`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".generate-models+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of models. If you set this to `false`, models will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__GENERATE_MODELS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__GENERATE_MODELS+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-equals-hashcode]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-equals-hashcode[`quarkus.openapi-generator.codegen.spec."spec-item".equals-hashcode`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".equals-hashcode+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of equals and hashcode in models. If you set this to `false`, the models will not have equals and hashcode. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__EQUALS_HASHCODE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__EQUALS_HASHCODE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-properties-as-attribute]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-additional-properties-as-attribute[`quarkus.openapi-generator.codegen.spec."spec-item".additional-properties-as-attribute`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".additional-properties-as-attribute+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Add additional properties as attribute. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_PROPERTIES_AS_ATTRIBUTE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__ADDITIONAL_PROPERTIES_AS_ATTRIBUTE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-initialize-empty-collections]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-initialize-empty-collections[`quarkus.openapi-generator.codegen.spec."spec-item".initialize-empty-collections`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".initialize-empty-collections+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Initialise collections as empty instead of null + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__INITIALIZE_EMPTY_COLLECTIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__INITIALIZE_EMPTY_COLLECTIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-base-package]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-base-package[`quarkus.openapi-generator.codegen.spec."spec-item".base-package`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".base-package+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Base package for where the generated code for the given OpenAPI specification will be added. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__BASE_PACKAGE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__BASE_PACKAGE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-config-key]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-config-key[`quarkus.openapi-generator.codegen.spec."spec-item".config-key`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".config-key+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Custom config key to use in place of the openapi spec file + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__CONFIG_KEY+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__CONFIG_KEY+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-api-name-suffix]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-api-name-suffix[`quarkus.openapi-generator.codegen.spec."spec-item".api-name-suffix`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".api-name-suffix+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Suffix name for generated api classes + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__API_NAME_SUFFIX+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__API_NAME_SUFFIX+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-model-name-suffix]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-model-name-suffix[`quarkus.openapi-generator.codegen.spec."spec-item".model-name-suffix`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".model-name-suffix+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Suffix name for generated model classes + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MODEL_NAME_SUFFIX+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MODEL_NAME_SUFFIX+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-model-name-prefix]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-model-name-prefix[`quarkus.openapi-generator.codegen.spec."spec-item".model-name-prefix`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".model-name-prefix+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Prefix name for generated model classes + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MODEL_NAME_PREFIX+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__MODEL_NAME_PREFIX+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-remove-operation-id-prefix]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-remove-operation-id-prefix[`quarkus.openapi-generator.codegen.spec."spec-item".remove-operation-id-prefix`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".remove-operation-id-prefix+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Remove operation id prefix + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-remove-operation-id-prefix-delimiter]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-remove-operation-id-prefix-delimiter[`quarkus.openapi-generator.codegen.spec."spec-item".remove-operation-id-prefix-delimiter`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".remove-operation-id-prefix-delimiter+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Remove operation id prefix + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX_DELIMITER+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX_DELIMITER+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-remove-operation-id-prefix-count]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-remove-operation-id-prefix-count[`quarkus.openapi-generator.codegen.spec."spec-item".remove-operation-id-prefix-count`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".remove-operation-id-prefix-count+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Remove operation id prefix + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX_COUNT+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX_COUNT+++` +endif::add-copy-button-to-env-var[] +-- +|int +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-serializable-model]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-serializable-model[`quarkus.openapi-generator.codegen.spec."spec-item".serializable-model`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".serializable-model+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Set serializable model + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__SERIALIZABLE_MODEL+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__SERIALIZABLE_MODEL+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-use-dynamic-url]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-codegen-spec-spec-item-use-dynamic-url[`quarkus.openapi-generator.codegen.spec."spec-item".use-dynamic-url`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.codegen.spec."spec-item".use-dynamic-url+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether to enable Dynamic URLs on APIs methods. By enabling this property every method on `RestClients` will be annotated with `io.quarkus.rest.client.reactive.Url`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__USE_DYNAMIC_URL+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_CODEGEN_SPEC__SPEC_ITEM__USE_DYNAMIC_URL+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +|`false` + +a| [[quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-token-propagation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-token-propagation[`quarkus.openapi-generator."item-configs".auth."auth-configs".token-propagation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator."item-configs".auth."auth-configs".token-propagation+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enables the authentication token propagation for this particular securityScheme. + +For example, given a file named petstore.json with a securityScheme named "petstore-auth" the following configuration must be used. + +quarkus.openapi-generator.petstore_json.auth.petstore_auth.token-propagation=true + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__TOKEN_PROPAGATION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__TOKEN_PROPAGATION+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a| [[quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-header-name]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-header-name[`quarkus.openapi-generator."item-configs".auth."auth-configs".header-name`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator."item-configs".auth."auth-configs".header-name+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Configures a particular http header attribute from were to take the security token from when the token propagation is enabled. Use this fine-grained configuration in very particular scenarios. + +For example, given a file named petstore.json with a securityScheme named "petstore-auth" the following configuration must be used. + +quarkus.openapi-generator.petstore_json.auth.petstore_auth.header-name=MyParticularHttpHeaderName + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__HEADER_NAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__HEADER_NAME+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a| [[quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-username]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-username[`quarkus.openapi-generator."item-configs".auth."auth-configs".username`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator."item-configs".auth."auth-configs".username+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Sets the Basic Authentication username for a given OpenAPI securityScheme. + +For example, given a file named petstore.json with a securityScheme named "petstore-basic-auth", that is of http basic authentication type, the following configuration can establish the user to be used. + +quarkus.openapi-generator.petstore_json.auth.petstore_basic_auth.username=MyUserName + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__USERNAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__USERNAME+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a| [[quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-password]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-password[`quarkus.openapi-generator."item-configs".auth."auth-configs".password`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator."item-configs".auth."auth-configs".password+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Sets the Basic Authentication password for a given OpenAPI securityScheme. + +For example, given a file named petstore.json with a securityScheme named "petstore-basic-auth", that is of http basic authentication type, the following configuration can establish the password to be used. + +quarkus.openapi-generator.petstore_json.auth.petstore_basic_auth.password=MyPassword + +Ignored if the given securityScheme is not Basic Authentication + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__PASSWORD+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__PASSWORD+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a| [[quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-bearer-token]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-bearer-token[`quarkus.openapi-generator."item-configs".auth."auth-configs".bearer-token`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator."item-configs".auth."auth-configs".bearer-token+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Sets the Bearer Token for a given OpenAPI securityScheme. + +For example, given a file named petstore.json with a securityScheme named "petstore-bearer-auth", that is of bearer authentication type, the following configuration can establish the token to be used. + +quarkus.openapi-generator.petstore_json.auth.petstore_bearer_auth.token=1234567890 + +Ignored if the given securityScheme is not Bearer Token Authentication + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__BEARER_TOKEN+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__BEARER_TOKEN+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a| [[quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-api-key]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-api-key[`quarkus.openapi-generator."item-configs".auth."auth-configs".api-key`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator."item-configs".auth."auth-configs".api-key+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Sets the API Key for a given OpenAPI securityScheme. + +For example, given a file named petstore.json with a securityScheme named "petstore-apikey-auth", that is of API Key authentication type, the following configuration can establish the API Key to be used. + +quarkus.openapi-generator.petstore_json.auth.petstore_apikey_auth.api-key=$++{++MY_SECRET_KEY_IN_AN_ENV_VAR++}++ + +Ignored if the given securityScheme is not API Key Authentication + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__API_KEY+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__API_KEY+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a| [[quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-use-authorization-header-value]] [.property-path]##link:#quarkus-openapi-generator_quarkus-openapi-generator-item-configs-auth-auth-configs-use-authorization-header-value[`quarkus.openapi-generator."item-configs".auth."auth-configs".use-authorization-header-value`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator."item-configs".auth."auth-configs".use-authorization-header-value+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Only valid for API Key Authentication. + +When to add the `Authorization` value to the API Key in the authentication header. + +For example, if this property is set to `true`, the API Key will be sent to the server in the header along with `Authorization`: + +++[++source++]++ --- Authentication: Authorization MY-API-KEY --- + +If set to `false`, the header should be: + +++[++source++]++ --- Authentication: MY-API-KEY --- + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__USE_AUTHORIZATION_HEADER_VALUE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR__ITEM_CONFIGS__AUTH__AUTH_CONFIGS__USE_AUTHORIZATION_HEADER_VALUE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +|=== + diff --git a/docs/modules/ROOT/pages/includes/server-getting-started.adoc b/docs/modules/ROOT/pages/includes/server-getting-started.adoc index 44bdf7660..ea91a4739 100644 --- a/docs/modules/ROOT/pages/includes/server-getting-started.adoc +++ b/docs/modules/ROOT/pages/includes/server-getting-started.adoc @@ -10,6 +10,16 @@ Add the following dependency to your project's `pom.xml` file: ---- +By default, the generated resources are annotated with Microprofile OpenAPI annotations, add the `io.quarkus:quarkus-smallrye-openapi` dependency to your project’s `pom.xml` file: + +[source,xml] +---- + + io.quarkus + quarkus-smallrye-openapi + +---- + Note that since this extension has not been yet released, you'll need a local build of the dependency. You will also need to add or update the `quarkus-maven-plugin` configuration with the following: diff --git a/docs/modules/ROOT/pages/moqu.adoc b/docs/modules/ROOT/pages/moqu.adoc new file mode 100644 index 000000000..cb8e62c1f --- /dev/null +++ b/docs/modules/ROOT/pages/moqu.adoc @@ -0,0 +1,206 @@ += Quarkus - Open API Generator - Moqu + +include::./includes/attributes.adoc[] + +The **OpenAPI Generator Moqu extension** converts an OpenAPI specification into a mock representation. This mock can then be mapped to the link:https://wiremock.org/[WireMock] for further use, providing a way to simulate APIs for testing and development purposes. + +[NOTE] +==== +Currently, this extension supports only link:https://wiremock.org/[WireMock] definitions. +==== + +[[getting-started]] +== Getting Started + +[source,xml] +---- + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-moqu-wiremock + {project-version} + +---- + +Now, create the following OpenAPI specification file under your `src/resources/openapi` directory: + +[source,yaml] +.src/main/resources/openapi/hello.yaml +---- +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Get framework by ID +paths: + "/frameworks/{id}": + get: + parameters: + - name: id + in: path + examples: + quarkus: + value: 1 + responses: + 200: + content: + "application/json": + examples: + quarkus: + $ref: "#/components/schemas/Framework" + description: Ok +components: + schemas: + Framework: + type: object + properties: + name: + type: string + example: "Quarkus" + versions: + type: array + example: ["999-SNAPSHOT", "3.15.1"] + supportsJava: + type: boolean + example: true + contributors: + type: integer + example: 1000 + rules: + type: object + example: + hello: world +---- + +Execute now your application on Dev mode, and access the Dev UI for getting you wiremock stubbing: + +image::moqu-devui-card-framework.png[alt=Moque Dev UI card,width=640,height=480] + +Click on `Moqu Wiremock`, you will se a table containing all wiremock definitions: + +image::table-wiremock.png[alt=Wiremock mappings table,width=640,height=480] + +Now, you can `see` or `download` the Wiremock stubbing. + +== Request matching + +The Moqu extension uses the request and response examples defined in the OpenAPI Specification to determine the appropriate response for a specific request, creating a corresponding request/response pair. + +Example: + +[source,yaml] +---- +openapi: 3.0.3 +info: + title: "Users API" + version: 1.0.0-alpha +servers: + - url: http://localhost:8888 +paths: + /users/{id}: + get: + description: Get user by ID + parameters: + - name: id + in: path + required: true + schema: + type: number + examples: + john: <1> + value: 1 <2> + responses: + "200": + description: Ok + content: + "application/json": + examples: + john: <3> + value: + '{"id": 1, "name": "John Doe"}' +---- + +<1> Defines an example named `john` for request +<2> Maps the request for path `/users/1` should use the response named as `john` +<3> Defines an example named `john` for response + + +In other words, if the user accesses `/users/1`, the response will be the one mapped for the `john` example in response. + +The Wiremock definition using the OpenAPI specification above, looks something like this: + +[source,json] +---- +{ + "mappings": [ + { + "request": { + "method": "GET", + "url": "/users/1" + }, + "response": { + "status": 200, + "body": "{\"name\":\"John\",\"age\": 80}", + "headers": {} + } + } + ] +} +---- + +=== Response as Schema + +You can use the `$ref` to reference a schema for mapping a response: + +[source,yaml] +---- +paths: + "/users/{id}": + get: + parameters: + - name: id + in: path + examples: + alice: + value: 1 + responses: + 200: + content: + "application/json": + examples: + alice: + $ref: "#/components/schemas/User" + description: Ok +components: + schemas: + User: + type: object + properties: + name: + type: string + example: "Alice" + age: + type: number + example: 80 +---- + +The Wiremock definition using the OpenAPI specification above, looks something like this: + +[source,json] +---- +{ + "mappings": [ + { + "request": { + "method": "GET", + "url": "/users/1" + }, + "response": { + "status": 200, + "body": "{\"name\":\"Alice\",\"age\":80}", + "headers": {} + } + } + ] +} +---- diff --git a/docs/modules/ROOT/pages/server.adoc b/docs/modules/ROOT/pages/server.adoc index 33d67a5b0..30dcac7fe 100644 --- a/docs/modules/ROOT/pages/server.adoc +++ b/docs/modules/ROOT/pages/server.adoc @@ -16,3 +16,7 @@ include::includes/want-to-contribute.adoc[] == Getting Started include::./includes/server-getting-started.adoc[leveloffset=+1, opts=optional] + +== Configuration Properties + +include::./includes/quarkus-openapi-generator-server.adoc[opts=optional, leveloffset=+1] diff --git a/docs/pom.xml b/docs/pom.xml index bc3f73da5..bfff96074 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -9,7 +9,7 @@ ../pom.xml quarkus-openapi-generator-docs - Quarkus - Openapi Generator - Client - Documentation + Quarkus - OpenAPI Generator - Documentation @@ -51,11 +51,6 @@ ${project.basedir}/modules/ROOT/pages/includes/ - - ${project.basedir}/../target/asciidoc/generated/config/ - quarkus-openapi-generator.adoc - false - ${project.basedir}/templates/includes attributes.adoc @@ -70,6 +65,14 @@ org.asciidoctor asciidoctor-maven-plugin + + io.quarkus + quarkus-config-doc-maven-plugin + true + + ${project.basedir}/modules/ROOT/pages/includes/ + + diff --git a/moqu/core/pom.xml b/moqu/core/pom.xml new file mode 100644 index 000000000..686e31cae --- /dev/null +++ b/moqu/core/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-moqu-parent + 3.0.0-SNAPSHOT + + + quarkus-openapi-generator-moqu-core + Quarkus :: Openapi Generator :: Moqu :: Core + + + 2.19.0 + + + + + io.swagger.parser.v3 + swagger-parser + ${version.io.swagger.parser} + + + org.assertj + assertj-core + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + commons-io + commons-io + ${commons.io.version} + + + org.jboss.logmanager + jboss-logmanager + + + diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/Moqu.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/Moqu.java new file mode 100644 index 000000000..1d0024cad --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/Moqu.java @@ -0,0 +1,37 @@ +package io.quarkiverse.openapi.moqu; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import io.quarkiverse.openapi.moqu.model.RequestResponsePair; + +/** + * Represents a collection of request-response pairs, providing methods to access + * these pairs in an immutable list. + */ +public class Moqu { + + private List requestResponsePairs = new ArrayList<>(); + + /** + * Constructs a {@code Moqu} instance with the provided list of request-response pairs. + * + * @param requestResponsePairs the list of {@link RequestResponsePair} objects to initialize + * the collection. Must not be {@code null}. + * @throws NullPointerException if {@code requestResponsePairs} is null. + */ + public Moqu(List requestResponsePairs) { + this.requestResponsePairs = Objects.requireNonNull(requestResponsePairs); + } + + /** + * Returns an unmodifiable list of request-response pairs. + * + * @return an immutable list of {@link RequestResponsePair}. + */ + public List getRequestResponsePairs() { + return Collections.unmodifiableList(requestResponsePairs); + } +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/MoquImporter.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/MoquImporter.java new file mode 100644 index 000000000..16d63747d --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/MoquImporter.java @@ -0,0 +1,17 @@ +package io.quarkiverse.openapi.moqu; + +/** + * {@link MoquImporter} aims to convert a specification into a {@link Moqu} model. + * It provides a method to parse the content, typically from an OpenAPI specification, + * and generate a corresponding {@link Moqu} instance. + */ +public interface MoquImporter { + + /** + * Parses the provided OpenAPI content and generates a new {@link Moqu} instance. + * + * @param content the OpenAPI content as a string, which will be parsed into a {@link Moqu} model. + * @return a new {@link Moqu} instance based on the provided content. + */ + Moqu parse(String content); +} \ No newline at end of file diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/MoquMapper.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/MoquMapper.java new file mode 100644 index 000000000..53ed02bbe --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/MoquMapper.java @@ -0,0 +1,19 @@ +package io.quarkiverse.openapi.moqu; + +import java.util.List; + +/** + * A generic interface for mapping a {@link Moqu} instance to a list of objects of type {@code T}. + * + * @param the type of objects to which the {@link Moqu} instance will be mapped. + */ +public interface MoquMapper { + + /** + * Maps the given {@link Moqu} instance to a list of objects of type {@code T}. + * + * @param moqu the {@link Moqu} instance to be mapped. + * @return a list of mapped objects of type {@code T}. + */ + List map(Moqu moqu); +} \ No newline at end of file diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/OpenAPIMoquImporter.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/OpenAPIMoquImporter.java new file mode 100644 index 000000000..a969d8582 --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/OpenAPIMoquImporter.java @@ -0,0 +1,253 @@ +package io.quarkiverse.openapi.moqu; + +import static io.swagger.v3.parser.util.SchemaTypeUtil.INTEGER_TYPE; +import static io.swagger.v3.parser.util.SchemaTypeUtil.OBJECT_TYPE; +import static io.swagger.v3.parser.util.SchemaTypeUtil.STRING_TYPE; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Strings; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; + +import io.quarkiverse.openapi.moqu.model.Header; +import io.quarkiverse.openapi.moqu.model.Request; +import io.quarkiverse.openapi.moqu.model.RequestResponsePair; +import io.quarkiverse.openapi.moqu.model.Response; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.examples.Example; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.parser.OpenAPIV3Parser; +import io.swagger.v3.parser.core.models.SwaggerParseResult; + +public class OpenAPIMoquImporter implements MoquImporter { + + private static final Logger LOGGER = LoggerFactory.getLogger(OpenAPIMoquImporter.class); + private static final String HTTP_HEADER_ACCEPT = "Accept"; + private static final String REFERENCE_PREFIX = "#/components/schemas/"; + + @Override + public Moqu parse(String content) { + + SwaggerParseResult swaggerParseResult = new OpenAPIV3Parser().readContents(content); + + if (LOGGER.isDebugEnabled()) { + for (String message : swaggerParseResult.getMessages()) { + LOGGER.debug("[context:SwaggerParseResult] {}", message); + } + } + + OpenAPI openAPI = swaggerParseResult.getOpenAPI(); + + if (Objects.isNull(openAPI)) { + throw new IllegalArgumentException("Cannot parse OpenAPI V3 content: " + content); + } + + return new Moqu( + getRequestResponsePairs(openAPI)); + } + + private List getRequestResponsePairs(OpenAPI openAPI) { + Map requestResponsePairs = new HashMap<>(); + + Map localSchemas = getSchemas(openAPI); + + Set> entries = Optional.ofNullable(openAPI.getPaths()) + .orElseThrow(IllegalArgumentException::new) + .entrySet(); + + for (Map.Entry entry : entries) { + + for (Map.Entry httpMethodOperation : entry.getValue().readOperationsMap() + .entrySet()) { + + if (!Objects.isNull(httpMethodOperation.getValue().getResponses())) { + + Set> statusApiResponses = httpMethodOperation.getValue().getResponses() + .entrySet(); + + for (Map.Entry statusApiResponse : statusApiResponses) { + + if (Objects.isNull(statusApiResponse.getValue())) { + continue; + } + + Map> examplesOnPath = extractParameters(httpMethodOperation.getValue(), + ParameterType.PATH); + + requestResponsePairs.putAll(getContentRequestResponsePairs(statusApiResponse, examplesOnPath, + httpMethodOperation.getKey(), entry.getKey(), localSchemas)); + } + } + } + } + + return requestResponsePairs.entrySet().stream().map(entry -> new RequestResponsePair(entry.getKey(), entry.getValue())) + .collect(Collectors.toList()); + } + + private Map getSchemas(OpenAPI openAPI) { + if (openAPI.getComponents() == null) { + return Map.of(); + } + return Objects.requireNonNullElse(openAPI.getComponents().getSchemas(), Map.of()); + } + + private int tryGetStatusCode(Map.Entry statusApiResponse) { + try { + return Integer.parseInt(statusApiResponse.getKey()); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid status code: " + statusApiResponse.getKey()); + } + } + + private Map> extractParameters(Operation operation, ParameterType parameterType) { + List parameters = Optional.ofNullable(operation.getParameters()).orElse(Collections.emptyList()); + Map> finalParameters = new HashMap<>(); + + for (Parameter parameter : parameters) { + if (isEligibleForExtraction(parameter, parameterType)) { + + Set exampleNames = parameter.getExamples().keySet(); + for (String exampleName : exampleNames) { + + Example example = parameter.getExamples().get(exampleName); + + Object object = example.getValue(); + String value = resolveContent(object); + finalParameters.computeIfAbsent(exampleName, + k -> ArrayListMultimap.create()).put(parameter.getName(), value); + } + } + } + + return finalParameters; + } + + private boolean isEligibleForExtraction(Parameter parameter, ParameterType type) { + return parameter.getIn().equals(type.value()) && !Objects.isNull(parameter.getExamples()); + } + + private Map getContentRequestResponsePairs(Map.Entry statusApiResponse, + Map> parametersOnPath, PathItem.HttpMethod httpMethod, String url, + Map localSchemas) { + Map requestResponseMap = new HashMap<>(); + + ApiResponse apiResponse = statusApiResponse.getValue(); + + int statusCode = tryGetStatusCode(statusApiResponse); + + for (Map.Entry entry : apiResponse.getContent().entrySet()) { + String contentType = entry.getKey(); + MediaType mediaType = entry.getValue(); + Map examples = Optional.ofNullable(mediaType.getExamples()).orElse(Collections.emptyMap()); + + examples.forEach((exampleName, example) -> { + + String content = resolveContent(localSchemas, example); + + Response response = new Response( + exampleName, + mediaType, + statusCode, + content, + List.of()); + + Multimap onPath = parametersOnPath.get(exampleName); + List reqParams = new ArrayList<>(); + + if (onPath != null) { + for (Map.Entry paramEntry : onPath.entries()) { + io.quarkiverse.openapi.moqu.model.Parameter parameter = new io.quarkiverse.openapi.moqu.model.Parameter( + paramEntry.getKey(), + paramEntry.getValue(), + ParameterType.PATH); + reqParams.add(parameter); + } + } + + List parameters = reqParams.stream() + .filter(reqParam -> reqParam.where().equals(ParameterType.PATH)).toList(); + String finalUrl = resolveUrlParameters(url, parameters); + Request request = new Request( + finalUrl, + httpMethod.name(), + exampleName, + new Header(HTTP_HEADER_ACCEPT, List.of(contentType)), + reqParams); + requestResponseMap.put(request, response); + }); + } + + return requestResponseMap; + } + + private String resolveContent(Map localSchemas, Example example) { + if (!Strings.isNullOrEmpty(example.get$ref())) { + return resolveRef(example.get$ref(), localSchemas); + } else { + return resolveContent(example.getValue()); + } + } + + private String resolveUrlParameters(String url, List parameters) { + for (io.quarkiverse.openapi.moqu.model.Parameter parameter : parameters) { + String placeholder = "{%s}".formatted(parameter.key()); + url = url.replace(placeholder, parameter.value()); + } + return url; + } + + private String resolveRef(String ref, Map localSchemas) { + if (!ref.startsWith(REFERENCE_PREFIX)) { + throw new IllegalArgumentException( + "There is no support for external $ref schemas. Please, configure the %s as local schema" + .formatted(ref)); + } + + String refName = ref.substring(REFERENCE_PREFIX.length(), ref.length()); + + Schema schema = localSchemas.get(refName); + + if (schema == null) { + throw new IllegalArgumentException("Schema not found: " + refName); + } + + return generateResponseBodyFromRefSchema(schema); + } + + private String resolveContent(Object object) { + if (object instanceof String) { + return (String) object; + } + if (object instanceof Integer) { + return String.valueOf((Integer) object); + } + throw new IllegalArgumentException("Object is not a String"); + } + + private static String generateResponseBodyFromRefSchema(final Schema schema) { + String schemaType = Optional.ofNullable(schema.getType()).orElse(OBJECT_TYPE); + return switch (schemaType) { + case STRING_TYPE, INTEGER_TYPE -> (String) schema.getExample(); + case OBJECT_TYPE -> SchemaReader.readObjectExample(schema); + default -> ""; + }; + } +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/ParameterType.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/ParameterType.java new file mode 100644 index 000000000..0c053f55b --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/ParameterType.java @@ -0,0 +1,43 @@ +package io.quarkiverse.openapi.moqu; + +/** + * Enum representing the type of a parameter in an HTTP request, indicating its location. + * The parameter can be part of the path, query string, or headers. + */ +public enum ParameterType { + + /** + * Indicates that the parameter is part of the URL path. + */ + PATH("path"), + + /** + * Indicates that the parameter is part of the query string. + */ + QUERY("query"), + + /** + * Indicates that the parameter is part of the HTTP headers. + */ + HEADER("header"); + + private final String value; + + /** + * Constructs a {@code ParameterType} with the given string value representing the parameter location. + * + * @param value the string value corresponding to the parameter type. + */ + ParameterType(String value) { + this.value = value; + } + + /** + * Returns the string value associated with this {@code ParameterType}. + * + * @return the string representation of the parameter type. + */ + public String value() { + return this.value; + } +} \ No newline at end of file diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/SchemaReader.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/SchemaReader.java new file mode 100644 index 000000000..7b330f0e5 --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/SchemaReader.java @@ -0,0 +1,64 @@ +package io.quarkiverse.openapi.moqu; + +import static io.swagger.v3.parser.util.SchemaTypeUtil.OBJECT_TYPE; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import io.quarkiverse.openapi.moqu.marshall.ObjectMapperFactory; +import io.swagger.v3.oas.models.media.Schema; + +/** + * Utility class for reading schema examples and converting them into JSON representations. + * This class provides methods to extract example data from a given schema and serialize it + * into JSON format. + */ +public class SchemaReader { + + static String EMPTY_JSON_OBJECT = "{}"; + + /** + * Reads the example object from the provided schema and converts it to a JSON string. + * + * @param schema the schema from which to extract the example object. + * @return a JSON string representation of the example object, or an empty JSON object + * if an error occurs during processing. + */ + static String readObjectExample(Schema schema) { + try { + Map map = mapObjectExample(schema); + return ObjectMapperFactory.getInstance().writeValueAsString(map); + } catch (JsonProcessingException e) { + return EMPTY_JSON_OBJECT; + } + } + + /** + * Recursively maps the properties of the provided schema to a map. + * + * @param schema the schema from which to map properties. + * @return a map representing the example properties of the schema. + */ + private static Map mapObjectExample(Schema schema) { + Map currentRoot = new HashMap<>(); + + Optional.ofNullable(schema.getProperties()) + .orElse(Map.of()) + .forEach((key, value) -> { + if (value.getType().equals(OBJECT_TYPE)) { + if (value.getExample() != null) { + currentRoot.put(key, value.getExample()); + } else { + currentRoot.put(key, mapObjectExample(value)); + } + } else { + currentRoot.put(key, value.getExample()); + } + }); + + return currentRoot; + } +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/marshall/ObjectMapperFactory.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/marshall/ObjectMapperFactory.java new file mode 100644 index 000000000..68957a738 --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/marshall/ObjectMapperFactory.java @@ -0,0 +1,15 @@ +package io.quarkiverse.openapi.moqu.marshall; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Responsible for providing a Single of {@link ObjectMapper} instance. + */ +public class ObjectMapperFactory { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + public static ObjectMapper getInstance() { + return objectMapper; + } +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Header.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Header.java new file mode 100644 index 000000000..476bd63ab --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Header.java @@ -0,0 +1,12 @@ +package io.quarkiverse.openapi.moqu.model; + +import java.util.List; + +/** + * Represents an HTTP header with a name and a set of associated values. + * + * @param name the name of the HTTP header (e.g., "Accept", "Content-Type"). + * @param value the set of values associated with the header, allowing multiple values (e.g., "application/json", "text/html"). + */ +public record Header(String name, List value) { +} \ No newline at end of file diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Operation.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Operation.java new file mode 100644 index 000000000..bac321819 --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Operation.java @@ -0,0 +1,10 @@ +package io.quarkiverse.openapi.moqu.model; + +/** + * Represents an HTTP operation. + *

+ * + * @param httpMethod the HTTP verb used for the current {@link Operation}. + */ +public record Operation(String httpMethod) { +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Parameter.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Parameter.java new file mode 100644 index 000000000..ee87f789b --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Parameter.java @@ -0,0 +1,14 @@ +package io.quarkiverse.openapi.moqu.model; + +import io.quarkiverse.openapi.moqu.ParameterType; + +/** + * Represents an HTTP request parameter with a key, value, and location indicating where the parameter is used. + * + * @param key the key of the parameter (e.g., "id", "query"). + * @param value the value of the parameter associated with the key. + * @param where the location of the parameter in the request (e.g., query string, path, header), defined by + * {@link ParameterType}. + */ +public record Parameter(String key, String value, ParameterType where) { +} \ No newline at end of file diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Request.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Request.java new file mode 100644 index 000000000..ff1b8fe8e --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Request.java @@ -0,0 +1,16 @@ +package io.quarkiverse.openapi.moqu.model; + +import java.util.Collection; + +/** + * Represents an HTTP request with essential details such as URL, HTTP method, + * example name, accepted header, and parameters. + * + * @param url the URL to which the request is sent. + * @param httpMethod the HTTP method (GET, POST, PUT, DELETE, etc.) used for the request. + * @param exampleName the name of the example associated with the request. + * @param accept the "Accept" header, which specifies the expected response format. + * @param parameters the list of parameters to be included in the request. + */ +public record Request(String url, String httpMethod, String exampleName, Header accept, Collection parameters) { +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/RequestResponsePair.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/RequestResponsePair.java new file mode 100644 index 000000000..214cb92de --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/RequestResponsePair.java @@ -0,0 +1,10 @@ +package io.quarkiverse.openapi.moqu.model; + +/** + * Represents a pair of an HTTP request and its corresponding response. + * + * @param request the HTTP request that was sent. + * @param response the HTTP response received for the given request. + */ +public record RequestResponsePair(Request request, Response response) { +} \ No newline at end of file diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Response.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Response.java new file mode 100644 index 000000000..532345b9f --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Response.java @@ -0,0 +1,20 @@ +package io.quarkiverse.openapi.moqu.model; + +import java.util.List; + +import io.swagger.v3.oas.models.media.MediaType; + +/** + * Represents an HTTP response with details such as the example name, media type, + * status code, content, and headers. + * + * @param exampleName the name of the example associated with this response. + * @param mediaType the media type of the response content (e.g., application/json, text/html), + * represented by {@link MediaType}. + * @param statusCode the HTTP status code of the response (e.g., 200, 404). + * @param content the body of the response as a string. + * @param headers the list of headers included in the response. + */ +public record Response(String exampleName, MediaType mediaType, int statusCode, + String content, List

headers) { +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockMapper.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockMapper.java new file mode 100644 index 000000000..83bf92e58 --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockMapper.java @@ -0,0 +1,41 @@ +package io.quarkiverse.openapi.moqu.wiremock.mapper; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.quarkiverse.openapi.moqu.Moqu; +import io.quarkiverse.openapi.moqu.MoquMapper; +import io.quarkiverse.openapi.moqu.model.Request; +import io.quarkiverse.openapi.moqu.model.RequestResponsePair; +import io.quarkiverse.openapi.moqu.model.Response; +import io.quarkiverse.openapi.moqu.wiremock.model.WiremockMapping; +import io.quarkiverse.openapi.moqu.wiremock.model.WiremockRequest; +import io.quarkiverse.openapi.moqu.wiremock.model.WiremockResponse; + +public class WiremockMapper implements MoquMapper { + + @Override + public List map(Moqu moqu) { + ArrayList definitions = new ArrayList<>(); + for (RequestResponsePair pair : moqu.getRequestResponsePairs()) { + + Request mockRequest = pair.request(); + Response mockResponse = pair.response(); + + WiremockRequest request = new WiremockRequest(mockRequest.httpMethod(), mockRequest.url()); + + Map headers = new HashMap<>(); + + mockResponse.headers().forEach(item -> headers.put(item.name(), item.value())); + + WiremockResponse response = new WiremockResponse(mockResponse.statusCode(), mockResponse.content(), headers); + + definitions.add(new WiremockMapping( + request, response)); + } + + return definitions; + } +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockMapping.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockMapping.java new file mode 100644 index 000000000..bf703d7f9 --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockMapping.java @@ -0,0 +1,4 @@ +package io.quarkiverse.openapi.moqu.wiremock.model; + +public record WiremockMapping(WiremockRequest request, WiremockResponse response) { +} \ No newline at end of file diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockRequest.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockRequest.java new file mode 100644 index 000000000..3d663e79a --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockRequest.java @@ -0,0 +1,6 @@ +package io.quarkiverse.openapi.moqu.wiremock.model; + +public record WiremockRequest( + String method, + String url) { +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockResponse.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockResponse.java new file mode 100644 index 000000000..bfbcb6eef --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockResponse.java @@ -0,0 +1,8 @@ +package io.quarkiverse.openapi.moqu.wiremock.model; + +import java.util.Map; + +public record WiremockResponse(Integer status, + String body, + Map headers) { +} diff --git a/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/OpenAPIMoquImporterTest.java b/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/OpenAPIMoquImporterTest.java new file mode 100644 index 000000000..8267b1ddd --- /dev/null +++ b/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/OpenAPIMoquImporterTest.java @@ -0,0 +1,181 @@ +package io.quarkiverse.openapi.moqu; + +import static io.quarkiverse.openapi.moqu.TestUtils.readContentFromFile; + +import java.util.List; +import java.util.Map; + +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import io.quarkiverse.openapi.moqu.marshall.ObjectMapperFactory; + +class OpenAPIMoquImporterTest { + + private final MoquImporter sut = new OpenAPIMoquImporter(); + + @Test + @DisplayName("Should create a new definition from OpenAPI specification") + void shouldCreateANewDefinitionFromOpenAPISpecification() { + // act + String content = readContentFromFile("wiremock/one_example_in_the_same_path.yml"); + Moqu moqu = sut.parse(content); + + // assert + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(moqu.getRequestResponsePairs()).isNotEmpty(); + softly.assertThat(moqu.getRequestResponsePairs()).hasSize(1); + softly.assertThat(moqu.getRequestResponsePairs().get(0)).satisfies(requestResponsePair -> { + softly.assertThat(requestResponsePair.request().accept().name()).isEqualTo("Accept"); + softly.assertThat(requestResponsePair.request().accept().value()).contains("application/json"); + softly.assertThat(requestResponsePair.request().exampleName()).isEqualTo("john"); + softly.assertThat(requestResponsePair.request().parameters()).hasSize(1); + softly.assertThat(requestResponsePair.request().parameters()).anySatisfy(parameters -> { + softly.assertThat(parameters.key()).isEqualTo("id"); + softly.assertThat(parameters.value()).isEqualTo("1"); + }); + }); + }); + } + + @Test + @DisplayName("Should throws exception when the OpenAPI is invalid") + void shouldThrowsExceptionWhenTheOpenAPIIsInvalid() { + // act, assert + Assertions.assertThrows(IllegalArgumentException.class, () -> { + sut.parse(""" + openapi: 3.0.3 + info: + version: 999-SNAPSHOT + """); + }); + } + + @Test + @DisplayName("Should handle OpenAPI with two path params") + void shouldHandleOpenAPIWithTwoPathParams() { + + // act + String content = readContentFromFile("wiremock/two_examples_in_the_same_path.yml"); + Moqu moqu = sut.parse(content); + + // assert + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(moqu.getRequestResponsePairs()).isNotEmpty(); + softly.assertThat(moqu.getRequestResponsePairs()).hasSize(2); + softly.assertThat(moqu.getRequestResponsePairs()).allSatisfy(requestResponsePair -> { + softly.assertThat(requestResponsePair.request().accept().name()).isEqualTo("Accept"); + softly.assertThat(requestResponsePair.request().accept().value()).contains("application/json"); + }); + softly.assertThat(moqu.getRequestResponsePairs()).anySatisfy(requestResponsePair -> { + softly.assertThat(requestResponsePair.request().exampleName()).isEqualTo("john"); + softly.assertThat(requestResponsePair.request().parameters()).hasSize(1); + }); + softly.assertThat(moqu.getRequestResponsePairs()).anySatisfy(requestResponsePair -> { + softly.assertThat(requestResponsePair.request().exampleName()).isEqualTo("mary"); + softly.assertThat(requestResponsePair.request().parameters()).hasSize(1); + }); + }); + } + + @Test + @DisplayName("Should generate a response from ref") + void shouldGenerateAResponseFromRef() { + String content = readContentFromFile("wiremock/response_from_ref.yml"); + + Moqu moqu = sut.parse(content); + + // assert + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(moqu.getRequestResponsePairs()).isNotEmpty(); + softly.assertThat(moqu.getRequestResponsePairs()).hasSize(1); + softly.assertThat(moqu.getRequestResponsePairs().get(0)).satisfies(requestResponsePair -> { + softly.assertThat(requestResponsePair.request().accept().name()).isEqualTo("Accept"); + softly.assertThat(requestResponsePair.request().accept().value()).contains("application/json"); + softly.assertThat(requestResponsePair.request().exampleName()).isEqualTo("quarkus"); + softly.assertThat(requestResponsePair.request().parameters()).hasSize(1); + softly.assertThat(requestResponsePair.request().parameters()).anySatisfy(parameters -> { + softly.assertThat(parameters.key()).isEqualTo("id"); + softly.assertThat(parameters.value()).isEqualTo("1"); + }); + }); + }); + } + + @Test + @DisplayName("Should generate a response from ref as array") + void shouldGenerateAResponseFromRefAsArray() { + String content = readContentFromFile("wiremock/response_from_ref_array.yml"); + Moqu moqu = sut.parse(content); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(moqu.getRequestResponsePairs()).isNotEmpty(); + softly.assertThat(moqu.getRequestResponsePairs()).hasSize(1); + softly.assertThat(moqu.getRequestResponsePairs().get(0)).satisfies(requestResponsePair -> { + Map map = ObjectMapperFactory.getInstance().readValue( + requestResponsePair.response() + .content(), + Map.class); + softly.assertThat((List) map.get("versions")) + .hasSize(2); + + softly.assertThat(map.get("supportsJava")).isEqualTo(true); + + }); + }); + } + + @Test + @DisplayName("Should generate a response from $ref and with no $ref") + void shouldGenerateAResponseFromRefAndNoRef() { + String content = readContentFromFile("wiremock/response_from_ref_and_noref.yml"); + Moqu moqu = sut.parse(content); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(moqu.getRequestResponsePairs()).isNotEmpty(); + softly.assertThat(moqu.getRequestResponsePairs()).hasSize(2); + softly.assertThat(moqu.getRequestResponsePairs()).anySatisfy(requestResponsePair -> { + Map map = ObjectMapperFactory.getInstance().readValue( + requestResponsePair.response() + .content(), + Map.class); + softly.assertThat((List) map.get("versions")) + .hasSize(2); + }); + + softly.assertThat(moqu.getRequestResponsePairs()).anySatisfy(requestResponsePair -> { + Map map = ObjectMapperFactory.getInstance().readValue( + requestResponsePair.response() + .content(), + Map.class); + softly.assertThat((List) map.get("versions")) + .hasSize(1); + }); + }); + } + + @Test + @DisplayName("Should generate a full OpenAPI specification") + void shouldGenerateAFullResponse() { + String content = readContentFromFile("wiremock/full.yml"); + Moqu moqu = sut.parse(content); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(moqu.getRequestResponsePairs()).isNotEmpty(); + softly.assertThat(moqu.getRequestResponsePairs()).hasSize(1); + softly.assertThat(moqu.getRequestResponsePairs().get(0)).satisfies(requestResponsePair -> { + Map map = ObjectMapperFactory.getInstance().readValue( + requestResponsePair.response() + .content(), + Map.class); + softly.assertThat((List) map.get("versions")) + .hasSize(2); + + softly.assertThat(map.get("supportsJava")).isEqualTo(true); + + softly.assertThat(map.get("contributors")).isEqualTo(1000); + + softly.assertThat(((Map) map.get("rules")).get("hello")).isEqualTo("world"); + }); + }); + } +} diff --git a/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/TestUtils.java b/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/TestUtils.java new file mode 100644 index 000000000..2f8b1b571 --- /dev/null +++ b/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/TestUtils.java @@ -0,0 +1,20 @@ +package io.quarkiverse.openapi.moqu; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; + +public class TestUtils { + + public static String readContentFromFile(String resourcePath) { + URL url = Thread.currentThread().getContextClassLoader().getResource((resourcePath)); + assert url != null; + try { + return Files.readString(Path.of(url.toURI())); + } catch (IOException | URISyntaxException e) { + return null; + } + } +} diff --git a/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockMapperTest.java b/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockMapperTest.java new file mode 100644 index 000000000..9a8611bab --- /dev/null +++ b/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockMapperTest.java @@ -0,0 +1,65 @@ +package io.quarkiverse.openapi.moqu.wiremock.mapper; + +import static io.quarkiverse.openapi.moqu.TestUtils.readContentFromFile; + +import java.util.List; + +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import io.quarkiverse.openapi.moqu.Moqu; +import io.quarkiverse.openapi.moqu.OpenAPIMoquImporter; +import io.quarkiverse.openapi.moqu.wiremock.model.WiremockMapping; + +class WiremockMapperTest { + + private final OpenAPIMoquImporter importer = new OpenAPIMoquImporter(); + private final WiremockMapper sut = new WiremockMapper(); + + @Test + @DisplayName("Should map one Wiremock definition") + void shouldMapOneWiremockDefinition() { + String content = readContentFromFile("wiremock/mapper/should_map_one_wiremock_definition.yml"); + + Moqu moqu = importer.parse(content); + + List definitions = sut.map(moqu); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(definitions).isNotEmpty(); + softly.assertThat(definitions).hasSize(1); + softly.assertThat(definitions).anySatisfy(definition -> { + softly.assertThat(definition.request().method()).isEqualTo("GET"); + softly.assertThat(definition.request().url()).isEqualTo("/users/1"); + softly.assertThat(definition.response().body()).isEqualTo("{\"id\": 1, \"name\": \"John Doe\"}"); + }); + }); + } + + @Test + @DisplayName("Should map two Wiremock definitions") + void shouldMapTwoWiremockDefinitions() { + String content = readContentFromFile("wiremock/mapper/should_map_two_wiremock_definition.yml"); + + Moqu mock = importer.parse(content); + + List definitions = sut.map(mock); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(definitions).isNotEmpty(); + softly.assertThat(definitions).hasSize(2); + softly.assertThat(definitions).anySatisfy(definition -> { + softly.assertThat(definition.request().method()).isEqualTo("GET"); + softly.assertThat(definition.request().url()).isEqualTo("/users/1"); + softly.assertThat(definition.response().body()).isEqualTo("{\"id\": 1, \"name\": \"John Doe\"}"); + }); + + softly.assertThat(definitions).anySatisfy(definition -> { + softly.assertThat(definition.request().method()).isEqualTo("GET"); + softly.assertThat(definition.request().url()).isEqualTo("/users/2"); + softly.assertThat(definition.response().body()).isEqualTo("{\"id\": 2, \"name\": \"Mary Doe\"}"); + }); + }); + } +} diff --git a/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockPathParamTest.java b/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockPathParamTest.java new file mode 100644 index 000000000..abff5f59a --- /dev/null +++ b/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockPathParamTest.java @@ -0,0 +1,164 @@ +package io.quarkiverse.openapi.moqu.wiremock.mapper; + +import java.util.List; + +import org.assertj.core.api.Assertions; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import io.quarkiverse.openapi.moqu.Moqu; +import io.quarkiverse.openapi.moqu.OpenAPIMoquImporter; +import io.quarkiverse.openapi.moqu.TestUtils; +import io.quarkiverse.openapi.moqu.wiremock.model.WiremockMapping; + +public class WiremockPathParamTest { + + @Test + @DisplayName("Should convert a OpenAPI with a single path param correctly") + void shouldMapOneWiremockDefinition() { + + String content = TestUtils.readContentFromFile("wiremock/path_param_one_path_param.yml"); + if (content == null) { + Assertions.fail("Was not possible to read the file!"); + } + + OpenAPIMoquImporter importer = new OpenAPIMoquImporter(); + + Moqu mock = importer.parse(content); + + WiremockMapper wiremockMapper = new WiremockMapper(); + + List definitions = wiremockMapper.map(mock); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(definitions).hasSize(1); + WiremockMapping definition = definitions.get(0); + + softly.assertThat(definition).satisfies(wiremockDefinition -> { + // request + softly.assertThat(wiremockDefinition.request().url()).isEqualTo("/users/1"); + softly.assertThat(wiremockDefinition.request().method()).isEqualTo("GET"); + // response + softly.assertThat(wiremockDefinition.response().status()).isEqualTo(200); + softly.assertThat(wiremockDefinition.response().body()).isEqualTo("{\"name\": \"Quarkus\"}"); + }); + }); + } + + @Test + @DisplayName("Should convert with a two OpenAPI#paths each one with one path param") + void shouldMapTwoWiremockDefinitions() { + + String content = TestUtils.readContentFromFile("wiremock/path_param_two_params_but_different_path.yml"); + if (content == null) { + Assertions.fail("Was not possible to read the file!"); + } + + OpenAPIMoquImporter importer = new OpenAPIMoquImporter(); + + Moqu mock = importer.parse(content); + + WiremockMapper wiremockMapper = new WiremockMapper(); + + List definitions = wiremockMapper.map(mock); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(definitions).hasSize(2); + + softly.assertThat(definitions).anySatisfy(wiremockDefinition -> { + // request + softly.assertThat(wiremockDefinition.request().url()).isEqualTo("/users/1"); + softly.assertThat(wiremockDefinition.request().method()).isEqualTo("GET"); + // response + softly.assertThat(wiremockDefinition.response().status()).isEqualTo(200); + softly.assertThat(wiremockDefinition.response().body()).isEqualTo("{\"name\": \"John Doe\"}"); + }); + + softly.assertThat(definitions).anySatisfy(wiremockDefinition -> { + // request + softly.assertThat(wiremockDefinition.request().url()).isEqualTo("/frameworks/quarkus"); + softly.assertThat(wiremockDefinition.request().method()).isEqualTo("GET"); + // response + softly.assertThat(wiremockDefinition.response().status()).isEqualTo(200); + softly.assertThat(wiremockDefinition.response().body()) + .isEqualTo("{\"description\": \"Quarkus, build time augmentation toolkit\"}"); + }); + }); + } + + @Test + @DisplayName("Should convert with a combination of path param") + void shouldConvertWithACombinationOfPathParam() { + + String content = TestUtils.readContentFromFile("wiremock/path_param_two_path_params_combination.yml"); + if (content == null) { + Assertions.fail("Was not possible to read the file!"); + } + + OpenAPIMoquImporter importer = new OpenAPIMoquImporter(); + + Moqu mock = importer.parse(content); + + WiremockMapper wiremockMapper = new WiremockMapper(); + + List definitions = wiremockMapper.map(mock); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(definitions).hasSize(2); + + softly.assertThat(definitions).anySatisfy(wiremockDefinition -> { + // request + softly.assertThat(wiremockDefinition.request().url()).isEqualTo("/users/1/books/80"); + softly.assertThat(wiremockDefinition.request().method()).isEqualTo("GET"); + // response + softly.assertThat(wiremockDefinition.response().status()).isEqualTo(200); + softly.assertThat(wiremockDefinition.response().body()) + .isEqualTo("{\"name\": \"Book for John\", \"chapters\": 8}"); + }); + + softly.assertThat(definitions).anySatisfy(wiremockDefinition -> { + // request + softly.assertThat(wiremockDefinition.request().url()).isEqualTo("/users/2/books/70"); + softly.assertThat(wiremockDefinition.request().method()).isEqualTo("GET"); + // response + softly.assertThat(wiremockDefinition.response().status()).isEqualTo(200); + softly.assertThat(wiremockDefinition.response().body()) + .isEqualTo("{\"name\": \"Book for Mary\", \"chapters\": 10}"); + }); + }); + } + + @Test + @DisplayName("Should convert with a combination but only one with example") + void shouldConvertPathParamCombinationOnlyOneWithExample() { + + String content = TestUtils.readContentFromFile("wiremock/path_param_two_path_params_only_one_with_example.yml"); + if (content == null) { + Assertions.fail("Was not possible to read the file!"); + } + + OpenAPIMoquImporter importer = new OpenAPIMoquImporter(); + + Moqu mock = importer.parse(content); + + WiremockMapper wiremockMapper = new WiremockMapper(); + + List definitions = wiremockMapper.map(mock); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(definitions).hasSize(1); + + softly.assertThat(definitions).anySatisfy(wiremockDefinition -> { + // request + softly.assertThat(wiremockDefinition.request().url()).isEqualTo("/users/1/books/{bookId}"); + softly.assertThat(wiremockDefinition.request().method()).isEqualTo("GET"); + // response + softly.assertThat(wiremockDefinition.response().status()).isEqualTo(200); + softly.assertThat(wiremockDefinition.response().body()) + .isEqualTo("{\"name\": \"Book for John\", \"chapters\": 8}"); + }); + }); + } + +} diff --git a/moqu/core/src/test/resources/wiremock/full.yml b/moqu/core/src/test/resources/wiremock/full.yml new file mode 100644 index 000000000..d91f48923 --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/full.yml @@ -0,0 +1,45 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/frameworks/{id}": + get: + parameters: + - name: id + in: path + examples: + quarkus: + value: 1 + responses: + 200: + content: + "application/json": + examples: + quarkus: + $ref: "#/components/schemas/Framework" + description: Ok +components: + schemas: + Framework: + type: object + properties: + name: + type: string + example: "Quarkus" + versions: + type: array + example: ["999-SNAPSHOT", "3.15.1"] + supportsJava: + type: boolean + example: true + contributors: + type: integer + example: 1000 + rules: + type: object + example: + hello: world + diff --git a/moqu/core/src/test/resources/wiremock/mapper/should_map_one_wiremock_definition.yml b/moqu/core/src/test/resources/wiremock/mapper/should_map_one_wiremock_definition.yml new file mode 100644 index 000000000..92ea0200c --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/mapper/should_map_one_wiremock_definition.yml @@ -0,0 +1,28 @@ +openapi: 3.0.3 +info: + title: "Users API" + version: 1.0.0-alpha +servers: + - url: http://localhost:8888 +paths: + /users/{userId}: + get: + description: Get user by ID + parameters: + - name: userId + in: path + required: true + schema: + type: number + examples: + john: + value: 1 + responses: + "200": + description: Ok + content: + "application/json": + examples: + john: + value: + '{"id": 1, "name": "John Doe"}' \ No newline at end of file diff --git a/moqu/core/src/test/resources/wiremock/mapper/should_map_two_wiremock_definition.yml b/moqu/core/src/test/resources/wiremock/mapper/should_map_two_wiremock_definition.yml new file mode 100644 index 000000000..2dabf118f --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/mapper/should_map_two_wiremock_definition.yml @@ -0,0 +1,33 @@ +openapi: 3.0.3 +info: + title: "Users API" + version: 1.0.0-alpha +servers: + - url: http://localhost:8888 +paths: + /users/{userId}: + get: + description: Get user by ID + parameters: + - name: userId + in: path + required: true + schema: + type: number + examples: + john: + value: 1 + mary: + value: 2 + responses: + "200": + description: Ok + content: + "application/json": + examples: + john: + value: + '{"id": 1, "name": "John Doe"}' + mary: + value: + '{"id": 2, "name": "Mary Doe"}' \ No newline at end of file diff --git a/moqu/core/src/test/resources/wiremock/one_example_in_the_same_path.yml b/moqu/core/src/test/resources/wiremock/one_example_in_the_same_path.yml new file mode 100644 index 000000000..eac9598b5 --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/one_example_in_the_same_path.yml @@ -0,0 +1,28 @@ +openapi: 3.0.3 +info: + title: "Users API" + version: 1.0.0-alpha +servers: + - url: http://localhost:8888 +paths: + /users/{id}: + get: + description: Get user by ID + parameters: + - name: id + in: path + required: true + schema: + type: number + examples: + john: + value: 1 + responses: + "200": + description: Ok + content: + "application/json": + examples: + john: + value: + '{"id": 1, "name": "John Doe"}' \ No newline at end of file diff --git a/moqu/core/src/test/resources/wiremock/path_param_one_path_param.yml b/moqu/core/src/test/resources/wiremock/path_param_one_path_param.yml new file mode 100644 index 000000000..a87227d57 --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/path_param_one_path_param.yml @@ -0,0 +1,23 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/users/{userId}": + get: + parameters: + - name: userId + in: path + examples: + quarkus: + value: 1 + responses: + 200: + content: + "application/json": + examples: + quarkus: + value: '{"name": "Quarkus"}' + description: Ok diff --git a/moqu/core/src/test/resources/wiremock/path_param_two_params_but_different_path.yml b/moqu/core/src/test/resources/wiremock/path_param_two_params_but_different_path.yml new file mode 100644 index 000000000..26eb2dba8 --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/path_param_two_params_but_different_path.yml @@ -0,0 +1,39 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/users/{userId}": + get: + parameters: + - name: userId + in: path + examples: + john: + value: 1 + responses: + 200: + content: + "application/json": + examples: + john: + value: '{"name": "John Doe"}' + description: Ok + "/frameworks/{name}": + get: + parameters: + - name: name + in: path + examples: + quarkus: + value: quarkus + responses: + 200: + content: + "application/json": + examples: + quarkus: + value: '{"description": "Quarkus, build time augmentation toolkit"}' + description: Ok diff --git a/moqu/core/src/test/resources/wiremock/path_param_two_path_params_combination.yml b/moqu/core/src/test/resources/wiremock/path_param_two_path_params_combination.yml new file mode 100644 index 000000000..140eec479 --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/path_param_two_path_params_combination.yml @@ -0,0 +1,35 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/users/{userId}/books/{bookId}": + get: + parameters: + - name: userId + in: path + examples: + john: + value: 1 + mary: + value: 2 + - name: bookId + in: path + examples: + john: + value: 80 + mary: + value: 70 + + responses: + 200: + content: + "application/json": + examples: + john: + value: '{"name": "Book for John", "chapters": 8}' + mary: + value: '{"name": "Book for Mary", "chapters": 10}' + description: Ok diff --git a/moqu/core/src/test/resources/wiremock/path_param_two_path_params_only_one_with_example.yml b/moqu/core/src/test/resources/wiremock/path_param_two_path_params_only_one_with_example.yml new file mode 100644 index 000000000..3b1f159a0 --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/path_param_two_path_params_only_one_with_example.yml @@ -0,0 +1,25 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/users/{userId}/books/{bookId}": + get: + parameters: + - name: userId + in: path + examples: + john: + value: 1 + - name: bookId + in: path + responses: + 200: + content: + "application/json": + examples: + john: + value: '{"name": "Book for John", "chapters": 8}' + description: Ok \ No newline at end of file diff --git a/moqu/core/src/test/resources/wiremock/response_from_ref.yml b/moqu/core/src/test/resources/wiremock/response_from_ref.yml new file mode 100644 index 000000000..4e62b37fb --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/response_from_ref.yml @@ -0,0 +1,31 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/frameworks/{id}": + get: + parameters: + - name: id + in: path + examples: + quarkus: + value: 1 + responses: + 200: + content: + "application/json": + examples: + quarkus: + $ref: "#/components/schemas/Framework" + description: Ok +components: + schemas: + Framework: + type: object + properties: + name: + type: string + example: "Quarkus" \ No newline at end of file diff --git a/moqu/core/src/test/resources/wiremock/response_from_ref_and_noref.yml b/moqu/core/src/test/resources/wiremock/response_from_ref_and_noref.yml new file mode 100644 index 000000000..b9537febc --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/response_from_ref_and_noref.yml @@ -0,0 +1,38 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/frameworks/{id}": + get: + parameters: + - name: id + in: path + examples: + quarkus: + value: 1 + vertx: + value: 2 + responses: + 200: + content: + "application/json": + examples: + quarkus: + $ref: "#/components/schemas/Framework" + vertx: + value: '{ "name": "Vert.x", "versions": ["999-SNAPSHOT"]}' + description: Ok +components: + schemas: + Framework: + type: object + properties: + name: + type: string + example: "Quarkus" + versions: + type: array + example: [ "999-SNAPSHOT", "3.15.1" ] diff --git a/moqu/core/src/test/resources/wiremock/response_from_ref_array.yml b/moqu/core/src/test/resources/wiremock/response_from_ref_array.yml new file mode 100644 index 000000000..8707012bd --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/response_from_ref_array.yml @@ -0,0 +1,37 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/frameworks/{id}": + get: + parameters: + - name: id + in: path + examples: + quarkus: + value: 1 + responses: + 200: + content: + "application/json": + examples: + quarkus: + $ref: "#/components/schemas/Framework" + description: Ok +components: + schemas: + Framework: + type: object + properties: + name: + type: string + example: "Quarkus" + versions: + type: array + example: ["999-SNAPSHOT", "3.15.1"] + supportsJava: + type: boolean + example: true diff --git a/moqu/core/src/test/resources/wiremock/two_examples_in_the_same_path.yml b/moqu/core/src/test/resources/wiremock/two_examples_in_the_same_path.yml new file mode 100644 index 000000000..2dabf118f --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/two_examples_in_the_same_path.yml @@ -0,0 +1,33 @@ +openapi: 3.0.3 +info: + title: "Users API" + version: 1.0.0-alpha +servers: + - url: http://localhost:8888 +paths: + /users/{userId}: + get: + description: Get user by ID + parameters: + - name: userId + in: path + required: true + schema: + type: number + examples: + john: + value: 1 + mary: + value: 2 + responses: + "200": + description: Ok + content: + "application/json": + examples: + john: + value: + '{"id": 1, "name": "John Doe"}' + mary: + value: + '{"id": 2, "name": "Mary Doe"}' \ No newline at end of file diff --git a/moqu/deployment/pom.xml b/moqu/deployment/pom.xml new file mode 100644 index 000000000..5d111692b --- /dev/null +++ b/moqu/deployment/pom.xml @@ -0,0 +1,65 @@ + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-moqu-parent + 3.0.0-SNAPSHOT + + 4.0.0 + + quarkus-openapi-generator-moqu-wiremock-deployment + Quarkus - Openapi Generator - Moqu - Wiremock - Deployment + + + + io.quarkus + quarkus-core-deployment + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-moqu-wiremock + ${project.version} + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-moqu-core + ${project.version} + + + + io.quarkus + quarkus-junit5-internal + + + + io.quarkus + quarkus-vertx-http-deployment + + + + io.rest-assured + rest-assured + test + + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/MoquProjectProcessor.java b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/MoquProjectProcessor.java new file mode 100644 index 000000000..94ed8ff79 --- /dev/null +++ b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/MoquProjectProcessor.java @@ -0,0 +1,102 @@ +package io.quarkiverse.openapi.generator; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; + +import org.jboss.logging.Logger; + +import io.quarkiverse.openapi.generator.items.MoquBuildItem; +import io.quarkiverse.openapi.generator.items.MoquProjectBuildItem; +import io.quarkiverse.openapi.generator.moqu.MoquConfig; +import io.quarkiverse.openapi.moqu.Moqu; +import io.quarkiverse.openapi.moqu.OpenAPIMoquImporter; +import io.quarkus.deployment.IsDevelopment; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.runtime.util.ClassPathUtils; + +public class MoquProjectProcessor { + + private static final Logger LOGGER = Logger.getLogger(MoquProjectProcessor.class); + + private static final Set SUPPORTED_EXTENSIONS = Set.of("yaml", "yml", "json"); + + @BuildStep + MoquProjectBuildItem generate(MoquConfig config) { + try { + + HashMap filesMap = new HashMap<>(); + ClassPathUtils.consumeAsPaths(config.resourceDir(), path -> { + try { + boolean directory = Files.isDirectory(path); + if (directory) { + try (Stream pathStream = Files.find(path, Integer.MAX_VALUE, + (p, a) -> Files.isRegularFile(p) && SUPPORTED_EXTENSIONS.contains( + getExtension(p.getFileName().toString())))) { + + pathStream.forEach(p -> { + try { + String filename = p.getFileName().toString(); + + MoquProjectBuildItem.File moquFile = new MoquProjectBuildItem.File( + removeExtension(filename), getExtension(filename), Files.readString(p)); + + filesMap.put(filename, moquFile); + + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + return new MoquProjectBuildItem(filesMap); + + } catch (IOException e) { + LOGGER.error("Was not possible to scan Moqu project files.", e); + throw new RuntimeException(e); + } + } + + @BuildStep(onlyIf = { IsDevelopment.class }) + void consume(Optional moquProject, + BuildProducer moquMocks) { + + OpenAPIMoquImporter importer = new OpenAPIMoquImporter(); + moquProject.ifPresent(project -> { + for (Map.Entry spec : project.specs().entrySet()) { + + MoquProjectBuildItem.File moquFile = spec.getValue(); + + Moqu moqu = importer.parse(moquFile.content()); + + moquMocks.produce(new MoquBuildItem( + moquFile.filename(), + moquFile.extension(), + moqu)); + } + }); + } + + public static String getExtension(String path) { + Objects.requireNonNull(path, "path is required"); + final int i = path.lastIndexOf("."); + return i > 0 ? path.substring(i + 1) : null; + } + + public static String removeExtension(String path) { + Objects.requireNonNull(path, "path is required"); + final int i = path.lastIndexOf("."); + return i > 0 ? path.substring(0, i) : path; + } +} diff --git a/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/MoquWiremockProcessor.java b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/MoquWiremockProcessor.java new file mode 100644 index 000000000..086f65f88 --- /dev/null +++ b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/MoquWiremockProcessor.java @@ -0,0 +1,12 @@ +package io.quarkiverse.openapi.generator; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.FeatureBuildItem; + +public class MoquWiremockProcessor { + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem("moqu-wiremock"); + } +} diff --git a/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/devui/MoquModel.java b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/devui/MoquModel.java new file mode 100644 index 000000000..38cc5c795 --- /dev/null +++ b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/devui/MoquModel.java @@ -0,0 +1,5 @@ +package io.quarkiverse.openapi.generator.devui; + +public record MoquModel(String name, String link) { + +} diff --git a/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/devui/MoquWiremockDevUIProcessor.java b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/devui/MoquWiremockDevUIProcessor.java new file mode 100644 index 000000000..0514701af --- /dev/null +++ b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/devui/MoquWiremockDevUIProcessor.java @@ -0,0 +1,81 @@ +package io.quarkiverse.openapi.generator.devui; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.quarkiverse.openapi.generator.items.MoquBuildItem; +import io.quarkiverse.openapi.generator.moqu.recorder.MoquRoutesRecorder; +import io.quarkiverse.openapi.moqu.marshall.ObjectMapperFactory; +import io.quarkiverse.openapi.moqu.wiremock.mapper.WiremockMapper; +import io.quarkiverse.openapi.moqu.wiremock.model.WiremockMapping; +import io.quarkus.deployment.IsDevelopment; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.devui.spi.page.CardPageBuildItem; +import io.quarkus.devui.spi.page.Page; +import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; +import io.quarkus.vertx.http.deployment.RouteBuildItem; + +public class MoquWiremockDevUIProcessor { + + private static final String MAPPINGS_KEY = "mappings"; + private static final String WIREMOCK_MAPPINGS_JSON = "/wiremock-mappings.json"; + + @BuildStep(onlyIf = IsDevelopment.class) + @Record(ExecutionTime.RUNTIME_INIT) + void generateWiremock(List mocks, NonApplicationRootPathBuildItem nonApplicationRootPath, + BuildProducer routes, + MoquRoutesRecorder recorder) { + + WiremockMapper wiremockMapper = new WiremockMapper(); + ObjectMapper objMapper = ObjectMapperFactory.getInstance(); + + for (MoquBuildItem mock : mocks) { + List wiremockMappings = wiremockMapper.map(mock.getMoqu()); + try { + String json = objMapper.writeValueAsString(Map.of( + MAPPINGS_KEY, wiremockMappings)); + + String uri = mock.prefixUri(nonApplicationRootPath.resolvePath("moqu")) + .concat(WIREMOCK_MAPPINGS_JSON); + + routes.produce(nonApplicationRootPath.routeBuilder() + .routeFunction(uri, recorder.handleFile(json)) + .displayOnNotFoundPage() + .build()); + + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + } + + @BuildStep(onlyIf = { IsDevelopment.class }) + CardPageBuildItem cardPageBuildItem( + List moquMocks, + NonApplicationRootPathBuildItem nonApplicationRootPath) { + CardPageBuildItem cardPageBuildItem = new CardPageBuildItem(); + + List models = moquMocks.stream() + .map(m -> new MoquModel(m.getFullFilename(), m.prefixUri( + nonApplicationRootPath.resolvePath("moqu")) + .concat("/wiremock-mappings.json"))) + .toList(); + + cardPageBuildItem.addBuildTimeData("mocks", models); + + cardPageBuildItem.addPage( + Page.webComponentPageBuilder() + .title("Moqu Wiremock") + .icon("font-awesome-solid:server") + .componentLink("qwc-moqu.js") + .staticLabel(String.valueOf(moquMocks.size()))); + + return cardPageBuildItem; + } +} diff --git a/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/items/MoquBuildItem.java b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/items/MoquBuildItem.java new file mode 100644 index 000000000..a6285f734 --- /dev/null +++ b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/items/MoquBuildItem.java @@ -0,0 +1,37 @@ +package io.quarkiverse.openapi.generator.items; + +import io.quarkiverse.openapi.moqu.Moqu; +import io.quarkus.builder.item.MultiBuildItem; + +public final class MoquBuildItem extends MultiBuildItem { + + private final String filename; + private final String extension; + private final Moqu moqu; + + public MoquBuildItem(String filename, String extension, Moqu moqu) { + this.filename = filename; + this.extension = extension; + this.moqu = moqu; + } + + public String getFilename() { + return filename; + } + + public String getExtension() { + return extension; + } + + public Moqu getMoqu() { + return moqu; + } + + public String getFullFilename() { + return filename + "." + extension; + } + + public String prefixUri(String basePath) { + return String.format("%s/%s/%s", basePath, extension, filename); + } +} diff --git a/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/items/MoquProjectBuildItem.java b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/items/MoquProjectBuildItem.java new file mode 100644 index 000000000..ea8de60a5 --- /dev/null +++ b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/items/MoquProjectBuildItem.java @@ -0,0 +1,22 @@ +package io.quarkiverse.openapi.generator.items; + +import java.util.Collections; +import java.util.Map; + +import io.quarkus.builder.item.SimpleBuildItem; + +public final class MoquProjectBuildItem extends SimpleBuildItem { + + private final Map specs; + + public MoquProjectBuildItem(Map specs) { + this.specs = specs; + } + + public Map specs() { + return Collections.unmodifiableMap(specs); + } + + public record File(String filename, String extension, String content) { + } +} diff --git a/moqu/deployment/src/main/resources/dev-ui/qwc-moqu.js b/moqu/deployment/src/main/resources/dev-ui/qwc-moqu.js new file mode 100644 index 000000000..31d42cded --- /dev/null +++ b/moqu/deployment/src/main/resources/dev-ui/qwc-moqu.js @@ -0,0 +1,96 @@ +import {LitElement, html, css} from 'lit'; +import {columnBodyRenderer} from '@vaadin/grid/lit.js'; +import {mocks} from 'build-time-data'; +import '@vaadin/grid'; +import '@vaadin/vertical-layout'; +import '@vaadin/icon'; + +/** + * This component shows the Moqu mocks + */ +export class QwcMoqu extends LitElement { + + static styles = css` + .arctable { + height: 100%; + padding-bottom: 10px; + } + + .moqu-icon { + font-size: small; + color: var(--lumo-contrast-50pct); + cursor: pointer; + } + `; + + static properties = { + _mocks: {state: true} + }; + + constructor() { + super(); + this._mocks = mocks; + } + + render() { + if (this._mocks) { + return this._renderMockList(); + } else { + return html`No mocks found`; + } + } + + _renderMockList() { + return html` + + + + + + + + + `; + } + + _nameRenderer(mock) { + return html` + + ${mock.name} + + `; + } + + _linkDownloadRenderer(mock) { + return html` + + + + + + `; + } + + _linkSeeRenderer(mock) { + return html` + + + + + + `; + } + + +} + +customElements.define('qwc-moqu', QwcMoqu); diff --git a/moqu/deployment/src/test/java/io/quarkiverse/openapi/generator/MoquProjectProcessorTest.java b/moqu/deployment/src/test/java/io/quarkiverse/openapi/generator/MoquProjectProcessorTest.java new file mode 100644 index 000000000..76347c237 --- /dev/null +++ b/moqu/deployment/src/test/java/io/quarkiverse/openapi/generator/MoquProjectProcessorTest.java @@ -0,0 +1,47 @@ +package io.quarkiverse.openapi.generator; + +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusDevModeTest; +import io.restassured.RestAssured; + +public class MoquProjectProcessorTest { + + @RegisterExtension + static final QuarkusDevModeTest unitTest = new QuarkusDevModeTest() + .withApplicationRoot(javaArchive -> javaArchive + .addAsResource("api.yaml", "openapi/openapi.yaml") + .addAsResource("apiv2.json", "openapi/api.json")); + + @Test + void testModeAsSee() { + RestAssured.given() + .when().get("/q/moqu/yaml/openapi/wiremock-mappings.json?mode=see") + .then() + .statusCode(200) + .body(Matchers.containsString("Alice")) + .log().ifError(); + } + + @Test + void testModeAsDownload() { + RestAssured.given() + .when().get("/q/moqu/yaml/openapi/wiremock-mappings.json") + .then() + .statusCode(200) + .body(Matchers.containsString("Alice")) + .log().ifError(); + } + + @Test + void testModeAsDownloadUsingJson() { + RestAssured.given() + .when().get("/q/moqu/json/api/wiremock-mappings.json") + .then() + .statusCode(200) + .body(Matchers.containsString("Alice")) + .log().ifError(); + } +} diff --git a/moqu/deployment/src/test/resources/api.yaml b/moqu/deployment/src/test/resources/api.yaml new file mode 100644 index 000000000..802ebe7e6 --- /dev/null +++ b/moqu/deployment/src/test/resources/api.yaml @@ -0,0 +1,35 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/users/{id}": + get: + parameters: + - name: id + in: path + examples: + alice: + value: 1 + responses: + 200: + content: + "application/json": + examples: + quarkus: + $ref: "#/components/schemas/User" + description: Ok +components: + schemas: + User: + type: object + properties: + name: + type: string + example: "Alice" + age: + type: number + example: 80 + diff --git a/moqu/deployment/src/test/resources/apiv2.json b/moqu/deployment/src/test/resources/apiv2.json new file mode 100644 index 000000000..4bbe37c02 --- /dev/null +++ b/moqu/deployment/src/test/resources/apiv2.json @@ -0,0 +1,60 @@ +{ + "openapi": "3.0.3", + "servers": [ + { + "url": "http://localhost:8888" + } + ], + "info": { + "version": "999-SNAPSHOT", + "title": "Method GET one path param" + }, + "paths": { + "/users/{id}": { + "get": { + "parameters": [ + { + "name": "id", + "in": "path", + "examples": { + "alice": { + "value": 1 + } + } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "examples": { + "quarkus": { + "$ref": "#/components/schemas/User" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "User": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "Alice" + }, + "age": { + "type": "number", + "example": 80 + } + } + } + } + } +} diff --git a/moqu/pom.xml b/moqu/pom.xml new file mode 100644 index 000000000..79569a72b --- /dev/null +++ b/moqu/pom.xml @@ -0,0 +1,19 @@ + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-parent + 3.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + pom + quarkus-openapi-generator-moqu-parent + Quarkus - Openapi Generator - Moqu - Parent + + + core + deployment + runtime + + diff --git a/moqu/runtime/pom.xml b/moqu/runtime/pom.xml new file mode 100644 index 000000000..404ab0227 --- /dev/null +++ b/moqu/runtime/pom.xml @@ -0,0 +1,77 @@ + + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-moqu-parent + 3.0.0-SNAPSHOT + + 4.0.0 + + quarkus-openapi-generator-moqu-wiremock + Quarkus - Openapi Generator - Moqu - Wiremock + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-moqu-core + ${project.version} + + + + io.quarkus + quarkus-core + + + + io.quarkus + quarkus-vertx-http + + + + io.quarkus + quarkus-junit5 + test + + + + io.rest-assured + rest-assured + test + + + + + + io.quarkus + quarkus-extension-maven-plugin + ${quarkus.version} + + + compile + + extension-descriptor + + + ${project.groupId}:${project.artifactId}-deployment:${project.version} + + true + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/moqu/runtime/src/main/java/io/quarkiverse/openapi/generator/moqu/MoquConfig.java b/moqu/runtime/src/main/java/io/quarkiverse/openapi/generator/moqu/MoquConfig.java new file mode 100644 index 000000000..ee9912ee8 --- /dev/null +++ b/moqu/runtime/src/main/java/io/quarkiverse/openapi/generator/moqu/MoquConfig.java @@ -0,0 +1,19 @@ +package io.quarkiverse.openapi.generator.moqu; + +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; + +@ConfigMapping(prefix = "quarkus.openapi-generator.moqu") +@ConfigRoot(phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED) +public interface MoquConfig { + + String DEFAULT_RESOURCE_DIR = "openapi"; + + /** + * Path to the Moqu (relative to the project). + */ + @WithDefault(DEFAULT_RESOURCE_DIR) + String resourceDir(); +} diff --git a/moqu/runtime/src/main/java/io/quarkiverse/openapi/generator/moqu/recorder/MoquRoutesRecorder.java b/moqu/runtime/src/main/java/io/quarkiverse/openapi/generator/moqu/recorder/MoquRoutesRecorder.java new file mode 100644 index 000000000..86d211c01 --- /dev/null +++ b/moqu/runtime/src/main/java/io/quarkiverse/openapi/generator/moqu/recorder/MoquRoutesRecorder.java @@ -0,0 +1,47 @@ +package io.quarkiverse.openapi.generator.moqu.recorder; + +import java.util.function.Consumer; + +import io.quarkus.runtime.annotations.Recorder; +import io.vertx.core.Handler; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.ext.web.Route; +import io.vertx.ext.web.RoutingContext; + +@Recorder +public class MoquRoutesRecorder { + + public Consumer handleFile(String content) { + return new Consumer() { + @Override + public void accept(Route route) { + route.method(HttpMethod.GET); + route.handler(new Handler() { + @Override + public void handle(RoutingContext routingContext) { + HttpServerResponse response = routingContext.response(); + HttpServerRequest request = routingContext.request(); + + String mode = request.getParam("mode"); + if (mode != null && mode.equalsIgnoreCase("see")) { + response.putHeader("Content-Type", "application/json; charset=utf-8"); + response.end(Buffer.buffer(content)); + } else { + setForDownloading(response, content); + } + } + + }); + } + }; + } + + private void setForDownloading(HttpServerResponse response, String content) { + response.putHeader("Content-Type", "application/octet-stream"); + response.putHeader("Content-Disposition", "attachment; filename=wiremock-mappings.json"); + response.end(Buffer.buffer(content)); + } +} diff --git a/moqu/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/moqu/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 000000000..c9383283a --- /dev/null +++ b/moqu/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,11 @@ +name: "OpenAPI Generator - Moqu - Wiremock Generator" +artifact: ${project.groupId}:${project.artifactId}:${project.version} +description: The OpenAPI Generator Moqu Wiremock extension converts an OpenAPI specification into a Wiremock definition. +metadata: + keywords: + - "openapi" + - "openapi-generator" + - "wiremock" + categories: + - "web" + status: "preview" diff --git a/pom.xml b/pom.xml index 8da081e65..b686f9cd0 100644 --- a/pom.xml +++ b/pom.xml @@ -4,16 +4,18 @@ io.quarkiverse quarkiverse-parent - 18 + 20 io.quarkiverse.openapi.generator quarkus-openapi-generator-parent 3.0.0-SNAPSHOT pom - Quarkus - Openapi Generator - Parent + Quarkus - OpenAPI Generator - Parent client server + docs + moqu :git:git@github.com:quarkiverse/quarkus-openapi-generator.git @@ -26,12 +28,14 @@ 17 UTF-8 UTF-8 - 3.15.1 - 1.1.1.Final - 3.26.2 - 3.26.3 - 4.1.1 + 3.23.0 + 1.2.1.Final + 3.26.4 + 3.27.3 + 4.1.2 + 3.13.0 2.35.2 + 2.1.29 @@ -63,9 +67,9 @@ ${apicurio.version} - com.github.tomakehurst - wiremock-jre8 - ${version.com.github.tomakehurst} + org.wiremock + wiremock + ${version.org.wiremock} @@ -118,6 +122,11 @@ -Xdoclint:none + + io.quarkus + quarkus-config-doc-maven-plugin + ${quarkus.version} + diff --git a/server/deployment/pom.xml b/server/deployment/pom.xml index 7c9619120..270e10270 100755 --- a/server/deployment/pom.xml +++ b/server/deployment/pom.xml @@ -9,7 +9,7 @@ 4.0.0 quarkus-openapi-generator-server-deployment - Quarkus - Openapi Generator - Server - Deployment + Quarkus - OpenAPI Generator - Server - Deployment @@ -38,9 +38,6 @@ ${quarkus.version} - - -AlegacyConfigRoot=true - diff --git a/server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/CodegenConfig.java b/server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/CodegenConfig.java index 139c2c1f8..69b474d57 100755 --- a/server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/CodegenConfig.java +++ b/server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/CodegenConfig.java @@ -2,29 +2,31 @@ import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; -@ConfigRoot(name = CodegenConfig.CODEGEN_TIME_CONFIG_PREFIX, phase = ConfigPhase.BUILD_TIME) -public class CodegenConfig { +@ConfigRoot(phase = ConfigPhase.BUILD_TIME) +@ConfigMapping(prefix = CodegenConfig.CODEGEN_TIME_CONFIG_PREFIX) +public interface CodegenConfig extends ServerCodegenConfig { - static final String CODEGEN_TIME_CONFIG_PREFIX = "quarkus.openapi.generator"; - private static final String CODEGEN_BASE_PACKAGE = CODEGEN_TIME_CONFIG_PREFIX + ".base-package"; - private static final String CODEGEN_SPEC = CODEGEN_TIME_CONFIG_PREFIX + ".spec"; - private static final String INPUT_BASE_DIR = CODEGEN_TIME_CONFIG_PREFIX + ".input-base-dir"; - private static final String CODEGEN_REACTIVE = CODEGEN_TIME_CONFIG_PREFIX + ".reactive"; + String CODEGEN_TIME_CONFIG_PREFIX = "quarkus.openapi.generator"; + String CODEGEN_BASE_PACKAGE = CODEGEN_TIME_CONFIG_PREFIX + ".base-package"; + String CODEGEN_SPEC = CODEGEN_TIME_CONFIG_PREFIX + ".spec"; + String INPUT_BASE_DIR = CODEGEN_TIME_CONFIG_PREFIX + ".input-base-dir"; + String CODEGEN_REACTIVE = CODEGEN_TIME_CONFIG_PREFIX + ".reactive"; - public static String getBasePackagePropertyName() { + static String getBasePackagePropertyName() { return CODEGEN_BASE_PACKAGE; } - public static String getSpecPropertyName() { + static String getSpecPropertyName() { return CODEGEN_SPEC; } - public static String getInputBaseDirPropertyName() { + static String getInputBaseDirPropertyName() { return INPUT_BASE_DIR; } - public static String getCodegenReactive() { + static String getCodegenReactive() { return CODEGEN_REACTIVE; } } diff --git a/server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/ServerCodegenConfig.java b/server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/ServerCodegenConfig.java new file mode 100644 index 000000000..f2a9873e8 --- /dev/null +++ b/server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/ServerCodegenConfig.java @@ -0,0 +1,34 @@ +package io.quarkiverse.openapi.server.generator.deployment; + +import java.util.Optional; + +import io.smallrye.config.WithDefault; + +public interface ServerCodegenConfig { + + String DEFAULT_PACKAGE = "io.apicurio.api"; + String DEFAULT_DIR = "openapi"; + + /** + * The OpenAPI specification filename. + */ + Optional spec(); + + /** + * The input base dir where the OpenAPI specification is. + */ + @WithDefault("src/main/resources/openapi") + Optional inputBaseDir(); + + /** + * Whether it must generate with reactive code. + */ + @WithDefault("false") + boolean reactive(); + + /** + * The base package to be used to generated sources. + */ + @WithDefault(DEFAULT_PACKAGE) + Optional basePackage(); +} diff --git a/server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/codegen/ApicurioCodegenWrapper.java b/server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/codegen/ApicurioCodegenWrapper.java index 2aac2a5af..eef7cf10b 100755 --- a/server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/codegen/ApicurioCodegenWrapper.java +++ b/server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/codegen/ApicurioCodegenWrapper.java @@ -2,6 +2,7 @@ import static io.quarkiverse.openapi.server.generator.deployment.CodegenConfig.getBasePackagePropertyName; import static io.quarkiverse.openapi.server.generator.deployment.CodegenConfig.getCodegenReactive; +import static io.quarkiverse.openapi.server.generator.deployment.ServerCodegenConfig.DEFAULT_PACKAGE; import java.io.File; import java.io.FileInputStream; @@ -26,7 +27,6 @@ public class ApicurioCodegenWrapper { private static final Logger log = LoggerFactory.getLogger(ApicurioCodegenWrapper.class); - private static final String DEFAULT_PACKAGE = "io.apicurio.api"; private final File outdir; private final JaxRsProjectSettings projectSettings; diff --git a/server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/codegen/ApicurioOpenApiServerCodegen.java b/server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/codegen/ApicurioOpenApiServerCodegen.java index 0f2e64ca6..84c58f0af 100755 --- a/server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/codegen/ApicurioOpenApiServerCodegen.java +++ b/server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/codegen/ApicurioOpenApiServerCodegen.java @@ -1,9 +1,12 @@ package io.quarkiverse.openapi.server.generator.deployment.codegen; +import static io.quarkiverse.openapi.server.generator.deployment.ServerCodegenConfig.DEFAULT_DIR; + import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; +import java.util.Optional; import org.eclipse.microprofile.config.Config; import org.slf4j.Logger; @@ -36,26 +39,37 @@ public String inputDirectory() { return "resources"; } - private Path getInputBaseDir(final Path sourceDir, final Config config) { - return config.getOptionalValue(CodegenConfig.getInputBaseDirPropertyName(), String.class) - .map(inputBaseDir -> { - int srcIndex = sourceDir.toString().lastIndexOf("src"); - return Path.of(sourceDir.toString().substring(0, srcIndex), inputBaseDir); - }).orElse(Path.of(sourceDir.toString(), "openapi")); + private Optional getInputBaseDirRelativeToModule(final Path sourceDir, final Config config) { + return config.getOptionalValue(CodegenConfig.getInputBaseDirPropertyName(), String.class).map(baseDir -> { + int srcIndex = sourceDir.toString().lastIndexOf("src"); + return srcIndex < 0 ? null : Path.of(sourceDir.toString().substring(0, srcIndex), baseDir).toString(); + }); } @Override public boolean shouldRun(Path sourceDir, Config config) { - if (config.getOptionalValue(CodegenConfig.getSpecPropertyName(), String.class).isEmpty()) { + Optional possibleSpecPropertyName = config.getOptionalValue(CodegenConfig.getSpecPropertyName(), String.class); + if (possibleSpecPropertyName.isEmpty()) { + log.warn("The {} property is not present, the code generation will be ignored", + CodegenConfig.getSpecPropertyName()); return false; } - Path path = getInputBaseDir(sourceDir, config); - return Files.isDirectory(path); + String specPropertyName = possibleSpecPropertyName.get(); + String relativeInputBaseDir = getInputBaseDirRelativeToModule(sourceDir, config).orElse(null); + if (relativeInputBaseDir != null) { + return Files.exists(Path.of(relativeInputBaseDir).resolve(specPropertyName)); + } else { + return Files.exists(sourceDir.resolve(DEFAULT_DIR).resolve(specPropertyName)); + } } @Override public boolean trigger(CodeGenContext context) throws CodeGenException { - final Path openApiDir = getInputBaseDir(context.inputDir(), context.config()); + final Path openApiDir = Path.of(getInputBaseDirRelativeToModule(context.inputDir(), context.config()) + .orElse(context.inputDir().resolve(DEFAULT_DIR).toString())); + + validateOpenApiDir(context, openApiDir); + final Path outDir = context.outDir(); final ApicurioCodegenWrapper apicurioCodegenWrapper = new ApicurioCodegenWrapper( context.config(), outDir.toFile()); @@ -92,6 +106,19 @@ public boolean trigger(CodeGenContext context) throws CodeGenException { return true; } + private static void validateOpenApiDir(CodeGenContext context, Path openApiDir) throws CodeGenException { + if (!Files.exists(openApiDir)) { + throw new CodeGenException( + "The OpenAPI input base directory does not exist. Please create the directory at " + context.inputDir()); + } + + if (!Files.isDirectory(openApiDir)) { + throw new CodeGenException( + "The OpenAPI input base directory is not a directory. Please create the directory at " + + context.inputDir()); + } + } + private File convertToJSON(Path yamlPath) throws CodeGenException { try { ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory()); diff --git a/server/deployment/src/test/java/io/quarkiverse/openapi/server/generator/deployment/CodegenTest.java b/server/deployment/src/test/java/io/quarkiverse/openapi/server/generator/deployment/CodegenTest.java index b642475d1..bf781f86e 100755 --- a/server/deployment/src/test/java/io/quarkiverse/openapi/server/generator/deployment/CodegenTest.java +++ b/server/deployment/src/test/java/io/quarkiverse/openapi/server/generator/deployment/CodegenTest.java @@ -9,6 +9,7 @@ import org.apache.commons.io.FileUtils; import org.eclipse.microprofile.config.Config; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -60,4 +61,14 @@ public void testInputDir() throws CodeGenException { Path.of("target/generated-test-sources/inputDir/io/petstore/PetResource.java"))); } + @Test + public void shouldGenerateAnErrorWhenInputDirIsNotExist() { + Config config = MockConfigUtils.getTestConfig("doesNotExistDir.application.properties"); + CodeGenContext codeGenContext = new CodeGenContext(null, Path.of(OUT_DIR, "inputDir"), WORK_DIR, + INPUT_DIR, false, config, true); + ApicurioOpenApiServerCodegen apicurioOpenApiServerCodegen = new ApicurioOpenApiServerCodegen(); + + Assertions.assertThrows(CodeGenException.class, () -> apicurioOpenApiServerCodegen.trigger(codeGenContext)); + } + } diff --git a/server/deployment/src/test/resources/io/quarkiverse/openapi/server/generator/deployment/doesNotExistDir.application.properties b/server/deployment/src/test/resources/io/quarkiverse/openapi/server/generator/deployment/doesNotExistDir.application.properties new file mode 100755 index 000000000..a0c722e68 --- /dev/null +++ b/server/deployment/src/test/resources/io/quarkiverse/openapi/server/generator/deployment/doesNotExistDir.application.properties @@ -0,0 +1,2 @@ +quarkus.openapi.generator.spec=petstore-openapi-2.json +quarkus.openapi.generator.base-package=io.petstore \ No newline at end of file diff --git a/server/integration-tests/codestarts/pom.xml b/server/integration-tests/codestarts/pom.xml new file mode 100644 index 000000000..dff0b139a --- /dev/null +++ b/server/integration-tests/codestarts/pom.xml @@ -0,0 +1,108 @@ + + + + quarkus-openapi-generator-server-integration-tests-parent + io.quarkiverse.openapi.generator + 3.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + quarkus-openapi-generator-server-integration-tests-codestarts + Quarkus - Openapi Generator - Server - Integration Tests - Codestarts + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-server + ${project.version} + + + io.quarkus + quarkus-devtools-testing + test + + + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus.version} + + + maven-surefire-plugin + ${version.surefire.plugin} + + + org.jboss.logmanager.LogManager + ${maven.home} + ${settings.localRepository} + + + + + maven-failsafe-plugin + ${failsafe-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + ${settings.localRepository} + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + -parameters + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.1 + + + copy-resources + generate-resources + + copy-resources + + + ${project.build.outputDirectory} + + + + ${project.basedir}/../runtime/src/main/codestarts/quarkus/openapi-generator-codestart/java + + false + + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + generate-code + + + + + + + diff --git a/server/integration-tests/codestarts/src/main/resources/application.properties b/server/integration-tests/codestarts/src/main/resources/application.properties new file mode 100755 index 000000000..e69de29bb diff --git a/server/integration-tests/codestarts/src/test/java/io/quarkiverse/openapi/server/generator/it/QuarkusOpenAPIGeneratorServerCodestartsTest.java b/server/integration-tests/codestarts/src/test/java/io/quarkiverse/openapi/server/generator/it/QuarkusOpenAPIGeneratorServerCodestartsTest.java new file mode 100644 index 000000000..d9a480c1b --- /dev/null +++ b/server/integration-tests/codestarts/src/test/java/io/quarkiverse/openapi/server/generator/it/QuarkusOpenAPIGeneratorServerCodestartsTest.java @@ -0,0 +1,27 @@ +package io.quarkiverse.openapi.server.generator.it; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.devtools.codestarts.quarkus.QuarkusCodestartCatalog; +import io.quarkus.devtools.testing.codestarts.QuarkusCodestartTest; + +public class QuarkusOpenAPIGeneratorServerCodestartsTest { + + @RegisterExtension + public static QuarkusCodestartTest codestartTest = QuarkusCodestartTest.builder() + .languages(QuarkusCodestartCatalog.Language.JAVA) + .setupStandaloneExtensionTest("io.quarkiverse.openapi.generator:quarkus-openapi-generator-server").build(); + + @Test + void testContent() throws Throwable { + codestartTest + .assertThatGeneratedFile(QuarkusCodestartCatalog.Language.JAVA, "src/main/resources/application.properties") + .content() + .contains("quarkus.openapi.generator.spec=openapi.yml"); + codestartTest + .assertThatGeneratedFile(QuarkusCodestartCatalog.Language.JAVA, "src/main/resources/openapi/openapi.yml") + .content() + .contains("title: Generated API"); + } +} diff --git a/server/integration-tests/pom.xml b/server/integration-tests/pom.xml index 985bb0a7d..7850ef7e7 100755 --- a/server/integration-tests/pom.xml +++ b/server/integration-tests/pom.xml @@ -11,11 +11,12 @@ pom quarkus-openapi-generator-server-integration-tests-parent - Quarkus - Openapi Generator - Server - Integration Tests + Quarkus - OpenAPI Generator - Server - Integration Tests reactive resteasy + codestarts - \ No newline at end of file + diff --git a/server/integration-tests/reactive/pom.xml b/server/integration-tests/reactive/pom.xml index efe96d116..398339507 100644 --- a/server/integration-tests/reactive/pom.xml +++ b/server/integration-tests/reactive/pom.xml @@ -9,7 +9,7 @@ 4.0.0 quarkus-openapi-generator-server-integration-tests-quarkus-rest - Quarkus - Openapi Generator - Server - Integration Tests - Quarkus REST (formerly RESTEasy Reactive) + Quarkus - OpenAPI Generator - Server - Integration Tests - Quarkus REST (formerly RESTEasy Reactive) @@ -48,6 +48,10 @@ rest-assured test + + io.quarkus + quarkus-smallrye-openapi + @@ -106,4 +110,4 @@ - \ No newline at end of file + diff --git a/server/integration-tests/resteasy/pom.xml b/server/integration-tests/resteasy/pom.xml index f89a2d395..e74007f47 100644 --- a/server/integration-tests/resteasy/pom.xml +++ b/server/integration-tests/resteasy/pom.xml @@ -9,7 +9,7 @@ 4.0.0 quarkus-openapi-generator-server-integration-tests-resteasy - Quarkus - Openapi Generator - Server - Integration Tests - Resteasy + Quarkus - OpenAPI Generator - Server - Integration Tests - Resteasy @@ -48,6 +48,10 @@ rest-assured test + + io.quarkus + quarkus-smallrye-openapi + @@ -106,4 +110,4 @@ - \ No newline at end of file + diff --git a/server/pom.xml b/server/pom.xml index 1900c318f..e60c5ed55 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-server-parent - Quarkus - Openapi Generator - Server - Parent + Quarkus - OpenAPI Generator - Server - Parent pom diff --git a/server/runtime/pom.xml b/server/runtime/pom.xml index 79ce725a3..69fd55b67 100755 --- a/server/runtime/pom.xml +++ b/server/runtime/pom.xml @@ -10,7 +10,8 @@ 4.0.0 quarkus-openapi-generator-server - Quarkus - Openapi Generator - Server + Quarkus - OpenAPI Generator - Server + Generates REST servers based on OpenAPI specification files @@ -55,9 +56,26 @@ ${quarkus.version} - - -AlegacyConfigRoot=true - + + + + + + maven-jar-plugin + + + generate-codestart-jar + generate-resources + + jar + + + ${project.basedir}/src/main + + codestarts/** + + codestarts + true diff --git a/server/runtime/src/main/codestarts/quarkus/openapi-generator-server-codestart/codestart.yml b/server/runtime/src/main/codestarts/quarkus/openapi-generator-server-codestart/codestart.yml new file mode 100644 index 000000000..edb0f3322 --- /dev/null +++ b/server/runtime/src/main/codestarts/quarkus/openapi-generator-server-codestart/codestart.yml @@ -0,0 +1,13 @@ +name: openapi-generator-server-codestart +ref: openapi-generator-server +type: code +tags: extension-codestart +metadata: + title: OpenAPI Generator Server + description: This codestart generates a simple API with OpenAPI documentation. + related-guide-section: https://docs.quarkiverse.io/quarkus-openapi-generator/dev/server.html +language: + base: + dependencies: + - io.quarkus:quarkus-resteasy + - io.quarkus:quarkus-smallrye-openapi \ No newline at end of file diff --git a/server/runtime/src/main/codestarts/quarkus/openapi-generator-server-codestart/java/README.tpl.qute.md b/server/runtime/src/main/codestarts/quarkus/openapi-generator-server-codestart/java/README.tpl.qute.md new file mode 100644 index 000000000..0d8dc1409 --- /dev/null +++ b/server/runtime/src/main/codestarts/quarkus/openapi-generator-server-codestart/java/README.tpl.qute.md @@ -0,0 +1,24 @@ +{#include readme-header /} + +## Requirements + +If you do not have added the `io.quarkus:quarkus-smallrye-openapi` extension in your project, add it first: + +### SmallRye OpenAPI: + +Quarkus CLI: + +```bash +quarkus ext add io.quarkus:quarkus-smallrye-openapi +``` + +Maven: +```bash +./mvnw quarkus:add-extension -Dextensions="io.quarkus:quarkus-smallrye-openapi" +``` + +Gradle: + +```bash +./gradlew addExtension --extensions="io.quarkus:quarkus-smallrye-openapi" +``` \ No newline at end of file diff --git a/server/runtime/src/main/codestarts/quarkus/openapi-generator-server-codestart/java/src/main/resources/application.yml b/server/runtime/src/main/codestarts/quarkus/openapi-generator-server-codestart/java/src/main/resources/application.yml new file mode 100644 index 000000000..ea7674128 --- /dev/null +++ b/server/runtime/src/main/codestarts/quarkus/openapi-generator-server-codestart/java/src/main/resources/application.yml @@ -0,0 +1,5 @@ +quarkus: + openapi: + generator: + spec: + openapi.yml \ No newline at end of file diff --git a/server/runtime/src/main/codestarts/quarkus/openapi-generator-server-codestart/java/src/main/resources/openapi/openapi.yml b/server/runtime/src/main/codestarts/quarkus/openapi-generator-server-codestart/java/src/main/resources/openapi/openapi.yml new file mode 100644 index 000000000..707eca341 --- /dev/null +++ b/server/runtime/src/main/codestarts/quarkus/openapi-generator-server-codestart/java/src/main/resources/openapi/openapi.yml @@ -0,0 +1,42 @@ +openapi: 3.0.3 +info: + title: Generated API + version: "1.0" +paths: + /pets: + get: + responses: + 200: + description: OK + content: + application/json: { } + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + responses: + 200: + description: OK + content: + application/json: { } + delete: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + responses: + 200: + description: OK + content: + application/json: { } +components: + schemas: + Pet: + properties: + description: + type: string + name: + type: string \ No newline at end of file diff --git a/server/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/server/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 000000000..fce904e16 --- /dev/null +++ b/server/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,19 @@ +name: "OpenAPI Generator - REST Server Generator" +description: "Provides personalized code generation to get started in a Server project " +artifact: ${project.groupId}:${project.artifactId}:${project.version} +metadata: + keywords: + - "openapi" + - "openapi-generator-server" + - "rest" + - "server" + categories: + - "web" + guide: "https://docs.quarkiverse.io/quarkus-openapi-generator/dev/index.html" + icon-url: "https://raw.githubusercontent.com/quarkiverse/quarkus-openapi-generator/main/docs/modules/ROOT/assets/images/openapi.svg" + status: "preview" + codestart: + name: "openapi-generator-server" + languages: + - "java" + artifact: "io.quarkiverse.openapi.generator:quarkus-openapi-generator-server:codestarts:jar:${project.version}" \ No newline at end of file