├── .github
├── dependabot.yml
└── workflows
│ ├── build.yaml
│ └── release.yaml
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── build.gradle
├── buildSrc
└── src
│ └── main
│ └── groovy
│ ├── VersionPlugin.groovy
│ └── VersionTask.groovy
├── docs
├── README.adoc
├── antora.yml
└── modules
│ └── ROOT
│ ├── images
│ ├── openapi-processor-spring-at-1280x200.png
│ └── openapi-processor-spring@1280x200.png
│ ├── nav.adoc
│ ├── pages
│ ├── data.adoc
│ ├── gradle.adoc
│ ├── index.adoc
│ ├── links.adoc
│ ├── mapping
│ │ ├── annotation.adoc
│ │ ├── basic.adoc
│ │ ├── endpoint.adoc
│ │ ├── extension.adoc
│ │ ├── global.adoc
│ │ ├── index.adoc
│ │ ├── logging.adoc
│ │ ├── null.adoc
│ │ ├── package-name.adoc
│ │ ├── parameter.adoc
│ │ ├── response.adoc
│ │ ├── result-status.adoc
│ │ ├── result-style.adoc
│ │ ├── result.adoc
│ │ ├── schema.adoc
│ │ ├── single-multi.adoc
│ │ └── structure.adoc
│ └── processor
│ │ ├── bean-validation.adoc
│ │ ├── configuration.adoc
│ │ ├── deprecated.adoc
│ │ ├── endpoint-content.adoc
│ │ ├── endpoint-interface.adoc
│ │ ├── enums.adoc
│ │ ├── identifier.adoc
│ │ ├── index.adoc
│ │ ├── models.adoc
│ │ ├── one-of-interface.adoc
│ │ ├── parameter.adoc
│ │ ├── requestbody.adoc
│ │ ├── response.adoc
│ │ └── server-url.adoc
│ └── partials
│ ├── links.adoc
│ └── vars.adoc
├── gradle.properties
├── gradle
├── libs.versions.toml
├── publishing.gradle
├── publishing.tasks.gradle.kts
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
├── openapi-processor-spring@1280x200.png
├── openapi-processor-spring@1280x640.png
└── openapi-processor-spring@400x200.png
├── justfile
├── settings.gradle
└── src
├── main
├── kotlin
│ └── io
│ │ └── openapiprocessor
│ │ └── spring
│ │ ├── model
│ │ └── parameters
│ │ │ ├── MultipartParameter.kt
│ │ │ └── QueryParameter.kt
│ │ ├── processor
│ │ ├── ProcessingException.kt
│ │ ├── SpringFramework.kt
│ │ ├── SpringFrameworkAnnotations.kt
│ │ ├── SpringProcessor.kt
│ │ ├── SpringService.kt
│ │ └── SpringServiceV2.kt
│ │ └── writer
│ │ └── java
│ │ ├── AdditionalEnumWriter.kt
│ │ ├── EnumConverterFactoryWriter.kt
│ │ ├── MappingAnnotationWriter.kt
│ │ ├── PackageInfoWriter.kt
│ │ ├── ParameterAnnotationWriter.kt
│ │ ├── SpringWriterFactory.kt
│ │ └── StatusAnnotationWriter.kt
└── resources
│ └── META-INF
│ └── services
│ ├── com.github.hauner.openapi.api.OpenApiProcessor
│ ├── io.openapiprocessor.api.OpenApiProcessor
│ ├── io.openapiprocessor.api.v1.OpenApiProcessor
│ └── io.openapiprocessor.api.v2.OpenApiProcessor
├── test
├── groovy
│ └── io
│ │ └── openapiprocessor
│ │ └── spring
│ │ └── writer
│ │ └── java
│ │ ├── CookieParameterAnnotationWriterSpec.groovy
│ │ ├── HeaderParameterAnnotationWriterSpec.groovy
│ │ ├── MappingAnnotationWriterSpec.groovy
│ │ ├── MethodWriterSpec.groovy
│ │ ├── ParameterAnnotationWriterSpec.groovy
│ │ ├── PathParameterAnnotationWriterSpec.groovy
│ │ └── QueryParameterAnnotationWriterSpec.groovy
└── kotlin
│ └── io
│ └── openapiprocessor
│ └── spring
│ ├── processor
│ ├── SpringFrameworkAnnotationsSpec.kt
│ └── SpringServiceSpec.kt
│ └── writer
│ └── java
│ └── StatusAnnotationWriterSpec.kt
└── testInt
├── kotlin
└── io
│ └── openapiprocessor
│ └── spring
│ ├── CompileExpectedSpec.kt
│ ├── ProcessorEndToEndJimfsSpec.kt
│ ├── ProcessorEndToEndSpec.kt
│ ├── ProcessorPendingSpec.kt
│ ├── ProcessorTestSets.kt
│ └── ProcessorTestSetsSupport.kt
└── resources
├── compile
└── Generated.java
├── logback-test.xml
└── tests
├── endpoint-http-mapping
├── inputs.yaml
├── inputs
│ ├── mapping.yaml
│ ├── openapi30.yaml
│ └── openapi31.yaml
├── outputs.yaml
└── outputs
│ └── api
│ └── EndpointApi.java
├── params-complex-data-types
├── inputs.yaml
├── inputs
│ ├── mapping.yaml
│ ├── openapi30.yaml
│ └── openapi31.yaml
├── outputs.yaml
└── outputs
│ ├── api
│ └── Api.java
│ └── model
│ ├── _default_
│ └── Props.java
│ └── _record_
│ └── Props.java
├── params-enum
├── inputs.yaml
├── inputs
│ ├── mapping.yaml
│ ├── openapi30.yaml
│ └── openapi31.yaml
├── outputs.yaml
└── outputs
│ ├── api
│ └── EnumApi.java
│ ├── model
│ └── _default_
│ │ └── Foo.java
│ └── spring
│ ├── StringToEnumConverterFactory.java
│ └── package-info.java
├── params-pageable-mapping
├── inputs.yaml
├── inputs
│ ├── mapping.yaml
│ ├── openapi30.yaml
│ └── openapi31.yaml
├── outputs.yaml
└── outputs
│ └── api
│ └── Api.java
├── params-path-simple-data-types
├── inputs.yaml
├── inputs
│ ├── mapping.yaml
│ ├── openapi30.yaml
│ └── openapi31.yaml
├── outputs.yaml
└── outputs
│ └── api
│ └── EndpointApi.java
├── params-query-annotate-simple-mapping
├── inputs.yaml
├── inputs
│ ├── mapping.yaml
│ ├── openapi30.yaml
│ └── openapi31.yaml
├── outputs.yaml
└── outputs
│ └── api
│ └── Api.java
├── params-request-body-multipart-mapping
├── inputs.yaml
├── inputs
│ ├── mapping.yaml
│ ├── openapi30.yaml
│ └── openapi31.yaml
├── outputs.yaml
└── outputs
│ └── api
│ └── Api.java
├── params-request-body
├── inputs.yaml
├── inputs
│ ├── mapping.yaml
│ ├── openapi30.yaml
│ └── openapi31.yaml
├── outputs.yaml
└── outputs
│ ├── api
│ └── Api.java
│ └── model
│ ├── _default_
│ └── Book.java
│ └── _record_
│ └── Book.java
├── params-simple-data-types
├── inputs.yaml
├── inputs
│ ├── mapping.yaml
│ ├── openapi30.yaml
│ └── openapi31.yaml
├── outputs.yaml
└── outputs
│ └── api
│ └── EndpointApi.java
└── response-status
├── inputs.yaml
├── inputs
├── mapping.yaml
├── openapi30.yaml
└── openapi31.yaml
├── outputs.yaml
└── outputs
└── api
└── Api.java
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
7 | day: friday
8 |
9 | - package-ecosystem: "gradle"
10 | directory: "/"
11 | schedule:
12 | interval: "weekly"
13 | day: friday
14 |
--------------------------------------------------------------------------------
/.github/workflows/build.yaml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | push:
5 | branches:
6 | - '**'
7 | paths-ignore:
8 | - 'gradle.properties'
9 | - 'LICENSE'
10 | - 'README.md'
11 | - 'docs/**'
12 | tags-ignore:
13 | - 'v*'
14 | workflow_dispatch:
15 |
16 | jobs:
17 |
18 | test:
19 | name: test
20 |
21 | runs-on: ${{ matrix.os }}
22 | strategy:
23 | matrix:
24 | os: [ubuntu-latest, windows-latest]
25 |
26 | steps:
27 | - name: checkout
28 | uses: actions/checkout@v4
29 |
30 | - name: set up jdk
31 | uses: actions/setup-java@v4
32 | with:
33 | distribution: 'temurin'
34 | java-version: |
35 | 17
36 | 11
37 |
38 | - name: set up gradle
39 | uses: gradle/actions/setup-gradle@v4
40 |
41 | - name: run tests
42 | run: |
43 | ./gradlew check
44 |
45 | - name: archive test results
46 | uses: actions/upload-artifact@v4
47 | if: always()
48 | with:
49 | name: test-results-${{ matrix.os }}
50 | path: build/reports
51 |
52 | publish:
53 | if: ${{ github.actor == 'hauner' }}
54 |
55 | needs: [test]
56 | name: publish snapshot
57 |
58 | runs-on: ubuntu-latest
59 |
60 | steps:
61 | - name: checkout
62 | uses: actions/checkout@v4
63 |
64 | - name: set up jdk
65 | uses: actions/setup-java@v4
66 | with:
67 | distribution: 'temurin'
68 | java-version: |
69 | 17
70 | 11
71 |
72 | - name: set up gradle
73 | uses: gradle/actions/setup-gradle@v4
74 |
75 | - name: publish snapshot
76 | env:
77 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
78 | SIGN_KEY: ${{ secrets.SIGN_KEY_ORG }}
79 | SIGN_PWD: ${{ secrets.SIGN_PWD }}
80 | PUBLISH_USER: ${{ secrets.PUBLISH_USER }}
81 | PUBLISH_KEY: ${{ secrets.PUBLISH_KEY }}
82 | run: |
83 | ./gradlew publishToSonatype --stacktrace
84 |
85 | - name: archive test results
86 | uses: actions/upload-artifact@v4
87 | if: always()
88 | with:
89 | name: publish results
90 | path: build
91 |
92 | sonar:
93 | needs: [publish]
94 | name: sonar
95 |
96 | runs-on: ubuntu-latest
97 |
98 | steps:
99 | - name: checkout
100 | uses: actions/checkout@v4
101 | with:
102 | fetch-depth: 0
103 |
104 | - name: cache sonar
105 | uses: actions/cache@v4
106 | with:
107 | path: |
108 | ~/.sonar/cache
109 | key: ${{ runner.os }}-sonar
110 | restore-keys: |
111 | ${{ runner.os }}-sonar
112 |
113 | - name: set up jdk
114 | uses: actions/setup-java@v4
115 | with:
116 | distribution: 'temurin'
117 | java-version: |
118 | 11
119 | 17
120 |
121 | - name: set up gradle
122 | uses: gradle/actions/setup-gradle@v4
123 |
124 | - name: run sonar
125 | env:
126 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
127 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
128 | run: |
129 | ./gradlew check sonar
130 |
131 | - name: archive sonar results
132 | uses: actions/upload-artifact@v4
133 | if: always()
134 | with:
135 | name: sonar results
136 | path: build
137 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: release
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | jobs:
8 |
9 | publish:
10 | name: publish release
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - name: checkout
16 | uses: actions/checkout@v4
17 |
18 | - name: set up jdk
19 | uses: actions/setup-java@v4
20 | with:
21 | distribution: 'temurin'
22 | java-version: |
23 | 11
24 | 17
25 |
26 | - name: set up gradle
27 | uses: gradle/actions/setup-gradle@v4
28 |
29 | - name: publish release
30 | env:
31 | PUBLISH_USER: ${{ secrets.PUBLISH_USER }}
32 | PUBLISH_KEY: ${{ secrets.PUBLISH_KEY }}
33 | SIGN_KEY: ${{ secrets.SIGN_KEY_ORG }}
34 | SIGN_PWD: ${{ secrets.SIGN_PWD }}
35 | run: |
36 | ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | out
2 | build
3 | .gradle
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # contributing to openapi-processor
2 |
3 | see [CONTRIBUTING.md](https://github.com/openapi-processor/openapi-processor-base/blob/main/CONTRIBUTING.md) in the openapi-processor-base repository.
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [![][badge-ci]][workflow-ci]
2 | [![][sonar-tecdebt]][sonar]
3 | [![][badge-central]][oap-central]
4 |
5 | 
6 |
7 | # openapi-processor-spring
8 |
9 | an [OpenAPI][openapi] interface only & model java code generator for [Spring Boot][springboot].
10 |
11 |
12 | # documentation
13 |
14 | See [here][oap-docs].
15 |
16 | # snapshot repository
17 |
18 | to use snapshot versions add `https://oss.sonatype.org/content/repositories/snapshots` as maven repository to your build file.
19 |
20 |
21 | [oap-central]: https://search.maven.org/search?q=io.openapiprocessor
22 | [badge-central]: https://img.shields.io/maven-central/v/io.openapiprocessor/openapi-processor-spring?label=Maven%20Central
23 | [badge-license]: https://img.shields.io/badge/License-Apache%202.0-blue.svg?labelColor=313A42
24 | [badge-ci]: https://github.com/openapi-processor/openapi-processor-spring/workflows/build/badge.svg
25 | [oap-license]: https://github.com/openapi-processor/openapi-processor-spring/blob/master/LICENSE
26 | [workflow-ci]: https://github.com/openapi-processor/openapi-processor-spring/actions?query=workflow%3Abuild
27 | [sonar-coverage]: https://sonarcloud.io/api/project_badges/measure?project=openapi-processor_openapi-processor-spring&metric=coverage
28 | [sonar-tecdebt]: https://sonarcloud.io/api/project_badges/measure?project=openapi-processor_openapi-processor-spring&metric=sqale_index
29 | [sonar]: https://sonarcloud.io/dashboard?id=openapi-processor_openapi-processor-spring
30 | [oap-docs]: https://docs.openapiprocessor.io
31 | [openapi]: https://www.openapis.org/
32 | [springboot]: https://spring.io/projects/spring-boot
33 |
--------------------------------------------------------------------------------
/buildSrc/src/main/groovy/VersionPlugin.groovy:
--------------------------------------------------------------------------------
1 | import org.gradle.api.Action
2 | import org.gradle.api.Plugin
3 | import org.gradle.api.Project
4 |
5 | /**
6 | * provides a "generateVersion" task to a create a simple Version.java class:
7 | *
8 | *
{@code
9 | * package io.openapiprocessor.spring;
10 | *
11 | * public class Version {
12 | * public static final String version = "${project.version}";
13 | * }
14 | * }
15 | *
16 | *
17 | * The io/openapiprocessor/spring/Version.java file is generated to:
18 | *
19 | * $(project.buildDir}/main/java
20 | *
21 | * Add it as a source directory to include it in compilation.
22 | */
23 | class VersionPlugin implements Plugin {
24 |
25 | void apply(Project project) {
26 | project.afterEvaluate (new Action () {
27 |
28 | @Override
29 | void execute (Project prj) {
30 | prj.tasks.register ('generateVersion', VersionTask , new Action() {
31 |
32 | @Override
33 | void execute (VersionTask task) {
34 | task.targetDir = prj.buildDir
35 | task.version = prj.version
36 | }
37 |
38 | })
39 | }
40 |
41 | })
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/buildSrc/src/main/groovy/VersionTask.groovy:
--------------------------------------------------------------------------------
1 | import org.gradle.api.DefaultTask
2 | import org.gradle.api.tasks.Internal
3 | import org.gradle.api.tasks.OutputDirectory
4 | import org.gradle.api.tasks.TaskAction
5 |
6 | import java.nio.file.Files
7 | import java.nio.file.Path
8 | import java.time.Instant
9 |
10 | /**
11 | * simple task to create a Version class.
12 | */
13 | class VersionTask extends DefaultTask {
14 |
15 | /**
16 | * Target directory for the generated version class.
17 | *
18 | * Used by gradle for the up-to-date check.
19 | */
20 | @OutputDirectory
21 | String targetDir
22 |
23 | @Internal
24 | String version
25 |
26 | /**
27 | * generate the version class.
28 | */
29 | @TaskAction
30 | void generateVersion () {
31 | def path = Path.of (targetDir, "version", "io", "openapiprocessor", "spring")
32 | Files.createDirectories(path)
33 |
34 | def target = path.resolve ("Version.java")
35 |
36 | target.text = """\
37 | /*
38 | * DO NOT MODIFY - this file was auto generated by buildSrc/src/main/groovy/VersionPlugin.groovy
39 | *
40 | * ${Instant.now ().toString ()}
41 | */
42 |
43 | package io.openapiprocessor.spring;
44 |
45 | public class Version {
46 | public static final String version = "${version}";
47 | }
48 |
49 | """
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/docs/README.adoc:
--------------------------------------------------------------------------------
1 | = Writer Notes
2 |
3 | == build with theme locally
4 |
5 | antora antora-playbook.yml --ui-bundle-url ../openapi-processor-site-ui/build/ui-bundle.zip
6 |
7 | == pitfalls
8 |
9 | * partials but `include::partial$ ...` !!
10 |
11 |
--------------------------------------------------------------------------------
/docs/antora.yml:
--------------------------------------------------------------------------------
1 | name: spring
2 | title: oap-spring
3 | version: master
4 | #start_page: ROOT:index.adoc
5 | nav:
6 | - modules/ROOT/nav.adoc
7 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/images/openapi-processor-spring-at-1280x200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openapi-processor/openapi-processor-spring/6687d7b56d1921110fc2b1cea70279877ae18141/docs/modules/ROOT/images/openapi-processor-spring-at-1280x200.png
--------------------------------------------------------------------------------
/docs/modules/ROOT/images/openapi-processor-spring@1280x200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openapi-processor/openapi-processor-spring/6687d7b56d1921110fc2b1cea70279877ae18141/docs/modules/ROOT/images/openapi-processor-spring@1280x200.png
--------------------------------------------------------------------------------
/docs/modules/ROOT/nav.adoc:
--------------------------------------------------------------------------------
1 | * xref:index.adoc[Introduction]
2 | * xref:processor/index.adoc[The Processor]
3 | ** xref:processor/configuration.adoc[Configuration]
4 | ** xref:processor/endpoint-interface.adoc[Endpoint Grouping]
5 | ** xref:processor/endpoint-content.adoc[Endpoint Content Type]
6 | ** xref:processor/models.adoc[Models]
7 | ** xref:processor/parameter.adoc[Parameter]
8 | ** xref:processor/requestbody.adoc[Request Body]
9 | ** xref:processor/response.adoc[Responses]
10 | ** xref:processor/enums.adoc[Enums]
11 | ** xref:processor/one-of-interface.adoc[oneOf]
12 | ** xref:processor/deprecated.adoc[Deprecated]
13 | ** xref:processor/identifier.adoc[Identifier]
14 | ** xref:processor/bean-validation.adoc[Bean Validation]
15 | ** xref:processor/server-url.adoc[Server Url]
16 | * xref:mapping/index.adoc[Type Mapping]
17 | ** xref:mapping/structure.adoc[Type Mapping Structure]
18 | ** xref:mapping/basic.adoc[Default Type Mapping]
19 | ** xref:mapping/global.adoc[Global Type Mapping]
20 | ** xref:mapping/schema.adoc[Global Schema Mapping]
21 | ** xref:mapping/parameter.adoc[Global Parameter Mapping]
22 | ** xref:mapping/response.adoc[Global Response Mapping]
23 | ** xref:mapping/result.adoc[Global Result Mapping]
24 | ** xref:mapping/result-status.adoc[Global Result Status Mapping]
25 | ** xref:mapping/result-style.adoc[Global Result Style]
26 | ** xref:mapping/single-multi.adoc[Global Single & Multi Mapping]
27 | ** xref:mapping/endpoint.adoc[Endpoint Mapping]
28 | ** xref:mapping/annotation.adoc[Annotation Mapping]
29 | ** xref:mapping/extension.adoc[Extension Mapping]
30 | ** xref:mapping/null.adoc[Null Mapping]
31 | ** xref:mapping/package-name.adoc[package-name Mapping]
32 | ** xref:mapping/logging.adoc[log Mapping lookup]
33 | * xref:gradle.adoc[Gradle Integration]
34 | * xref:links.adoc[Links]
35 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/data.adoc:
--------------------------------------------------------------------------------
1 |
2 | //:page-layout: oap
3 |
4 | Huhu!
5 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/gradle.adoc:
--------------------------------------------------------------------------------
1 | = Gradle Plugin
2 | include::partial$links.adoc[]
3 |
4 | The xref:gradle::index.adoc[openapi-processor-gradle] gradle plugin can run any of the **openapi-processor**'s.
5 |
6 | To use it in a Gradle project, the Gradle file of the project requires a few additional instructions. The following sections describe how to activate & configure **openapi-processor-spring** in a `build.gradle` file.
7 |
8 |
9 | == adding the plugin
10 |
11 | To activate the plugin add it to (like any other gradle plugin) the `plugins` configuration:
12 |
13 | [source,groovy]
14 | ----
15 | plugins {
16 | ....
17 | // add openapi-processor-gradle plugin
18 | id 'io.openapiprocessor.openapi-processor' version ''
19 | }
20 | ----
21 |
22 | == configuring processor-spring
23 |
24 | The plugin will add an `openapiProcessor` configuration block that is used to configure the processors. Configuration for a specific processor belongs inside it with the processor name as configuration block name.
25 |
26 | [source,groovy]
27 | ----
28 | openapiProcessor {
29 |
30 | spring {
31 | processor 'io.openapiprocessor:openapi-processor-spring:'
32 | apiPath "$projectDir/src/api/openapi.yaml"
33 | targetDir "$projectDir/build/openapi"
34 | mapping "$projectDir/mapping.yaml"
35 | parser "INTERNAL"
36 | }
37 |
38 | }
39 | ----
40 |
41 | * `processor`: (**required**) the processor dependency. This works in the same way as adding a dependency to a configuration in the gradle `dependencies` block. It is given here to avoid unwanted side effects on the build dependencies of the project.
42 |
43 | * `apiPath`: (**required**) the path to the `openapi.yaml` file and the main input for the processor. If set in the top-level block, it will be used for all configured processors.
44 |
45 | * `targetDir`: (**required**) the output folder for generating interfaces and models. This is the parent of the `packageName` folder tree. It is recommended to set this to a subfolder of gradle's standard `build` directory, so it is cleared by the `clean` task and does not pollute the `sources`
46 | directory.
47 | +
48 | See <> how to include the `targetDir` in compilation and packing.
49 |
50 | * `mapping`: (**required**) provides the processor mapping options. This is a path to the YAML file. See xref:processor/configuration.adoc[Configuration] for a description of the mapping YAML. This replaces the `typeMappings` option.
51 |
52 | * `showWarnings`: (**optional**) `true` to show warnings from the open api parser or `false` (default) to show no warnings (this option has currently no effect).
53 |
54 | * `parser`: (**optional**), sets the openapi parser used to read the OpenAPI description. Available values are `SWAGGER`, `OPENAPI4J` or `INTERNAL` (default).
55 | ** `INTERNAL`: link:{openapi-parser}[internal OpenAPI parser, window="_blank"], supports *OpenAPI 3.0.x* & *OpenAPI 3.1.0*.
56 | ** `SWAGGER`: link:{swagger-parser}[Swagger OpenAPI parser, window="_blank"], supports *OpenAPI 3.0.x*
57 | ** `OPENAPI4J`: link:{openapi4j}[openapi4j OpenAPI parser, window="_blank"], supports *OpenAPI 3.0.x*. It provides better validation than `SWAGGER`, unfortunately it is no longer maintained and is deprecated.
58 | *** the parser provides JSON schema validation.
59 |
60 | == running processor-spring
61 |
62 | The plugin will add a gradle task `processSpring` to run the processor.
63 |
64 | To automatically generate & compile the processor output two additional configurations are necessary.
65 |
66 | * the `sourceSets` are extended to include the processor output (assuming a java project):
67 | +
68 | [source,groovy]
69 | ----
70 | sourceSets {
71 | main {
72 | java {
73 | // add generated files
74 | srcDir 'build/openapi'
75 | }
76 | }
77 | }
78 | ----
79 |
80 | * and the `compileJava` task gets a dependency on `processSpring`, so it runs before compilation (again, assuming a java project):
81 | +
82 | [source,groovy]
83 | ----
84 | // generate api before compiling
85 | compileJava.dependsOn ('processSpring')
86 | ----
87 |
88 | Adding automatic compilation in this way will also automatically include the generated files into the `jar` build artifact.
89 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/index.adoc:
--------------------------------------------------------------------------------
1 | :author: Martin Hauner
2 | :page-title: openapi-processor-spring
3 | //:page-aliases: latest@spring:ROOT:index.adoc
4 | include::partial$links.adoc[]
5 |
6 | //
7 | // content
8 | //
9 | image:openapi-processor-spring-at-1280x200.png[openapi-processor-spring]
10 |
11 | // badges
12 | link:{oaps-ci}[image:{badge-ci}[]]
13 | link:{oaps-license}[image:{badge-license}[]]
14 | link:{oap-central}[image:{badge-central}[]]
15 |
16 |
17 | *openapi-processor-spring* is an link:{openapi}[OpenAPI] interface & dto java code generator for link:{springboot}[Spring Boot].
18 |
19 | It supports an approach where you explicitly define and document your Service API (using OpenAPI)with the Interface to the outside and its usage in mind before you implement it. You do not derive the API later from the implementation and its implicit design. (of course, adapt as you go...)
20 |
21 | The advantages are:
22 |
23 | * you have a single place to maintain the api which makes it easier to create a consistent api and keep the overview.
24 | * it is easy to document in plain text. You can use Markdown in the OpenAPI `description` properties.
25 |
26 | The processor generates java interfaces based on the endpoint description of the API and simple POJO classes or records for parameter or response schemas defined in the API. The processor adds all the required spring & jackson annotations to the interface, and all that is left to *you* is the implementation of the generated interfaces in any way you like.
27 |
28 | The interfaces will help to keep the implementation in sync with the API. If anything relevant changes in the API, the interface changes and the compiler will warn that the interface is not implemented correctly.
29 |
30 | The target programming language is Java. Therefore, the generated code is usable from most JVM languages.
31 |
32 | See the xref:processor/index.adoc[processor intro] for a short example.
33 |
34 | == Playground
35 |
36 | openapi-processor has a link:{oap-playground}[playground, window="_blank"] application. You can try out some samples or your own YAML and view the code that the processor generates.
37 |
38 | == Features
39 |
40 | - generates **only java interfaces and java dto classes** (get/set POJOs or records) for all defined endpoints and schemas to allow full control of the endpoint implementation. It does not generate any other file. See xref:processor/index.adoc[processor].
41 |
42 | - **powerful type mappings with generic support** to map schemas defined in the openapi.yaml to existing java types.
43 | +
44 | For example, to map the openapi `array` type to a different java collection or to map paging parameters and results to th Spring types like `Page<>` & `Pageable`.
45 | +
46 | mappings can be defined globally, for a specific response, parameter or endpoint and or http method (of an endpoint). See xref:mapping/index.adoc[type mapping].
47 |
48 | - Annotation-based **WebFlux support**. Actually, there is *no* explicit WebFlux support, but the mapping allows defining a *single*, and a *multi* wrapper class. *single* wraps non-array like result types(e.g. `Mono<>`). *multi* replaces array like result types or parameters with the given multi mapping. For example, it will replace `List` with `Flux` if the multi mapping contains the fully qualified `Flux` type. +
49 | //[.badge .badge-since]+since 1.0.0.M13+
50 |
51 | - generates **human-readable code**.
52 |
53 | - add **additional parameters** to an endpoint which are not defined in the OpenAPI description. For
54 | example to pass a `HttpServletRequest` to the endpoint implementation. +
55 | //[.badge .badge-since]+since 1.0.0.M6+
56 |
57 | - supports **bean validations**. The constraints of the openapi description map to java bean validation annotations.
58 | //[.badge .badge-since]+since 1.0.0.M6+
59 |
60 | - allows **excluding endpoints** from generation. This is useful if the processor does not create the
61 | correct code for an endpoint. That way the processor can still be used for all the other endpoints. +
62 | //[.badge .badge-since]+since 1.0.0.M6+
63 |
64 | - handle **multiple responses** by generating one endpoint method for each response content type. +
65 | //[.badge .badge-since]+since 1.0.0.M11+
66 |
67 | - the generated code does not use swagger annotations. There is no need to generate the
68 | documentation from the code when the code originates from the documentation (i.e., an openapi.yaml).
69 |
70 | - *maven & gradle support* The plugin docs show how to run a processor and how to add the generated sources to the build.
71 |
72 | ** xref:maven::index.adoc[openapi-processor-maven] plugin.
73 | ** xref:gradle::index.adoc[openapi-processor-gradle] plugin.
74 |
75 | == Releases
76 |
77 | See the link:{oaps-releases}[release notes, window="_blank"] to find the latest release. The full
78 | artifact name is:
79 |
80 | .in gradle short notation
81 | ----
82 | io.openapiprocessor:openapi-processor-spring:
83 | ----
84 |
85 | == Feedback
86 |
87 | In case some feature is missing, or the generated code is not 100% what you would expect, create an link:{oaps-issues}[issue]. Preferably with a test case. Providing a test case will help significantly. :-)
88 |
89 | A _perfect_ test case is a single folder with two subfolders containing the source files and the expected output files:
90 |
91 | my-test-case
92 | +--- inputs.yaml
93 | |--- inputs
94 | | +--- mapping.yaml
95 | | \--- openapi.yaml
96 | +--- generated.yaml
97 | \--- generated
98 | +--- api
99 | | \--- Api.java
100 | \--- model
101 | \--- Foo.java
102 |
103 | `inputs.yaml` and `generated.yaml` use the same simple format:
104 |
105 | items:
106 | - inputs/openapi.yaml
107 | - inputs/mapping.yaml
108 |
109 | or
110 |
111 | items:
112 | - generated/api/Api.java
113 | - generated/model/Foo.java
114 |
115 |
116 | The link:{oapc-inttests}[core project], and the link:{oaps-inttests}[spring processor] have a number of existing integration tests that can be used as examples.
117 |
118 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/links.adoc:
--------------------------------------------------------------------------------
1 | = Links
2 | include::partial$links.adoc[]
3 |
4 | == OpenAPI
5 |
6 | * link:{openapi}[OpenAPI, window="_blank"] Homepage of the OpenAPI Initiative.
7 | * link:{openapi-spec}[OpenAPI specification, window="_blank"] Github repository of the OpenAPI specification.
8 | * link:{openapi-tools}[OpenAPI tools, window="_blank"] List of OpenAPI tools.
9 | * link:{openapi-generator}[OpenAPI generator, window="_blank"] Generate clients, servers and documentation from OpenAPI 2.0/3.x documents.
10 | * link:{swagger-parser}:[Swagger OpenAPI parser, window="_blank"] Java parser for OpenAPI specs.
11 | * link:{openapi4j}[openapi4j OpenAPI parser, window="_blank"] Alternative Java parser for OpenAPI specs.
12 |
13 | == Articles
14 |
15 | * link:https://dev.to/hauner/openapi-processor-spring-368k[openapi-processor-spring] an intro to openapi-processor-spring.
16 | * link:https://dev.to/hauner/openapi-processor-spring-type-mapping-4ecj[openapi-processor-spring type mapping] an intro to openapi-processor-spring type mapping.
17 | * link:https://dev.to/hauner/configuring-openapi-processor-maven-39pf[openapi-processor-maven] an intro to the maven plugin.
18 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/mapping/basic.adoc:
--------------------------------------------------------------------------------
1 | = Basic (primitive) mappings
2 | include::partial$links.adoc[]
3 |
4 | The OpenAPI specification defines a couple of basic link:{openapi-spec-types}[data types]. The basic data types are built-in into the processor. That means it will map the basic types automatically to a corresponding java type. There is no explicit type mapping required.
5 |
6 | The types with no default mapping can be mapped to a java type using the mapping configuration.
7 |
8 | == OpenAPI to Java type mapping
9 |
10 | The following table shows the automatic mapping of OpenAPIs primitive types to Java.
11 |
12 |
13 | |===
14 | | `type` | `format` | java type
15 |
16 | | `boolean`
17 | |
18 | | `java.lang.Boolean`
19 |
20 | | `integer`
21 | |
22 | | `java.lang.Integer`
23 |
24 | | `integer`
25 | | `int32`
26 | | `java.lang.Integer`
27 |
28 | | `integer`
29 | | `int64`
30 | | `java.lang.Long`
31 |
32 | | `number`
33 | |
34 | | `java.lang.Float`
35 |
36 | | `number`
37 | | `float`
38 | | `java.lang.Float`
39 |
40 | | `number`
41 | | `double`
42 | | `java.lang.Double`
43 |
44 | | `string`
45 | |
46 | | `java.lang.String`
47 |
48 | | `string`
49 | | `binary`
50 | | no default mapping
51 |
52 | | `string`
53 | | `byte`
54 | | no default mapping
55 |
56 | | `string`
57 | | `date`
58 | | `java.time.LocalDate`
59 |
60 | | `string`
61 | | `date-time`
62 | | `java.time.OffsetDataTime` (better would be `java.time.Instant`)
63 |
64 | | `string`
65 | | `password`
66 | | no default mapping
67 |
68 | |===
69 |
70 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/mapping/endpoint.adoc:
--------------------------------------------------------------------------------
1 | = Endpoint mappings
2 | include::partial$links.adoc[]
3 | include::partial$vars.adoc[]
4 |
5 | The global mapping variations are also available as explicit endpoint mappings. Instead of adding the mapping in the global sections `map/types`, `map/parameters` and `map/responses` they can be placed in the `map/paths` section as properties to an endpoint given by its path.
6 |
7 | [source,yaml]
8 | ----
9 | map:
10 |
11 | # path mappings, only valid for the given path
12 | paths:
13 |
14 | # a path
15 | /foo:
16 |
17 | # path-specific result wrapper
18 | result: {target type}
19 |
20 | # path-specific single wrapper
21 | single: {target type}
22 |
23 | # path-specific multi wrapper
24 | multi: {target type}
25 |
26 | # list of path-specific mappings
27 | types:
28 | - from: {source type} => {target type}
29 |
30 | # list of path-specific parameter mappings, mapped by parameter name
31 | parameters:
32 | - name: {parameter name} => {target type}
33 |
34 | # add a (usually technical) parameter that is not described in the OpenAPI
35 | - add: {parameter name} => {target type}
36 |
37 | # add an extra annotation to parameters of type source type
38 | - type: {source type} @ {annotation type}
39 |
40 | # list of path-specific content mappings, mapped by content type
41 | responses:
42 | - content: {content type} => {target type}
43 |
44 | # another path
45 | /foobar:
46 | # excluding an endpoint
47 | exclude: true
48 |
49 | # path-specific result wrapper
50 | result: {target type}
51 |
52 | # path-specific single wrapper
53 | single: {target type}
54 |
55 | # path-specific multi wrapper
56 | multi: {target type}
57 |
58 | # list of path-specific mappings
59 | types:
60 | - from: {source type} => {target type}
61 |
62 | # list of path-specific parameter mappings, mapped by parameter name
63 | parameters:
64 | - name: {parameter name} => {target type}
65 |
66 | # add a (usually technical) parameter not described in the OpenAPI
67 | - add: {parameter name} => {target type}
68 |
69 | # add an extra annotation to parameters of type source type
70 | - type: {source type} @ {annotation type}
71 |
72 | # list of path-specific content mappings, mapped by content type
73 | responses:
74 | - content: {content type} => {target type}
75 | ----
76 |
77 | The mappings defined as properties of an endpoint will be used only for this endpoint. They don't
78 | have any effect on other endpoints.
79 |
80 | == Endpoint mappings by http method
81 |
82 | It is possible to add mappings that apply only to a specific http method. The motivation for this is limiting the mapping only to the place where it is necessary. Http method mappings have priority over other mappings. In general, the most specific mapping is used.
83 |
84 | Here are a few examples of possible http endpoint mappings:
85 |
86 | [source,yaml,subs="attributes"]
87 | ----
88 | openapi-processor-mapping: {var-mapping-version}
89 |
90 | map:
91 |
92 | paths:
93 | /foo:
94 |
95 | # normal endpoint mappings apply to all http methods (behaves exactly as before)
96 | types:
97 | - type: Foo => java.util.Collection
98 |
99 | # endpoint http method mappings apply only the specified http method
100 | get:
101 | result: org.springframework.http.ResponseEntity
102 |
103 | post:
104 | parameters:
105 | - add: request => javax.servlet.http.HttpServletRequest
106 |
107 | patch:
108 | null: org.openapitools.jackson.nullable.JsonNullable = JsonNullable.undefined()
109 | ----
110 |
111 | The structure follows the OpenAPI, i.e. the http methods (or OpenAPI operations) are properties of the endpoint path.
112 |
113 | An http method mapping allows the same mappings as the endpoint mapping without http method, i.e. `exclude`, `result`, `single`, `multi`, `null`, `types`, `parameters` and `responses` (see the link:{json-schema}[json schema]).
114 |
115 | The last example is using the a `null` mapping that may only be interesting for the `PATCH` http method because there is no need for `nullable` properties for `GET` or `PUT`.
116 |
117 | Note that it is **not** possible to use different `null` mappings (or one http mapping with `null` and one without) on the **same** model schema. The processor generates only a *single* class for model schemas and with two different and ambiguous mappings the result is (currently) undefined. It is recommended to use two different schemas if the `null` mapping should only apply to a single method.
118 |
119 | == excluding endpoints
120 |
121 | //[.badge .badge-since]+since 1.0.0.M6+
122 |
123 | It is possible to exclude endpoints from generation to make it easier to provide a hand written
124 | interface for the excluded endpoint.
125 |
126 | Excluding does not completely ignore the endpoint. Instead of generating it into the normal
127 | interface it is generated to a new interface with `Excluded` attached to its name. Type mappings
128 | still apply.
129 |
130 | That way the generated code is still available for reference, but it can be skipped by not
131 | implementing the `Excluded` interface.
132 |
133 | [source,yaml]
134 | ----
135 | map:
136 | /foo:
137 | # excluding an endpoint
138 | exclude: true
139 | ----
140 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/mapping/extension.adoc:
--------------------------------------------------------------------------------
1 | = Extension mapping
2 | include::partial$links.adoc[]
3 | include::partial$vars.adoc[]
4 |
5 | [.badge .badge-since]+since 2024.1+
6 |
7 | This is part of annotation mapping. See xref:mapping/annotation.adoc[Annotation mapping] that shows more details about the annotation mapping format.
8 |
9 | Apart from the annotation mapping by an OpenAPI type, openapi-processor can use `x-` tension properties in the OpenAPI to add additional annotations to a schema property.
10 |
11 | Here is a simple schema that has `x-` tensions on the `bar` property.
12 |
13 | [source,yaml,subs="attributes"]
14 | ----
15 | openapi: {var-openapi-version}
16 | # ...
17 | components:
18 | schemas:
19 | Foo:
20 | type: object
21 | properties:
22 | bar:
23 | type: string
24 | x-foo: single
25 | x-bar:
26 | - listA
27 | - listB
28 | ----
29 |
30 | In general openapi-processor will ignore the `x-` tension properties unless we map the `x-` tensions/values to annotations like this:
31 |
32 | [source,yaml,subs="attributes"]
33 | ----
34 | openapi-processor-mapping: {var-mapping-version}
35 | options:
36 | # ...
37 |
38 | map:
39 | extensions:
40 | x-foo: single @ io.oap.FooA(value = "any") # <1>
41 | x-bar:
42 | - listA @ io.oap.FooB # <2>
43 | - listB @ io.oap.FooC
44 | ----
45 |
46 | NOTE: openapi-processor will only recognize *string* values of an extension. It will ignore any other type.
47 |
48 | The mapping allows two variations:
49 |
50 | <1> in case the `x-` tension property has only a single value we can directly map that value to an annotation.
51 | <2> in case the `x-` tension property has a list of values we can map each value to a different annotation.
52 |
53 |
54 | With this mapping the generated Dto class will have the additional annotations on the property.
55 |
56 | [source,java]
57 | ----
58 | package generated.model;
59 |
60 | import com.fasterxml.jackson.annotation.JsonProperty;
61 | import generated.support.Generated;
62 | import io.oap.FooA;
63 | import io.oap.FooB;
64 | import io.oap.FooC;
65 |
66 | @Generated(value = "openapi-processor-core")
67 | public class Foo {
68 |
69 | @FooA(value = "any")
70 | @FooB
71 | @FooC
72 | @JsonProperty("bar")
73 | private String bar;
74 |
75 | public String getBar() {
76 | return bar;
77 | }
78 |
79 | public void setBar(String bar) {
80 | this.bar = bar;
81 | }
82 |
83 | }
84 | ----
85 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/mapping/index.adoc:
--------------------------------------------------------------------------------
1 | = Type mapping
2 |
3 | Type mapping is an important feature of the processor and helps it to create the expected code.
4 |
5 | Using type mapping, we can tell the processor to map types (schemas) from an openapi.yaml description to a specific existing java type instead of generating a model class from the source OpenAPI type.
6 |
7 | This can be a type created by us or a type from a framework. For example, to map paging parameters and result to the Spring types `Page<>` & `Pageable`.
8 |
9 | It can also be used to map the OpenAPI `array` type to a different java collection type if the default does not fulfill our needs.
10 |
11 | Type mapping is very flexible. It is possible to define the mapping globally, globally for a specific response or parameter or limited to a specific endpoint or even http method for an endpoint.
12 |
13 | Type mapping also supports (nested) generic parameters to the target type. One level.
14 |
15 | Type mapping works best with named schemas (i.e., schemas `$ref` erenced by their name).
16 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/mapping/logging.adoc:
--------------------------------------------------------------------------------
1 | = log mapping lookup
2 | include::partial$vars.adoc[]
3 |
4 | It is possible to let a processor log all the mapping lookups. It *may* be useful to understand why mapping does not work.
5 |
6 | If a mapping doesn't work, the first step is to check if the processor is applying it or if it ignores it. The mapping logging will report which mappings were selected for an OpenAPI type.
7 |
8 | If the processor doesn't select the mapping, there may be something wrong with the mapping. Maybe a typo.
9 |
10 | In case the processor uses the mapping, but it still doesn't behave as expected, it may be a bug.
11 |
12 | To control the logging, there are two new xref:processor/configuration.adoc#_logging[logging options].
13 |
14 | [source,yaml,subs="attributes"]
15 | ----
16 | openapi-processor-mapping: {var-mapping-version}
17 | options:
18 | package-name: io.openapiprocessor.generated
19 |
20 | map:
21 | # ...
22 |
23 | logging:
24 | mapping: true # <1>
25 | mapping-target: stdout #<2>
26 | ----
27 |
28 | <1> apart from enabling logging of the mapping lookups in the `mapping.yaml` you may want to set the `mapping-target`.
29 |
30 | <2> If set to `logger` the mapping lookup gets logged at `info` level to link:https://www.slf4j.org/[slf4j]. If set to `stdout` the mapping lookup gets written directly to `stdout` without slf4j.
31 |
32 | Enabling the logging will produce many blocks similar to:
33 |
34 | ----
35 | looking for any type mapping of name: 'foo2' path: GET '/fooA' type: 'array' A
36 | + global
37 | - parameters (type)
38 | - name: foo2 => java.util.List
39 | - name: bar => io.openapiprocessor.Bar1
40 | - name: param @ io.openapiprocessor.ParamAnnotation
41 | - type: Bar @ io.openapiprocessor.ParamAnnotation
42 | + parameters (name)
43 | + name: foo2 => java.util.List
44 | - name: bar => io.openapiprocessor.Bar1
45 | - name: param @ io.openapiprocessor.ParamAnnotation
46 | - type: Bar @ io.openapiprocessor.ParamAnnotation
47 | ----
48 |
49 | It always starts with a `looking for ..` followed by what it is looking for and the OpenAPI name or type to find, related to which path and its type. Then it lists all mappings checked with their location in the mapping file.
50 |
51 | In this case it looks for *any* mapping of `foo2`. *any* means that it is looking for any mapping, by testing all mappings by priority. More specific mappings have a higher priority and win.
52 |
53 | It did not find a mapping by its type (`array` in this case), but it found a name mapping for `foo2`, indicated by the `+` sign. The mappings that do not match get marked with a `-` sign.
54 |
55 | Here is a snippet from the OpenAPI yaml that is processed here. We have an endpoint `fooA` with a query parameter `foo2` of type `array`.
56 |
57 | [source,yaml]
58 | ----
59 | /fooA:
60 | get:
61 | summary: foo A summary.
62 | description: foo A endpoint
63 | tags: [foo]
64 | parameters:
65 | - in: query
66 | name: foo1
67 | # ...
68 | - in: query
69 | name: foo2
70 | description: parameter foo2
71 | schema:
72 | type: array
73 | items:
74 | type: string
75 | - in: query
76 | name: bar
77 | # ...
78 | responses:
79 | '200':
80 | # ...
81 | ----
82 |
83 | The example is a small part of the link:https://github.com/openapi-processor/openapi-processor-base/tree/main/openapi-processor-core/src/testInt/resources/tests/map-many[`map many`] integration test.
84 |
85 |
86 | == maven
87 |
88 | Maven can handle both mapping targets. If the `mapping-target` is set to `logger` it is necessary to enable the mapping logger `io.openapiprocessor.core.converter.mapping` to see any output.
89 |
90 | For example, by running `maven` with:
91 |
92 | ----
93 | ./mvnw compile -Dorg.slf4j.simpleLogger.log.io.openapiprocessor.core.converter.mapping=info
94 | ----
95 |
96 | If the `mapping-target` is `stdout` the processor output will be written without the usual log level prefix.
97 |
98 |
99 | === summary
100 |
101 | to enable logging with maven use:
102 |
103 | [source,yaml,subs="attributes"]
104 | ----
105 | openapi-processor-mapping: {var-mapping-version}
106 | options:
107 | package-name: ...
108 |
109 | map:
110 | # ...
111 |
112 | logging:
113 | mapping: true
114 | mapping-target: stdout
115 | ----
116 |
117 | to get the simple output, or
118 |
119 | [source,yaml,subs="attributes"]
120 | ----
121 | openapi-processor-mapping: {var-mapping-version}
122 | options:
123 | package-name: ...
124 |
125 | map:
126 | # ...
127 |
128 | logging:
129 | mapping: true
130 | ----
131 |
132 | to get the log-level-based output. Remember to enable the logger in this case as described above.
133 |
134 | == gradle
135 |
136 | Gradle requires the `mapping-target` to be `stdout`. Gradle can only globally enable log levels, which is deafening. The best option to log the mapping lookups is simply to write them to `stdout`.
137 |
138 | === summary
139 |
140 | to enable logging with gradle use:
141 |
142 | [source,yaml,subs="attributes"]
143 | ----
144 | openapi-processor-mapping: {var-mapping-version}
145 | options:
146 | package-name: ...
147 |
148 | map:
149 | # ...
150 |
151 | logging:
152 | mapping: true
153 | mapping-target: stdout
154 | ----
155 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/mapping/null.adoc:
--------------------------------------------------------------------------------
1 | include::partial$vars.adoc[]
2 | :nullable: https://github.com/OpenAPITools/jackson-databind-nullable
3 |
4 | = Null mapping
5 |
6 | The `null` mapping is used to map (or better wrap) OpenAPI `nullable` properties to link:{nullable}[jackson-databind-nullable].
7 |
8 | This is useful to implement a link:{https://tools.ietf.org/html/rfc7386}[json merge patch] api that needs to know if a property was not set at all or explicitly set to *"null"* ("null" means to clear the property value).
9 |
10 | After the (standard jackson) binding of the request payload to pojos there is no way to distinguish between `null`, and non-existing properties in Java. That's where link:{nullable}[jackson-databind-nullable] comes into play.
11 |
12 | It provides a wrapper type that distinguishes between `null` and non-existent.
13 |
14 | For example, the `/foo` api endpoint uses the following schema as the request body
15 |
16 | [source,yaml]
17 | ----
18 | components:
19 | schemas:
20 |
21 | Foo:
22 | description: a Foo
23 | type: object
24 | properties:
25 | bar:
26 | nullable: true
27 | type: string
28 | ----
29 |
30 | Normally the processor would generate a simple pojo with a `String` property.
31 |
32 | By adding a `null` mapping for the `/foo` endpoint (this does work only on the endpoint level. A global null mapping gets ignored):
33 |
34 | [source,yaml,subs="attributes"]
35 | ----
36 | openapi-processor-mapping: {var-mapping-version}
37 |
38 | map:
39 | paths:
40 | /foo:
41 | null: org.openapitools.jackson.nullable.JsonNullable
42 | # with initialization:
43 | # null: org.openapitools.jackson.nullable.JsonNullable = JsonNullable.undefined()
44 |
45 | # or even better, limiting it to the patch http method
46 | /bar:
47 | patch:
48 | null: org.openapitools.jackson.nullable.JsonNullable
49 | # with initialization:
50 | # null: org.openapitools.jackson.nullable.JsonNullable = JsonNullable.undefined()
51 | ----
52 |
53 | it will generate the following code:
54 |
55 | [source,java]
56 | ----
57 | import com.fasterxml.jackson.annotation.JsonProperty;
58 | import org.openapitools.jackson.nullable.JsonNullable;
59 |
60 | public class Foo {
61 |
62 | @JsonProperty("bar")
63 | private JsonNullable bar;
64 | // with initialization:
65 | // private JsonNullable bar = JsonNullable.undefined();
66 |
67 | public JsonNullable getBar() {
68 | return bar;
69 | }
70 |
71 | public void setBar(JsonNullable bar) {
72 | this.bar = bar;
73 | }
74 |
75 | }
76 | ----
77 |
78 | It is now possible to check if a property was explicitly set to `null` or if it was not set at all.
79 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/mapping/package-name.adoc:
--------------------------------------------------------------------------------
1 | = package-name mapping
2 |
3 | The type mapping (v2 and up) configuration allows to reference the target `package-name` in generic parameters using the `+{package-name}+` expression. This makes it possible to adjust the `package-name` without touching the mapping, and it does reduce duplication.
4 |
5 | [source,yaml,subs="attributes"]
6 | ----
7 | openapi-processor-mapping: {var-mapping-version}
8 | options:
9 | package-name: io.openapiprocessor.generated
10 |
11 | map:
12 | types:
13 | - type: FooPage => org.springframework.data.domain.Page<{package-name}.model.Foo>
14 | ----
15 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/mapping/response.adoc:
--------------------------------------------------------------------------------
1 | = (global) Response mappings
2 |
3 | Global response mapping will replace the result type of the endpoint in the api description based on its **content type** to the given java type.
4 |
5 | It is defined like below, and it should be added to the `map/responses` section in the mapping.yaml which is a list of global response mappings.
6 |
7 | A single global response mapping can have the following properties:
8 |
9 | [source,yaml]
10 | ----
11 | - content: {content type} => {target type}
12 | generics:
13 | - {a generic type}
14 | - {another generic type}
15 | ----
16 |
17 | * **content** is required.
18 |
19 | ** **{content type}** is the content type of the endpoint response that should be replaced by **{target type}**.
20 |
21 | ** **{target type}** is the fully qualified class name of the java type that should be used for all endpoint content types **{content type}**.
22 |
23 | * **generics** defines the list of types that should be used as generic type parameters to the java type given by **{target type}**.
24 |
25 | [CAUTION]
26 | ====
27 | Since the processor will simply match the content type string, take care that all responses of this content type should really use the same type!
28 |
29 | This is probably only useful for vendor content types. Globally mapping the content type for example of `application/json` does not look like a good idea.
30 | ====
31 |
32 | == Example
33 |
34 | Given the following (global) response mapping
35 |
36 | [source,yaml]
37 | ----
38 | map:
39 |
40 | # list of global response mappings, mapped by content type
41 | responses:
42 | - content: application/vnd.something => io.openapiprocessor.Something
43 | ----
44 |
45 | and an openapi.yaml with multiple endpoints returning their result as content type `application/vnd.something`
46 |
47 | [source,yaml,subs="attributes"]
48 | ----
49 | openapi: {var-openapi-version}
50 | info:
51 | title: global response content type mapping example
52 | version: 1.0.0
53 |
54 | paths:
55 | /do-something:
56 | get:
57 | responses:
58 | '200':
59 | description: response
60 | content:
61 | application/vnd.something:
62 | schema:
63 | type: string
64 |
65 | /do-something-else:
66 | get:
67 | responses:
68 | '200':
69 | description: response
70 | content:
71 | application/vnd.something:
72 | schema:
73 | type: string
74 | ----
75 |
76 | the processor will use `io.openapiprocessor.Something` as the java type for **all** responses with the content type `application/vnd.something`.
77 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/mapping/result-status.adoc:
--------------------------------------------------------------------------------
1 | include::partial$links.adoc[]
2 | include::partial$vars.adoc[]
3 |
4 | = Result Status
5 |
6 | [.badge .badge-since]+since 2025.3+
7 |
8 | The `result-status` configuration controls if the processor adds a `@ResponseStatus` annotation. If enabled (default) it will automatically add a `@ResponseStatus` annotation if the OpenAPI endpoint has a success result code not equal to 200 OK. I.e, 2xx != 200.
9 |
10 | The default response status of Spring Boot is 200, so the processor will not add an unnecessary annotation for 200.
11 |
12 | [NOTE]
13 | ====
14 | This will conflict with manually added `@ResponseStatus` annotations.
15 |
16 | To keep the old behavior, i.e., no automatically added `@ResponseStatus` annotations, set `result-status: false` on the global mapping level.
17 | ====
18 |
19 | It is configured by adding it to the mapping section of the configuration file. It is available on all levels, i.e., global, endpoint and endpoint method.
20 |
21 | [source,yaml]
22 | ----
23 | openapi-processor-mapping: {var-mapping-version}
24 |
25 | options:
26 | # ...
27 |
28 | map:
29 | # result-status: true is the default
30 | # setting it to false on the global level disables it
31 | result-status: false
32 |
33 | paths:
34 | # enable it for a specific endpoint
35 | /foo:
36 | result-status: true
37 |
38 | # ... or for a specific method of an endpoint
39 | #get:
40 | # result-status: true
41 | ----
42 |
43 | * **result-status** (optional).
44 |
45 | ** `true`: add a `@ResponseStatus` annotation if the response status of a response is a success code not equal to 200.
46 |
47 | ** `false` (default before 2025.3): do not generate any `@ResponseStatus` annotation.
48 |
49 | ** `result-status` is available at the endpoint & http method level.
50 |
51 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/mapping/result-style.adoc:
--------------------------------------------------------------------------------
1 | = Result Style
2 | include::partial$links.adoc[]
3 |
4 | The `result-style` configuration controls how the processor handles the return type of endpoint success and error response types if both are defined in the OpenAPI.
5 |
6 | [source,yaml]
7 | ----
8 | # mapping.yaml header
9 | # ...
10 |
11 | map:
12 | #result-style: success # use the success result type, this is the default
13 | result-style: all # use an Object return type
14 |
15 | # result-style is available at the endpoint & http method level.
16 | /foo:
17 | #result-style: success
18 |
19 | get:
20 | result-style: success
21 | ----
22 |
23 | * **result-style** (optional).
24 |
25 | ** `success` ([.badge .badge-since]+default since 2021.5+): generates endpoint methods with the success response type even if it has error responses. This assumes that the errors are reported by exceptions.
26 |
27 | ** `all` (default before 2021.5): generates endpoint methods with an `Object` return type if it has error responses.
28 |
29 | ** [.badge .badge-since]+since 2025.2+ `result-style` is available at the endpoint & http method level.
30 |
31 |
32 | See xref:processor/endpoint-content.adoc[endpoint content types] for a more detailed description.
33 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/mapping/result.adoc:
--------------------------------------------------------------------------------
1 | = Result mapping
2 | include::partial$links.adoc[]
3 |
4 | A link:{spring-responseentity}[`ResponseEntity<>`] allows an endpoint implementation full control of the response.
5 |
6 | Here is a super simple example:
7 |
8 | [source,java]
9 | ----
10 | public ResponseEntity getFoo() {
11 | return ResponseEntity.ok("foo");
12 | }
13 | ----
14 |
15 | To enable a result wrapper set the `result` mapping in the mapping yaml to a fully qualified java type.
16 |
17 | [source,yaml]
18 | ----
19 | map:
20 | result: org.springframework.http.ResponseEntity
21 | ----
22 |
23 | NOTE: The processor expects that it takes a single generic parameter.
24 |
25 | Depending on the number of defined response content types the parameter of the `ResponseEntity<>` will be either the java type or the *unknown type*.
26 |
27 | |===
28 | |responses | ResponseEntity<>
29 |
30 | |one
31 | |`ResponseEntity`
32 |
33 | |multiple
34 | |`ResponseEntity>`
35 | |===
36 |
37 | NOTE: prior to 1.0.0.M13 all results were auto-wrapped with `ResponseEntity<>`.
38 |
39 |
40 | == Limit to Endpoint
41 |
42 | The `result` mapping works as endpoint-specific mapping too. That way it is possible to use the `ResponseEntity<>` only on single endpoints.
43 |
44 |
45 | So a mapping like this:
46 |
47 | [source,yaml]
48 | ----
49 | map:
50 |
51 | /foo:
52 | result: org.springframework.http.ResponseEntity
53 | ----
54 |
55 | will only wrap the result of the endpoint `/foo`.
56 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/mapping/schema.adoc:
--------------------------------------------------------------------------------
1 | = (global) Schema mappings
2 | include::partial$vars.adoc[]
3 |
4 | [.badge .badge-since]+since 2025.1+
5 |
6 | Schema mappings add a new (global) mapping level. They apply only to (object, i.e., dto) schema properties.
7 |
8 | That means the mappings are only used when the source type is used as a property type in a generated dto class.
9 |
10 | [NOTE]
11 | ====
12 | This is (currently) only supported on the global level and only for xref::mapping/annotation.adoc[].
13 | ====
14 |
15 | Schema mappings try to solve the case where a type should be annotated, but *only* if it is used in a generated dto object. Especially it should *not* annotate parameters.
16 |
17 | The example will make this more clear.
18 |
19 | == Example
20 |
21 | In the example OpenAPI below is a year value (<1>) that is used on the response schema and as query parameter.
22 |
23 | [source,yaml,subs="attributes"]
24 | ----
25 | openapi: {var-openapi-version}
26 | info:
27 | title: schema mapping
28 | version: 1.0.0
29 |
30 | paths:
31 |
32 | /foo:
33 | get:
34 | parameters:
35 | - name: year
36 | description: year parameter
37 | in: query
38 | required: true
39 | schema:
40 | type: integer # <1>
41 | format: year
42 | responses:
43 | '200':
44 | description: the foo result
45 | content:
46 | application/json:
47 | schema:
48 | $ref: '#/components/schemas/Foo'
49 |
50 | components:
51 | schemas:
52 |
53 | Foo:
54 | type: object
55 | properties:
56 | year:
57 | type: integer # <1>
58 | format: year
59 | ----
60 |
61 | Using a typical mapping the processor will use `java.time.Year` instead of a simple `Integer` type in the generated code.
62 |
63 | [source,yaml,subs="attributes"]
64 | ----
65 | openapi-processor-mapping: {var-mapping-version}
66 |
67 | options:
68 | package-name: generated
69 | format-code: false
70 |
71 | map:
72 | types:
73 | - type: integer:year => java.time.Year
74 | ----
75 |
76 | Spring (with Jackson) may not serialize the type in the expected format by default. In case of `java.time.Year` it will be `String` and not a number.
77 |
78 | To change serialization, Jackson provides the `JsonFormat` annotation:
79 |
80 | @JsonFormat(JsonFormat.Shape.NUMBER_INT)
81 |
82 | would change serialization of `java.time.Year` to a number.
83 |
84 |
85 | Adding the annotation mapping for this at the global type level
86 |
87 | [source,yaml,subs="attributes"]
88 | ----
89 | openapi-processor-mapping: {var-mapping-version}
90 |
91 | options:
92 | package-name: generated
93 | format-code: false
94 |
95 | map:
96 | types:
97 | - type: integer:year => java.time.Year
98 | - type: integer:year @ com.fasterxml.jackson.annotation.JsonFormat(shape = com.fasterxml.jackson.annotation.JsonFormat.Shape.NUMBER_INT)
99 | ----
100 |
101 | would add the annotation, but not only in the dto, as wanted
102 |
103 | [source,java]
104 | ----
105 | package generated.model;
106 |
107 | import com.fasterxml.jackson.annotation.JsonFormat;
108 | import com.fasterxml.jackson.annotation.JsonProperty;
109 | import generated.support.Generated;
110 | import java.time.Year;
111 |
112 | @Generated(value = "openapi-processor-core", version = "latest")
113 | public class Foo {
114 |
115 | @JsonFormat(shape = JsonFormat.Shape.NUMBER_INT)
116 | @JsonProperty("year")
117 | private Year year;
118 |
119 | // ...
120 | }
121 | ----
122 |
123 | but also at the method parameter of the generated interface:
124 |
125 | [source,java]
126 | ----
127 | package generated.api;
128 |
129 | import com.fasterxml.jackson.annotation.JsonFormat;
130 | import generated.model.Foo;
131 | import generated.support.Generated;
132 | import java.time.Year;
133 | import org.springframework.web.bind.annotation.GetMapping;
134 | import org.springframework.web.bind.annotation.RequestParam;
135 |
136 | @Generated(value = "openapi-processor-core", version = "test")
137 | public interface Api {
138 |
139 | @GetMapping(path = "/foo", produces = {"application/json"})
140 | Foo getFoo(@RequestParam(name = "year", required = false) @JsonFormat(shape = JsonFormat.Shape.NUMBER_INT) Year year);
141 |
142 | }
143 | ----
144 |
145 | That is not wanted. To avoid it, the annotation mapping should be added to the new `schemas` mapping level:
146 |
147 | [source,yaml,subs="attributes"]
148 | ----
149 | openapi-processor-mapping: {var-mapping-version}
150 |
151 | options:
152 | package-name: generated
153 | format-code: false
154 |
155 | map:
156 | types:
157 | - type: integer:year => java.time.Year
158 |
159 | schemas:
160 | - type: integer:year @ com.fasterxml.jackson.annotation.JsonFormat(shape = com.fasterxml.jackson.annotation.JsonFormat.Shape.NUMBER_INT)
161 | ----
162 |
163 | This tells the processor to add it only to the generated dto class and not to the interface.
164 |
165 | [source,java]
166 | ----
167 | package generated.api;
168 |
169 | import generated.model.Foo;
170 | import generated.support.Generated;
171 | import java.time.Year;
172 | import org.springframework.web.bind.annotation.GetMapping;
173 | import org.springframework.web.bind.annotation.RequestParam;
174 |
175 | @Generated(value = "openapi-processor-core", version = "test")
176 | public interface Api {
177 |
178 | @GetMapping(path = "/foo", produces = {"application/json"})
179 | Foo getFoo(@RequestParam(name = "year", required = false) Year year);
180 |
181 | }
182 | ----
183 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/mapping/single-multi.adoc:
--------------------------------------------------------------------------------
1 | :responseentity: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html
2 | include::partial$vars.adoc[]
3 |
4 | = (global) Single & Multi mapping
5 |
6 | == single & multi wrapper
7 |
8 | //[.badge .badge-since]+since 1.0.0.M13+
9 |
10 | When using WebFlux we like to wrap certain parameters & results types in reactive types like `Mono<>` or `Flux<>`.
11 |
12 | To achieve this, the processor knows two special mappings:
13 |
14 | * `single`: to wrap a non-array like type (i.e., not a collection)
15 | * `multi`: to wrap an array like type (i.e., a collection)
16 |
17 |
18 | === multi
19 |
20 | [source,yaml]
21 | ----
22 | map:
23 | multi: reactor.core.publisher.Flux
24 | ----
25 |
26 | Which will use `Flux<>` as collection wrapper instead of the original java collection type for all
27 | list *responses* (or *parameters*). `multi` does not affect collections in model types.
28 |
29 | === single
30 |
31 | To map non-array like responses to a `Mono<>` set the `single` mapping:
32 |
33 | [source,yaml]
34 | ----
35 | map:
36 | single: reactor.core.publisher.Mono
37 | ----
38 |
39 | The processor will now wrap all non-array like response types with the given `single` mapping.
40 |
41 | == endpoint-specific mapping
42 |
43 | it is also possible to configure `single` & `multi` on the xref:mapping/endpoint.adoc[endpoint level].
44 |
45 |
46 | == single & multi with result mapping
47 |
48 | It is possible to use `single` & `multi` mappings together with the `result` mapping, i.e. `ResponseEntity`.
49 |
50 | `result` will wrap `single`
51 |
52 | [source, java]
53 | ----
54 | ResponseEntity>
55 | ----
56 |
57 | and `multi`
58 |
59 | [source, java]
60 | ----
61 | ResponseEntity>
62 | ----
63 |
64 | Unfortunately, if you need the reactive result to modify the http response, something like this:
65 |
66 | [source, java]
67 | ----
68 | // does not work
69 | public ResponseEntity> someEndpoint() {
70 | return someBean.getResult()
71 | .map(r -> ResponseEntity
72 | .ok()
73 | .eTag(r.eTag())
74 | .body(Mono.just(r)));
75 | }
76 | ----
77 |
78 | it will not work because the result type of the statement is `Mono>>` and not the expected `ResponseEntity>`. This can be fixed by modifying the `result` mapping to
79 |
80 | [source, yaml,,subs="attributes"]
81 | ----
82 | openapi-processor-mapping: {var-mapping-version}
83 |
84 | options:
85 | # ...
86 |
87 | map:
88 | # wrap the ResponseEntity with Mono
89 | result: reactor.core.publisher.Mono
90 |
91 | single: reactor.core.publisher.Mono
92 | multi: reactor.core.publisher.Flux
93 | ----
94 |
95 | which will now generate the endpoint signature as
96 |
97 | [source, java]
98 | ----
99 | public Mono>> someEndpoint() {
100 | // ...
101 | }
102 | ----
103 |
104 | and the above code will now work.
105 |
106 | It is recommended to configure this on the endpoint level if you just need this for a few endpoints.
107 |
108 | See also Spring link:{responseentity}[`ResponseEntity`] documentation.
109 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/mapping/structure.adoc:
--------------------------------------------------------------------------------
1 | = type mapping structure
2 | include::partial$links.adoc[]
3 | include::partial$vars.adoc[]
4 |
5 | The type mapping is part of the mapping YAML (see xref:processor/configuration.adoc[Configuration]) and configured under the `map` key. The `map` key contains multiple sections to define the different kinds of type mappings.
6 |
7 | All sections are optional.
8 |
9 | == type mapping structure
10 |
11 | //[.badge .badge-since]+since 1.0.0.M15+
12 |
13 | [IMPORTANT]
14 | ====
15 | The mapping file needs the following key on the top-level. Best place is the first line of the `mapping.yaml` file.
16 |
17 | [source,yaml,subs="attributes"]
18 | ----
19 | openapi-processor-mapping: {var-mapping-version}
20 | ----
21 | ====
22 |
23 | The version increases from time to time when openapi-processor requires a new or changed configuration. In case the version changes, it is mentioned in the release notes.
24 |
25 |
26 | === basic mapping
27 |
28 | To map a source type to a destination type, it is using an `=>` arrow as a *mapping operator* instead of individual keywords:
29 |
30 | [source,yaml]
31 | ----
32 |
33 | some-key: {source type} => {target type}
34 |
35 | ----
36 |
37 | *source type* is usually a name (or type) from the OpenAPI description and *target type* is usually a java type.
38 |
39 | === full structure
40 |
41 | The full structure of the mapping looks like this (a real mapping file will usually use just a few of the possible keys):
42 |
43 | [source,yaml]
44 | ----
45 | map:
46 | # global mappings, applies to all paths/endpoints
47 |
48 | # result wrapper, e.g. org.springframework.http.ResponseEntity
49 | result: {target type}
50 |
51 | # result-style:
52 | result-style: {success|all}
53 |
54 | # result status annotation (default true)
55 | result-status: {true|false}
56 |
57 | # single wrapper, e.g. reactor.core.publisher.Mono
58 | single: {target type}
59 |
60 | # multi wrapper, e.g. reactor.core.publisher.Flux
61 | multi: {target type}
62 |
63 | # list of global mappings
64 | types:
65 | # replace a source type with the given target type
66 | - type: {source type} => {target type}
67 |
68 | # add an extra annotation to the source type
69 | - type: {source type} @ {target type}
70 |
71 | # list of global schema mappings
72 | schemas:
73 | # add an extra annotation to the source type, but only on object properties
74 | - type: {source type} @ {target type}
75 |
76 | # list of global parameter mappings
77 | parameters:
78 | - name: {parameter name} => {target type}
79 |
80 | # add a (usually technical) parameter not described in the OpenAPI
81 | - add: {parameter name} => {target type}
82 |
83 | # add an extra annotation to parameters of the source type
84 | - type: {source type} @ {annotation type}
85 |
86 | # list of global content mappings, mapped by content type
87 | responses:
88 | - content: {content type} => {target type}
89 |
90 | # path-, endpoint-, method-specific mappings
91 | paths:
92 |
93 | # a path
94 | /foo:
95 | # exclude endpoint
96 | exclude: {true|false}
97 |
98 | # path-specific result wrapper
99 | result: {target type}
100 |
101 | # result-style:
102 | result-style: {success|all}
103 |
104 | # result status annotation (default true)
105 | result-status: {true|false}
106 |
107 | # path-specific single wrapper
108 | single: {target type}
109 |
110 | # path-specific multi wrapper
111 | multi: {target type}
112 |
113 | # nullable mapping with optional initial value
114 | null: {target type} (= {initializer})
115 |
116 | # list of path-specific mappings
117 | types:
118 | # replace the source type with the given target type
119 | - from: {source type} => {target type}
120 |
121 | # add an extra annotation to the source type
122 | - type: {source type} @ {target type}
123 |
124 | # list of path-specific parameter mappings
125 | parameters:
126 | - name: {parameter name} => {target type}
127 |
128 | # add a (usually technical) parameter not described in the OpenAPI
129 | - add: {parameter name} => {target type}
130 |
131 | # add an extra annotation to parameters of source type
132 | - type: {source type} @ {annotation type}
133 |
134 | # list of path-specific content mappings, mapped by content type
135 | responses:
136 | - content: {content type} => {target type}
137 |
138 | # limit mapping to a specific http method (all methods are allowed)
139 | get:
140 | # ... allows any of the above keys below the endpoint path (except http methods)
141 |
142 | patch:
143 | # ...
144 |
145 |
146 | ----
147 |
148 | The structure below `paths` is similar to an OpenAPI YAML file to make it easier to locate a specific mapping.
149 |
150 | == json schema
151 |
152 | Some IDEs support JSON schemas to provide editing support, (auto-completion and validation) for text-based files. To support this, openapi-processor provides JSON schemas for the mapping formats at link:{json-schema-site}[`https://openapiprocessor.io/schemas/mapping/mapping-v++{version}++.json`].
153 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/processor/bean-validation.adoc:
--------------------------------------------------------------------------------
1 | = Bean Validation
2 | include::partial$links.adoc[]
3 |
4 | == WebFlux
5 |
6 | The position of the `@Valid` annotation on reactive types has changed in 2024.2. Until then the `@Valid` was placed on the generic type of the reactive wrapper, like this:
7 |
8 | [source,java]
9 | ----
10 | @PostMapping(path = "/foo-flux")
11 | void postFooFlux(@Parameter Flux<@Valid Bar> body);
12 | ----
13 |
14 | Unfortunately, validation did not happen. Spring needs the `@Valid` annotation on the reactive wrapper to trigger the validation. Therefore `@Valid` is placed by default on the reactive wrapper:
15 |
16 | [source,java]
17 | ----
18 | @PostMapping(path = "/foo-flux")
19 | void postFooFlux(@Parameter @Valid Flux body);
20 | ----
21 |
22 | To keep the old behavior see xref:processor/configuration.adoc#_compatibility[compatibility].
23 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/processor/deprecated.adoc:
--------------------------------------------------------------------------------
1 | = Deprecated items
2 |
3 | OpenAPI allows adding `deprecated: true` at several places. openapi-processor-spring translates them to Java's `@Deprecated` annotation.
4 |
5 |
6 | == deprecated endpoint
7 |
8 | *OpenAPI*
9 | [source,yaml]
10 | ----
11 | /foo:
12 | get:
13 | deprecated: true # <1>
14 | ----
15 |
16 | *Java*
17 | [source,java]
18 | ----
19 | @Deprecated // <2>
20 | @GetMapping("/foo")
21 | /*...*/ getFoo();
22 | ----
23 |
24 | <1> a deprecated endpoint
25 | <2> the generated endpoint method with a `@Deprecated` annotation
26 |
27 | == deprecated parameter
28 |
29 | *OpenAPI*
30 | [source,yaml]
31 | ----
32 | /foo:
33 | get:
34 | parameters:
35 | - name: bar
36 | deprecated: true # <1>
37 | in: query
38 | schema:
39 | type: string
40 | ----
41 |
42 | *Java*
43 | [source,java]
44 | ----
45 | @GetMapping("/foo")
46 | /* ... */ getFoo(@Deprecated String bar); // <2>
47 | ----
48 |
49 | <1> a deprecated parameter
50 | <2> the generated endpoint method with a `@Deprecated` annotation on the `bar` parameter.
51 |
52 | == deprecated schema
53 |
54 | *OpenAPI*
55 | [source,yaml]
56 | ----
57 | Bar:
58 | type: object
59 | deprecated: true # <1>
60 | properties:
61 | foobar:
62 | type: string
63 |
64 | ----
65 |
66 | *Java*
67 | [source,java]
68 | ----
69 | @Deprecated // <2>
70 | public class Bar {
71 | /* ... */
72 | }
73 | ----
74 |
75 | <1> a deprecated schema
76 | <2> the generated model class with a `@Deprecated` annotation.
77 |
78 |
79 | == deprecated schema property
80 |
81 | *OpenAPI*
82 | [source,yaml]
83 | ----
84 | Bar:
85 | type: object
86 | properties:
87 | foobar:
88 | deprecated: true # <1>
89 | type: string
90 | ----
91 |
92 | *Java*
93 | [source,java]
94 | ----
95 | public class Bar {
96 |
97 | @Deprecated // <2>
98 | @JsonProperty("foobar")
99 | private String foobar;
100 |
101 | @Deprecated // <2>
102 | public String getFoobar() {
103 | return foobar;
104 | }
105 |
106 | @Deprecated // <2>
107 | public void setFoobar(String foobar) {
108 | this.foobar = foobar;
109 | }
110 |
111 | }
112 | ----
113 |
114 | <1> a deprecated schema property
115 | <2> the generated model class with `@Deprecated` annotations at the property, getter and setter. (the annotated property may be a bit too much... )
116 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/processor/endpoint-interface.adoc:
--------------------------------------------------------------------------------
1 | include::partial$vars.adoc[]
2 |
3 | = Endpoint interface grouping
4 |
5 | The processor groups endpoints based on their _first_ tag. Using the `/ping` example again its first (and only) tag is **ping**:
6 |
7 | [source,yaml,subs="attributes"]
8 | ----
9 | openapi: {var-openapi-version}
10 | info:
11 | title: openapi-processor-spring sample api
12 | version: 1.0.0
13 |
14 | paths:
15 | /ping:
16 | get:
17 | tags:
18 | - ping
19 | summary: returns a single "pong" string.
20 | description: very simple sample endpoint
21 | responses:
22 | '200':
23 | description: pong
24 | content:
25 | text/plain:
26 | schema:
27 | type: string
28 | ----
29 |
30 | The interface name used for this api will be `PingApi`. `Ping` because `ping` is the tags name and`Api` is a fixed string added to `Ping`.
31 |
32 | In case no tags are available, all endpoints will be added to an `Api` interface.
33 |
34 | The package name gets created from the configurable `packageName` parameter of the processor, and a sub package named `api`.
35 |
36 | If the `packageName` is configured as `io.openapiprocessor` the final package name for the interface is `io.openapiprocessor.api`, and the full class & package name is `io.openapiprocessor.api.PingApi`.
37 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/processor/enums.adoc:
--------------------------------------------------------------------------------
1 | include::partial$links.adoc[]
2 | include::partial$vars.adoc[]
3 |
4 | = Enums
5 |
6 | [#_default]
7 | == default
8 |
9 | By default, openapi-processor creates a java `Enum` from an OpenAPI schema that's using the `enum` keyword.Something like this:
10 |
11 | [source,yaml,title=OpenAPI enum]
12 | ----
13 | components:
14 | schemas:
15 | Type:
16 | type: string
17 | enum:
18 | - one
19 | - two
20 | ----
21 |
22 | [source,java,title=generated Java enum]
23 | ----
24 | package io.openapiprocessor.openapi2.model;
25 |
26 | import com.fasterxml.jackson.annotation.JsonCreator;
27 | import com.fasterxml.jackson.annotation.JsonValue;
28 | import io.openapiprocessor.openapi2.support.Generated;
29 |
30 | @Generated(value = "openapi-processor-spring", version = "latest")
31 | public enum Type {
32 | ONE("one"),
33 | TWO("two");
34 |
35 | private final String value;
36 |
37 | Type(String value) {
38 | this.value = value;
39 | }
40 |
41 | @JsonValue
42 | public String getValue() {
43 | return this.value;
44 | }
45 |
46 | @JsonCreator
47 | public static Type fromValue(String value) {
48 | for (Type val : Type.values()) {
49 | if (val.value.equals(value)) {
50 | return val;
51 | }
52 | }
53 | throw new IllegalArgumentException(value);
54 | }
55 | }
56 | ----
57 |
58 | This works without issues if used as part of a request payload.
59 |
60 | Unfortunately, it may cause an error, like the following if the enum is used as a query parameter:
61 |
62 | ====
63 | Failed to convert value of type `'java.lang.String'` to required type `'io.openapiprocessor.openapi.model.Type'`; Failed to convert from type [`java.lang.String`] to type [`@org.springframework.web.bind.annotation.RequestParam io.openapiprocessor.openapi.model.Type`] for value [`one`]
64 | ====
65 |
66 | The reason is, that Spring uses `org.springframework.core.convert.converter.Converter` implementations to deserialize parameters and the default enum deserialization expects the incoming string value to exactly match an enum value.
67 |
68 | That is, to successfully convert to the enum value `ONE` the incoming value string has to be `ONE`. It will not accept the lowercase `one`.
69 |
70 | The converter doesn't use jackson, so it won't use the `@JsonCreator` method to convert from the incoming lowercase value to the corresponding enum value.
71 |
72 | To handle this issue we can either use the `enum-type` <<_enum_type_string>> or <<_enum_type_framework>>.
73 |
74 | [#_enum_type_string]
75 | == string
76 |
77 | Do not create Java enum classes for OpenAPI enums and simply use `java.lang.String`.
78 |
79 | [source,yaml,title=mapping.yaml,subs="attributes"]
80 | ----
81 | openapi-processor-mapping: {var-mapping-version}
82 |
83 | options:
84 | enum-type: string
85 | ----
86 |
87 | This is an alternative to generating enum classes. It will pass the enum value string as given in the api request to avoid the issue described in the <<_default>> section.
88 |
89 | [source,java,title=api interface]
90 | ----
91 | public interface FooApi {
92 |
93 | @PostMapping(path = "/foo", produces = {"application/json"})
94 | Foo postFoo(@RequestParam(name = "enum", required = false) String aEnum);
95 |
96 | }
97 | ----
98 |
99 | In this simple form it doesn't provide any help to make sure that the incoming values is a valid value as described in the OpenAPI.
100 |
101 | By enabling bean-validation, the processor will generate and use a custom validation annotation to check that the incoming string is an allowed value.
102 |
103 | [source,yaml,title=mapping.yaml,subs="attributes"]
104 | ----
105 | openapi-processor-mapping: {var-mapping-version}
106 |
107 | options:
108 | bean-validation: jakarta
109 | enum-type: string
110 | ----
111 |
112 | [source,java,title=api interface with validation]
113 | ----
114 | public interface FooApi {
115 |
116 | @PostMapping(path = "/foo", produces = {"application/json"})
117 | Foo postFoo(@RequestParam(name = "enum", required = false) @Values(values = {"one", "two"}) String aEnum);
118 |
119 | }
120 | ----
121 |
122 | [NOTE]
123 | ====
124 | make sure you annotate the controller with `@Validated` to run the `@Values` check.
125 |
126 | [source,java,title=api interface with validation]
127 | ----
128 | @Validated
129 | @RestController
130 | public class ApiController implements FooApi {
131 | // ...
132 | }
133 | ----
134 | ====
135 |
136 | [#_enum_type_framework]
137 | == framework
138 |
139 | This is another alternative to the <<_default>> enum classes to avoid the issue described above.
140 |
141 | It creates Java enum classes and a Spring `ConverterFactory` with the name `\{package-name}.spring.StringToEnumConverterFactory` that does create enum converters for all generated enums.The enum converters convert incoming strings to their enum by comparing with the OpenAPI enum values.
142 |
143 | [source,yaml,title=mapping.yaml,subs="attributes"]
144 | ----
145 | openapi-processor-mapping: {var-mapping-version}
146 |
147 | options:
148 | enum-type: framework
149 | ----
150 |
151 | To enable the converter factory use a `WebMvcConfigurer` (or `WebFluxConfigurer`) like the code below:
152 |
153 | [source,java,title=enable enum converter factory]
154 | ----
155 | package io.openapiprocessor.samples;
156 |
157 | import io.openapiprocessor.openapi.spring.StringToEnumConverterFactory;
158 | import org.springframework.context.annotation.Configuration;
159 | import org.springframework.format.FormatterRegistry;
160 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
161 |
162 | @Configuration
163 | public class WebConfig implements WebMvcConfigurer {
164 |
165 | @SuppressWarnings("rawtypes")
166 | @Override
167 | public void addFormatters(FormatterRegistry registry) {
168 | registry.addConverterFactory(new StringToEnumConverterFactory());
169 | }
170 | }
171 |
172 | ----
173 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/processor/identifier.adoc:
--------------------------------------------------------------------------------
1 | = Identifiers
2 |
3 | :java-char-start: https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/Character.html#isJavaIdentifierStart(char)
4 | :java-char-part: https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/Character.html#isJavaIdentifierPart(char)
5 | :jackson-json-property: https://fasterxml.github.io/jackson-annotations/javadoc/2.8/com/fasterxml/jackson/annotation/JsonProperty.html
6 |
7 | == general
8 |
9 | The processor will map identifiers used in the OpenAPI description (i.e. `yaml` file) to valid Java identifiers.
10 |
11 | The Java identifiers will use camel case, starting with an upper case letter if it is a type name and a lower case letter if it is a variable name.
12 |
13 | Camel case will be produced by detecting word breaks on special characters and using an upper case first letter on the next word. The special characters are:
14 |
15 | * characters that are not allowed in java identifiers, (for example, a `-` (minus)). This is checked
16 | by using link:{java-char-start}[`Character.isJavaIdentifierStart()`] and
17 | link:{java-char-part}[`Character.isJavaIdentifierPart()`]
18 |
19 | * `_` (underscore). The underscore is possible in java identifiers but usually not used apart from enums.
20 |
21 | * a change from letter to number. (see table below).
22 |
23 | given an identifier from the OpenAPI description, the processor would generate the following names for different kinds of identifiers:
24 |
25 | |===
26 | | | OpenAPI | camel case | variable | class | enum
27 |
28 | | since 2024.2
29 | | foo2Bar
30 | | foo2**B**ar
31 | | foo2**B**ar
32 | | Foo2**B**ar
33 | | FOO2_BAR
34 |
35 | | before 2024.2
36 | | foo2Bar
37 | | foo2bar
38 | | foo2bar
39 | | Foo2bar
40 | | FOO2BAR
41 | |===
42 |
43 | == model
44 |
45 | For properties of model classes, the properties will be annotated with `@JsonProperty` to provide the mapping from the OpenAPI identifier to the Java identifier.
46 |
47 | [source,java]
48 | ----
49 | class Example {
50 |
51 | @JsonProperty("foo-bar")
52 | private String fooBar;
53 |
54 | // ...
55 | }
56 | ----
57 |
58 | [NOTE]
59 | ====
60 | The `@JsonProperty(...)` annotations are necessary in case a json property name is not a valid java identifier.
61 |
62 | Any JSON identifier gets converted to a valid java identifier. If it differs from the JSON identifier, Spring (jackson) would be unable to correctly map the properties.
63 |
64 | To avoid this issue, the processor adds the annotation.
65 | ====
66 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/processor/index.adoc:
--------------------------------------------------------------------------------
1 | include::partial$links.adoc[]
2 | include::partial$vars.adoc[]
3 |
4 | = the processor
5 |
6 | == a quick example
7 |
8 | The processor will only generate java interfaces describing the endpoints and the required model(POJOs) classes from an link:{openapi}[OpenAPI] YAML document.
9 |
10 | Let's take a look at a basic example. The following OpenAPI YAML describes a single endpoint. A call to the `/ping` endpoint will simply respond with a plain text string result.
11 |
12 | [source,yaml,subs="attributes"]
13 | ----
14 | openapi: {var-openapi-version}
15 | info:
16 | title: openapi-processor-spring sample
17 | version: 1.0.0
18 |
19 | paths:
20 | /ping:
21 | get:
22 | tags:
23 | - ping
24 | summary: returns a single "pong" string.
25 | description: very simple sample endpoint.
26 | responses:
27 | '200':
28 | description: pong
29 | content:
30 | text/plain:
31 | schema:
32 | type: string
33 | ----
34 |
35 | Running the processor on this openapi yaml will create the following java interface:
36 |
37 | [source,java]
38 | ----
39 | package com.github.hauner.openapi.api;
40 |
41 | import org.springframework.http.ResponseEntity;
42 | import org.springframework.web.bind.annotation.GetMapping;
43 |
44 | public interface PingApi {
45 |
46 | @GetMapping(path = "/ping", produces = {"text/plain"})
47 | String getPing();
48 |
49 | }
50 | ----
51 |
52 | It is now up to you to implement the interface e.g., like this:
53 |
54 | [source,java]
55 | ----
56 | package com.github.hauner.openapi;
57 |
58 | import com.github.hauner.openapi.api.PingApi;
59 | import org.springframework.http.ResponseEntity;
60 | import org.springframework.stereotype.Controller;
61 |
62 | @RestController
63 | public class PingController implements PingApi {
64 |
65 | @Override
66 | public String getPing () {
67 | return "pong";
68 | }
69 |
70 | }
71 | ----
72 |
73 | That's it.
74 |
75 | Of course, the processor is capable of handling more interesting endpoint descriptions. The other sections provide some more detail about what is generated from which input.
76 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/processor/models.adoc:
--------------------------------------------------------------------------------
1 | include::partial$vars.adoc[]
2 |
3 | = Models
4 |
5 | The processor will create simple POJOs classes for the `object` schemas used in the OpenAPI description. A POJO will only have (annotated) properties and get/set methods for its properties.
6 |
7 | The following api describes two endpoints:
8 |
9 | - the first one `/book-inline` defines the response **schema** *inline*. This is interesting because the api does not provide a `schema` name.
10 |
11 | - the second one `/book` references a **named schema**.
12 |
13 | [source,yaml,subs="attributes"]
14 | ----
15 | openapi: {var-openapi-version}
16 | info:
17 | title: model example
18 | version: 1.0.0
19 |
20 | paths:
21 | /book-inline:
22 | get:
23 | responses:
24 | '200':
25 | description: none
26 | content:
27 | application/json:
28 | schema:
29 | type: object
30 | properties:
31 | isbn:
32 | type: string
33 | title:
34 | type: string
35 |
36 | /book:
37 | get:
38 | responses:
39 | '200':
40 | description: none
41 | content:
42 | application/json:
43 | schema:
44 | $ref: '#/components/schemas/Book'
45 |
46 | components:
47 | schemas:
48 | Book:
49 | type: object
50 | properties:
51 | isbn:
52 | type: string
53 | title:
54 | type: string
55 | ----
56 |
57 | The second endpoint uses a **schema** with a name, so the processor can simply create a POJO using the name as the Java class name.
58 |
59 | [source,java]
60 | ----
61 | package generated.model;
62 |
63 | import com.fasterxml.jackson.annotation.JsonProperty;
64 |
65 | public class Book {
66 |
67 | @JsonProperty("isbn")
68 | private String isbn;
69 |
70 | @JsonProperty("title")
71 | private String title;
72 |
73 | public String getIsbn () {
74 | return isbn;
75 | }
76 |
77 | public void setIsbn (String isbn) {
78 | this.isbn = isbn;
79 | }
80 |
81 | public String getTitle () {
82 | return title;
83 | }
84 |
85 | public void setTitle (String title) {
86 | this.title = title;
87 | }
88 |
89 | }
90 | ----
91 |
92 | The first endpoint has no name, and the processor invents a name based on the endpoint description. In this case the name will be `BookInlineResponse200`. To create a unique name and avoid name collisions with other inline objects, it is created by concatenating:
93 |
94 | * the path of the endpoint, `/book-inline` is mapped to `BookInline`
95 | * `Response`, because it is an inline object described under `responses:`
96 | * `200`, which is the http status code of the response
97 |
98 | which is finally the bulky `BookInlineResponse200`.
99 |
100 | Apart from the generated name it will have exactly the same content (i.e., properties and setter/getter) since the schema description is identical.
101 |
102 | == `readOnly`/`writeOnly`
103 |
104 | Using `readOnly`/`writeOnly` on object schema properties
105 |
106 | [source,yaml]
107 | ----
108 | Foo:
109 | type: object
110 | properties:
111 | barRead:
112 | readOnly: true
113 | allOf:
114 | - $ref: '#/components/schemas/Bar'
115 | barWrite:
116 | writeOnly: true
117 | allOf:
118 | - $ref: '#/components/schemas/Bar'
119 | ----
120 |
121 | will translate to `@JsonProperty` annotations with read-only or write-only `access`:
122 |
123 | [source,java]
124 | ----
125 | public class Foo {
126 |
127 | @JsonProperty(value = "barRead", access = JsonProperty.Access.READ_ONLY)
128 | private Bar barRead;
129 |
130 | @JsonProperty(value = "barWrite", access = JsonProperty.Access.WRITE_ONLY)
131 | private Bar barWrite;
132 |
133 | // ....
134 | }
135 | ----
136 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/processor/one-of-interface.adoc:
--------------------------------------------------------------------------------
1 | include::partial$vars.adoc[]
2 |
3 | = oneOf
4 |
5 | Generating model classes from an `oneOf` has the challenge to handle a number of usually unrelated objects with different properties.
6 |
7 | Java has no way to define a class member that can have multiple unrelated types (e.g. it can be of class `Foo` or class `Bar`), except using `Object`.
8 |
9 | This is the default behavior of the processor.
10 |
11 | The problem with `Object` is that it doesn't provide any information at all. You have to know (from the OpenAPI) what that `Object` could be.
12 |
13 | To improve usability, the processor is able to generate marker interfaces to provide a bit more information than `Object`.
14 |
15 | [#_marker_interfaces]
16 | == marker interfaces
17 |
18 | [.badge .badge-since]+since 2022.3+
19 |
20 | Generation of marker interfaces is enabled by setting the `one-of-interface` option to `true` (See xref:processor/configuration.adoc[configuration]). For backward compatibility it is `false` by default.
21 |
22 | [source,yaml,subs="attributes"]
23 | ----
24 | openapi-processor-mapping: {var-mapping-version}
25 |
26 | options:
27 | one-of-interface: true
28 | ----
29 |
30 | The processor will now create a marker interface for a `oneOf` of `object` s that is implemented by all `object` s in the `oneOf` list.
31 |
32 | Here is an example. The response is an object `Foo` with a `foo` property that can have the type `Foo` or `Bar`.
33 |
34 | [source,yaml,subs="attributes"]
35 | ----
36 | openapi: {var-openapi-version}
37 | info:
38 | title: oneOf marker interface
39 | version: 1.0.0
40 |
41 | paths:
42 | /foo:
43 | get:
44 | responses:
45 | '200':
46 | description: oneOf
47 | content:
48 | application/json:
49 | schema:
50 | $ref: '#/components/schemas/Foo'
51 |
52 | components:
53 | schemas:
54 |
55 | Foo:
56 | type: object
57 | properties:
58 | foo:
59 | $ref: '#/components/schemas/FooOneOf'
60 |
61 | FooOneOf:
62 | oneOf:
63 | - $ref: '#/components/schemas/Foo'
64 | - $ref: '#/components/schemas/Bar'
65 |
66 | # omitted description of Foo & Bar
67 | ----
68 |
69 | The processor generates the class `Foo` as
70 |
71 | [source,java]
72 | ----
73 | // simplified
74 | public class Foo {
75 | private FooOneOf foo;
76 | }
77 | ----
78 |
79 | with the type `FooOneOf` instead of `Object`. `FooOneOf` is the marker interface:
80 |
81 | [source,java]
82 | ----
83 | public interface FooOneOf {}
84 | ----
85 |
86 | The two model classes `Foo` & `Bar` implement the marker interface:
87 |
88 | [source,java]
89 | ----
90 | // simplified
91 | public class Foo implements FooOneOf { /* ... */ }
92 | ----
93 |
94 | [source,java]
95 | ----
96 | // simplified
97 | public class Bar implements FooOneOf { /* ... */ }
98 | ----
99 |
100 | Which is better than having `foo` just as `Object`. The marker interface helps to find the possible types of `foo`.
101 |
102 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/processor/parameter.adoc:
--------------------------------------------------------------------------------
1 | = Parameters
2 |
3 | == query parameters
4 |
5 | Query parameters get converted to method parameters with a `@RequestParam()` annotation.
6 |
7 | For example the following query parameter description:
8 |
9 | [source, yaml]
10 | ----
11 | paths:
12 | /endpoint:
13 | get:
14 | parameters:
15 | - name: foo
16 | description: simple query parameter with default value
17 | in: query
18 | required: false
19 | schema:
20 | type: string
21 | default: 'not set'
22 | responses:
23 | '204':
24 | description: empty
25 | ----
26 |
27 | will generate the following interface method:
28 |
29 | [source,java]
30 | ----
31 | @GetMapping(path = "/endpoint")
32 | ResponseEntity getEndpoint(@RequestParam(name = "foo", required = false, defaultValue = "not set") String foo);
33 | ----
34 |
35 | == path parameters
36 |
37 | Path parameters get converted to method parameters with a `@PathVariable()` annotation.
38 |
39 | For example the following endpoint description:
40 |
41 |
42 | [source, yaml]
43 | ----
44 | paths:
45 | /endpoint/{foo}:
46 | get:
47 | parameters:
48 | - name: foo
49 | description: a path parameter
50 | in: path
51 | required: true
52 | schema:
53 | type: string
54 | responses:
55 | '200':
56 | description: plain text response
57 | content:
58 | plain/text:
59 | schema:
60 | type: string
61 | ----
62 |
63 |
64 | will generate the following interface method:
65 |
66 | [source,java]
67 | ----
68 | @GetMapping(
69 | path = "/path/{foo}",
70 | produces = {"plain/text"})
71 | String getEndpointFoo(@PathVariable(name = "foo") String foo);
72 | ----
73 |
74 | == header parameters
75 |
76 | Header parameters use the `@RequestHeader` annotation.
77 |
78 | Given the following endpoint description:
79 |
80 | [source, yaml]
81 | ----
82 | paths:
83 | /endpoint:
84 | get:
85 | tags:
86 | - endpoint
87 | parameters:
88 | - name: x-foo
89 | description: a header parameter
90 | in: header
91 | required: true
92 | schema:
93 | type: string
94 | responses:
95 | '204':
96 | description: empty
97 | ----
98 |
99 | the processor generates the following interface method:
100 |
101 | [source,java]
102 | ----
103 | @GetMapping(path = "/endpoint")
104 | void getEndpoint(@RequestHeader(name = "x-foo") String xFoo);
105 | ----
106 |
107 | == cookie parameters
108 |
109 | Cookie parameters use the `@CookieValue` annotation.
110 |
111 | Given the following endpoint description:
112 |
113 |
114 | [source, yaml]
115 | ----
116 | paths:
117 | /endpoint:
118 |
119 | get:
120 | parameters:
121 | - name: foo
122 | description: a cookie
123 | in: cookie
124 | required: true
125 | schema:
126 | type: string
127 | responses:
128 | '204':
129 | description: empty
130 | ----
131 |
132 | the processor generates the following interface method:
133 |
134 | [source,java]
135 | ----
136 | @GetMapping(path = "/endpoint")
137 | void getEndpoint(@CookieValue(name = "foo") String foo);
138 | ----
139 |
140 | == additional parameters
141 |
142 | Sometimes it may be useful to have an additional endpoint parameter that is required for the implementation, but it should not be part of the OpenAPI description. Think of an `HttpServletRequest` or a custom Spring `HandlerMethodArgumentResolver`.
143 |
144 | Such an additional parameter can be described in the mappings as an endpoint parameter. Assuming there is an endpoint `/foo` defined in the OpenAPI interfaces it is possible to add extra parameters by using an `add ` `as ` entry.
145 |
146 | [source, yaml]
147 | ----
148 | map:
149 | paths:
150 | /foo:
151 |
152 | parameters:
153 | - add: request => javax.servlet.http.HttpServletRequest
154 | ----
155 |
156 | will add the *additional* parameter to the generated interface method.
157 |
158 | [source, java]
159 | ----
160 | @GetMapping(path = "/foo")
161 | ResponseEntity> getFoo(@RequestParam(name = "bar") String bar, HttpServletRequest request);
162 | ----
163 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/processor/requestbody.adoc:
--------------------------------------------------------------------------------
1 | include::partial$vars.adoc[]
2 |
3 | = Request Body
4 |
5 | This OpenAPI describes an endpoint with `requestBody`:
6 |
7 | [source,yaml,subs="attributes"]
8 | ----
9 | openapi: {var-openapi-version}
10 | info:
11 | title: request body
12 | version: 1.0.0
13 |
14 | paths:
15 | /book:
16 | post:
17 | requestBody:
18 | content:
19 | application/json:
20 | schema:
21 | $ref: '#/components/schemas/Book'
22 | required: true
23 | responses:
24 | '201':
25 | description: created book
26 | content:
27 | application/json:
28 | schema:
29 | $ref: '#/components/schemas/Book'
30 |
31 | components:
32 | schemas:
33 | Book:
34 | type: object
35 | properties:
36 | isbn:
37 | type: string
38 | title:
39 | type: string
40 | ----
41 |
42 | that the processor will convert into the interface:
43 |
44 | [source,java]
45 | ----
46 | package generated.api;
47 |
48 | import generated.model.Book;
49 | import org.springframework.http.ResponseEntity;
50 | import org.springframework.web.bind.annotation.PostMapping;
51 | import org.springframework.web.bind.annotation.RequestBody;
52 |
53 | public interface Api {
54 |
55 | @PostMapping(path = "/book", consumes = {"application/json"}, produces = {"application/json"})
56 | ResponseEntity postBook(@RequestBody Book body);
57 |
58 | }
59 | ----
60 |
61 | and a `Book` pojo.
62 |
63 | == multipart/form-data
64 |
65 | For file uploads, where the `content` of the `requestBody` is `multipart/form-data`, the resulting code looks a bit different and requires a specific type mapping.
66 |
67 | The file upload in OpenAPI is described like this:
68 |
69 | [source,yaml]
70 | ----
71 | /file-upload:
72 | summary: upload a file
73 | post:
74 | requestBody:
75 | content:
76 | multipart/form-data:
77 | schema:
78 | type: object
79 | properties:
80 | file:
81 | type: string
82 | format: binary
83 | ----
84 |
85 | (See also xref:oap:openapi:file_upload.adoc[OpenAPI - describing a file upload endpoint])
86 |
87 | The `schema` must be of type `object` defining a property for each part in the multipart body.
88 |
89 | Instead of generating a `@RequestBody` parameter for the `object` schema the processor creates a parameter for each property of the object annotated with `@RequestParam`:
90 |
91 | [source,java]
92 | ----
93 | package generated.api;
94 |
95 | import org.springframework.http.ResponseEntity;
96 | import org.springframework.web.bind.annotation.PostMapping;
97 | import org.springframework.web.bind.annotation.RequestParam;
98 | import org.springframework.web.multipart.MultipartFile;
99 |
100 | public interface Api {
101 |
102 | @PostMapping(path = "/file-upload")
103 | ResponseEntity postFileUpload(@RequestParam(name = "file") MultipartFile file);
104 |
105 | }
106 | ----
107 |
108 | Note that the `file` property (type `string` format `binary`) is mapped to Springs `MultipartFile` type.
109 |
110 | The processor does not have a default mapping for the `binary` format so to get the code above, we have to configure it in the type mapping YAML:
111 |
112 | [source,yaml]
113 | ----
114 | map:
115 | paths:
116 | /file-upload:
117 | types:
118 | - type: string:binary => org.springframework.web.multipart.MultipartFile
119 | ----
120 |
121 | To upload multiple files we can define the body `object` with an `array` property:
122 |
123 | [source,yaml]
124 | ----
125 | type: object
126 | properties:
127 | files:
128 | type: array
129 | items:
130 | type: string
131 | format: binary
132 | ----
133 |
134 | to get the following code:
135 |
136 | [source,java]
137 | ----
138 | @PostMapping(path = "/file-upload")
139 | ResponseEntity postFileUpload(@RequestParam(name = "files") MultipartFile[] files);
140 | ----
141 |
142 | === multi-part encoding
143 |
144 | [.badge .badge-since]+since 2021.4+
145 |
146 | the parsing step extracts the `encoding/contentType` of a multipart content. This allows a processor to consider the encoding content type when selecting the annotation for the part.
147 |
148 | the Spring processor uses this to select between `@RequestPart` and `@RequestParam` annotation. If an `encoding/contentType` is available it will use `@RequestPart`, if no `encoding/contentType` is available it will use `@RequestParam`.
149 |
150 | [source,yaml,subs="attributes"]
151 | ----
152 | openapi: {var-openapi-version}
153 | info:
154 | title: params-request-body-multipart
155 | version: 1.0.0
156 |
157 | paths:
158 | /multipart:
159 | post:
160 | requestBody:
161 | required: true
162 | content:
163 | multipart/form-data:
164 | schema:
165 | type: object
166 | properties:
167 | file:
168 | type: string
169 | format: binary
170 | json:
171 | type: object
172 | properties:
173 | foo:
174 | type: string
175 | bar:
176 | type: string
177 | encoding:
178 | file:
179 | contentType: application/octet-stream
180 | json:
181 | contentType: application/json
182 | responses:
183 | '204':
184 | description: empty
185 | ----
186 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/processor/response.adoc:
--------------------------------------------------------------------------------
1 | include::partial$links.adoc[]
2 |
3 | = Responses
4 |
5 | All generated endpoints return their java result type by default. This may be to simple for some endpoint implementations.
6 |
7 | There are two mappings available to customize the result type:
8 |
9 | . If, for example, the response needs some customization, we would like to use a `ResponseEntity<>` to modify it. This is possible using the *result* mapping.
10 |
11 | . Another case is WebFlux, where we need the result to be either a `Flux<>` in case of an array type, or a `Mono<>` in case it is not an array type. This is possible using the *single* mapping.
12 |
13 | NOTE: *single* and *result* mappings are _independent_, i.e., both mappings can be used at the same time. For example, it is possible to create a `Mono<>` result and modify the response using `ResponseEntity<>`. The response type would be `ResponseEntity>`.
14 |
15 | == result wrapper
16 |
17 | //[.badge .badge-since]+since 1.0.0.M13+
18 |
19 | A link:{spring-responseentity}[`ResponseEntity<>`] allows an endpoint implementation full control of the response.
20 |
21 | Here is a super simple example:
22 |
23 | [source,java]
24 | ----
25 | public ResponseEntity getFoo() {
26 | return ResponseEntity.ok("foo");
27 | }
28 | ----
29 |
30 | To enable a result wrapper set the `result` mapping in the mapping yaml to a fully qualified java type.
31 |
32 | [source,yaml]
33 | ----
34 | map:
35 | result: org.springframework.http.ResponseEntity
36 | ----
37 |
38 | NOTE: The processor expects that it takes a single generic parameter.
39 |
40 | Depending on the number of defined response content types the parameter of the `ResponseEntity<>` will be either the java type or the *unknown type*.
41 |
42 | |===
43 | |responses | ResponseEntity<>
44 |
45 | |one
46 | |`ResponseEntity`
47 |
48 | |multiple
49 | |`ResponseEntity>`
50 | |===
51 |
52 | NOTE: prior to 1.0.0.M13 all results were auto-wrapped with `ResponseEntity<>`.
53 |
54 | See also xref:mapping/result.adoc[result mapping].
55 |
56 | == single & multi wrapper
57 |
58 | //[.badge .badge-since]+since 1.0.0.M13+
59 |
60 | When using WebFlux, we want to wrap certain parameters and results types in reactive types like `Mono<>` or `Flux<>`.
61 |
62 | To achieve this, the processor knows two special mappings:
63 |
64 | * single: to wrap non-array types (i.e., not a collection)
65 | * multi: to wrap array types (i.e., a collection)
66 |
67 | === multi
68 |
69 | [source,yaml]
70 | ----
71 | map:
72 | multi: reactor.core.publisher.Flux
73 | ----
74 |
75 | Which will use `Flux<>` as collection wrapper instead of the original java collection type for all list *responses* (or *parameters*). The `multi` does not affect collections in model types.
76 |
77 | === single
78 |
79 | To map non-array like responses to a `Mono<>` set the `single` mapping:
80 |
81 | [source,yaml]
82 | ----
83 | map:
84 | single: reactor.core.publisher.Mono
85 | ----
86 |
87 | The processor will now wrap all non-array like response types with the given `single` mapping.
88 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/processor/server-url.adoc:
--------------------------------------------------------------------------------
1 | include::partial$vars.adoc[]
2 |
3 | = Server Url
4 |
5 | == base path
6 |
7 | OpenAPI offers a `servers` section to describe the server urls available to access the api.
8 |
9 | openapi-processor has (simple) support to generate a resource property file with the path of a server url. The processor will resolve all variables with their default value and extracts the urls path.
10 |
11 | Given an OpenAPI description with a servers key:
12 |
13 | [source,yaml,subs="attributes"]
14 | ----
15 | openapi: {var-openapi-version}
16 | info:
17 | title: server url example
18 | version: 1.0.0
19 |
20 | servers:
21 | - url: "https://openapiprocessor.io/{path}"
22 | variables:
23 | path:
24 | default: api
25 | # ...
26 | ----
27 |
28 | and a mapping
29 |
30 | [source,yaml,subs="attributes"]
31 | ----
32 | openapi-processor-mapping: {var-mapping-version}
33 | options:
34 | server-url: true
35 | ----
36 |
37 | it will generate a simple resource properties file with a single property `openapi.base.path`:
38 |
39 | [source,properties]
40 | ----
41 | # api.properties
42 | openapi.base.path = /api
43 | ----
44 |
45 | If there are multiple servers, its index can be used to select it:
46 |
47 | [source,yaml,subs="attributes"]
48 | ----
49 | openapi-processor-mapping: {var-mapping-version}
50 | options:
51 | server-url: 0 # same as true
52 | # server-url: 1
53 | # server-url: 2
54 | ----
55 |
56 | == using the generated properties file
57 |
58 | The properties file is used to configure Spring Boots `server.servlet.context-path`:
59 |
60 | [source,properties]
61 | ----
62 | # application.properties
63 |
64 | #spring.config.import = api.properties
65 | server.servlet.context-path=${openapi.base.path}
66 | ----
67 |
68 | While it is possible to import the generated properties file, it is probably better to simply use the generated properties as an additional profile.
69 |
70 | == name of the properties file
71 |
72 | The default name of the generated properties file is `api.properties`. it is configurable using the xref:processor/configuration.adoc#_basepath_propertiesname[server-url:properties-name] option.
73 |
74 | [source,yaml,subs="attributes"]
75 | ----
76 | openapi-processor-mapping: {var-mapping-version}
77 |
78 | options:
79 | server-url:
80 | properties-name: base-path.properties
81 | ----
82 |
83 | == destination directory
84 |
85 | By default, the processor will generate the java package structure directly below the `targetDir`. To create the resource file, it needs a second (`resources`) directory as target directory.
86 |
87 | This is handled by a new xref:processor/configuration.adoc#_target_dirlayout[option] to set the layout of the `targetDir`. Setting it to `standard`
88 |
89 | [source,yaml,subs="attributes"]
90 | ----
91 | openapi-processor-mapping: {var-mapping-version}
92 |
93 | options:
94 | target-dir:
95 | layout: standard
96 | ----
97 |
98 | will create the following directory layout:
99 |
100 | targetDir
101 | +--- java
102 | | \--- io
103 | | \--- openapiprocessor
104 | | +--- api
105 | | \--- model
106 | \--- resources
107 |
108 | and write the properties file to the `resources` directory.
109 |
110 | [NOTE]
111 | To have a destination directory for generating the resource file, setting `server-url` to a truthy value will *automatically* enable the xref:processor/configuration.adoc#_target_dirlayout[`standard`] target dir layout. It is still recommended to set it explicitly for documentation.
112 |
113 | == build configuration
114 |
115 | The consequence of the new layout is that for compilation it is necessary to update the configuration of the sources and resources directories in the build configuration.
116 |
117 | For example, with gradle the `sourceSets` configuration would change to something like this:
118 |
119 | [source,kotlin]
120 | ----
121 | sourceSets {
122 | main {
123 | java {
124 | // add generated files
125 | srcDir(layout.buildDirectory.dir("openapi/java"))
126 | }
127 | resources {
128 | // add generated resources
129 | srcDir(layout.buildDirectory.dir("openapi/resources"))
130 | }
131 | }
132 | }
133 | ----
134 |
135 | == options summary
136 |
137 | Here is a short snippet of a `mapping.yaml` as a summary of the options used to configure the base path property file generation.
138 |
139 | [source,yaml,subs="attributes"]
140 | ----
141 | openapi-processor-mapping: {var-mapping-version}
142 |
143 | options:
144 | # ... other options
145 |
146 | target-dir:
147 | layout: standard
148 |
149 | base-path:
150 | server-url: 0
151 | properties-name: base-path.properties
152 | ----
153 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/partials/links.adoc:
--------------------------------------------------------------------------------
1 | //
2 | // external openapi-processor links
3 | //
4 | :badge-license: https://img.shields.io/badge/License-Apache%202.0-blue.svg?labelColor=313A42
5 | :badge-ci: https://github.com/openapi-processor/openapi-processor-spring/workflows/build/badge.svg
6 | :oaps-ci: https://github.com/openapi-processor/openapi-processor-spring/actions?query=workflow%3Abuild
7 | :oaps-issues: https://github.com/openapi-processor/openapi-processor-spring/issues
8 | :oaps-inttests: https://github.com/openapi-processor/openapi-processor-spring/tree/master/src/testInt/resources/tests
9 | :oaps-license: https://github.com/openapi-processor/openapi-processor-spring/blob/master/LICENSE
10 | :oaps-releases: https://github.com/openapi-processor/openapi-processor-spring/releases
11 | :oaps-bintray: https://bintray.com/openapi-processor/openapi-processor
12 | :oap-gradle: https://github.com/openapi-processor/openapi-processor-gradle
13 | :oap-playground: https://playground.openapiprocessor.io
14 | :oap-samples: https://github.com/openapi-processor/openapi-processor-samples
15 | :oap-central: https://search.maven.org/search?q=io.openapiprocessor
16 | :badge-central: https://img.shields.io/maven-central/v/io.openapiprocessor/openapi-processor-spring?label=Maven%20Central
17 | :oapc-inttests: https://github.com/openapi-processor/openapi-processor-core/tree/master/src/testInt/resources/tests
18 | :json-schema: https://github.com/openapi-processor/openapi-processor-core/blob/master/src/main/resources/mapping/v2/mapping.yaml.json
19 | :json-schema-site: https://openapiprocessor.io/schemas/mapping/mapping-{var-mapping-version}.json
20 |
21 | //
22 | // other external links
23 | //
24 | :openapi: https://www.openapis.org/
25 | :openapi-spec: https://github.com/OAI/OpenAPI-Specification
26 | :openapi-spec-types: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#dataTypes
27 | :openapi-generator: https://openapi-generator.tech/
28 | :openapi-tools: https://openapi.tools/
29 |
30 | :swagger-parser: https://github.com/swagger-api/swagger-parser
31 | :openapi4j: https://github.com/openapi4j/openapi4j
32 | :openapi-parser: https://github.com/openapi-processor/openapi-parser
33 |
34 | :springboot: https://spring.io/projects/spring-boot
35 | :license: http://www.apache.org/licenses/LICENSE-2.0.txt
36 | :bean-validation: https://beanvalidation.org/
37 |
38 | :spring-responseentity: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html
39 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/partials/vars.adoc:
--------------------------------------------------------------------------------
1 | :var-mapping-version: v13
2 | :var-openapi-version: 3.1.0
3 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | projectVersion=2025.3-SNAPSHOT
2 | projectGroupId=io.openapiprocessor
3 |
4 | projectUrl=https://openapiprocessor.io
5 | projectTitle=openapi-processor
6 | projectDesc=OpenAPI Processor Spring
7 |
8 | projectGithubRepo=openapi-processor/openapi-processor-spring
9 |
10 | kotlin.jvm.target.validation.mode=ignore
11 |
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | kotlin = "2.1.21"
3 | build-jdk = "11"
4 | test-jdk = "17"
5 |
6 | api = "2024.2"
7 | base = "2025.3-SNAPSHOT"
8 |
9 | junit = "5.9.3"
10 | jacoco = "0.8.7"
11 |
12 | spring-web = "5.3.39"
13 | spring-data = "2.7.18"
14 |
15 | # although testInt builds with jdk 17 it is complaing that the dependecies need jdk 17
16 | #spring-web = "6.2.7"
17 | #spring-data = "3.5.0"
18 |
19 | [libraries]
20 | openapi-processor-api = { module = "io.openapiprocessor:openapi-processor-api", version.ref = "api" }
21 | openapi-processor-test-base = { module = "io.openapiprocessor:openapi-processor-test", version.ref = "base" }
22 | openapi-processor-test-api = { module = "io.openapiprocessor:openapi-processor-test-api", version.ref = "base" }
23 | openapi-processor-core = { module = "io.openapiprocessor:openapi-processor-core", version.ref = "base" }
24 | openapi-processor-parser-api = { module = "io.openapiprocessor:openapi-processor-core-parser-api", version.ref = "base" }
25 | openapi-processor-parser-swagger = { module = "io.openapiprocessor:openapi-processor-core-parser-swagger", version.ref = "base" }
26 | openapi-processor-parser-openapi4j = { module ="io.openapiprocessor:openapi-processor-core-parser-openapi4j", version.ref = "base" }
27 |
28 | spring-web = { module ="org.springframework:spring-web", version.ref = "spring-web" }
29 | spring-data = { module ="org.springframework.data:spring-data-commons", version.ref = "spring-data" }
30 |
31 | checker = "org.checkerframework:checker:3.49.3"
32 | jimfs = "com.google.jimfs:jimfs:1.3.0"
33 | slf4j = "org.slf4j:slf4j-api:2.0.17"
34 | logback = "ch.qos.logback:logback-classic:1.5.18"
35 |
36 | kotest-bom = "io.kotest:kotest-bom:5.9.1"
37 | kotest-runner = { module = "io.kotest:kotest-runner-junit5" }
38 | kotest-datatest = { module = "io.kotest:kotest-framework-datatest" }
39 | mockk = "io.mockk:mockk:1.14.2"
40 |
41 | groovy-bom = "org.apache.groovy:groovy-bom:4.0.27"
42 | spock = "org.spockframework:spock-core:2.3-groovy-4.0"
43 |
44 | plugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
45 | plugin-checker = "org.checkerframework:checkerframework-gradle-plugin:0.6.5"
46 | plugin-outdated = "com.github.ben-manes:gradle-versions-plugin:0.52.0"
47 |
48 | [plugins]
49 | kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
50 | nexus = { id = "io.github.gradle-nexus.publish-plugin", version = "2.0.0" }
51 | jacoco = { id = "org.barfuin.gradle.jacocolog", version = "3.1.0" }
52 | versions = { id = "com.github.ben-manes.versions", version = "0.52.0" }
53 | sonar = { id = "org.sonarqube", version = "6.2.0.5505" }
54 |
--------------------------------------------------------------------------------
/gradle/publishing.gradle:
--------------------------------------------------------------------------------
1 | configure(project.rootProject) {
2 | ext {
3 | publishUser = getBuildProperty ('PUBLISH_USER')
4 | publishKey = getBuildProperty ('PUBLISH_KEY')
5 |
6 | signKey = getBuildProperty ('SIGN_KEY')
7 | signPwd = getBuildProperty ('SIGN_PWD')
8 |
9 | isReleaseVersion = !version.endsWith("SNAPSHOT")
10 | }
11 | }
12 |
13 | publishing {
14 | publications {
15 | processorSpring (MavenPublication) {
16 | groupId = project.group
17 | artifactId = project.name
18 | version = project.version
19 |
20 | from components.java
21 |
22 | pom {
23 | name = project.projectTitle
24 | description = project.projectDesc
25 | url = project.projectUrl
26 |
27 | licenses {
28 | license {
29 | name = 'The Apache Software License, Version 2.0'
30 | url = 'https://www.apache.org/licenses/LICENSE-2.0.txt'
31 | distribution = "repo"
32 | }
33 | }
34 |
35 | developers {
36 | developer {
37 | id = 'hauner'
38 | name = 'Martin Hauner'
39 | }
40 | }
41 |
42 | scm {
43 | url = "https://github.com/${project.projectGithubRepo}".toString ()
44 | }
45 | }
46 |
47 | }
48 | }
49 |
50 | repositories {
51 | maven {
52 | def releaseRepository = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2")
53 | def snapshotRepository = uri("https://oss.sonatype.org/content/repositories/snapshots")
54 | url = project.isReleaseVersion ? releaseRepository : snapshotRepository
55 |
56 | credentials {
57 | username = publishUser
58 | password = publishKey
59 | }
60 | }
61 | }
62 | }
63 |
64 | signing {
65 | setRequired({ gradle.taskGraph.hasTask("${project.path}:publishToSonatype") })
66 |
67 | useInMemoryPgpKeys(signKey, signPwd)
68 |
69 | signing {
70 | sign publishing.publications.processorSpring
71 | }
72 | }
73 |
74 | nexusPublishing {
75 | repositories {
76 | sonatype() {
77 | username = publishUser
78 | password = publishKey
79 | }
80 | }
81 | }
82 |
83 | // helper
84 |
85 | String getBuildProperty(String property) {
86 | project.findProperty (property) ?: System.getenv (property) ?: 'n/a'
87 | }
88 |
--------------------------------------------------------------------------------
/gradle/publishing.tasks.gradle.kts:
--------------------------------------------------------------------------------
1 | registerPublishTask("snapshot") { hasSnapshotVersion() }
2 | registerPublishTask("release") { !hasSnapshotVersion() }
3 |
4 | fun registerPublishTask(type: String, condition: () -> Boolean) {
5 | tasks.register("publish${type.capitalize()}") {
6 | group = "publishing"
7 | description = "Publish only if the current version is a ${type.capitalize()} version"
8 |
9 | if (condition()) {
10 | println("enabling $type publishing")
11 | dependsOn(tasks.withType())
12 | } else {
13 | doLast {
14 | println("skipping - no $type version")
15 | }
16 | }
17 | }
18 | }
19 |
20 | fun hasSnapshotVersion(): Boolean {
21 | return version.toString().endsWith("-SNAPSHOT")
22 | }
23 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openapi-processor/openapi-processor-spring/6687d7b56d1921110fc2b1cea70279877ae18141/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/images/openapi-processor-spring@1280x200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openapi-processor/openapi-processor-spring/6687d7b56d1921110fc2b1cea70279877ae18141/images/openapi-processor-spring@1280x200.png
--------------------------------------------------------------------------------
/images/openapi-processor-spring@1280x640.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openapi-processor/openapi-processor-spring/6687d7b56d1921110fc2b1cea70279877ae18141/images/openapi-processor-spring@1280x640.png
--------------------------------------------------------------------------------
/images/openapi-processor-spring@400x200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openapi-processor/openapi-processor-spring/6687d7b56d1921110fc2b1cea70279877ae18141/images/openapi-processor-spring@400x200.png
--------------------------------------------------------------------------------
/justfile:
--------------------------------------------------------------------------------
1 | default:
2 | @just --list --unsorted
3 |
4 | # update gradle wrapper
5 | wrapper version="8.14.1":
6 | ./gradlew wrapper --gradle-version={{version}}
7 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.gradle.enterprise") version("3.14.1")
3 | }
4 |
5 | gradleEnterprise {
6 | if (System.getenv("CI") != null) {
7 | buildScan {
8 | publishAlways()
9 | termsOfServiceUrl = "https://gradle.com/terms-of-service"
10 | termsOfServiceAgree = "yes"
11 | }
12 | }
13 | }
14 |
15 | rootProject.name = 'openapi-processor-spring'
16 |
17 | //includeBuild '../openapi-processor-base'
18 |
--------------------------------------------------------------------------------
/src/main/kotlin/io/openapiprocessor/spring/model/parameters/MultipartParameter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring.model.parameters
7 |
8 | import io.openapiprocessor.core.model.datatypes.DataType
9 | import io.openapiprocessor.core.model.parameters.MultipartParameter as CoreMultipartParameter
10 |
11 | class MultipartParameter(
12 | name: String,
13 | dataType: DataType,
14 | required: Boolean = false,
15 | deprecated: Boolean = false,
16 | description: String? = null,
17 | val contentType: String? = null
18 | ): CoreMultipartParameter(name, dataType, required, deprecated, description)
19 |
--------------------------------------------------------------------------------
/src/main/kotlin/io/openapiprocessor/spring/model/parameters/QueryParameter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring.model.parameters
7 |
8 | import io.openapiprocessor.core.model.datatypes.*
9 | import io.openapiprocessor.core.model.parameters.ParameterBase
10 |
11 | val Maps = listOf(
12 | Map::class.java.name,
13 | "org.springframework.util.MultiValueMap"
14 | )
15 |
16 | /**
17 | * OpenAPI query parameter.
18 | */
19 | class QueryParameter(
20 | name: String,
21 | dataType: DataType,
22 | required: Boolean = false,
23 | deprecated: Boolean = false,
24 | description: String? = null
25 | ): ParameterBase(name, dataType, required, deprecated, description) {
26 |
27 | /**
28 | * controls if a parameter should have a {@code @RequestParam} annotation.
29 | */
30 | override val withAnnotation: Boolean
31 | get() {
32 | // Map should be annotated
33 | if (isMappedMap) {
34 | return true
35 | }
36 |
37 | // Pojo's should NOT be annotated
38 | if (dataType is ObjectDataType) {
39 | return false
40 | }
41 |
42 | // Mapped should NOT be annotated if it was object schema
43 | // Mapped should be annotated if it was a simple schema
44 | if (isMappedObject) {
45 | return false
46 | }
47 |
48 | return true
49 | }
50 |
51 | /**
52 | * controls if a {@code @RequestParam} should have any parameters.
53 | */
54 | override val withParameters: Boolean
55 | get() {
56 | // Map should not have parameters
57 | if (isMappedMap) {
58 | return false
59 | }
60 |
61 | // Pojo should not have parameters
62 | if (dataType is ObjectDataType) {
63 | return false
64 | }
65 |
66 | return true
67 | }
68 |
69 | private val isMappedMap: Boolean
70 | get() {
71 | if (dataType !is MappedDataType) {
72 | return false
73 | }
74 |
75 | val type = dataType.getImports().first()
76 | return Maps.contains(type)
77 | }
78 |
79 | private val isMappedObject: Boolean
80 | get() {
81 | if (dataType !is SourceDataType) {
82 | return false
83 | }
84 |
85 | val mapped = dataType as SourceDataType
86 | return mapped.sourceDataType is ObjectDataType
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/main/kotlin/io/openapiprocessor/spring/processor/ProcessingException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring.processor
7 |
8 | class ProcessingException(ex: Exception): RuntimeException(ex) {
9 |
10 | override val message: String
11 | get() = "failed to run openapi-processor-spring!"
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/kotlin/io/openapiprocessor/spring/processor/SpringFramework.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring.processor
7 |
8 | import io.openapiprocessor.core.framework.FrameworkBase
9 | import io.openapiprocessor.core.model.datatypes.DataType
10 | import io.openapiprocessor.core.model.parameters.Parameter
11 | import io.openapiprocessor.core.parser.MultipartParameter as MultipartParserParameter
12 | import io.openapiprocessor.core.parser.Parameter as ParserParameter
13 | import io.openapiprocessor.spring.model.parameters.MultipartParameter
14 | import io.openapiprocessor.spring.model.parameters.QueryParameter
15 |
16 | /**
17 | * Spring model factory.
18 | *
19 | * @author Martin Hauner
20 | */
21 | class SpringFramework: FrameworkBase() {
22 |
23 | @Override
24 | override fun createQueryParameter(parameter: ParserParameter, dataType: DataType): Parameter {
25 | return QueryParameter (
26 | parameter.getName(),
27 | dataType,
28 | parameter.isRequired(),
29 | parameter.isDeprecated())
30 | }
31 |
32 | override fun createMultipartParameter(parameter: ParserParameter, dataType: DataType): Parameter {
33 | val mpp = parameter as MultipartParserParameter
34 |
35 | return MultipartParameter(
36 | parameter.getName(),
37 | dataType,
38 | parameter.isRequired(),
39 | parameter.isDeprecated(),
40 | parameter.description,
41 | mpp.contentType
42 | )
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/kotlin/io/openapiprocessor/spring/processor/SpringService.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | @file:Suppress("DEPRECATION")
7 |
8 | package io.openapiprocessor.spring.processor
9 |
10 | /**
11 | * Entry point of openapi-processor-spring loaded via [java.util.ServiceLoader] by the v1 interface
12 | * [io.openapiprocessor.api.v1.OpenApiProcessor].
13 | */
14 | class SpringService(private val testMode: Boolean = false):
15 | io.openapiprocessor.api.v1.OpenApiProcessor,
16 | io.openapiprocessor.api.OpenApiProcessor
17 | {
18 | override fun getName(): String {
19 | return "spring"
20 | }
21 |
22 | override fun run(processorOptions: MutableMap) {
23 | try {
24 | val processor = SpringProcessor()
25 | if (testMode) {
26 | processor.enableTestMode()
27 | }
28 | processor.run(processorOptions)
29 |
30 | } catch (ex: Exception) {
31 | throw ex
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/kotlin/io/openapiprocessor/spring/processor/SpringServiceV2.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | //@file:Suppress("DEPRECATION")
7 |
8 | package io.openapiprocessor.spring.processor
9 |
10 | import io.openapiprocessor.api.v2.Version
11 | import io.openapiprocessor.core.version.GitHubVersionException
12 | import io.openapiprocessor.core.version.GitHubVersionProvider
13 | import io.openapiprocessor.test.api.OpenApiProcessorTest
14 | import org.slf4j.Logger
15 | import org.slf4j.LoggerFactory
16 |
17 | /**
18 | * Entry point of openapi-processor-spring loaded via [java.util.ServiceLoader]. by the v2 interface
19 | * [io.openapiprocessor.api.v1.OpenApiProcessor].
20 | *
21 | * the v2 interfaces *must* be implemented by its own class and not by [SpringService] to be downward
22 | * compatible with gradle/maven plugin versions that do not know the v2 interfaces.
23 | */
24 | class SpringServiceV2(
25 | private val provider: GitHubVersionProvider = GitHubVersionProvider("openapi-processor-spring"),
26 | private val testMode: Boolean = false):
27 | io.openapiprocessor.api.v2.OpenApiProcessor,
28 | io.openapiprocessor.api.v2.OpenApiProcessorVersion,
29 | OpenApiProcessorTest
30 | {
31 | private val log: Logger = LoggerFactory.getLogger(this.javaClass.name)
32 |
33 | private var sourceRoot: String? = null
34 | private var resourceRoot: String? = null
35 |
36 | override fun getName(): String {
37 | return "spring"
38 | }
39 |
40 | override fun run(processorOptions: MutableMap) {
41 | try {
42 | val processor = SpringProcessor()
43 | if (testMode) {
44 | processor.enableTestMode()
45 | }
46 | processor.run(processorOptions)
47 |
48 | sourceRoot = processor.sourceRoot
49 | resourceRoot = processor.resourceRoot
50 | } catch (ex: Exception) {
51 | throw ex
52 | }
53 | }
54 |
55 | override fun getVersion(): String {
56 | return io.openapiprocessor.spring.Version.version
57 | }
58 |
59 | override fun getLatestVersion(): Version {
60 | return provider.getVersion()
61 | }
62 |
63 | override fun hasNewerVersion(): Boolean {
64 | try {
65 | val version = version
66 | val latest = latestVersion
67 |
68 | if (latest.name > version) {
69 | log.info("openapi-processor-spring version ${latest.name} is available! I'm version ${version}.")
70 | return true
71 | }
72 |
73 | return false
74 | } catch (ex: GitHubVersionException) {
75 | // just ignore, do not complain
76 | return false
77 | }
78 | }
79 |
80 | override fun getSourceRoot(): String? {
81 | return sourceRoot
82 | }
83 |
84 | override fun getResourceRoot(): String? {
85 | return resourceRoot
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/kotlin/io/openapiprocessor/spring/writer/java/AdditionalEnumWriter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring.writer.java
7 |
8 | import io.openapiprocessor.core.converter.ApiOptions
9 | import io.openapiprocessor.core.writer.SourceFormatter
10 | import io.openapiprocessor.core.writer.WriterFactory
11 | import java.io.StringWriter
12 | import java.io.Writer
13 |
14 | class AdditionalEnumWriter {
15 |
16 | fun write(options: ApiOptions, formatter: SourceFormatter, factory: WriterFactory) {
17 | if (options.enumType != "framework") {
18 | return
19 | }
20 |
21 | val piWriter = createPackageInfoWriter(options, factory)
22 | writePackageInfo(options, piWriter, formatter)
23 | piWriter.close()
24 |
25 | val writer = createFactoryWriter(options, factory)
26 | writeEnumConverterFactory(options, writer, formatter)
27 | writer.close()
28 | }
29 |
30 | private fun writePackageInfo(options: ApiOptions, writer: Writer, formatter: SourceFormatter) {
31 | val raw = StringWriter()
32 | PackageInfoWriter(options).writePackageInfo(raw)
33 | writer.write(formatter.format(raw.toString()))
34 | }
35 |
36 | private fun writeEnumConverterFactory(options: ApiOptions, writer: Writer, formatter: SourceFormatter) {
37 | val raw = StringWriter()
38 | EnumConverterFactoryWriter(options).writeConverterFactory(raw)
39 | writer.write(formatter.format(raw.toString()))
40 | }
41 |
42 | private fun createFactoryWriter(options: ApiOptions, writerFactory: WriterFactory): Writer {
43 | return writerFactory.createWriter(
44 | "${options.packageName}.spring",
45 | "StringToEnumConverterFactory")
46 | }
47 |
48 | private fun createPackageInfoWriter(options: ApiOptions, writerFactory: WriterFactory): Writer {
49 | return writerFactory.createWriter(
50 | "${options.packageName}.spring",
51 | "package-info")
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/kotlin/io/openapiprocessor/spring/writer/java/EnumConverterFactoryWriter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring.writer.java
7 |
8 | import io.openapiprocessor.core.converter.ApiOptions
9 | import java.io.Writer
10 |
11 | class EnumConverterFactoryWriter(val options: ApiOptions) {
12 |
13 | fun writeConverterFactory(target: Writer) {
14 | target.write("""
15 | package ${options.packageName}.spring;
16 |
17 | import org.springframework.core.convert.converter.Converter;
18 | import org.springframework.core.convert.converter.ConverterFactory;
19 |
20 | import java.util.EnumSet;
21 | import java.util.function.Supplier;
22 |
23 | public class StringToEnumConverterFactory & Supplier>
24 | implements ConverterFactory {
25 |
26 | @Override
27 | @SuppressWarnings({"unchecked", "rawtypes"})
28 | public Converter getConverter(Class targetType) {
29 | return new StringToEnumConverter(targetType);
30 | }
31 |
32 | static class StringToEnumConverter & Supplier> implements Converter {
33 |
34 | private final Class enumType;
35 |
36 | public StringToEnumConverter(Class enumType) {
37 | this.enumType = enumType;
38 | }
39 |
40 | public T convert(String source) {
41 | String sourceValue = source.trim();
42 |
43 | for (T e : EnumSet.allOf(enumType)) {
44 | if (e.get().equals(sourceValue)) {
45 | return e;
46 | }
47 | }
48 |
49 | throw new IllegalArgumentException(String.format("No enum constant of %s has the value %s",
50 | enumType.getCanonicalName(),
51 | sourceValue));
52 | }
53 | }
54 | }
55 | """.trimIndent())
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/kotlin/io/openapiprocessor/spring/writer/java/MappingAnnotationWriter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring.writer.java
7 |
8 | import io.openapiprocessor.core.model.Endpoint
9 | import io.openapiprocessor.core.model.EndpointResponse
10 | import io.openapiprocessor.spring.processor.SpringFrameworkAnnotations
11 | import java.io.Writer
12 | import io.openapiprocessor.core.writer.java.MappingAnnotationWriter as CoreMappingAnnotationWriter
13 |
14 | /**
15 | * spring mapping annotation writer
16 | */
17 | class MappingAnnotationWriter(private val annotations: SpringFrameworkAnnotations): CoreMappingAnnotationWriter {
18 |
19 | override fun write(target: Writer, endpoint: Endpoint, endpointResponse: EndpointResponse) {
20 | target.write(createAnnotation(endpoint, endpointResponse))
21 | }
22 |
23 | private fun createAnnotation(endpoint: Endpoint, endpointResponse: EndpointResponse): String {
24 | val annotation = annotations.getAnnotation(endpoint.method)
25 |
26 | var mapping = annotation.annotationName
27 | mapping += "("
28 | mapping += "path = " + quote(endpoint.path)
29 |
30 | val consumes = endpoint.getConsumesContentTypes()
31 | if (consumes.isNotEmpty()) {
32 | mapping += ", "
33 | mapping += "consumes = {"
34 | mapping += consumes.joinToString(", ") {
35 | quote(it)
36 | }
37 | mapping += '}'
38 | }
39 |
40 | val produces = endpointResponse.contentTypes
41 | if (produces.isNotEmpty()) {
42 | mapping += ", "
43 | mapping += "produces = {"
44 |
45 | mapping += produces.joinToString(", ") {
46 | quote(it)
47 | }
48 |
49 | mapping += "}"
50 | }
51 |
52 | annotation.parameters.forEach {
53 | mapping += ", "
54 | mapping += "${it.key} = ${it.value.value}"
55 | }
56 |
57 | mapping += ")"
58 | return mapping
59 | }
60 |
61 | private fun quote(content: String): String {
62 | return '"' + content + '"'
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/kotlin/io/openapiprocessor/spring/writer/java/PackageInfoWriter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring.writer.java
7 |
8 | import io.openapiprocessor.core.converter.ApiOptions
9 | import java.io.Writer
10 |
11 | class PackageInfoWriter(val options: ApiOptions) {
12 |
13 | fun writePackageInfo(target: Writer) {
14 | target.write("""
15 | @org.springframework.lang.NonNullApi
16 | @org.springframework.lang.NonNullFields
17 | package ${options.packageName}.spring;
18 | """.trimIndent())
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/kotlin/io/openapiprocessor/spring/writer/java/ParameterAnnotationWriter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring.writer.java
7 |
8 | import io.openapiprocessor.core.framework.FrameworkAnnotations
9 | import io.openapiprocessor.core.model.RequestBody
10 | import io.openapiprocessor.core.model.parameters.Parameter
11 | import io.openapiprocessor.core.writer.java.ParameterAnnotationWriter as CoreParameterAnnotationWriter
12 | import java.io.Writer
13 |
14 | /**
15 | * spring parameter annotation writer
16 | */
17 | class ParameterAnnotationWriter(private val annotations: FrameworkAnnotations)
18 | : CoreParameterAnnotationWriter {
19 |
20 | override fun write(target: Writer, parameter: Parameter) {
21 | if (parameter is RequestBody) {
22 | target.write(createRequestBodyAnnotation(parameter))
23 | } else {
24 | target.write(createParameterAnnotation(parameter))
25 | }
26 | }
27 |
28 | private fun createRequestBodyAnnotation(requestBody: RequestBody): String {
29 | var annotation = getAnnotationName(requestBody)
30 |
31 | // required is default, so add required only if the parameter is not required
32 | if (!requestBody.required) {
33 | annotation += "(required = false)"
34 | }
35 |
36 | return annotation
37 | }
38 |
39 | private fun createParameterAnnotation(parameter: Parameter): String {
40 | if (! parameter.withAnnotation) {
41 | return ""
42 | }
43 |
44 | var annotation = getAnnotationName (parameter)
45 |
46 | if (! parameter.withParameters) {
47 | return annotation
48 | }
49 |
50 | annotation += "("
51 | annotation += "name = " + quote (parameter.name)
52 |
53 | // required is the default, add required only if the parameter is not required
54 | if (!parameter.required) {
55 | annotation += ", "
56 | annotation += "required = false"
57 | }
58 |
59 | if (!parameter.required && hasDefault (parameter)) {
60 | annotation += ", "
61 | annotation += "defaultValue = ${getDefault(parameter)}"
62 | }
63 |
64 | annotation += ")"
65 | return annotation
66 | }
67 |
68 | private fun getAnnotationName(parameter: Parameter): String {
69 | return annotations.getAnnotation (parameter).annotationName
70 | }
71 |
72 | private fun hasDefault(parameter: Parameter): Boolean {
73 | return parameter.constraints.hasDefault()
74 | }
75 |
76 | private fun getDefault(parameter: Parameter): String {
77 | return quote(parameter.constraints.default.toString())
78 | }
79 |
80 | private fun quote(content: String): String {
81 | return '"' + content + '"'
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/kotlin/io/openapiprocessor/spring/writer/java/SpringWriterFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring.writer.java
7 |
8 | import io.openapiprocessor.core.converter.ApiOptions
9 | import io.openapiprocessor.core.writer.DefaultWriterFactory
10 | import java.nio.file.Path
11 |
12 | class SpringWriterFactory(options: ApiOptions): DefaultWriterFactory(options) {
13 |
14 | override fun initAdditionalPackages(options: ApiOptions): Map {
15 | val pkgPaths = HashMap()
16 |
17 | if (options.enumType == "framework") {
18 | val (name, path) = initTargetPackage("spring")
19 | pkgPaths[name] = path
20 | }
21 |
22 | return pkgPaths
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/kotlin/io/openapiprocessor/spring/writer/java/StatusAnnotationWriter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring.writer.java
7 |
8 | import io.openapiprocessor.core.framework.FrameworkAnnotations
9 | import io.openapiprocessor.core.model.Endpoint
10 | import io.openapiprocessor.core.model.EndpointResponse
11 | import io.openapiprocessor.core.model.EndpointResponseStatus
12 | import java.io.Writer
13 | import io.openapiprocessor.core.writer.java.StatusAnnotationWriter as CoreStatusAnnotationWriter
14 |
15 | class StatusAnnotationWriter(private val annotations: FrameworkAnnotations): CoreStatusAnnotationWriter {
16 |
17 | override fun write(
18 | target: Writer,
19 | endpoint: Endpoint,
20 | endpointResponse: EndpointResponse
21 | ) {
22 | target.write(createStatusAnnotation(endpointResponse))
23 | }
24 |
25 | private fun createStatusAnnotation(status: EndpointResponseStatus): String {
26 | val data = annotations.getAnnotation(status)
27 | val status = data.parameters["code"]
28 |
29 | var annotation = data.annotationName
30 | annotation += "(${status!!.value})"
31 | return annotation
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/services/com.github.hauner.openapi.api.OpenApiProcessor:
--------------------------------------------------------------------------------
1 | io.openapiprocessor.spring.processor.SpringService
2 |
3 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/services/io.openapiprocessor.api.OpenApiProcessor:
--------------------------------------------------------------------------------
1 | io.openapiprocessor.spring.processor.SpringService
2 |
3 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/services/io.openapiprocessor.api.v1.OpenApiProcessor:
--------------------------------------------------------------------------------
1 | io.openapiprocessor.spring.processor.SpringService
2 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/services/io.openapiprocessor.api.v2.OpenApiProcessor:
--------------------------------------------------------------------------------
1 | io.openapiprocessor.spring.processor.SpringServiceV2
2 |
--------------------------------------------------------------------------------
/src/test/groovy/io/openapiprocessor/spring/writer/java/CookieParameterAnnotationWriterSpec.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 the original authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.openapiprocessor.spring.writer.java
18 |
19 | import io.openapiprocessor.spring.processor.SpringFrameworkAnnotations
20 | import io.openapiprocessor.core.model.datatypes.DataTypeConstraints
21 | import io.openapiprocessor.core.model.datatypes.StringDataType
22 | import io.openapiprocessor.core.model.parameters.CookieParameter
23 | import spock.lang.Specification
24 |
25 | class CookieParameterAnnotationWriterSpec extends Specification {
26 | def writer = new ParameterAnnotationWriter(new SpringFrameworkAnnotations())
27 | def target = new StringWriter()
28 |
29 | void "write simple (required) cookie parameter" () {
30 | def param = new CookieParameter(
31 | 'foo', new StringDataType(), true, false, null)
32 |
33 | when:
34 | writer.write (target, param)
35 |
36 | then:
37 | target.toString () == '@CookieValue(name = "foo")'
38 | }
39 |
40 | void "write simple (optional) cookie parameter" () {
41 | def param = new CookieParameter(
42 | 'foo', new StringDataType(), false, false, null)
43 |
44 | when:
45 | writer.write (target, param)
46 |
47 | then:
48 | target.toString () == '@CookieValue(name = "foo", required = false)'
49 | }
50 |
51 | void "write simple (optional with default) cookie parameter" () {
52 | def param = new CookieParameter('foo',
53 | new StringDataType('string',
54 | createConstraints ('bar'),
55 | false, null),
56 | false, false, null)
57 |
58 | when:
59 | writer.write (target, param)
60 |
61 | then:
62 | target.toString () == '@CookieValue(name = "foo", required = false, defaultValue = "bar")'
63 | }
64 |
65 | DataTypeConstraints createConstraints(def defaultValue) {
66 | def c = new DataTypeConstraints()
67 | c.defaultValue = defaultValue
68 | c
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/test/groovy/io/openapiprocessor/spring/writer/java/HeaderParameterAnnotationWriterSpec.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 the original authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.openapiprocessor.spring.writer.java
18 |
19 | import io.openapiprocessor.spring.processor.SpringFrameworkAnnotations
20 | import io.openapiprocessor.core.model.datatypes.DataTypeConstraints
21 | import io.openapiprocessor.core.model.datatypes.StringDataType
22 | import io.openapiprocessor.core.model.parameters.HeaderParameter
23 | import spock.lang.Specification
24 |
25 | class HeaderParameterAnnotationWriterSpec extends Specification {
26 | def writer = new ParameterAnnotationWriter(new SpringFrameworkAnnotations())
27 | def target = new StringWriter()
28 |
29 | void "write simple (required) header parameter" () {
30 | def param = new HeaderParameter(
31 | 'foo', new StringDataType(), true, false, null)
32 |
33 | when:
34 | writer.write (target, param)
35 |
36 | then:
37 | target.toString () == '@RequestHeader(name = "foo")'
38 | }
39 |
40 | void "write simple (optional) header parameter" () {
41 | def param = new HeaderParameter(
42 | 'foo', new StringDataType(), false, false, null)
43 |
44 | when:
45 | writer.write (target, param)
46 |
47 | then:
48 | target.toString () == '@RequestHeader(name = "foo", required = false)'
49 | }
50 |
51 | void "write simple (optional with default) header parameter" () {
52 | def param = new HeaderParameter('foo',
53 | new StringDataType('string',
54 | createConstraints ('bar'),
55 | false, null),
56 | false, false, null)
57 |
58 | when:
59 | writer.write (target, param)
60 |
61 | then:
62 | target.toString () == '@RequestHeader(name = "foo", required = false, defaultValue = "bar")'
63 | }
64 |
65 | DataTypeConstraints createConstraints(def defaultValue) {
66 | def c = new DataTypeConstraints()
67 | c.defaultValue = defaultValue
68 | c
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/test/groovy/io/openapiprocessor/spring/writer/java/ParameterAnnotationWriterSpec.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 the original authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.openapiprocessor.spring.writer.java
18 |
19 | import io.openapiprocessor.core.model.datatypes.DataTypeName
20 | import io.openapiprocessor.spring.processor.SpringFrameworkAnnotations
21 | import io.openapiprocessor.core.model.RequestBody
22 | import io.openapiprocessor.core.model.datatypes.DataTypeConstraints
23 | import io.openapiprocessor.core.model.datatypes.LongDataType
24 | import io.openapiprocessor.core.model.datatypes.ObjectDataType
25 | import io.openapiprocessor.core.model.datatypes.StringDataType
26 | import io.openapiprocessor.core.model.parameters.CookieParameter
27 | import io.openapiprocessor.core.model.parameters.HeaderParameter
28 | import io.openapiprocessor.core.model.parameters.PathParameter
29 | import io.openapiprocessor.spring.model.parameters.QueryParameter
30 | import spock.lang.Specification
31 | import spock.lang.Unroll
32 |
33 | class ParameterAnnotationWriterSpec extends Specification {
34 | def writer = new ParameterAnnotationWriter(new SpringFrameworkAnnotations())
35 | def target = new StringWriter()
36 |
37 | @Unroll
38 | void "writes simple (optional) query parameter with quoted string default value" () {
39 | def param = clazz.newInstance('foo',
40 | new StringDataType(
41 | 'string',
42 | createConstraints ('bar'), false, null),
43 | false, false, null)
44 |
45 | when:
46 | writer.write (target, param)
47 |
48 | then:
49 | target.toString () == """${annotation}(name = "foo", required = false, defaultValue = "bar")"""
50 |
51 | where:
52 | clazz | annotation
53 | QueryParameter | "@RequestParam"
54 | PathParameter | "@PathVariable"
55 | CookieParameter | "@CookieValue"
56 | HeaderParameter | "@RequestHeader"
57 | }
58 |
59 | void "writes simple (optional) query parameter with quoted number default value" () {
60 | def param = clazz.newInstance ('foo',
61 | new LongDataType (
62 | 'integer:int64',
63 | createConstraints (5), false, null),
64 | false, false, null)
65 |
66 | when:
67 | writer.write (target, param)
68 |
69 | then:
70 | target.toString () == """${annotation}(name = "foo", required = false, defaultValue = "5")"""
71 |
72 | where:
73 | clazz | annotation
74 | QueryParameter | "@RequestParam"
75 | PathParameter | "@PathVariable"
76 | CookieParameter | "@CookieValue"
77 | HeaderParameter | "@RequestHeader"
78 | }
79 |
80 | void "writes required request body parameter" () {
81 | def body = new RequestBody (
82 | 'body', 'application/json',
83 | new ObjectDataType (new DataTypeName('FooRequestBody', 'FooRequestBody'), '',
84 | ['foo': new StringDataType ()], null, false, null),
85 | true, false, null)
86 |
87 | when:
88 | writer.write (target, body)
89 |
90 | then:
91 | target.toString () == "@RequestBody"
92 | }
93 |
94 | void "writes optional request body parameter" () {
95 | def body = new RequestBody (
96 | 'body', 'application/json',
97 | new ObjectDataType (new DataTypeName('FooRequestBody', 'FooRequestBody'), '',
98 | ['foo': new StringDataType ()], null, false, null),
99 | false, false, null)
100 |
101 | when:
102 | writer.write (target, body)
103 |
104 | then:
105 | target.toString () == "@RequestBody(required = false)"
106 | }
107 |
108 | DataTypeConstraints createConstraints(def defaultValue) {
109 | def c = new DataTypeConstraints()
110 | c.defaultValue = defaultValue
111 | c
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/src/test/groovy/io/openapiprocessor/spring/writer/java/PathParameterAnnotationWriterSpec.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 the original authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.openapiprocessor.spring.writer.java
18 |
19 | import io.openapiprocessor.spring.processor.SpringFrameworkAnnotations
20 | import io.openapiprocessor.core.model.datatypes.DataTypeConstraints
21 | import io.openapiprocessor.core.model.datatypes.StringDataType
22 | import io.openapiprocessor.core.model.parameters.PathParameter
23 | import spock.lang.Specification
24 |
25 | class PathParameterAnnotationWriterSpec extends Specification {
26 | def writer = new ParameterAnnotationWriter(new SpringFrameworkAnnotations())
27 | def target = new StringWriter()
28 |
29 | void "write simple (required) path parameter" () {
30 | def param = new PathParameter(
31 | 'foo', new StringDataType(), true, false, null)
32 |
33 | when:
34 | writer.write (target, param)
35 |
36 | then:
37 | target.toString () == '@PathVariable(name = "foo")'
38 | }
39 |
40 | void "write simple (optional) path parameter" () {
41 | def param = new PathParameter(
42 | 'foo', new StringDataType(), false, false, null)
43 |
44 | when:
45 | writer.write (target, param)
46 |
47 | then:
48 | target.toString () == '@PathVariable(name = "foo", required = false)'
49 | }
50 |
51 | void "write simple (optional with default) path parameter" () {
52 | def param = new PathParameter('foo',
53 | new StringDataType(
54 | 'string',
55 | createConstraints ('bar'),
56 | false, null),
57 | false, false, null)
58 |
59 | when:
60 | writer.write (target, param)
61 |
62 | then:
63 | target.toString () == '@PathVariable(name = "foo", required = false, defaultValue = "bar")'
64 | }
65 |
66 | DataTypeConstraints createConstraints(def defaultValue) {
67 | def c = new DataTypeConstraints()
68 | c.defaultValue = defaultValue
69 | c
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/src/test/groovy/io/openapiprocessor/spring/writer/java/QueryParameterAnnotationWriterSpec.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 the original authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.openapiprocessor.spring.writer.java
18 |
19 | import io.openapiprocessor.core.model.datatypes.DataTypeConstraints
20 | import io.openapiprocessor.core.model.datatypes.DataTypeName
21 | import io.openapiprocessor.core.model.datatypes.ObjectDataType
22 | import io.openapiprocessor.core.model.datatypes.StringDataType
23 | import io.openapiprocessor.spring.model.parameters.QueryParameter
24 | import io.openapiprocessor.spring.processor.SpringFrameworkAnnotations
25 | import spock.lang.Specification
26 |
27 | class QueryParameterAnnotationWriterSpec extends Specification {
28 | def writer = new ParameterAnnotationWriter(new SpringFrameworkAnnotations())
29 | def target = new StringWriter()
30 |
31 | void "write simple (required) query parameter" () {
32 | def param = new QueryParameter(
33 | 'foo', new StringDataType(), true, false, null)
34 |
35 | when:
36 | writer.write (target, param)
37 |
38 | then:
39 | target.toString () == '@RequestParam(name = "foo")'
40 | }
41 |
42 | void "write simple (optional) query parameter" () {
43 | def param = new QueryParameter(
44 | 'foo', new StringDataType(), false, false, null)
45 |
46 | when:
47 | writer.write (target, param)
48 |
49 | then:
50 | target.toString () == '@RequestParam(name = "foo", required = false)'
51 | }
52 |
53 | void "write simple (optional with default) query parameter" () {
54 | def param = new QueryParameter('foo',
55 | new StringDataType(
56 | 'string',
57 | new DataTypeConstraints(defaultValue: 'bar'),
58 | false, null),
59 | false, false, null)
60 |
61 | when:
62 | writer.write (target, param)
63 |
64 | then:
65 | target.toString () == '@RequestParam(name = "foo", required = false, defaultValue = "bar")'
66 | }
67 |
68 | void "writes object query parameter without annotation" () {
69 | def param = new QueryParameter(
70 | 'foo',
71 | new ObjectDataType (
72 | new DataTypeName('Foo', 'Foo'), '', [
73 | foo1: new StringDataType (),
74 | foo2: new StringDataType ()
75 | ], null, false, null
76 | ), false, false, null
77 | )
78 |
79 | when:
80 | writer.write (target, param)
81 |
82 | then:
83 | target.toString () == ""
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/src/test/kotlin/io/openapiprocessor/spring/processor/SpringFrameworkAnnotationsSpec.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring.processor
7 |
8 | import io.kotest.core.spec.style.StringSpec
9 | import io.kotest.matchers.collections.shouldContainExactly
10 | import io.kotest.matchers.shouldBe
11 | import io.openapiprocessor.core.model.EndpointResponseStatus
12 | import io.openapiprocessor.core.model.HttpStatus
13 |
14 |
15 | class SpringFrameworkAnnotationsSpec : StringSpec({
16 |
17 | class Status(override val statusCode: HttpStatus) : EndpointResponseStatus
18 |
19 | "provides known HttpStatus annotation with specific status code import" {
20 | val framework = SpringFrameworkAnnotations()
21 |
22 | val annotation200 = framework.getAnnotation(Status("200"))
23 | annotation200.referencedImports shouldContainExactly setOf("org.springframework.http.HttpStatus")
24 | annotation200.parameters["code"]!!.value shouldBe "HttpStatus.OK"
25 |
26 | val annotation400 = framework.getAnnotation(Status("400"))
27 | annotation400.referencedImports shouldContainExactly setOf("org.springframework.http.HttpStatus")
28 | annotation400.parameters["code"]!!.value shouldBe "HttpStatus.BAD_REQUEST"
29 |
30 | val annotation500 = framework.getAnnotation(Status("500"))
31 | annotation500.referencedImports shouldContainExactly setOf("org.springframework.http.HttpStatus")
32 | annotation500.parameters["code"]!!.value shouldBe "HttpStatus.INTERNAL_SERVER_ERROR"
33 | }
34 | })
35 |
--------------------------------------------------------------------------------
/src/test/kotlin/io/openapiprocessor/spring/processor/SpringServiceSpec.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring.processor
7 |
8 | import io.kotest.core.spec.style.StringSpec
9 | import io.kotest.matchers.booleans.shouldBeFalse
10 | import io.kotest.matchers.booleans.shouldBeTrue
11 | import io.kotest.matchers.shouldBe
12 | import io.mockk.every
13 | import io.mockk.mockk
14 | import io.openapiprocessor.core.version.GitHubVersion
15 | import io.openapiprocessor.core.version.GitHubVersionProvider
16 | import io.openapiprocessor.spring.Version
17 | import java.time.Instant
18 |
19 | class SpringServiceSpec : StringSpec({
20 |
21 | "get version" {
22 | val service = SpringServiceV2()
23 | service.version.shouldBe(Version.version)
24 | }
25 |
26 | "gets latest version" {
27 | val provider = mockk()
28 | every { provider.getVersion() } returns GitHubVersion("1", Instant.now(), "any")
29 |
30 | val service = SpringServiceV2(provider)
31 |
32 | service.latestVersion.name shouldBe "1"
33 | }
34 |
35 | "checks newer version available" {
36 | val provider = mockk()
37 | every { provider.getVersion() } returns GitHubVersion("3000", Instant.now(), "any")
38 |
39 | val service = SpringServiceV2(provider)
40 | service.hasNewerVersion().shouldBeTrue()
41 | }
42 |
43 | "checks newer version not available" {
44 | val provider = mockk()
45 | every { provider.getVersion() } returns GitHubVersion(Version.version, Instant.now(), "any")
46 |
47 | val service = SpringServiceV2(provider)
48 | service.hasNewerVersion().shouldBeFalse()
49 | }
50 |
51 | })
52 |
--------------------------------------------------------------------------------
/src/test/kotlin/io/openapiprocessor/spring/writer/java/StatusAnnotationWriterSpec.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring.writer.java
7 |
8 | import io.kotest.core.spec.style.StringSpec
9 | import io.kotest.matchers.equals.shouldBeEqual
10 | import io.openapiprocessor.core.model.Endpoint
11 | import io.openapiprocessor.core.model.Response
12 | import io.openapiprocessor.core.model.datatypes.NoneDataType
13 | import io.openapiprocessor.core.parser.HttpMethod
14 | import io.openapiprocessor.spring.processor.SpringFrameworkAnnotations
15 | import java.io.StringWriter
16 |
17 |
18 | class StatusAnnotationWriterSpec: StringSpec({
19 | val writer = StatusAnnotationWriter(SpringFrameworkAnnotations())
20 | val target = StringWriter()
21 |
22 | "write response status annotation" {
23 | val ep = Endpoint(
24 | "/foo",
25 | HttpMethod.GET,
26 | emptyList(),
27 | emptyList(),
28 | mapOf(
29 | "204" to listOf(Response("?", NoneDataType()))
30 | )
31 | )
32 |
33 | writer.write(target, ep, ep.endpointResponses.first())
34 |
35 | target.toString() shouldBeEqual """@ResponseStatus(HttpStatus.NO_CONTENT)"""
36 | }
37 | })
38 |
--------------------------------------------------------------------------------
/src/testInt/kotlin/io/openapiprocessor/spring/CompileExpectedSpec.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring
7 |
8 | import io.kotest.core.spec.style.StringSpec
9 | import io.kotest.engine.spec.tempdir
10 | import io.kotest.matchers.booleans.shouldBeTrue
11 | import io.openapiprocessor.core.parser.ParserType
12 | import io.openapiprocessor.test.*
13 | import kotlin.collections.filter
14 | import kotlin.collections.map
15 | import kotlin.collections.plus
16 |
17 |
18 | class CompileExpectedSpec: StringSpec({
19 |
20 | for (testSet in sources()) {
21 | "compile - $testSet".config(enabled = true) {
22 | val folder = tempdir()
23 | val reader = ResourceReader(CompileExpectedSpec::class.java)
24 |
25 | val testFiles = TestFilesNative(folder, reader)
26 |
27 | TestSetCompiler(testSet, testFiles)
28 | .run(setOf(
29 | "src/testInt/resources/compile/Generated.java"
30 | ))
31 | .shouldBeTrue()
32 | }
33 | }
34 | })
35 |
36 | private fun sources(): Collection {
37 | val compile30 = ALL_30.map {
38 | testSet(it.name, ParserType.INTERNAL, it.openapi, model = "default", outputs = it.outputs, expected = it.expected)
39 | }
40 |
41 | val compile31 = ALL_31.map {
42 | testSet(it.name, ParserType.INTERNAL, it.openapi, model = "default", outputs = it.outputs, expected = it.expected)
43 | }
44 |
45 | val compile30r = ALL_30.filter { it.modelTypes.contains(ModelTypes.RECORD) }.map {
46 | testSet(it.name, ParserType.INTERNAL, it.openapi, model = "record", outputs = it.outputs, expected = it.expected)
47 | }
48 |
49 | val compile31r = ALL_31.filter { it.modelTypes.contains(ModelTypes.RECORD) }.map {
50 | testSet(it.name, ParserType.INTERNAL, it.openapi, model = "record", outputs = it.outputs, expected = it.expected)
51 | }
52 |
53 | return compile30 + compile31 + compile30r + compile31r
54 | }
55 |
--------------------------------------------------------------------------------
/src/testInt/kotlin/io/openapiprocessor/spring/ProcessorEndToEndJimfsSpec.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring
7 |
8 | import com.google.common.jimfs.Configuration
9 | import com.google.common.jimfs.Jimfs
10 | import io.kotest.core.spec.style.StringSpec
11 | import io.kotest.matchers.booleans.shouldBeTrue
12 | import io.openapiprocessor.core.parser.ParserType
13 | import io.openapiprocessor.test.*
14 | import io.openapiprocessor.test.TestSet
15 |
16 | /**
17 | * runs integration tests with Jimfs.
18 | */
19 | class ProcessorEndToEndJimfsSpec: StringSpec({
20 |
21 | for (testSet in sources()) {
22 | "jimfs - $testSet".config(enabled = true) {
23 | val fs = Jimfs.newFileSystem (Configuration.unix ())
24 | val reader = ResourceReader(ProcessorEndToEndJimfsSpec::class.java)
25 |
26 | val testFiles = TestFilesJimfs(fs, reader)
27 | val test = Test(testSet, testFiles)
28 |
29 | TestSetRunner(test, testSet)
30 | .run()
31 | .shouldBeTrue()
32 | }
33 | }
34 |
35 | })
36 |
37 | private fun sources(): Collection {
38 | // the swagger parser does not work with a custom FileSystem
39 |
40 | val openapi4j = ALL_30.map {
41 | testSet(it.name, ParserType.OPENAPI4J, it.openapi, model = "default", outputs = it.outputs, expected = it.expected)
42 | }
43 |
44 | val openapi30 = ALL_30.map {
45 | testSet(it.name, ParserType.INTERNAL, it.openapi, model = "default", outputs = it.outputs, expected = it.expected)
46 | }
47 |
48 | val openapi31 = ALL_31.map {
49 | testSet(it.name, ParserType.INTERNAL, it.openapi, model = "default", outputs = it.outputs, expected = it.expected)
50 | }
51 |
52 | val openapi30r = ALL_30.filter { it.modelTypes.contains(ModelTypes.RECORD) }.map {
53 | testSet(it.name, ParserType.INTERNAL, it.openapi, model = "record", outputs = it.outputs, expected = it.expected)
54 | }
55 |
56 | val openapi31r = ALL_31.filter { it.modelTypes.contains(ModelTypes.RECORD) }.map {
57 | testSet(it.name, ParserType.INTERNAL, it.openapi, model = "record", outputs = it.outputs, expected = it.expected)
58 | }
59 |
60 | return openapi4j + openapi30 + openapi31 + openapi30r + openapi31r
61 | }
62 |
--------------------------------------------------------------------------------
/src/testInt/kotlin/io/openapiprocessor/spring/ProcessorEndToEndSpec.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring
7 |
8 | import io.kotest.core.spec.style.StringSpec
9 | import io.kotest.engine.spec.tempdir
10 | import io.kotest.matchers.booleans.shouldBeTrue
11 | import io.openapiprocessor.core.parser.ParserType
12 | import io.openapiprocessor.test.*
13 | import io.openapiprocessor.test.TestSet
14 |
15 | /**
16 | * run end-to-end integration test.
17 | */
18 | class ProcessorEndToEndSpec: StringSpec({
19 |
20 | for (testSet in sources()) {
21 | "native - $testSet".config(enabled = true) {
22 | val folder = tempdir()
23 | val reader = ResourceReader(ProcessorEndToEndSpec::class.java)
24 |
25 | val testFiles = TestFilesNative(folder, reader)
26 | val test = Test(testSet, testFiles)
27 |
28 | TestSetRunner(test, testSet)
29 | .run()
30 | .shouldBeTrue()
31 | }
32 | }
33 | })
34 |
35 | private fun sources(): Collection {
36 | val swagger = ALL_30.map {
37 | testSet(it.name, ParserType.SWAGGER, it.openapi, model = "default", outputs = it.outputs, expected = it.expected)
38 | }
39 |
40 | val openapi4j = ALL_30.map {
41 | testSet(it.name, ParserType.OPENAPI4J, it.openapi, model = "default", outputs = it.outputs, expected = it.expected)
42 | }
43 |
44 | val openapi30 = ALL_30.map {
45 | testSet(it.name, ParserType.INTERNAL, it.openapi, model = "default", outputs = it.outputs, expected = it.expected)
46 | }
47 |
48 | val openapi31 = ALL_31.map {
49 | testSet(it.name, ParserType.INTERNAL, it.openapi, model = "default", outputs = it.outputs, expected = it.expected)
50 | }
51 |
52 | val openapi30r = ALL_30.filter { it.modelTypes.contains(ModelTypes.RECORD) }.map {
53 | testSet(it.name, ParserType.INTERNAL, it.openapi, model = "record", outputs = it.outputs, expected = it.expected)
54 | }
55 |
56 | val openapi31r = ALL_31.filter { it.modelTypes.contains(ModelTypes.RECORD) }.map {
57 | testSet(it.name, ParserType.INTERNAL, it.openapi, model = "record", outputs = it.outputs, expected = it.expected)
58 | }
59 |
60 | return swagger + openapi4j + openapi30 + openapi31 + openapi30r + openapi31r
61 | }
62 |
--------------------------------------------------------------------------------
/src/testInt/kotlin/io/openapiprocessor/spring/ProcessorPendingSpec.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring
7 |
8 | import io.kotest.core.spec.style.StringSpec
9 | import io.kotest.engine.spec.tempdir
10 | import io.kotest.matchers.booleans.shouldBeTrue
11 | import io.openapiprocessor.core.parser.ParserType.INTERNAL
12 | import io.openapiprocessor.test.*
13 | import io.openapiprocessor.test.API_30
14 | import io.openapiprocessor.test.TestSet
15 |
16 | /**
17 | * helper to run selected integration tests.
18 | */
19 | class ProcessorPendingSpec: StringSpec({
20 |
21 | for (testSet in sources()) {
22 | "native - $testSet".config(enabled = true) {
23 | val folder = tempdir()
24 | val reader = ResourceReader(ProcessorPendingSpec::class.java)
25 |
26 | val testFiles = TestFilesNative(folder, reader)
27 | val test = Test(testSet, testFiles)
28 |
29 | TestSetRunner(test, testSet)
30 | .run()
31 | .shouldBeTrue()
32 | }
33 | }
34 | })
35 |
36 | private fun sources(): Collection {
37 | return listOf(
38 | testSet("params-enum", INTERNAL, API_30, model = "default", outputs = "outputs.yaml", expected = "outputs"),
39 | // testSet("params-request-body-multipart-mapping", INTERNAL, API_30, model = "default", outputs = "outputs.yaml", expected = "outputs"),
40 | // testSet("params-request-body-multipart-mapping", INTERNAL, API_31, model = "default", outputs = "outputs.yaml", expected = "outputs")
41 | )
42 | }
43 |
--------------------------------------------------------------------------------
/src/testInt/kotlin/io/openapiprocessor/spring/ProcessorTestSets.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring
7 |
8 | import io.openapiprocessor.test.*
9 |
10 | val ALL_30: List = listOf(
11 | test30_D_("endpoint-http-mapping"),
12 | test30_DR("params-complex-data-types"),
13 | test30_D_("params-enum"),
14 | test30_D_("params-pageable-mapping"),
15 | test30_D_("params-path-simple-data-types"),
16 | test30_D_("params-query-annotate-simple-mapping"),
17 | test30_DR("params-request-body"),
18 | test30_D_("params-request-body-multipart-mapping"),
19 | test30_D_("params-simple-data-types"),
20 | test30_D_("response-status")
21 | )
22 |
23 | val ALL_31: List = listOf(
24 | test31_D_("endpoint-http-mapping"),
25 | test31_DR("params-complex-data-types"),
26 | test31_D_("params-enum"),
27 | test31_D_("params-pageable-mapping"),
28 | test31_D_("params-path-simple-data-types"),
29 | test31_D_("params-query-annotate-simple-mapping"),
30 | test31_DR("params-request-body"),
31 | test31_DR("params-request-body-multipart-mapping"),
32 | test31_D_("params-simple-data-types"),
33 | test31_D_("response-status")
34 | )
35 |
--------------------------------------------------------------------------------
/src/testInt/kotlin/io/openapiprocessor/spring/ProcessorTestSetsSupport.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 https://github.com/openapi-processor/openapi-processor-spring
3 | * PDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package io.openapiprocessor.spring
7 |
8 | import io.openapiprocessor.core.parser.ParserType
9 | import io.openapiprocessor.spring.processor.SpringServiceV2
10 | import io.openapiprocessor.test.TestSet
11 |
12 | @Suppress("SameParameterValue")
13 | fun testSet(
14 | name: String,
15 | parser: ParserType,
16 | openapi: String = "openapi.yaml",
17 | model: String = "default",
18 | inputs: String = "inputs.yaml",
19 | outputs: String = "generated.yaml",
20 | expected: String = "generated"
21 | ): TestSet {
22 | val testSet = TestSet()
23 | testSet.name = name
24 | testSet.processor = SpringServiceV2(testMode = true)
25 | testSet.parser = parser.name
26 | testSet.modelType = model
27 | testSet.openapi = openapi
28 | testSet.inputs = inputs
29 | testSet.outputs = outputs
30 | testSet.expected = expected
31 | return testSet
32 | }
33 |
--------------------------------------------------------------------------------
/src/testInt/resources/compile/Generated.java:
--------------------------------------------------------------------------------
1 | package generated.support;
2 |
3 | import java.lang.annotation.*;
4 | import static java.lang.annotation.ElementType.*;
5 | import static java.lang.annotation.RetentionPolicy.*;
6 |
7 | @Documented
8 | @Retention(CLASS)
9 | @Target({TYPE, METHOD})
10 | @Generated(value = "openapi-processor-spring", version = "test")
11 | public @interface Generated {
12 | /**
13 | * The name of the source code generator, i.e. openapi-processor-*.
14 | *
15 | * @return name of the generator
16 | */
17 | String value();
18 |
19 | /**
20 | * @return version of the generator
21 | */
22 | String version();
23 |
24 | /**
25 | * The date & time of generation (ISO 8601) or "-" if no date was set.
26 | *
27 | * @return date of generation
28 | */
29 | String date() default "-";
30 |
31 | /**
32 | * The url of the generator.
33 | *
34 | * @return url of generator
35 | */
36 | String url() default "https://openapiprocessor.io";
37 | }
38 |
--------------------------------------------------------------------------------
/src/testInt/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/endpoint-http-mapping/inputs.yaml:
--------------------------------------------------------------------------------
1 | items:
2 | - inputs/openapi30.yaml
3 | - inputs/openapi31.yaml
4 | - inputs/mapping.yaml
5 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/endpoint-http-mapping/inputs/mapping.yaml:
--------------------------------------------------------------------------------
1 | openapi-processor-mapping: v4
2 |
3 | options:
4 | package-name: generated
5 | format-code: false
6 |
7 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/endpoint-http-mapping/inputs/openapi30.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.2
2 | info:
3 | title: test endpoint http method mapping
4 | version: 1.0.0
5 |
6 | paths:
7 | /endpoint:
8 |
9 | delete:
10 | tags:
11 | - endpoint
12 | responses:
13 | '204':
14 | description: empty
15 |
16 | get:
17 | tags:
18 | - endpoint
19 | responses:
20 | '204':
21 | description: empty
22 |
23 | head:
24 | tags:
25 | - endpoint
26 | responses:
27 | '204':
28 | description: empty
29 |
30 | options:
31 | tags:
32 | - endpoint
33 | responses:
34 | '204':
35 | description: empty
36 |
37 | patch:
38 | tags:
39 | - endpoint
40 | responses:
41 | '204':
42 | description: empty
43 |
44 | post:
45 | tags:
46 | - endpoint
47 | responses:
48 | '204':
49 | description: empty
50 |
51 | put:
52 | tags:
53 | - endpoint
54 | responses:
55 | '204':
56 | description: empty
57 |
58 | trace:
59 | tags:
60 | - endpoint
61 | responses:
62 | '204':
63 | description: empty
64 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/endpoint-http-mapping/inputs/openapi31.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.1.0
2 | info:
3 | title: test endpoint http method mapping
4 | version: 1.0.0
5 |
6 | paths:
7 | /endpoint:
8 |
9 |
10 | delete:
11 | tags:
12 | - endpoint
13 | responses:
14 | '204':
15 | description: empty
16 |
17 | get:
18 | tags:
19 | - endpoint
20 | responses:
21 | '204':
22 | description: empty
23 |
24 | head:
25 | tags:
26 | - endpoint
27 | responses:
28 | '204':
29 | description: empty
30 |
31 | options:
32 | tags:
33 | - endpoint
34 | responses:
35 | '204':
36 | description: empty
37 |
38 | patch:
39 | tags:
40 | - endpoint
41 | responses:
42 | '204':
43 | description: empty
44 |
45 | post:
46 | tags:
47 | - endpoint
48 | responses:
49 | '204':
50 | description: empty
51 |
52 | put:
53 | tags:
54 | - endpoint
55 | responses:
56 | '204':
57 | description: empty
58 |
59 | trace:
60 | tags:
61 | - endpoint
62 | responses:
63 | '204':
64 | description: empty
65 |
66 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/endpoint-http-mapping/outputs.yaml:
--------------------------------------------------------------------------------
1 | prefix: generated
2 | items:
3 | - outputs/api/EndpointApi.java
4 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/endpoint-http-mapping/outputs/api/EndpointApi.java:
--------------------------------------------------------------------------------
1 | package generated.api;
2 |
3 | import generated.support.Generated;
4 | import org.springframework.http.HttpStatus;
5 | import org.springframework.web.bind.annotation.DeleteMapping;
6 | import org.springframework.web.bind.annotation.GetMapping;
7 | import org.springframework.web.bind.annotation.PatchMapping;
8 | import org.springframework.web.bind.annotation.PostMapping;
9 | import org.springframework.web.bind.annotation.PutMapping;
10 | import org.springframework.web.bind.annotation.RequestMapping;
11 | import org.springframework.web.bind.annotation.RequestMethod;
12 | import org.springframework.web.bind.annotation.ResponseStatus;
13 |
14 | @Generated(value = "openapi-processor-spring", version = "test")
15 | public interface EndpointApi {
16 |
17 | @ResponseStatus(HttpStatus.NO_CONTENT)
18 | @DeleteMapping(path = "/endpoint")
19 | void deleteEndpoint();
20 |
21 | @ResponseStatus(HttpStatus.NO_CONTENT)
22 | @GetMapping(path = "/endpoint")
23 | void getEndpoint();
24 |
25 | @ResponseStatus(HttpStatus.NO_CONTENT)
26 | @RequestMapping(path = "/endpoint", method = RequestMethod.HEAD)
27 | void headEndpoint();
28 |
29 | @ResponseStatus(HttpStatus.NO_CONTENT)
30 | @RequestMapping(path = "/endpoint", method = RequestMethod.OPTIONS)
31 | void optionsEndpoint();
32 |
33 | @ResponseStatus(HttpStatus.NO_CONTENT)
34 | @PatchMapping(path = "/endpoint")
35 | void patchEndpoint();
36 |
37 | @ResponseStatus(HttpStatus.NO_CONTENT)
38 | @PostMapping(path = "/endpoint")
39 | void postEndpoint();
40 |
41 | @ResponseStatus(HttpStatus.NO_CONTENT)
42 | @PutMapping(path = "/endpoint")
43 | void putEndpoint();
44 |
45 | @ResponseStatus(HttpStatus.NO_CONTENT)
46 | @RequestMapping(path = "/endpoint", method = RequestMethod.TRACE)
47 | void traceEndpoint();
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-complex-data-types/inputs.yaml:
--------------------------------------------------------------------------------
1 | items:
2 | - inputs/openapi30.yaml
3 | - inputs/openapi31.yaml
4 | - inputs/mapping.yaml
5 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-complex-data-types/inputs/mapping.yaml:
--------------------------------------------------------------------------------
1 | openapi-processor-mapping: v4
2 |
3 | options:
4 | package-name: generated
5 | format-code: false
6 |
7 | map:
8 |
9 | paths:
10 |
11 | /endpoint-map:
12 | types:
13 | - type: Props => java.util.Map
14 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-complex-data-types/inputs/openapi30.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.2
2 | info:
3 | title: test complex parameters
4 | version: 1.0.0
5 |
6 | paths:
7 |
8 | /endpoint-object:
9 | get:
10 | description: <
11 | should be mapped to a `Props` pojo
12 |
13 | /endpoint-object?prop1=foo&prop2=bar
14 |
15 | parameters:
16 | - name: props
17 | description: query parameter object
18 | in: query
19 | schema:
20 | $ref: '#/components/schemas/Props'
21 | responses:
22 | '204':
23 | description: empty
24 |
25 | /endpoint-map:
26 | get:
27 | description: <
28 | should be mapped to a `Map` (from mapping)
29 | parameters:
30 | - name: props
31 | description: query parameter object
32 | in: query
33 | schema:
34 | $ref: '#/components/schemas/Props'
35 | responses:
36 | '204':
37 | description: empty
38 |
39 | components:
40 |
41 | schemas:
42 |
43 | Props:
44 | type: object
45 | properties:
46 | prop1:
47 | type: string
48 | prop2:
49 | type: string
50 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-complex-data-types/inputs/openapi31.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.2
2 | info:
3 | title: test complex parameters
4 | version: 1.0.0
5 |
6 | paths:
7 |
8 | /endpoint-object:
9 | get:
10 | description: <
11 | should be mapped to a `Props` pojo
12 |
13 | /endpoint-object?prop1=foo&prop2=bar
14 |
15 | parameters:
16 | - name: props
17 | description: query parameter object
18 | in: query
19 | schema:
20 | $ref: '#/components/schemas/Props'
21 | responses:
22 | '204':
23 | description: empty
24 |
25 | /endpoint-map:
26 | get:
27 | description: <
28 | should be mapped to a `Map` (from mapping)
29 | parameters:
30 | - name: props
31 | description: query parameter object
32 | in: query
33 | schema:
34 | $ref: '#/components/schemas/Props'
35 | responses:
36 | '204':
37 | description: empty
38 |
39 | components:
40 |
41 | schemas:
42 |
43 | Props:
44 | type: object
45 | properties:
46 | prop1:
47 | type: string
48 | prop2:
49 | type: string
50 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-complex-data-types/outputs.yaml:
--------------------------------------------------------------------------------
1 | prefix: generated
2 | items:
3 | - outputs/api/Api.java
4 | - outputs/model//Props.java
5 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-complex-data-types/outputs/api/Api.java:
--------------------------------------------------------------------------------
1 | package generated.api;
2 |
3 | import generated.model.Props;
4 | import generated.support.Generated;
5 | import java.util.Map;
6 | import org.springframework.http.HttpStatus;
7 | import org.springframework.web.bind.annotation.GetMapping;
8 | import org.springframework.web.bind.annotation.RequestParam;
9 | import org.springframework.web.bind.annotation.ResponseStatus;
10 |
11 | @Generated(value = "openapi-processor-spring", version = "test")
12 | public interface Api {
13 |
14 | @ResponseStatus(HttpStatus.NO_CONTENT)
15 | @GetMapping(path = "/endpoint-object")
16 | void getEndpointObject(Props props);
17 |
18 | @ResponseStatus(HttpStatus.NO_CONTENT)
19 | @GetMapping(path = "/endpoint-map")
20 | void getEndpointMap(@RequestParam Map props);
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-complex-data-types/outputs/model/_default_/Props.java:
--------------------------------------------------------------------------------
1 | package generated.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import generated.support.Generated;
5 |
6 | @Generated(value = "openapi-processor-spring", version = "test")
7 | public class Props {
8 |
9 | @JsonProperty("prop1")
10 | private String prop1;
11 |
12 | @JsonProperty("prop2")
13 | private String prop2;
14 |
15 | public String getProp1() {
16 | return prop1;
17 | }
18 |
19 | public void setProp1(String prop1) {
20 | this.prop1 = prop1;
21 | }
22 |
23 | public String getProp2() {
24 | return prop2;
25 | }
26 |
27 | public void setProp2(String prop2) {
28 | this.prop2 = prop2;
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-complex-data-types/outputs/model/_record_/Props.java:
--------------------------------------------------------------------------------
1 | package generated.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import generated.support.Generated;
5 |
6 | @Generated(value = "openapi-processor-spring", version = "test")
7 | public record Props(
8 | @JsonProperty("prop1")
9 | String prop1,
10 |
11 | @JsonProperty("prop2")
12 | String prop2
13 | ) {}
14 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-enum/inputs.yaml:
--------------------------------------------------------------------------------
1 | items:
2 | - inputs/openapi30.yaml
3 | - inputs/openapi31.yaml
4 | - inputs/mapping.yaml
5 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-enum/inputs/mapping.yaml:
--------------------------------------------------------------------------------
1 | openapi-processor-mapping: v5
2 |
3 | options:
4 | package-name: generated
5 | format-code: false
6 | enum-type: framework
7 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-enum/inputs/openapi30.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.3
2 | info:
3 | title: test enum parameters
4 | version: 1.0.0
5 |
6 | paths:
7 |
8 | /endpoint:
9 | get:
10 | tags:
11 | - enum
12 | parameters:
13 | - name: foo
14 | description: enum parameter
15 | in: query
16 | required: true
17 | schema:
18 | $ref: '#/components/schemas/Foo'
19 | responses:
20 | '204':
21 | description: empty
22 |
23 | components:
24 | schemas:
25 |
26 | Foo:
27 | type: string
28 | enum:
29 | - foo
30 | - foo-2
31 | - foo-foo
32 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-enum/inputs/openapi31.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.1.0
2 | info:
3 | title: test enum parameters
4 | version: 1.0.0
5 |
6 | paths:
7 |
8 | /endpoint:
9 | get:
10 | tags:
11 | - enum
12 | parameters:
13 | - name: foo
14 | description: enum parameter
15 | in: query
16 | required: true
17 | schema:
18 | $ref: '#/components/schemas/Foo'
19 | responses:
20 | '204':
21 | description: empty
22 |
23 | components:
24 | schemas:
25 |
26 | Foo:
27 | type: string
28 | enum:
29 | - foo
30 | - foo-2
31 | - foo-foo
32 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-enum/outputs.yaml:
--------------------------------------------------------------------------------
1 | prefix: generated
2 | items:
3 | - outputs/api/EnumApi.java
4 | - outputs/model//Foo.java
5 | - outputs/spring/package-info.java
6 | - outputs/spring/StringToEnumConverterFactory.java
7 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-enum/outputs/api/EnumApi.java:
--------------------------------------------------------------------------------
1 | package generated.api;
2 |
3 | import generated.model.Foo;
4 | import generated.support.Generated;
5 | import org.springframework.http.HttpStatus;
6 | import org.springframework.web.bind.annotation.GetMapping;
7 | import org.springframework.web.bind.annotation.RequestParam;
8 | import org.springframework.web.bind.annotation.ResponseStatus;
9 |
10 | @Generated(value = "openapi-processor-spring", version = "test")
11 | public interface EnumApi {
12 |
13 | @ResponseStatus(HttpStatus.NO_CONTENT)
14 | @GetMapping(path = "/endpoint")
15 | void getEndpoint(@RequestParam(name = "foo") Foo foo);
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-enum/outputs/model/_default_/Foo.java:
--------------------------------------------------------------------------------
1 | package generated.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonValue;
5 | import generated.support.Generated;
6 | import java.util.function.Supplier;
7 |
8 | @Generated(value = "openapi-processor-spring", version = "test")
9 | public enum Foo implements Supplier {
10 | FOO("foo"),
11 | FOO_2("foo-2"),
12 | FOO_FOO("foo-foo");
13 |
14 | private final String value;
15 |
16 | Foo(String value) {
17 | this.value = value;
18 | }
19 |
20 | @JsonValue
21 | public String get() {
22 | return this.value;
23 | }
24 |
25 | @JsonCreator
26 | public static Foo fromValue(String value) {
27 | for (Foo val : Foo.values()) {
28 | if (val.value.equals(value)) {
29 | return val;
30 | }
31 | }
32 | throw new IllegalArgumentException(value);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-enum/outputs/spring/StringToEnumConverterFactory.java:
--------------------------------------------------------------------------------
1 | package generated.spring;
2 |
3 | import org.springframework.core.convert.converter.Converter;
4 | import org.springframework.core.convert.converter.ConverterFactory;
5 |
6 | import java.util.EnumSet;
7 | import java.util.function.Supplier;
8 |
9 | public class StringToEnumConverterFactory & Supplier>
10 | implements ConverterFactory {
11 |
12 | @Override
13 | @SuppressWarnings({"unchecked", "rawtypes"})
14 | public Converter getConverter(Class targetType) {
15 | return new StringToEnumConverter(targetType);
16 | }
17 |
18 | static class StringToEnumConverter & Supplier> implements Converter {
19 |
20 | private final Class enumType;
21 |
22 | public StringToEnumConverter(Class enumType) {
23 | this.enumType = enumType;
24 | }
25 |
26 | public T convert(String source) {
27 | String sourceValue = source.trim();
28 |
29 | for (T e : EnumSet.allOf(enumType)) {
30 | if (e.get().equals(sourceValue)) {
31 | return e;
32 | }
33 | }
34 |
35 | throw new IllegalArgumentException(String.format("No enum constant of %s has the value %s",
36 | enumType.getCanonicalName(),
37 | sourceValue));
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-enum/outputs/spring/package-info.java:
--------------------------------------------------------------------------------
1 | @org.springframework.lang.NonNullApi
2 | @org.springframework.lang.NonNullFields
3 | package generated.spring;
4 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-pageable-mapping/inputs.yaml:
--------------------------------------------------------------------------------
1 | items:
2 | - inputs/openapi30.yaml
3 | - inputs/openapi31.yaml
4 | - inputs/mapping.yaml
5 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-pageable-mapping/inputs/mapping.yaml:
--------------------------------------------------------------------------------
1 | openapi-processor-mapping: v4
2 |
3 | options:
4 | package-name: generated
5 | format-code: false
6 |
7 | map:
8 | result: org.springframework.http.ResponseEntity
9 |
10 | types:
11 | - type: Pageable => org.springframework.data.domain.Pageable
12 | - type: StringPage => org.springframework.data.domain.Page
13 |
14 | paths:
15 |
16 | /page-inline:
17 |
18 | parameters:
19 | - name: pageable => org.springframework.data.domain.Pageable
20 |
21 | responses:
22 | - content: application/json => org.springframework.data.domain.Page
23 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-pageable-mapping/inputs/openapi30.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.2
2 | info:
3 | title: Spring Page/Pageable API
4 | version: 1.0.0
5 |
6 | paths:
7 | /page:
8 | get:
9 | parameters:
10 | - in: query
11 | name: pageable
12 | required: false
13 | schema:
14 | $ref: '#/components/schemas/Pageable'
15 | responses:
16 | '200':
17 | description: none
18 | content:
19 | application/json:
20 | schema:
21 | $ref: '#/components/schemas/StringPage'
22 |
23 | /page-inline:
24 | get:
25 | parameters:
26 | - in: query
27 | name: pageable
28 | required: false
29 | schema:
30 | type: object
31 | properties:
32 | page:
33 | type: integer
34 | size:
35 | type: integer
36 | responses:
37 | '200':
38 | description: none
39 | content:
40 | application/json:
41 | schema:
42 | type: object
43 | allOf:
44 | - $ref: '#/components/schemas/Page'
45 | - $ref: '#/components/schemas/StringContent'
46 |
47 | components:
48 | schemas:
49 |
50 | Pageable:
51 | description: minimal Pageable query parameters
52 | type: object
53 | properties:
54 | page:
55 | type: integer
56 | size:
57 | type: integer
58 |
59 | Page:
60 | description: minimal Page response without content property
61 | type: object
62 | properties:
63 | number:
64 | type: integer
65 | size:
66 | type: integer
67 |
68 | StringContent:
69 | description: specific content List of the Page response
70 | type: object
71 | properties:
72 | content:
73 | type: array
74 | items:
75 | type: string
76 |
77 | StringPage:
78 | description: typed Page
79 | type: object
80 | allOf:
81 | - $ref: '#/components/schemas/Page'
82 | - $ref: '#/components/schemas/StringContent'
83 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-pageable-mapping/inputs/openapi31.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.1.0
2 | info:
3 | title: Spring Page/Pageable API
4 | version: 1.0.0
5 |
6 | paths:
7 | /page:
8 | get:
9 | parameters:
10 | - in: query
11 | name: pageable
12 | required: false
13 | schema:
14 | $ref: '#/components/schemas/Pageable'
15 | responses:
16 | '200':
17 | description: none
18 | content:
19 | application/json:
20 | schema:
21 | $ref: '#/components/schemas/StringPage'
22 |
23 | /page-inline:
24 | get:
25 | parameters:
26 | - in: query
27 | name: pageable
28 | required: false
29 | schema:
30 | type: object
31 | properties:
32 | page:
33 | type: integer
34 | size:
35 | type: integer
36 | responses:
37 | '200':
38 | description: none
39 | content:
40 | application/json:
41 | schema:
42 | type: object
43 | allOf:
44 | - $ref: '#/components/schemas/Page'
45 | - $ref: '#/components/schemas/StringContent'
46 |
47 | components:
48 | schemas:
49 |
50 | Pageable:
51 | description: minimal Pageable query parameters
52 | type: object
53 | properties:
54 | page:
55 | type: integer
56 | size:
57 | type: integer
58 |
59 | Page:
60 | description: minimal Page response without content property
61 | type: object
62 | properties:
63 | number:
64 | type: integer
65 | size:
66 | type: integer
67 |
68 | StringContent:
69 | description: specific content List of the Page response
70 | type: object
71 | properties:
72 | content:
73 | type: array
74 | items:
75 | type: string
76 |
77 | StringPage:
78 | description: typed Page
79 | type: object
80 | allOf:
81 | - $ref: '#/components/schemas/Page'
82 | - $ref: '#/components/schemas/StringContent'
83 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-pageable-mapping/outputs.yaml:
--------------------------------------------------------------------------------
1 | prefix: generated
2 | items:
3 | - outputs/api/Api.java
4 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-pageable-mapping/outputs/api/Api.java:
--------------------------------------------------------------------------------
1 | package generated.api;
2 |
3 | import generated.support.Generated;
4 | import org.springframework.data.domain.Page;
5 | import org.springframework.data.domain.Pageable;
6 | import org.springframework.http.ResponseEntity;
7 | import org.springframework.web.bind.annotation.GetMapping;
8 |
9 | @Generated(value = "openapi-processor-spring", version = "test")
10 | public interface Api {
11 |
12 | @GetMapping(path = "/page", produces = {"application/json"})
13 | ResponseEntity> getPage(Pageable pageable);
14 |
15 | @GetMapping(path = "/page-inline", produces = {"application/json"})
16 | ResponseEntity> getPageInline(Pageable pageable);
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-path-simple-data-types/inputs.yaml:
--------------------------------------------------------------------------------
1 | items:
2 | - inputs/mapping.yaml
3 | - inputs/openapi30.yaml
4 | - inputs/openapi31.yaml
5 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-path-simple-data-types/inputs/mapping.yaml:
--------------------------------------------------------------------------------
1 | openapi-processor-mapping: v4
2 |
3 | options:
4 | package-name: generated
5 | format-code: false
6 |
7 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-path-simple-data-types/inputs/openapi30.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.2
2 | info:
3 | title: test simple path parameters
4 | version: 1.0.0
5 |
6 | paths:
7 | /endpoint/{foo}:
8 | get:
9 | tags:
10 | - endpoint
11 | parameters:
12 | - name: foo
13 | description: path, required, string
14 | in: path
15 | required: true
16 | schema:
17 | type: string
18 | responses:
19 | '204':
20 | description: empty
21 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-path-simple-data-types/inputs/openapi31.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.1.0
2 | info:
3 | title: test simple path parameters
4 | version: 1.0.0
5 |
6 | paths:
7 | /endpoint/{foo}:
8 | get:
9 | tags:
10 | - endpoint
11 | parameters:
12 | - name: foo
13 | description: path, required, string
14 | in: path
15 | required: true
16 | schema:
17 | type: string
18 | responses:
19 | '204':
20 | description: empty
21 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-path-simple-data-types/outputs.yaml:
--------------------------------------------------------------------------------
1 | prefix: generated
2 | items:
3 | - outputs/api/EndpointApi.java
4 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-path-simple-data-types/outputs/api/EndpointApi.java:
--------------------------------------------------------------------------------
1 | package generated.api;
2 |
3 | import generated.support.Generated;
4 | import org.springframework.http.HttpStatus;
5 | import org.springframework.web.bind.annotation.GetMapping;
6 | import org.springframework.web.bind.annotation.PathVariable;
7 | import org.springframework.web.bind.annotation.ResponseStatus;
8 |
9 | @Generated(value = "openapi-processor-spring", version = "test")
10 | public interface EndpointApi {
11 |
12 | @ResponseStatus(HttpStatus.NO_CONTENT)
13 | @GetMapping(path = "/endpoint/{foo}")
14 | void getEndpointFoo(@PathVariable(name = "foo") String foo);
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-query-annotate-simple-mapping/inputs.yaml:
--------------------------------------------------------------------------------
1 | items:
2 | - inputs/openapi30.yaml
3 | - inputs/openapi31.yaml
4 | - inputs/mapping.yaml
5 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-query-annotate-simple-mapping/inputs/mapping.yaml:
--------------------------------------------------------------------------------
1 | openapi-processor-mapping: v2
2 |
3 | options:
4 | package-name: generated
5 | format-code: false
6 |
7 | map:
8 | types:
9 | - type: string:uuid => java.util.UUID
10 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-query-annotate-simple-mapping/inputs/openapi30.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.2
2 | info:
3 | title: test simple data type mapping
4 | version: 1.0.0
5 |
6 | paths:
7 |
8 | /foo:
9 | get:
10 | parameters:
11 | - name: id
12 | description: mapped to java UUID
13 | in: query
14 | required: true
15 | schema:
16 | type: string
17 | format: uuid
18 | responses:
19 | '204':
20 | description: empty
21 |
22 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-query-annotate-simple-mapping/inputs/openapi31.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.1.0
2 | info:
3 | title: test simple data type mapping
4 | version: 1.0.0
5 |
6 | paths:
7 |
8 | /foo:
9 | get:
10 | parameters:
11 | - name: id
12 | description: mapped to java UUID
13 | in: query
14 | required: true
15 | schema:
16 | type: string
17 | format: uuid
18 | responses:
19 | '204':
20 | description: empty
21 |
22 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-query-annotate-simple-mapping/outputs.yaml:
--------------------------------------------------------------------------------
1 | prefix: generated
2 | items:
3 | - outputs/api/Api.java
4 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-query-annotate-simple-mapping/outputs/api/Api.java:
--------------------------------------------------------------------------------
1 | package generated.api;
2 |
3 | import generated.support.Generated;
4 | import java.util.UUID;
5 | import org.springframework.http.HttpStatus;
6 | import org.springframework.web.bind.annotation.GetMapping;
7 | import org.springframework.web.bind.annotation.RequestParam;
8 | import org.springframework.web.bind.annotation.ResponseStatus;
9 |
10 | @Generated(value = "openapi-processor-spring", version = "test")
11 | public interface Api {
12 |
13 | @ResponseStatus(HttpStatus.NO_CONTENT)
14 | @GetMapping(path = "/foo")
15 | void getFoo(@RequestParam(name = "id") UUID id);
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-request-body-multipart-mapping/inputs.yaml:
--------------------------------------------------------------------------------
1 | items:
2 | - inputs/openapi30.yaml
3 | - inputs/openapi31.yaml
4 | - inputs/mapping.yaml
5 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-request-body-multipart-mapping/inputs/mapping.yaml:
--------------------------------------------------------------------------------
1 | openapi-processor-mapping: v2
2 |
3 | options:
4 | package-name: generated
5 | format-code: false
6 |
7 | map:
8 | result: org.springframework.http.ResponseEntity
9 |
10 | types:
11 | - type: string:binary => org.springframework.web.multipart.MultipartFile
12 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-request-body-multipart-mapping/inputs/openapi30.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.2
2 | info:
3 | title: params-request-body-multipart-form-data
4 | version: 1.0.0
5 |
6 | paths:
7 | /multipart/single-file:
8 | post:
9 | requestBody:
10 | required: true
11 | content:
12 | multipart/form-data:
13 | schema:
14 | type: object
15 | properties:
16 | file:
17 | type: string
18 | format: binary
19 | other:
20 | type: string
21 | responses:
22 | '204':
23 | description: empty
24 |
25 | /multipart/multiple-files:
26 | post:
27 | requestBody:
28 | required: true
29 | content:
30 | multipart/form-data:
31 | schema:
32 | type: object
33 | properties:
34 | files:
35 | type: array
36 | items:
37 | type: string
38 | format: binary
39 | other:
40 | type: string
41 | responses:
42 | '204':
43 | description: empty
44 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-request-body-multipart-mapping/inputs/openapi31.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.1.0
2 | info:
3 | title: params-request-body-multipart-form-data
4 | version: 1.0.0
5 |
6 | paths:
7 | /multipart/single-file:
8 | post:
9 | requestBody:
10 | required: true
11 | content:
12 | multipart/form-data:
13 | schema:
14 | type: object
15 | properties:
16 | file:
17 | type: string
18 | format: binary
19 | other:
20 | type: string
21 | responses:
22 | '204':
23 | description: empty
24 |
25 | /multipart/multiple-files:
26 | post:
27 | requestBody:
28 | required: true
29 | content:
30 | multipart/form-data:
31 | schema:
32 | type: object
33 | properties:
34 | files:
35 | type: array
36 | items:
37 | type: string
38 | format: binary
39 | other:
40 | type: string
41 | responses:
42 | '204':
43 | description: empty
44 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-request-body-multipart-mapping/outputs.yaml:
--------------------------------------------------------------------------------
1 | prefix: generated
2 | items:
3 | - outputs/api/Api.java
4 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-request-body-multipart-mapping/outputs/api/Api.java:
--------------------------------------------------------------------------------
1 | package generated.api;
2 |
3 | import generated.support.Generated;
4 | import org.springframework.http.HttpStatus;
5 | import org.springframework.http.ResponseEntity;
6 | import org.springframework.web.bind.annotation.PostMapping;
7 | import org.springframework.web.bind.annotation.RequestParam;
8 | import org.springframework.web.bind.annotation.ResponseStatus;
9 | import org.springframework.web.multipart.MultipartFile;
10 |
11 | @Generated(value = "openapi-processor-spring", version = "test")
12 | public interface Api {
13 |
14 | @ResponseStatus(HttpStatus.NO_CONTENT)
15 | @PostMapping(path = "/multipart/single-file", consumes = {"multipart/form-data"})
16 | ResponseEntity postMultipartSingleFile(
17 | @RequestParam(name = "file") MultipartFile file,
18 | @RequestParam(name = "other") String other);
19 |
20 | @ResponseStatus(HttpStatus.NO_CONTENT)
21 | @PostMapping(path = "/multipart/multiple-files", consumes = {"multipart/form-data"})
22 | ResponseEntity postMultipartMultipleFiles(
23 | @RequestParam(name = "files") MultipartFile[] files,
24 | @RequestParam(name = "other") String other);
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-request-body/inputs.yaml:
--------------------------------------------------------------------------------
1 | items:
2 | - inputs/mapping.yaml
3 | - inputs/openapi30.yaml
4 | - inputs/openapi31.yaml
5 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-request-body/inputs/mapping.yaml:
--------------------------------------------------------------------------------
1 | openapi-processor-mapping: v4
2 |
3 | options:
4 | package-name: generated
5 | format-code: false
6 |
7 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-request-body/inputs/openapi30.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.2
2 | info:
3 | title: test request body parameters
4 | version: 1.0.0
5 |
6 | paths:
7 | /book:
8 | post:
9 | requestBody:
10 | content:
11 | application/json:
12 | schema:
13 | $ref: '#/components/schemas/Book'
14 | required: true
15 | responses:
16 | '201':
17 | description: created book
18 | content:
19 | application/json:
20 | schema:
21 | $ref: '#/components/schemas/Book'
22 |
23 | components:
24 | schemas:
25 | Book:
26 | type: object
27 | properties:
28 | isbn:
29 | type: string
30 | title:
31 | type: string
32 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-request-body/inputs/openapi31.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.1.0
2 | info:
3 | title: test request body parameters
4 | version: 1.0.0
5 |
6 | paths:
7 | /book:
8 | post:
9 | requestBody:
10 | content:
11 | application/json:
12 | schema:
13 | $ref: '#/components/schemas/Book'
14 | required: true
15 | responses:
16 | '201':
17 | description: created book
18 | content:
19 | application/json:
20 | schema:
21 | $ref: '#/components/schemas/Book'
22 |
23 | components:
24 | schemas:
25 | Book:
26 | type: object
27 | properties:
28 | isbn:
29 | type: string
30 | title:
31 | type: string
32 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-request-body/outputs.yaml:
--------------------------------------------------------------------------------
1 | prefix: generated
2 | items:
3 | - outputs/api/Api.java
4 | - outputs/model//Book.java
5 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-request-body/outputs/api/Api.java:
--------------------------------------------------------------------------------
1 | package generated.api;
2 |
3 | import generated.model.Book;
4 | import generated.support.Generated;
5 | import org.springframework.http.HttpStatus;
6 | import org.springframework.web.bind.annotation.PostMapping;
7 | import org.springframework.web.bind.annotation.RequestBody;
8 | import org.springframework.web.bind.annotation.ResponseStatus;
9 |
10 | @Generated(value = "openapi-processor-spring", version = "test")
11 | public interface Api {
12 |
13 | @ResponseStatus(HttpStatus.CREATED)
14 | @PostMapping(path = "/book", consumes = {"application/json"}, produces = {"application/json"})
15 | Book postBook(@RequestBody Book body);
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-request-body/outputs/model/_default_/Book.java:
--------------------------------------------------------------------------------
1 | package generated.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import generated.support.Generated;
5 |
6 | @Generated(value = "openapi-processor-spring", version = "test")
7 | public class Book {
8 |
9 | @JsonProperty("isbn")
10 | private String isbn;
11 |
12 | @JsonProperty("title")
13 | private String title;
14 |
15 | public String getIsbn() {
16 | return isbn;
17 | }
18 |
19 | public void setIsbn(String isbn) {
20 | this.isbn = isbn;
21 | }
22 |
23 | public String getTitle() {
24 | return title;
25 | }
26 |
27 | public void setTitle(String title) {
28 | this.title = title;
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-request-body/outputs/model/_record_/Book.java:
--------------------------------------------------------------------------------
1 | package generated.model;
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty;
4 | import generated.support.Generated;
5 |
6 | @Generated(value = "openapi-processor-spring", version = "test")
7 | public record Book(
8 | @JsonProperty("isbn")
9 | String isbn,
10 |
11 | @JsonProperty("title")
12 | String title
13 | ) {}
14 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-simple-data-types/inputs.yaml:
--------------------------------------------------------------------------------
1 | items:
2 | - inputs/mapping.yaml
3 | - inputs/openapi30.yaml
4 | - inputs/openapi31.yaml
5 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-simple-data-types/inputs/mapping.yaml:
--------------------------------------------------------------------------------
1 | openapi-processor-mapping: v4
2 |
3 | options:
4 | package-name: generated
5 | format-code: false
6 |
7 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-simple-data-types/inputs/openapi30.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.2
2 | info:
3 | title: test simple parameters
4 | version: 1.0.0
5 |
6 | paths:
7 | /endpoint:
8 | get:
9 | tags:
10 | - endpoint
11 | parameters:
12 | - name: foo
13 | description: query, required, string
14 | in: query
15 | required: true
16 | schema:
17 | type: string
18 | responses:
19 | '204':
20 | description: empty
21 |
22 | /endpoint-optional:
23 | get:
24 | tags:
25 | - endpoint
26 | parameters:
27 | - name: foo
28 | description: query, not required, string
29 | in: query
30 | required: false
31 | schema:
32 | type: string
33 | default: bar
34 | responses:
35 | '204':
36 | description: empty
37 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-simple-data-types/inputs/openapi31.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.1.0
2 | info:
3 | title: test simple parameters
4 | version: 1.0.0
5 |
6 | paths:
7 | /endpoint:
8 | get:
9 | tags:
10 | - endpoint
11 | parameters:
12 | - name: foo
13 | description: query, required, string
14 | in: query
15 | required: true
16 | schema:
17 | type: string
18 | responses:
19 | '204':
20 | description: empty
21 |
22 | /endpoint-optional:
23 | get:
24 | tags:
25 | - endpoint
26 | parameters:
27 | - name: foo
28 | description: query, not required, string
29 | in: query
30 | required: false
31 | schema:
32 | type: string
33 | default: bar
34 | responses:
35 | '204':
36 | description: empty
37 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-simple-data-types/outputs.yaml:
--------------------------------------------------------------------------------
1 | prefix: generated
2 | items:
3 | - outputs/api/EndpointApi.java
4 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/params-simple-data-types/outputs/api/EndpointApi.java:
--------------------------------------------------------------------------------
1 | package generated.api;
2 |
3 | import generated.support.Generated;
4 | import org.springframework.http.HttpStatus;
5 | import org.springframework.web.bind.annotation.GetMapping;
6 | import org.springframework.web.bind.annotation.RequestParam;
7 | import org.springframework.web.bind.annotation.ResponseStatus;
8 |
9 | @Generated(value = "openapi-processor-spring", version = "test")
10 | public interface EndpointApi {
11 |
12 | @ResponseStatus(HttpStatus.NO_CONTENT)
13 | @GetMapping(path = "/endpoint")
14 | void getEndpoint(@RequestParam(name = "foo") String foo);
15 |
16 | @ResponseStatus(HttpStatus.NO_CONTENT)
17 | @GetMapping(path = "/endpoint-optional")
18 | void getEndpointOptional(@RequestParam(name = "foo", required = false, defaultValue = "bar") String foo);
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/response-status/inputs.yaml:
--------------------------------------------------------------------------------
1 | prefix: api
2 | items:
3 | - inputs/openapi30.yaml
4 | - inputs/openapi31.yaml
5 | - inputs/mapping.yaml
6 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/response-status/inputs/mapping.yaml:
--------------------------------------------------------------------------------
1 | openapi-processor-mapping: v13
2 |
3 | options:
4 | package-name: generated
5 | format-code: false
6 |
7 | map:
8 | result-style: success
9 | result-status: true
10 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/response-status/inputs/openapi30.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.2
2 | info:
3 | title: response status
4 | version: 1.0.0
5 |
6 | paths:
7 |
8 | /foo:
9 | get:
10 | responses:
11 | '204':
12 | description: empty
13 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/response-status/inputs/openapi31.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.1.0
2 | info:
3 | title: response status
4 | version: 1.0.0
5 |
6 | paths:
7 |
8 | /foo:
9 | get:
10 | responses:
11 | '204':
12 | description: empty
13 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/response-status/outputs.yaml:
--------------------------------------------------------------------------------
1 | prefix: generated
2 | items:
3 | - outputs/api/Api.java
4 |
--------------------------------------------------------------------------------
/src/testInt/resources/tests/response-status/outputs/api/Api.java:
--------------------------------------------------------------------------------
1 | package generated.api;
2 |
3 | import generated.support.Generated;
4 | import org.springframework.http.HttpStatus;
5 | import org.springframework.web.bind.annotation.GetMapping;
6 | import org.springframework.web.bind.annotation.ResponseStatus;
7 |
8 | @Generated(value = "openapi-processor-spring", version = "test")
9 | public interface Api {
10 |
11 | @ResponseStatus(HttpStatus.NO_CONTENT)
12 | @GetMapping(path = "/foo")
13 | void getFoo();
14 |
15 | }
16 |
--------------------------------------------------------------------------------