mirror of
https://github.com/OpenAPITools/openapi-generator.git
synced 2026-02-10 07:10:53 +00:00
Compare commits
42 Commits
python-ser
...
wing328-pa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3fb9aeef8 | ||
|
|
7c5f7cf4e5 | ||
|
|
2ab70fa46b | ||
|
|
95911180f6 | ||
|
|
1cafc1673a | ||
|
|
268213004e | ||
|
|
ad2044c581 | ||
|
|
f3a21a8bba | ||
|
|
811529cb66 | ||
|
|
883bd56ea9 | ||
|
|
897590ae6a | ||
|
|
3c052d8b64 | ||
|
|
34dc09b676 | ||
|
|
f8e1fadf4e | ||
|
|
41a8573437 | ||
|
|
fd17603109 | ||
|
|
7506b6ca02 | ||
|
|
3df34f6aab | ||
|
|
95e8360c19 | ||
|
|
ba8327386c | ||
|
|
8b6df51cdf | ||
|
|
66a211ec56 | ||
|
|
4da2d8095d | ||
|
|
d26f67c576 | ||
|
|
63151d3a68 | ||
|
|
6f211a20e8 | ||
|
|
82ad061fe9 | ||
|
|
063a780c7a | ||
|
|
b4430804c1 | ||
|
|
c56ea5557a | ||
|
|
a625300e3f | ||
|
|
8b563222bd | ||
|
|
572b315f8b | ||
|
|
c5993af2ea | ||
|
|
f7ac63a188 | ||
|
|
c0d555ba4a | ||
|
|
683b024154 | ||
|
|
3db927e060 | ||
|
|
9fa18d0c81 | ||
|
|
6022e4ec7d | ||
|
|
11e06d1e77 | ||
|
|
3ed013966b |
2
.github/workflows/samples-python-server.yaml
vendored
2
.github/workflows/samples-python-server.yaml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: '3.9'
|
||||
- name: Test
|
||||
working-directory: ${{ matrix.sample }}
|
||||
run: make test-all
|
||||
|
||||
9
.github/workflows/samples-rust-server.yaml
vendored
9
.github/workflows/samples-rust-server.yaml
vendored
@@ -64,6 +64,15 @@ jobs:
|
||||
if cargo read-manifest | grep -q '"validate"'; then
|
||||
cargo build --features validate --all-targets
|
||||
fi
|
||||
# Test TLS features if they exist
|
||||
if cargo read-manifest | grep -q '"client-tls"'; then
|
||||
# Client without TLS (HTTP-only)
|
||||
cargo build --no-default-features --features=client --lib
|
||||
# Client with TLS (using native-tls)
|
||||
cargo build --no-default-features --features=client,client-tls --lib
|
||||
# Server without TLS
|
||||
cargo build --no-default-features --features=server --lib
|
||||
fi
|
||||
cargo fmt
|
||||
cargo test
|
||||
cargo clippy
|
||||
|
||||
@@ -723,3 +723,10 @@ Into this securityScheme:
|
||||
scheme: bearer
|
||||
type: http
|
||||
```
|
||||
|
||||
- `SORT_MODEL_PROPERTIES`: When set to true, model properties will be sorted alphabetically by name. This ensures deterministic code generation output regardless of property ordering in the source spec.
|
||||
|
||||
Example:
|
||||
```
|
||||
java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java -i modules/openapi-generator/src/test/resources/3_0/petstore.yaml -o /tmp/java-okhttp/ --openapi-normalizer SORT_MODEL_PROPERTIES=true
|
||||
```
|
||||
|
||||
@@ -30,6 +30,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|camelCaseDollarSign|Fix camelCase when starting with $ sign. when true : $Value when false : $value| |false|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|legacy|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -44,6 +44,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|configPackage|configuration package for generated code| |org.openapitools.configuration|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|java8|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|delegatePattern|Whether to generate the server files using the delegate pattern| |false|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
@@ -110,7 +111,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|useOptional|Use Optional container for optional parameters| |false|
|
||||
|useResponseEntity|Use the `ResponseEntity` type to wrap return values of generated API methods. If disabled, method are annotated using a `@ResponseStatus` annotation, which has the status of the first response declared in the Api definition| |true|
|
||||
|useSealed|Whether to generate sealed model interfaces and classes| |false|
|
||||
|useSpringBoot3|Generate code and provide dependencies for use with Spring Boot 3.x. (Use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.| |false|
|
||||
|useSpringBoot3|Generate code and provide dependencies for use with Spring Boot ≥ 3 (use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.| |false|
|
||||
|useSpringBuiltInValidation|Disable `@Validated` at the class level when using built-in validation.| |false|
|
||||
|useSpringController|Annotate the generated API as a Spring Controller| |false|
|
||||
|useSwaggerUI|Open the OpenApi specification in swagger-ui. Will also import and configure needed dependencies| |true|
|
||||
|
||||
@@ -34,6 +34,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|configPackage|configuration package for generated code| |org.openapitools.configuration|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|java8|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -33,6 +33,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|configKey|Config key in @RegisterRestClient. Default to none.| |null|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|java8|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|disableDiscriminatorJsonIgnoreProperties|Ignore discriminator field type for Jackson serialization| |false|
|
||||
|disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|<dl><dt>**false**</dt><dd>The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.</dd><dt>**true**</dt><dd>Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.</dd></dl>|true|
|
||||
|discriminatorCaseSensitive|Whether the discriminator value lookup should be case-sensitive or not. This option only works for Java API client| |true|
|
||||
|
||||
@@ -32,6 +32,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|camelCaseDollarSign|Fix camelCase when starting with $ sign. when true : $Value when false : $value| |false|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|java8|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|disableDiscriminatorJsonIgnoreProperties|Ignore discriminator field type for Jackson serialization| |false|
|
||||
|disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|<dl><dt>**false**</dt><dd>The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.</dd><dt>**true**</dt><dd>Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.</dd></dl>|true|
|
||||
|discriminatorCaseSensitive|Whether the discriminator value lookup should be case-sensitive or not. This option only works for Java API client| |true|
|
||||
|
||||
@@ -32,6 +32,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|camelCaseDollarSign|Fix camelCase when starting with $ sign. when true : $Value when false : $value| |false|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|legacy|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -41,6 +41,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|dateFormat|Specify the format pattern of date as a string| |null|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|java8|
|
||||
|datetimeFormat|Specify the format pattern of date-time as a string| |null|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -36,6 +36,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|dateFormat|Specify the format pattern of date as a string| |null|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|java8|
|
||||
|datetimeFormat|Specify the format pattern of date-time as a string| |null|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -37,6 +37,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|configKeyFromClassName|If true, set tag as key in @RegisterRestClient. Default to false. Only `microprofile` supports this option.| |null|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|java8|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -32,6 +32,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|camelCaseDollarSign|Fix camelCase when starting with $ sign. when true : $Value when false : $value| |false|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|legacy|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -33,6 +33,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|camelCaseDollarSign|Fix camelCase when starting with $ sign. when true : $Value when false : $value| |false|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|java8|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -35,6 +35,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|controllerOnly|Whether to generate only API interface stubs without the server files.| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|java8|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -32,6 +32,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|camelCaseDollarSign|Fix camelCase when starting with $ sign. when true : $Value when false : $value| |false|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|legacy|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -32,6 +32,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|camelCaseDollarSign|Fix camelCase when starting with $ sign. when true : $Value when false : $value| |false|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|java8|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -32,6 +32,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|camelCaseDollarSign|Fix camelCase when starting with $ sign. when true : $Value when false : $value| |false|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|java8|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -32,6 +32,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|camelCaseDollarSign|Fix camelCase when starting with $ sign. when true : $Value when false : $value| |false|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|java8|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -37,6 +37,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|configKeyFromClassName|If true, set tag as key in @RegisterRestClient. Default to false. Only `microprofile` supports this option.| |null|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|java8|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -32,6 +32,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|camelCaseDollarSign|Fix camelCase when starting with $ sign. when true : $Value when false : $value| |false|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|legacy|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -33,6 +33,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|camelCaseDollarSign|Fix camelCase when starting with $ sign. when true : $Value when false : $value| |false|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|legacy|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -34,6 +34,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|camelCaseDollarSign|Fix camelCase when starting with $ sign. when true : $Value when false : $value| |false|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|legacy|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -34,6 +34,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|camelCaseDollarSign|Fix camelCase when starting with $ sign. when true : $Value when false : $value| |false|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|legacy|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -32,6 +32,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|camelCaseDollarSign|Fix camelCase when starting with $ sign. when true : $Value when false : $value| |false|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|legacy|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -32,6 +32,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|camelCaseDollarSign|Fix camelCase when starting with $ sign. when true : $Value when false : $value| |false|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|legacy|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -32,6 +32,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|camelCaseDollarSign|Fix camelCase when starting with $ sign. when true : $Value when false : $value| |false|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|legacy|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -32,6 +32,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|camelCaseDollarSign|Fix camelCase when starting with $ sign. when true : $Value when false : $value| |false|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|legacy|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|
||||
|
||||
@@ -56,7 +56,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|useFeignClientUrl|Whether to generate Feign client with url parameter.| |true|
|
||||
|useFlowForArrayReturnType|Whether to use Flow for array/collection return types when reactive is enabled. If false, will use List instead.| |true|
|
||||
|useResponseEntity|Whether (when false) to return actual type (e.g. List<Fruit>) and handle non-happy path responses via exceptions flow or (when true) return entire ResponseEntity (e.g. ResponseEntity<List<Fruit>>). If disabled, method are annotated using a @ResponseStatus annotation, which has the status of the first response declared in the Api definition| |true|
|
||||
|useSpringBoot3|Generate code and provide dependencies for use with Spring Boot 3.x. (Use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.| |false|
|
||||
|useSpringBoot3|Generate code and provide dependencies for use with Spring Boot ≥ 3 (use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.| |false|
|
||||
|useSwaggerUI|Open the OpenApi specification in swagger-ui. Will also import and configure needed dependencies| |true|
|
||||
|useTags|Whether to use tags for creating interface and controller class names| |false|
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|aggregateModelsName|Aggregated model filename. If set, all generated models will be combined into this single file.| |null|
|
||||
|customOptionsApi|Custom options for the api files.| |null|
|
||||
|customOptionsModel|Custom options for the model files.| |null|
|
||||
|extractEnumsToSeparateFiles|Extract enums to separate protobuf files and import them in models| |false|
|
||||
|numberedFieldNumberList|Field numbers in order.| |false|
|
||||
|startEnumsWithUnspecified|Introduces "UNSPECIFIED" as the first element of enumerations.| |false|
|
||||
|supportMultipleResponses|Support multiple responses| |true|
|
||||
|
||||
@@ -10,7 +10,7 @@ title: Documentation for the python-fastapi Generator
|
||||
| generator stability | BETA | |
|
||||
| generator type | SERVER | |
|
||||
| generator language | Python | |
|
||||
| generator language version | 3.7 | |
|
||||
| generator language version | 3.10 | |
|
||||
| generator default templating engine | mustache | |
|
||||
| helpTxt | Generates a Python FastAPI server (beta). Models are defined with the pydantic library | |
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|configPackage|configuration package for generated code| |org.openapitools.configuration|
|
||||
|containerDefaultToNull|Set containers (array, set, map) default to null| |false|
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|java8|
|
||||
|defaultToEmptyContainer|Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values| |null|
|
||||
|delegatePattern|Whether to generate the server files using the delegate pattern| |false|
|
||||
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|
||||
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|
||||
@@ -103,7 +104,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|useOptional|Use Optional container for optional parameters| |false|
|
||||
|useResponseEntity|Use the `ResponseEntity` type to wrap return values of generated API methods. If disabled, method are annotated using a `@ResponseStatus` annotation, which has the status of the first response declared in the Api definition| |true|
|
||||
|useSealed|Whether to generate sealed model interfaces and classes| |false|
|
||||
|useSpringBoot3|Generate code and provide dependencies for use with Spring Boot 3.x. (Use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.| |false|
|
||||
|useSpringBoot3|Generate code and provide dependencies for use with Spring Boot ≥ 3 (use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.| |false|
|
||||
|useSpringBuiltInValidation|Disable `@Validated` at the class level when using built-in validation.| |false|
|
||||
|useSpringController|Annotate the generated API as a Spring Controller| |false|
|
||||
|useSwaggerUI|Open the OpenApi specification in swagger-ui. Will also import and configure needed dependencies| |true|
|
||||
|
||||
@@ -162,8 +162,8 @@ This gives access to the following tasks:
|
||||
|
||||
| Task | Description |
|
||||
|---------------------------|---------------------------------------------------------------------------------------------|
|
||||
| <configName>.generate | Generate code via Open API Tools Generator for Open API 2.0 or 3.x specification documents. |
|
||||
| <configName>.validateSpec | Validates the configured spec |
|
||||
| <configName>.generate | Generate code via Open API Tools Generator for Open API 2.0 or 3.x specification documents. |
|
||||
| <configName>.validateSpec | Validates the configured spec |
|
||||
|
||||
and a command
|
||||
|
||||
|
||||
@@ -456,6 +456,7 @@ public class CodegenConstants {
|
||||
public static final String USE_DEFAULT_VALUES_FOR_REQUIRED_VARS = "useDefaultValuesForRequiredVars";
|
||||
|
||||
public static final String DEFAULT_TO_EMPTY_CONTAINER = "defaultToEmptyContainer";
|
||||
public static final String DEFAULT_TO_EMPTY_CONTAINER_DESC = "Initialize containers (array/set/map) to empty containers instead of null by default. Usage: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md#default-values";
|
||||
|
||||
// Vendor extensions
|
||||
public static final String X_INTERNAL = "x-internal";
|
||||
|
||||
@@ -88,6 +88,10 @@ public class CodegenDiscriminator {
|
||||
this.explicitMapping = explicitMapping;
|
||||
}
|
||||
|
||||
public boolean isExplicitMapping() {
|
||||
return explicitMapping;
|
||||
}
|
||||
|
||||
public MappedModel(String mappingName, String modelName) {
|
||||
this(mappingName, modelName, false);
|
||||
}
|
||||
|
||||
@@ -151,6 +151,9 @@ public class OpenAPINormalizer {
|
||||
boolean updateNumberToNullable;
|
||||
boolean updateBooleanToNullable;
|
||||
|
||||
// when set to true, sort model properties by name to ensure deterministic output
|
||||
final String SORT_MODEL_PROPERTIES = "SORT_MODEL_PROPERTIES";
|
||||
|
||||
// ============= end of rules =============
|
||||
|
||||
/**
|
||||
@@ -209,6 +212,7 @@ public class OpenAPINormalizer {
|
||||
ruleNames.add(SET_PRIMITIVE_TYPES_TO_NULLABLE);
|
||||
ruleNames.add(SIMPLIFY_ONEOF_ANYOF_ENUM);
|
||||
ruleNames.add(REMOVE_PROPERTIES_FROM_TYPE_OTHER_THAN_OBJECT);
|
||||
ruleNames.add(SORT_MODEL_PROPERTIES);
|
||||
|
||||
// rules that are default to true
|
||||
rules.put(SIMPLIFY_ONEOF_ANYOF, true);
|
||||
@@ -768,7 +772,7 @@ public class OpenAPINormalizer {
|
||||
}
|
||||
|
||||
if (schema.getProperties() != null && !schema.getProperties().isEmpty()) {
|
||||
normalizeProperties(schema.getProperties(), visitedSchemas);
|
||||
normalizeProperties(schema, visitedSchemas);
|
||||
}
|
||||
|
||||
if (schema.getAdditionalProperties() != null) {
|
||||
@@ -777,7 +781,7 @@ public class OpenAPINormalizer {
|
||||
|
||||
return schema;
|
||||
} else if (schema.getProperties() != null && !schema.getProperties().isEmpty()) {
|
||||
normalizeProperties(schema.getProperties(), visitedSchemas);
|
||||
normalizeProperties(schema, visitedSchemas);
|
||||
} else if (schema.getAdditionalProperties() instanceof Schema) { // map
|
||||
normalizeMapSchema(schema);
|
||||
normalizeSchema((Schema) schema.getAdditionalProperties(), visitedSchemas);
|
||||
@@ -880,10 +884,19 @@ public class OpenAPINormalizer {
|
||||
processSetPrimitiveTypesToNullable(schema);
|
||||
}
|
||||
|
||||
protected void normalizeProperties(Map<String, Schema> properties, Set<Schema> visitedSchemas) {
|
||||
protected void normalizeProperties(Schema schema, Set<Schema> visitedSchemas) {
|
||||
Map<String, Schema> properties = schema.getProperties();
|
||||
if (properties == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort properties by name if rule is enabled
|
||||
if (getRule(SORT_MODEL_PROPERTIES)) {
|
||||
Map<String, Schema> sortedProperties = new TreeMap<>(properties);
|
||||
schema.setProperties(sortedProperties);
|
||||
properties = sortedProperties;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Schema> propertiesEntry : properties.entrySet()) {
|
||||
Schema property = propertiesEntry.getValue();
|
||||
|
||||
@@ -1089,7 +1102,7 @@ public class OpenAPINormalizer {
|
||||
protected Schema normalizeComplexComposedSchema(Schema schema, Set<Schema> visitedSchemas) {
|
||||
// loop through properties, if any
|
||||
if (schema.getProperties() != null && !schema.getProperties().isEmpty()) {
|
||||
normalizeProperties(schema.getProperties(), visitedSchemas);
|
||||
normalizeProperties(schema, visitedSchemas);
|
||||
}
|
||||
|
||||
processRemoveAnyOfOneOfAndKeepPropertiesOnly(schema);
|
||||
|
||||
@@ -378,6 +378,8 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
|
||||
|
||||
CliOption enumPropertyNamingOpt = new CliOption(CodegenConstants.ENUM_PROPERTY_NAMING, ENUM_PROPERTY_NAMING_DESC);
|
||||
cliOptions.add(enumPropertyNamingOpt.defaultValue(enumPropertyNaming.name()));
|
||||
|
||||
cliOptions.add(CliOption.newString(CodegenConstants.DEFAULT_TO_EMPTY_CONTAINER, CodegenConstants.DEFAULT_TO_EMPTY_CONTAINER_DESC));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -608,7 +608,7 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
} else if (codegenParameter.isPrimitiveType) { // primitive type
|
||||
if (codegenParameter.isString) {
|
||||
if (!StringUtils.isEmpty(codegenParameter.example) && !"null".equals(codegenParameter.example)) {
|
||||
return "\"" + codegenParameter.example + "\"";
|
||||
return "\"" + escapeText(codegenParameter.example) + "\"";
|
||||
} else {
|
||||
return "\"" + codegenParameter.paramName + "_example\"";
|
||||
}
|
||||
@@ -640,7 +640,7 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
return constructExampleCode(modelMaps.get(codegenParameter.dataType), modelMaps, processedModelMap, 0);
|
||||
} else if (codegenParameter.isEmail) { // email
|
||||
if (!StringUtils.isEmpty(codegenParameter.example) && !"null".equals(codegenParameter.example)) {
|
||||
return "\"" + codegenParameter.example + "\"";
|
||||
return "\"" + escapeText(codegenParameter.example) + "\"";
|
||||
} else {
|
||||
return "\"" + codegenParameter.paramName + "@example.com\"";
|
||||
}
|
||||
@@ -681,7 +681,7 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
} else if (codegenProperty.isPrimitiveType) { // primitive type
|
||||
if (codegenProperty.isString) {
|
||||
if (!StringUtils.isEmpty(codegenProperty.example) && !"null".equals(codegenProperty.example)) {
|
||||
return "\"" + codegenProperty.example + "\"";
|
||||
return "\"" + escapeText(codegenProperty.example) + "\"";
|
||||
} else {
|
||||
return "\"" + codegenProperty.name + "_example\"";
|
||||
}
|
||||
@@ -714,7 +714,7 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
return constructExampleCode(modelMaps.get(codegenProperty.dataType), modelMaps, processedModelMap, depth + 1);
|
||||
} else if (codegenProperty.isEmail) { // email
|
||||
if (!StringUtils.isEmpty(codegenProperty.example) && !"null".equals(codegenProperty.example)) {
|
||||
return "\"" + codegenProperty.example + "\"";
|
||||
return "\"" + escapeText(codegenProperty.example) + "\"";
|
||||
} else {
|
||||
return "\"" + codegenProperty.name + "@example.com\"";
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ public class KotlinSpringServerCodegen extends AbstractKotlinCodegen
|
||||
addSwitch(BEAN_QUALIFIERS, "Whether to add fully-qualifier class names as bean qualifiers in @Component and " +
|
||||
"@RestController annotations. May be used to prevent bean names clash if multiple generated libraries" +
|
||||
" (contexts) added to single project.", beanQualifiers);
|
||||
addSwitch(USE_SPRING_BOOT3, "Generate code and provide dependencies for use with Spring Boot 3.x. (Use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.", useSpringBoot3);
|
||||
addSwitch(USE_SPRING_BOOT3, "Generate code and provide dependencies for use with Spring Boot ≥ 3 (use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.", useSpringBoot3);
|
||||
addSwitch(USE_FLOW_FOR_ARRAY_RETURN_TYPE, "Whether to use Flow for array/collection return types when reactive is enabled. If false, will use List instead.", useFlowForArrayReturnType);
|
||||
addSwitch(INCLUDE_HTTP_REQUEST_CONTEXT, "Whether to include HttpServletRequest (blocking) or ServerWebExchange (reactive) as additional parameter in generated methods.", includeHttpRequestContext);
|
||||
addSwitch(USE_RESPONSE_ENTITY,
|
||||
|
||||
@@ -214,7 +214,11 @@ public class PhpNextgenClientCodegen extends AbstractPhpCodegen {
|
||||
String phpReturnType = String.join("|", phpReturnTypeOptions);
|
||||
String docReturnType = String.join("|", docReturnTypeOptions);
|
||||
if (hasEmptyResponse) {
|
||||
phpReturnType = "?" + phpReturnType;
|
||||
if (phpReturnTypeOptions.size() > 1) {
|
||||
phpReturnType = phpReturnType + "|null";
|
||||
} else {
|
||||
phpReturnType = "?" + phpReturnType;
|
||||
}
|
||||
docReturnType = docReturnType + "|null";
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -330,7 +330,7 @@ public class PythonFastAPIServerCodegen extends AbstractPythonCodegen {
|
||||
|
||||
@Override
|
||||
public String generatorLanguageVersion() {
|
||||
return "3.7";
|
||||
return "3.10";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -601,6 +601,7 @@ public class RClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
public ModelsMap postProcessModels(ModelsMap objs) {
|
||||
for (ModelMap mo : objs.getModels()) {
|
||||
CodegenModel cm = mo.getModel();
|
||||
boolean needsExtractSimpleType = false;
|
||||
for (CodegenProperty var : cm.vars) {
|
||||
// check to see if base name is an empty string
|
||||
if ("".equals(var.baseName)) {
|
||||
@@ -608,10 +609,17 @@ public class RClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
var.baseName = "empty_string";
|
||||
}
|
||||
|
||||
if (!var.isPrimitiveType) {
|
||||
needsExtractSimpleType = true;
|
||||
}
|
||||
|
||||
// create extension x-r-doc-type to store the data type in r doc format
|
||||
var.vendorExtensions.put("x-r-doc-type", constructRdocType(var));
|
||||
}
|
||||
|
||||
// create extension x-r-has-non-primitive-field to indicate whether generated models need special handling for complex types
|
||||
cm.vendorExtensions.put("x-r-has-non-primitive-field", needsExtractSimpleType);
|
||||
|
||||
// apply the same fix, enhancement for allVars
|
||||
for (CodegenProperty var : cm.allVars) {
|
||||
// check to see if base name is an empty string
|
||||
|
||||
@@ -610,6 +610,10 @@ public class RustServerCodegen extends AbstractRustCodegen implements CodegenCon
|
||||
processParam(param, op);
|
||||
}
|
||||
|
||||
for (CodegenParameter param : op.pathParams) {
|
||||
processParam(param, op);
|
||||
}
|
||||
|
||||
// We keep track of the 'default' model type for this API. If there are
|
||||
// *any* XML responses, then we set the default to XML, otherwise we
|
||||
// let the default be JSON. It would be odd for an API to want to use
|
||||
@@ -1459,6 +1463,45 @@ public class RustServerCodegen extends AbstractRustCodegen implements CodegenCon
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the appropriate Rust integer type based on format and min/max constraints.
|
||||
* Returns the fitted data type, or null if the baseType is not an integer.
|
||||
*
|
||||
* @param dataFormat The data format (e.g., "int32", "int64", "uint32", "uint64")
|
||||
* @param minimum The minimum value constraint
|
||||
* @param maximum The maximum value constraint
|
||||
* @param exclusiveMinimum Whether the minimum is exclusive
|
||||
* @param exclusiveMaximum Whether the maximum is exclusive
|
||||
* @return The fitted Rust integer type.
|
||||
*/
|
||||
private String applyIntegerTypeFitting(String dataFormat,
|
||||
String minimum, String maximum,
|
||||
boolean exclusiveMinimum, boolean exclusiveMaximum) {
|
||||
BigInteger min = Optional.ofNullable(minimum).filter(s -> !s.isEmpty()).map(BigInteger::new).orElse(null);
|
||||
BigInteger max = Optional.ofNullable(maximum).filter(s -> !s.isEmpty()).map(BigInteger::new).orElse(null);
|
||||
|
||||
boolean unsigned = canFitIntoUnsigned(min, exclusiveMinimum);
|
||||
|
||||
if (Strings.isNullOrEmpty(dataFormat)) {
|
||||
return bestFittingIntegerType(min, exclusiveMinimum, max, exclusiveMaximum, true);
|
||||
} else {
|
||||
switch (dataFormat) {
|
||||
// custom integer formats (legacy)
|
||||
case "uint32":
|
||||
return "u32";
|
||||
case "uint64":
|
||||
return "u64";
|
||||
case "int32":
|
||||
return unsigned ? "u32" : "i32";
|
||||
case "int64":
|
||||
return unsigned ? "u64" : "i64";
|
||||
default:
|
||||
LOGGER.warn("The integer format '{}' is not recognized and will be ignored.", dataFormat);
|
||||
return bestFittingIntegerType(min, exclusiveMinimum, max, exclusiveMaximum, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
|
||||
super.postProcessModelProperty(model, property);
|
||||
@@ -1492,41 +1535,12 @@ public class RustServerCodegen extends AbstractRustCodegen implements CodegenCon
|
||||
// Integer type fitting
|
||||
if (Objects.equals(property.baseType, "integer")) {
|
||||
|
||||
BigInteger minimum = Optional.ofNullable(property.getMinimum()).map(BigInteger::new).orElse(null);
|
||||
BigInteger maximum = Optional.ofNullable(property.getMaximum()).map(BigInteger::new).orElse(null);
|
||||
|
||||
boolean unsigned = canFitIntoUnsigned(minimum, property.getExclusiveMinimum());
|
||||
|
||||
if (Strings.isNullOrEmpty(property.dataFormat)) {
|
||||
property.dataType = bestFittingIntegerType(minimum,
|
||||
property.getExclusiveMinimum(),
|
||||
maximum,
|
||||
property.getExclusiveMaximum(),
|
||||
true);
|
||||
} else {
|
||||
switch (property.dataFormat) {
|
||||
// custom integer formats (legacy)
|
||||
case "uint32":
|
||||
property.dataType = "u32";
|
||||
break;
|
||||
case "uint64":
|
||||
property.dataType = "u64";
|
||||
break;
|
||||
case "int32":
|
||||
property.dataType = unsigned ? "u32" : "i32";
|
||||
break;
|
||||
case "int64":
|
||||
property.dataType = unsigned ? "u64" : "i64";
|
||||
break;
|
||||
default:
|
||||
LOGGER.warn("The integer format '{}' is not recognized and will be ignored.", property.dataFormat);
|
||||
property.dataType = bestFittingIntegerType(minimum,
|
||||
property.getExclusiveMinimum(),
|
||||
maximum,
|
||||
property.getExclusiveMaximum(),
|
||||
true);
|
||||
}
|
||||
}
|
||||
property.dataType = applyIntegerTypeFitting(
|
||||
property.dataFormat,
|
||||
property.getMinimum(),
|
||||
property.getMaximum(),
|
||||
property.getExclusiveMinimum(),
|
||||
property.getExclusiveMaximum());
|
||||
}
|
||||
|
||||
property.name = underscore(property.name);
|
||||
@@ -1580,6 +1594,17 @@ public class RustServerCodegen extends AbstractRustCodegen implements CodegenCon
|
||||
private void processParam(CodegenParameter param, CodegenOperation op) {
|
||||
String example = null;
|
||||
|
||||
// If a parameter is an integer, fit it into the right type.
|
||||
// Note: For CodegenParameter, baseType may be null, so we check isInteger/isLong/isShort flags instead.
|
||||
if (param.isInteger || param.isLong || param.isShort) {
|
||||
param.dataType = applyIntegerTypeFitting(
|
||||
param.dataFormat,
|
||||
param.minimum,
|
||||
param.maximum,
|
||||
param.exclusiveMinimum,
|
||||
param.exclusiveMaximum);
|
||||
}
|
||||
|
||||
// If a parameter uses UUIDs, we need to import the UUID package.
|
||||
if (uuidType.equals(param.dataType)) {
|
||||
additionalProperties.put("apiUsesUuid", true);
|
||||
|
||||
@@ -276,7 +276,7 @@ public class SpringCodegen extends AbstractJavaCodegen
|
||||
"Use `equalsIgnoreCase` when String for enum comparison",
|
||||
useEnumCaseInsensitive));
|
||||
cliOptions.add(CliOption.newBoolean(USE_SPRING_BOOT3,
|
||||
"Generate code and provide dependencies for use with Spring Boot 3.x. (Use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.",
|
||||
"Generate code and provide dependencies for use with Spring Boot ≥ 3 (use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.",
|
||||
useSpringBoot3));
|
||||
cliOptions.add(CliOption.newBoolean(GENERATE_CONSTRUCTOR_WITH_REQUIRED_ARGS,
|
||||
"Whether to generate constructors with required args for models",
|
||||
|
||||
34
modules/openapi-generator/src/main/resources/Java/additional_properties.mustache
vendored
Normal file
34
modules/openapi-generator/src/main/resources/Java/additional_properties.mustache
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
{{#jackson}}
|
||||
{{#additionalPropertiesType}}
|
||||
/**
|
||||
* Set the additional (undeclared) property with the specified name and value.
|
||||
* Creates the property if it does not already exist, otherwise replaces it.
|
||||
* @param key the name of the property
|
||||
* @param value the value of the property
|
||||
* @return self reference
|
||||
*/
|
||||
@JsonAnySetter
|
||||
public {{classname}} putAdditionalProperty(String key, {{{.}}} value) {
|
||||
this.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the additional (undeclared) properties.
|
||||
* @return the additional (undeclared) properties
|
||||
*/
|
||||
@JsonAnyGetter
|
||||
public Map<String, {{{.}}}> getAdditionalProperties() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the additional (undeclared) property with the specified name.
|
||||
* @param key the name of the property
|
||||
* @return the additional (undeclared) property with the specified name
|
||||
*/
|
||||
public {{{.}}} getAdditionalProperty(String key) {
|
||||
return this.get(key);
|
||||
}
|
||||
{{/additionalPropertiesType}}
|
||||
{{/jackson}}
|
||||
@@ -311,7 +311,7 @@
|
||||
{{/useJakartaEe}}
|
||||
<threetenbp-version>2.9.10</threetenbp-version>
|
||||
<maven-plugin-version>1.0.0</maven-plugin-version>
|
||||
<assertj-version>3.23.1</assertj-version>
|
||||
<assertj-version>3.27.7</assertj-version>
|
||||
<junit-version>5.10.2</junit-version>
|
||||
</properties>
|
||||
</project>
|
||||
|
||||
@@ -784,9 +784,7 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
|
||||
break;
|
||||
} catch (HttpServerErrorException | HttpClientErrorException ex) {
|
||||
if (ex instanceof HttpServerErrorException
|
||||
|| ((HttpClientErrorException) ex)
|
||||
.getStatusCode()
|
||||
.equals(HttpStatus.TOO_MANY_REQUESTS)) {
|
||||
|| ex.getStatusCode().equals(HttpStatus.TOO_MANY_REQUESTS)) {
|
||||
attempts++;
|
||||
if (attempts < maxAttemptsForRetry) {
|
||||
try {
|
||||
|
||||
@@ -17,6 +17,17 @@ import java.io.Serializable;
|
||||
{{#jackson}}
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
{{#models}}
|
||||
{{#model}}
|
||||
{{#additionalPropertiesType}}
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
{{/additionalPropertiesType}}
|
||||
{{/model}}
|
||||
{{/models}}
|
||||
{{#withXml}}
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.*;
|
||||
{{/withXml}}
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
@JsonTypeName("{{name}}")
|
||||
{{/hasDiscriminatorWithNonEmptyMapping}}
|
||||
{{/isClassnameSanitized}}
|
||||
{{#additionalPropertiesType}}
|
||||
@JsonFormat(shape=JsonFormat.Shape.OBJECT)
|
||||
{{/additionalPropertiesType}}
|
||||
{{/jackson}}
|
||||
{{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{>xmlAnnotation}}
|
||||
{{#vendorExtensions.x-class-extra-annotation}}
|
||||
@@ -281,6 +284,8 @@ public class {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{{#vendorExtens
|
||||
{{/isReadOnly}}
|
||||
|
||||
{{/vars}}
|
||||
{{>additional_properties}}
|
||||
|
||||
{{#parent}}
|
||||
{{#readWriteVars}}
|
||||
{{#isOverridden}}
|
||||
|
||||
32
modules/openapi-generator/src/main/resources/JavaJaxRS/spec/additional_properties.mustache
vendored
Normal file
32
modules/openapi-generator/src/main/resources/JavaJaxRS/spec/additional_properties.mustache
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{{#additionalProperties}}
|
||||
/**
|
||||
* Set the additional (undeclared) property with the specified name and value.
|
||||
* Creates the property if it does not already exist, otherwise replaces it.
|
||||
* @param key the name of the property
|
||||
* @param value the value of the property
|
||||
* @return self reference
|
||||
*/
|
||||
@JsonAnySetter
|
||||
public {{classname}} putAdditionalProperty(String key, {{{datatypeWithEnum}}} value) {
|
||||
this.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the additional (undeclared) properties.
|
||||
* @return the additional (undeclared) properties
|
||||
*/
|
||||
@JsonAnyGetter
|
||||
public Map<String, {{{datatypeWithEnum}}}> getAdditionalProperties() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the additional (undeclared) property with the specified name.
|
||||
* @param key the name of the property
|
||||
* @return the additional (undeclared) property with the specified name
|
||||
*/
|
||||
public {{{datatypeWithEnum}}} getAdditionalProperty(String key) {
|
||||
return this.get(key);
|
||||
}
|
||||
{{/additionalProperties}}
|
||||
@@ -6,6 +6,8 @@ import com.fasterxml.jackson.annotation.JsonValue;
|
||||
/**
|
||||
* {{description}}{{^description}}Gets or Sets {{{name}}}{{/description}}
|
||||
*/
|
||||
{{>generatedAnnotation}}
|
||||
|
||||
{{>additionalEnumTypeAnnotations}}public enum {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} {
|
||||
{{#gson}}
|
||||
{{#allowableValues}}{{#enumVars}}
|
||||
|
||||
@@ -10,6 +10,13 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
{{#additionalProperties}}
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
{{/additionalProperties}}
|
||||
{{/jackson}}
|
||||
{{#openApiNullable}}
|
||||
import org.openapitools.jackson.nullable.JsonNullable;
|
||||
@@ -30,7 +37,12 @@ import {{javaxPackage}}.xml.bind.annotation.XmlEnumValue;
|
||||
{{#useSwaggerAnnotations}}{{#description}}@ApiModel(description = "{{{.}}}"){{/description}}{{/useSwaggerAnnotations}}{{#useSwaggerV3Annotations}}
|
||||
@Schema({{#title}}title="{{{.}}}", {{/title}}{{#description}}description="{{{.}}}"{{/description}}{{^description}}description=""{{/description}}){{/useSwaggerV3Annotations}}{{#useMicroProfileOpenAPIAnnotations}}
|
||||
@org.eclipse.microprofile.openapi.annotations.media.Schema({{#title}}title="{{{.}}}", {{/title}}{{#description}}description="{{{.}}}"{{/description}}{{^description}}description=""{{/description}}){{/useMicroProfileOpenAPIAnnotations}}
|
||||
{{#jackson}}@JsonTypeName("{{name}}"){{/jackson}}
|
||||
{{#jackson}}
|
||||
@JsonTypeName("{{name}}")
|
||||
{{#additionalProperties}}
|
||||
@JsonFormat(shape=JsonFormat.Shape.OBJECT)
|
||||
{{/additionalProperties}}
|
||||
{{/jackson}}
|
||||
{{>generatedAnnotation}}{{>additionalModelTypeAnnotations}}{{>xmlPojoAnnotation}}
|
||||
{{#vendorExtensions.x-class-extra-annotation}}
|
||||
{{{vendorExtensions.x-class-extra-annotation}}}
|
||||
@@ -196,6 +208,7 @@ public class {{classname}} {{#parent}}extends {{{.}}}{{/parent}} {{#vendorExtens
|
||||
}
|
||||
{{/isMap}}
|
||||
{{/vars}}
|
||||
{{>additional_properties}}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
@@ -176,7 +176,7 @@ import {{packageName}}.infrastructure.ITransformForStorage
|
||||
|
||||
override fun deserialize(decoder: Decoder): {{nameInPascalCase}} {
|
||||
val value = decoder.decodeSerializableValue({{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}kotlin.String{{/isContainer}}.serializer())
|
||||
return {{nameInPascalCase}}.values().firstOrNull { it.value == value }
|
||||
return {{nameInPascalCase}}.entries.firstOrNull { it.value == value }
|
||||
?: {{nameInPascalCase}}.{{#allowableValues}}{{#enumVars}}{{#-last}}{{&name}}{{/-last}}{{/enumVars}}{{/allowableValues}}
|
||||
}
|
||||
|
||||
@@ -360,14 +360,14 @@ import {{packageName}}.infrastructure.ITransformForStorage
|
||||
{{#isEnum}}
|
||||
{{#required}}
|
||||
// validate the required field `{{{baseName}}}`
|
||||
require({{{datatypeWithEnum}}}.values().any { it.value == jsonObj["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"].asString }) {
|
||||
require({{{datatypeWithEnum}}}.entries.any { it.value == jsonObj["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"].asString }) {
|
||||
String.format("Expected the field `{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}` to be valid `{{{datatypeWithEnum}}}` enum value in the JSON string but got `%s`", jsonObj["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"].toString())
|
||||
}
|
||||
{{/required}}
|
||||
{{^required}}
|
||||
// validate the optional field `{{{baseName}}}`
|
||||
if (jsonObj["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"] != null && !jsonObj["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"].isJsonNull) {
|
||||
require({{{datatypeWithEnum}}}.values().any { it.value == jsonObj["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"].asString }) {
|
||||
require({{{datatypeWithEnum}}}.entries.any { it.value == jsonObj["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"].asString }) {
|
||||
String.format("Expected the field `{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}` to be valid `{{{datatypeWithEnum}}}` enum value in the JSON string but got `%s`", jsonObj["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"].toString())
|
||||
}
|
||||
}
|
||||
@@ -376,14 +376,14 @@ import {{packageName}}.infrastructure.ITransformForStorage
|
||||
{{#isEnumRef}}
|
||||
{{#required}}
|
||||
// validate the required field `{{{baseName}}}`
|
||||
require({{{dataType}}}.values().any { it.value == jsonObj["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"].asString }) {
|
||||
require({{{dataType}}}.entries.any { it.value == jsonObj["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"].asString }) {
|
||||
String.format("Expected the field `{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}` to be valid `{{{dataType}}}` enum value in the JSON string but got `%s`", jsonObj["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"].toString())
|
||||
}
|
||||
{{/required}}
|
||||
{{^required}}
|
||||
// validate the optional field `{{{baseName}}}`
|
||||
if (jsonObj["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"] != null && !jsonObj["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"].isJsonNull) {
|
||||
require({{{dataType}}}.values().any { it.value == jsonObj["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"].asString }) {
|
||||
require({{{dataType}}}.entries.any { it.value == jsonObj["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"].asString }) {
|
||||
String.format("Expected the field `{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}` to be valid `{{{dataType}}}` enum value in the JSON string but got `%s`", jsonObj["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"].toString())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,4 +21,4 @@
|
||||
{{#deprecated}}
|
||||
@Deprecated(message = "This property is deprecated.")
|
||||
{{/deprecated}}
|
||||
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}{{#uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}Set{{/uniqueItems}}{{^uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}? = {{^defaultValue}}null{{/defaultValue}}{{#defaultValue}}{{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/defaultValue}}
|
||||
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}{{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}? = {{^defaultValue}}null{{/defaultValue}}{{#defaultValue}}{{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/defaultValue}}
|
||||
@@ -21,4 +21,4 @@
|
||||
{{#deprecated}}
|
||||
@Deprecated(message = "This property is deprecated.")
|
||||
{{/deprecated}}
|
||||
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}{{#uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}Set{{/uniqueItems}}{{^uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/defaultValue}}
|
||||
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}{{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/defaultValue}}
|
||||
@@ -109,7 +109,7 @@ import kotlinx.serialization.*
|
||||
{{^jackson}}
|
||||
{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}fun decode(data: kotlin.Any?): {{classname}}? = data?.let {
|
||||
val normalizedData = "$it".lowercase()
|
||||
values().firstOrNull { value ->
|
||||
entries.firstOrNull { value ->
|
||||
it == value || normalizedData == "$value".lowercase()
|
||||
}
|
||||
}
|
||||
@@ -127,7 +127,7 @@ import kotlinx.serialization.*
|
||||
{{/isNullable}}
|
||||
}
|
||||
val normalizedData = "$data".lowercase()
|
||||
return values().firstOrNull { value ->
|
||||
return entries.firstOrNull { value ->
|
||||
data == value || normalizedData == "$value".lowercase()
|
||||
}{{#isNullable}}{{/isNullable}}{{^isNullable}}{{#enumUnknownDefaultCase}}
|
||||
?: {{#allowableValues}}{{#enumVars}}{{#-last}}{{&name}}{{/-last}}{{/enumVars}}{{/allowableValues}}{{/enumUnknownDefaultCase}}{{^enumUnknownDefaultCase}}
|
||||
@@ -142,7 +142,7 @@ internal object {{classname}}Serializer : KSerializer<{{classname}}> {
|
||||
|
||||
override fun deserialize(decoder: Decoder): {{classname}} {
|
||||
val value = decoder.decodeSerializableValue({{{dataType}}}.serializer())
|
||||
return {{classname}}.values().firstOrNull { it.value == value }
|
||||
return {{classname}}.entries.firstOrNull { it.value == value }
|
||||
{{#isString}}
|
||||
?: {{classname}}.{{#allowableValues}}{{#enumVars}}{{#-last}}{{&name}}{{/-last}}{{/enumVars}}{{/allowableValues}}
|
||||
{{/isString}}
|
||||
@@ -162,7 +162,7 @@ internal object {{classname}}Serializer : KSerializer<{{classname}}> {
|
||||
|
||||
override fun deserialize(decoder: Decoder): {{classname}} {
|
||||
val value = decoder.decodeSerializableValue({{{dataType}}}.serializer())
|
||||
return {{classname}}.values().firstOrNull { it.value == value }
|
||||
return {{classname}}.entries.firstOrNull { it.value == value }
|
||||
?: throw IllegalArgumentException("Unknown enum value: $value")
|
||||
}
|
||||
|
||||
|
||||
@@ -15,4 +15,4 @@
|
||||
{{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}")
|
||||
{{/kotlinx_serialization}}
|
||||
{{/multiplatform}}
|
||||
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") abstract {{/multiplatform}}{{#kotlinx_serialization}}abstract {{/kotlinx_serialization}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}?
|
||||
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") abstract {{/multiplatform}}{{#kotlinx_serialization}}abstract {{/kotlinx_serialization}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}{{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}?
|
||||
@@ -15,4 +15,4 @@
|
||||
{{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}")
|
||||
{{/kotlinx_serialization}}
|
||||
{{/multiplatform}}
|
||||
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required abstract {{/multiplatform}}{{#kotlinx_serialization}}abstract {{/kotlinx_serialization}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}
|
||||
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required abstract {{/multiplatform}}{{#kotlinx_serialization}}abstract {{/kotlinx_serialization}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}{{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}
|
||||
@@ -1,7 +1,26 @@
|
||||
enum {{classname}} {
|
||||
{{#allowableValues}}
|
||||
{{#enumVars}}
|
||||
{{{name}}} = {{{protobuf-enum-index}}};
|
||||
{{/enumVars}}
|
||||
{{/allowableValues}}
|
||||
{{!
|
||||
Generates an extracted enum wrapped in a message container.
|
||||
|
||||
This wrapper pattern is used to prevent enum value name collisions
|
||||
in the Protocol Buffers global enum namespace. Multiple enums can
|
||||
have values with the same name when wrapped in separate messages.
|
||||
|
||||
Generated format:
|
||||
message EnumName {
|
||||
enum Enum {
|
||||
VALUE1 = 0;
|
||||
VALUE2 = 1;
|
||||
}
|
||||
}
|
||||
|
||||
Usage in models: EnumName.Enum field_name = 1;
|
||||
}}
|
||||
message {{classname}} {
|
||||
enum Enum {
|
||||
{{#allowableValues}}
|
||||
{{#enumVars}}
|
||||
{{{name}}} = {{{protobuf-enum-index}}};
|
||||
{{/enumVars}}
|
||||
{{/allowableValues}}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import public "{{{.}}}.proto";
|
||||
{{#vendorExtensions.x-protobuf-type}}{{{.}}} {{/vendorExtensions.x-protobuf-type}}{{{vendorExtensions.x-protobuf-data-type}}} {{{name}}} = {{vendorExtensions.x-protobuf-index}}{{#vendorExtensions.x-protobuf-packed}} [packed=true]{{/vendorExtensions.x-protobuf-packed}}{{#vendorExtensions.x-protobuf-json-name}} [json_name="{{vendorExtensions.x-protobuf-json-name}}"]{{/vendorExtensions.x-protobuf-json-name}};
|
||||
{{/isEnum}}
|
||||
{{#isEnum}}
|
||||
{{^vendorExtensions.x-protobuf-enum-reference-import}}
|
||||
enum {{enumName}} {
|
||||
{{#allowableValues}}
|
||||
{{#enumVars}}
|
||||
@@ -45,7 +46,8 @@ import public "{{{.}}}.proto";
|
||||
{{/allowableValues}}
|
||||
}
|
||||
|
||||
{{enumName}} {{name}} = {{vendorExtensions.x-protobuf-index}};
|
||||
{{/vendorExtensions.x-protobuf-enum-reference-import}}
|
||||
{{#vendorExtensions.x-protobuf-type}}{{{.}}} {{/vendorExtensions.x-protobuf-type}}{{{vendorExtensions.x-protobuf-data-type}}} {{name}} = {{vendorExtensions.x-protobuf-index}};
|
||||
{{/isEnum}}
|
||||
|
||||
{{/vars}}
|
||||
|
||||
@@ -242,7 +242,7 @@
|
||||
self$`{{name}}`
|
||||
{{/isPrimitiveType}}
|
||||
{{^isPrimitiveType}}
|
||||
lapply(self$`{{name}}`, function(x) x$toSimpleType())
|
||||
self$extractSimpleType(self$`{{name}}`)
|
||||
{{/isPrimitiveType}}
|
||||
{{/isArray}}
|
||||
{{#isMap}}
|
||||
@@ -250,7 +250,7 @@
|
||||
self$`{{name}}`
|
||||
{{/isPrimitiveType}}
|
||||
{{^isPrimitiveType}}
|
||||
lapply(self$`{{name}}`, function(x) x$toSimpleType())
|
||||
self$extractSimpleType(self$`{{name}}`)
|
||||
{{/isPrimitiveType}}
|
||||
{{/isMap}}
|
||||
{{/isContainer}}
|
||||
@@ -259,7 +259,7 @@
|
||||
self$`{{name}}`
|
||||
{{/isPrimitiveType}}
|
||||
{{^isPrimitiveType}}
|
||||
self$`{{name}}`$toSimpleType()
|
||||
self$extractSimpleType(self$`{{name}}`)
|
||||
{{/isPrimitiveType}}
|
||||
{{/isContainer}}
|
||||
}
|
||||
@@ -272,6 +272,31 @@
|
||||
{{/isAdditionalPropertiesTrue}}
|
||||
return({{classname}}Object)
|
||||
},
|
||||
{{#vendorExtensions.x-r-has-non-primitive-field}}
|
||||
|
||||
extractSimpleType = function(x) {
|
||||
if (R6::is.R6(x)) {
|
||||
return(x$toSimpleType())
|
||||
} else if (!self$hasNestedR6(x)) {
|
||||
return(x)
|
||||
}
|
||||
lapply(x, self$extractSimpleType)
|
||||
},
|
||||
|
||||
hasNestedR6 = function(x) {
|
||||
if (R6::is.R6(x)) {
|
||||
return(TRUE)
|
||||
}
|
||||
if (is.list(x)) {
|
||||
for (item in x) {
|
||||
if (self$hasNestedR6(item)) {
|
||||
return(TRUE)
|
||||
}
|
||||
}
|
||||
}
|
||||
FALSE
|
||||
},
|
||||
{{/vendorExtensions.x-r-has-non-primitive-field}}
|
||||
|
||||
#' @description
|
||||
#' Deserialize JSON string into an instance of {{{classname}}}
|
||||
|
||||
@@ -32,7 +32,7 @@ homepage = "{{.}}"
|
||||
{{/homePageUrl}}
|
||||
|
||||
[features]
|
||||
default = ["client", "server"]
|
||||
default = ["client", "server", "client-tls"]
|
||||
client = [
|
||||
{{#apiUsesMultipartFormData}}
|
||||
"multipart", "multipart/client", "swagger/multipart_form",
|
||||
@@ -47,7 +47,19 @@ client = [
|
||||
"serde_ignored", "percent-encoding", {{^apiUsesByteArray}}"lazy_static", "regex",{{/apiUsesByteArray}}
|
||||
{{/hasCallbacks}}
|
||||
{{! Anything added to the list below, should probably be added to the callbacks list below }}
|
||||
"hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "hyper-openssl", "hyper-tls", "native-tls", "openssl", "url"
|
||||
"hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "url"
|
||||
]
|
||||
# TLS support - automatically selects backend based on target OS:
|
||||
# - macOS/Windows/iOS: native-tls via hyper-tls
|
||||
# - Other platforms: OpenSSL via hyper-openssl
|
||||
# Dependencies are in target-specific sections below
|
||||
client-tls = [
|
||||
"client",
|
||||
"dep:native-tls",
|
||||
"dep:hyper-tls",
|
||||
"dep:openssl",
|
||||
"dep:hyper-openssl",
|
||||
"swagger/tls"
|
||||
]
|
||||
server = [
|
||||
{{#apiUsesMultipartFormData}}
|
||||
@@ -57,7 +69,7 @@ server = [
|
||||
"mime_multipart", "swagger/multipart_related",
|
||||
{{/apiUsesMultipartRelated}}
|
||||
{{#hasCallbacks}}
|
||||
"native-tls", "hyper-openssl", "hyper-tls", "openssl",
|
||||
"hyper-util/http1", "hyper-util/http2",
|
||||
{{/hasCallbacks}}
|
||||
{{! Anything added to the list below, should probably be added to the callbacks list above }}
|
||||
"serde_ignored", "hyper", "percent-encoding", "url",
|
||||
@@ -74,20 +86,12 @@ conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-
|
||||
mock = ["mockall"]
|
||||
validate = [{{^apiUsesByteArray}}"regex",{{/apiUsesByteArray}} "serde_valid", "swagger/serdevalid"]
|
||||
|
||||
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
|
||||
native-tls = { version = "0.2", optional = true }
|
||||
hyper-tls = { version = "0.6", optional = true }
|
||||
|
||||
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies]
|
||||
hyper-openssl = { version = "0.10", optional = true }
|
||||
openssl = { version = "0.10", optional = true }
|
||||
|
||||
[dependencies]
|
||||
# Common
|
||||
async-trait = "0.1.88"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
futures = "0.3"
|
||||
swagger = { version = "7.0.0", features = ["serdejson", "server", "client", "tls"] }
|
||||
swagger = { version = "7.0.0", features = ["serdejson", "server", "client"] }
|
||||
headers = "0.4.0"
|
||||
log = "0.4.27"
|
||||
|
||||
@@ -157,6 +161,17 @@ frunk_core = { version = "0.4.3", optional = true }
|
||||
frunk-enum-derive = { version = "0.3.0", optional = true }
|
||||
frunk-enum-core = { version = "0.3.0", optional = true }
|
||||
|
||||
# TLS dependencies - platform-specific backends
|
||||
# On macOS/Windows/iOS, use native-tls via hyper-tls
|
||||
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
|
||||
native-tls = { version = "0.2", optional = true }
|
||||
hyper-tls = { version = "0.6", optional = true }
|
||||
|
||||
# On other platforms (Linux, etc.), use OpenSSL via hyper-openssl
|
||||
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies]
|
||||
openssl = { version = "0.10", optional = true }
|
||||
hyper-openssl = { version = "0.10", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
always_send = "0.1.1"
|
||||
clap = "4.5"
|
||||
@@ -169,8 +184,8 @@ pin-project = "1.1.10"
|
||||
jsonwebtoken = {version = "10.0.0", features = ["rust_crypto"]}
|
||||
|
||||
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies]
|
||||
tokio-openssl = "0.6"
|
||||
openssl = "0.10"
|
||||
tokio-openssl = "0.6"
|
||||
|
||||
[[example]]
|
||||
name = "client"
|
||||
|
||||
@@ -126,6 +126,10 @@ The generated library has a few optional features that can be activated through
|
||||
* `client`
|
||||
* This defaults to enabled and creates the basic skeleton of a client implementation based on hyper
|
||||
* The constructed client implements the API trait by making remote API call.
|
||||
* `client-tls`
|
||||
* This default to enabled and provides HTTPS support with automatic TLS backend selection:
|
||||
- macOS/Windows/iOS: native-tls + hyper-tls
|
||||
- Linux/Unix/others: OpenSSL + hyper-openssl
|
||||
* `conversions`
|
||||
* This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types.
|
||||
* `cli`
|
||||
@@ -134,6 +138,25 @@ The generated library has a few optional features that can be activated through
|
||||
* This defaults to disabled and allows JSON Schema validation of received data using `MakeService::set_validation` or `Service::set_validation`.
|
||||
* Note, enabling validation will have a performance penalty, especially if the API heavily uses regex based checks.
|
||||
|
||||
### HTTPS/TLS Support
|
||||
|
||||
HTTPS support is included by default. To disable it (for example, to reduce dependencies), you can:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
{{{packageName}}} = { version = "{{{packageVersion}}}", default-features = false, features = ["client", "server"] }
|
||||
```
|
||||
|
||||
**For server with callbacks that need HTTPS:**
|
||||
```toml
|
||||
[dependencies]
|
||||
{{{packageName}}} = { version = "{{{packageVersion}}}", features = ["server", "client-tls"] }
|
||||
```
|
||||
|
||||
The TLS backend is automatically selected based on your target platform:
|
||||
- **macOS, Windows, iOS**: Uses `native-tls` (system TLS libraries)
|
||||
- **Linux, Unix, other platforms**: Uses `openssl`
|
||||
|
||||
See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`.
|
||||
|
||||
## Documentation for API Endpoints
|
||||
|
||||
@@ -45,17 +45,17 @@ struct Cli {
|
||||
server_address: String,
|
||||
|
||||
/// Path to the client private key if using client-side TLS authentication
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))]
|
||||
#[clap(long, requires_all(&["client_certificate", "server_certificate"]))]
|
||||
client_key: Option<String>,
|
||||
|
||||
/// Path to the client's public certificate associated with the private key
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))]
|
||||
#[clap(long, requires_all(&["client_key", "server_certificate"]))]
|
||||
client_certificate: Option<String>,
|
||||
|
||||
/// Path to CA certificate used to authenticate the server
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))]
|
||||
#[clap(long)]
|
||||
server_certificate: Option<String>,
|
||||
|
||||
@@ -130,7 +130,8 @@ enum Operation {
|
||||
{{/apiInfo}}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
// On Linux/Unix with OpenSSL (client-tls feature), support certificate pinning and mutual TLS
|
||||
#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))]
|
||||
fn create_client(args: &Cli, context: ClientContext) -> Result<Box<dyn ApiNoContext<ClientContext>>> {
|
||||
if args.client_certificate.is_some() {
|
||||
debug!("Using mutual TLS");
|
||||
@@ -156,8 +157,15 @@ fn create_client(args: &Cli, context: ClientContext) -> Result<Box<dyn ApiNoCont
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
// On macOS/Windows/iOS or without client-tls feature, use simple client (no cert pinning/mutual TLS)
|
||||
#[cfg(any(
|
||||
not(feature = "client-tls"),
|
||||
all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios"))
|
||||
))]
|
||||
fn create_client(args: &Cli, context: ClientContext) -> Result<Box<dyn ApiNoContext<ClientContext>>> {
|
||||
// Client::try_new() automatically detects the URL scheme (http:// or https://)
|
||||
// and creates the appropriate client.
|
||||
// Note: Certificate pinning and mutual TLS are only available on Linux/Unix with OpenSSL
|
||||
let client =
|
||||
Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?;
|
||||
Ok(Box::new(client.with_context(context)))
|
||||
|
||||
@@ -118,10 +118,17 @@ impl<Connector, C> Client<
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
type HyperHttpsConnector = hyper_tls::HttpsConnector<hyper_util::client::legacy::connect::HttpConnector>;
|
||||
|
||||
#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))]
|
||||
type HyperHttpsConnector = hyper_openssl::client::legacy::HttpsConnector<hyper_util::client::legacy::connect::HttpConnector>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum HyperClient {
|
||||
Http(hyper_util::client::legacy::Client<hyper_util::client::legacy::connect::HttpConnector, BoxBody<Bytes, Infallible>>),
|
||||
Https(hyper_util::client::legacy::Client<HttpsConnector, BoxBody<Bytes, Infallible>>),
|
||||
#[cfg(feature = "client-tls")]
|
||||
Https(hyper_util::client::legacy::Client<HyperHttpsConnector, BoxBody<Bytes, Infallible>>),
|
||||
}
|
||||
|
||||
impl Service<Request<BoxBody<Bytes, Infallible>>> for HyperClient {
|
||||
@@ -132,7 +139,8 @@ impl Service<Request<BoxBody<Bytes, Infallible>>> for HyperClient {
|
||||
fn call(&self, req: Request<BoxBody<Bytes, Infallible>>) -> Self::Future {
|
||||
match self {
|
||||
HyperClient::Http(client) => client.request(req),
|
||||
HyperClient::Https(client) => client.request(req)
|
||||
#[cfg(feature = "client-tls")]
|
||||
HyperClient::Https(client) => client.request(req),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,11 +166,17 @@ impl<C> Client<DropContextService<HyperClient, C>, C> where
|
||||
"http" => {
|
||||
HyperClient::Http(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector.build()))
|
||||
},
|
||||
#[cfg(feature = "client-tls")]
|
||||
"https" => {
|
||||
let connector = connector.https()
|
||||
.build()
|
||||
.map_err(ClientInitError::SslError)?;
|
||||
HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector))
|
||||
let https_connector = connector
|
||||
.https()
|
||||
.build()
|
||||
.map_err(ClientInitError::SslError)?;
|
||||
HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(https_connector))
|
||||
},
|
||||
#[cfg(not(feature = "client-tls"))]
|
||||
"https" => {
|
||||
return Err(ClientInitError::TlsNotEnabled);
|
||||
},
|
||||
_ => {
|
||||
return Err(ClientInitError::InvalidScheme);
|
||||
@@ -206,12 +220,13 @@ impl<C> Client<
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
type HttpsConnector = hyper_tls::HttpsConnector<hyper_util::client::legacy::connect::HttpConnector>;
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))]
|
||||
type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector<hyper_util::client::legacy::connect::HttpConnector>;
|
||||
|
||||
#[cfg(feature = "client-tls")]
|
||||
impl<C> Client<
|
||||
DropContextService<
|
||||
hyper_util::service::TowerToHyperService<
|
||||
@@ -226,10 +241,11 @@ impl<C> Client<
|
||||
> where
|
||||
C: Clone + Send + Sync + 'static
|
||||
{
|
||||
/// Create a client with a TLS connection to the server
|
||||
/// Create a client with a TLS connection to the server.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_path` - base path of the client API, i.e. "<http://www.my-api-implementation.com>"
|
||||
/// * `base_path` - base path of the client API, i.e. "<https://www.my-api-implementation.com>"
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
pub fn try_new_https(base_path: &str) -> Result<Self, ClientInitError>
|
||||
{
|
||||
let https_connector = Connector::builder()
|
||||
@@ -239,10 +255,24 @@ impl<C> Client<
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server using a pinned certificate
|
||||
/// Create a client with a TLS connection to the server using OpenSSL via swagger.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_path` - base path of the client API, i.e. "<http://www.my-api-implementation.com>"
|
||||
/// * `base_path` - base path of the client API, i.e. "<https://www.my-api-implementation.com>"
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn try_new_https(base_path: &str) -> Result<Self, ClientInitError>
|
||||
{
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.build()
|
||||
.map_err(ClientInitError::SslError)?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server using a pinned certificate.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_path` - base path of the client API, i.e. "<https://www.my-api-implementation.com>"
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn try_new_https_pinned<CA>(
|
||||
@@ -263,7 +293,7 @@ impl<C> Client<
|
||||
/// Create a client with a mutually authenticated TLS connection to the server.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_path` - base path of the client API, i.e. "<http://www.my-api-implementation.com>"
|
||||
/// * `base_path` - base path of the client API, i.e. "<https://www.my-api-implementation.com>"
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
/// * `client_key` - Path to the client private key
|
||||
/// * `client_certificate` - Path to the client's public certificate associated with the private key
|
||||
@@ -325,12 +355,15 @@ pub enum ClientInitError {
|
||||
/// Missing Hostname
|
||||
MissingHost,
|
||||
|
||||
/// HTTPS requested but TLS features not enabled
|
||||
TlsNotEnabled,
|
||||
|
||||
/// SSL Connection Error
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
SslError(native_tls::Error),
|
||||
|
||||
/// SSL Connection Error
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))]
|
||||
SslError(openssl::error::ErrorStack),
|
||||
}
|
||||
|
||||
|
||||
@@ -115,17 +115,33 @@ fn main() {
|
||||
let context: ClientContext =
|
||||
swagger::make_context!(ContextBuilder, EmptyContext, auth_data, XSpanIdString::default());
|
||||
|
||||
let mut client : Box<dyn ApiNoContext<ClientContext>> = if is_https {
|
||||
// Using Simple HTTPS
|
||||
let client = Box::new(Client::try_new_https(&base_url)
|
||||
.expect("Failed to create HTTPS client"));
|
||||
Box::new(client.with_context(context))
|
||||
} else {
|
||||
// Using HTTP
|
||||
let client = Box::new(Client::try_new_http(
|
||||
&base_url)
|
||||
.expect("Failed to create HTTP client"));
|
||||
Box::new(client.with_context(context))
|
||||
let mut client : Box<dyn ApiNoContext<ClientContext>> = {
|
||||
#[cfg(feature = "client-tls")]
|
||||
{
|
||||
if is_https {
|
||||
// Using HTTPS with native-tls
|
||||
let client = Box::new(Client::try_new_https(&base_url)
|
||||
.expect("Failed to create HTTPS client"));
|
||||
Box::new(client.with_context(context))
|
||||
} else {
|
||||
// Using HTTP
|
||||
let client = Box::new(Client::try_new_http(&base_url)
|
||||
.expect("Failed to create HTTP client"));
|
||||
Box::new(client.with_context(context))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "client-tls"))]
|
||||
{
|
||||
if is_https {
|
||||
panic!("HTTPS requested but TLS support not enabled. \
|
||||
Enable the 'client-tls' feature to use HTTPS.");
|
||||
}
|
||||
// Using HTTP only
|
||||
let client = Box::new(Client::try_new_http(&base_url)
|
||||
.expect("Failed to create HTTP client"));
|
||||
Box::new(client.with_context(context))
|
||||
}
|
||||
};
|
||||
|
||||
let mut rt = tokio::runtime::Runtime::new().unwrap();
|
||||
|
||||
@@ -97,16 +97,17 @@ impl<C> Client<DropContextService<hyper_util::service::TowerToHyperService<hyper
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
type HttpsConnector = hyper_tls::HttpsConnector<hyper_util::client::legacy::connect::HttpConnector>;
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))]
|
||||
type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector<hyper_util::client::legacy::connect::HttpConnector>;
|
||||
|
||||
#[cfg(feature = "client-tls")]
|
||||
impl<C> Client<DropContextService<hyper_util::service::TowerToHyperService<hyper_util::client::legacy::Client<HttpsConnector, BoxBody<Bytes, Infallible>>>, C>, C> where
|
||||
C: Clone + Send + Sync + 'static
|
||||
{
|
||||
/// Create a client with a TLS connection to the server.
|
||||
/// Create a client with a TLS connection to the server using native-tls.
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
pub fn new_https() -> Result<Self, native_tls::Error>
|
||||
{
|
||||
@@ -114,7 +115,7 @@ impl<C> Client<DropContextService<hyper_util::service::TowerToHyperService<hyper
|
||||
Ok(Self::new_with_connector(https_connector))
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server.
|
||||
/// Create a client with a TLS connection to the server using OpenSSL.
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn new_https() -> Result<Self, openssl::error::ErrorStack>
|
||||
{
|
||||
|
||||
@@ -110,7 +110,8 @@ export const setSearchParams = function (url: URL, ...objects: any[]) {
|
||||
* This function will run for every key-value pair encountered by JSON.stringify while traversing an object.
|
||||
* Converting a set to a string will return an empty object, so an intermediate conversion to an array is required.
|
||||
*/
|
||||
export const replaceWithSerializableTypeIfNeeded = function(key: any, value: any) {
|
||||
// @ts-ignore
|
||||
export const replaceWithSerializableTypeIfNeeded = function(key: string, value: any) {
|
||||
if (value instanceof Set) {
|
||||
return Array.from(value);
|
||||
} else {
|
||||
|
||||
@@ -70,15 +70,15 @@ export function {{classname}}FromJSONTyped(json: any, ignoreDiscriminator: boole
|
||||
{{#items}}
|
||||
{{#isDateType}}
|
||||
if (Array.isArray(json)) {
|
||||
if (json.every(item => !(isNaN(new Date(json).getTime()))) {
|
||||
return json.map(value => new Date(json);
|
||||
if (json.every(item => !(isNaN(new Date(item).getTime())))) {
|
||||
return json.map(value => new Date(value));
|
||||
}
|
||||
}
|
||||
{{/isDateType}}
|
||||
{{#isDateTimeType}}
|
||||
if (Array.isArray(json)) {
|
||||
if (json.every(item => !(isNaN(new Date(json).getTime()))) {
|
||||
return json.map(value => new Date(json);
|
||||
if (json.every(item => !(isNaN(new Date(item).getTime())))) {
|
||||
return json.map(value => new Date(value));
|
||||
}
|
||||
}
|
||||
{{/isDateTimeType}}
|
||||
@@ -188,14 +188,14 @@ export function {{classname}}ToJSONTyped(value?: {{classname}} | null, ignoreDis
|
||||
{{#isDateType}}
|
||||
if (Array.isArray(value)) {
|
||||
if (value.every(item => item instanceof Date) {
|
||||
return value.map(value => value.toISOString().substring(0,10)));
|
||||
return value.map(value => value.toISOString().substring(0,10));
|
||||
}
|
||||
}
|
||||
{{/isDateType}}
|
||||
{{#isDateTimeType}}
|
||||
if (Array.isArray(value)) {
|
||||
if (value.every(item => item instanceof Date) {
|
||||
return value.map(value => value.toISOString();
|
||||
if (value.every(item => item instanceof Date)) {
|
||||
return value.map(item => item.toISOString());
|
||||
}
|
||||
}
|
||||
{{/isDateTimeType}}
|
||||
|
||||
@@ -216,6 +216,11 @@ export class RequestContext {
|
||||
export interface ResponseBody {
|
||||
text(): Promise<string>;
|
||||
binary(): Promise<{{{fileContentDataType}}}>;
|
||||
{{#platforms}}
|
||||
{{#node}}
|
||||
stream(): ReadableStream<Uint8Array> | null;
|
||||
{{/node}}
|
||||
{{/platforms}}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,6 +258,14 @@ export class SelfDecodingBody implements ResponseBody {
|
||||
{{/deno}}
|
||||
{{/platforms}}
|
||||
}
|
||||
|
||||
{{#platforms}}
|
||||
{{#node}}
|
||||
stream(): ReadableStream<Uint8Array> | null {
|
||||
return null;
|
||||
}
|
||||
{{/node}}
|
||||
{{/platforms}}
|
||||
}
|
||||
|
||||
export class ResponseContext {
|
||||
|
||||
@@ -38,7 +38,8 @@ export class IsomorphicFetchHttpLibrary implements HttpLibrary {
|
||||
{{#node}}
|
||||
const body = {
|
||||
text: () => resp.text(),
|
||||
binary: () => resp.buffer()
|
||||
binary: () => resp.buffer(),
|
||||
stream: () => resp.body
|
||||
};
|
||||
{{/node}}
|
||||
{{^node}}
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
{{#fetch-api}}
|
||||
{{#platforms}}
|
||||
{{#node}}
|
||||
"undici": "^7.16.0",
|
||||
"undici": "^7.18.2",
|
||||
{{/node}}
|
||||
{{#browser}}
|
||||
"whatwg-fetch": "^3.0.0",
|
||||
|
||||
@@ -1306,6 +1306,59 @@ public class OpenAPINormalizerTest {
|
||||
assertNotNull(inlinePropertyAfter.getProperties().get("nestedNumber"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortModelProperties() {
|
||||
// Create a schema with properties in non-alphabetical order
|
||||
Schema schema = new ObjectSchema()
|
||||
.addProperty("zebra", new StringSchema())
|
||||
.addProperty("apple", new StringSchema())
|
||||
.addProperty("mango", new IntegerSchema());
|
||||
|
||||
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("TestModel", schema);
|
||||
|
||||
// Verify original order (LinkedHashMap preserves insertion order)
|
||||
List<String> originalOrder = new ArrayList<>(schema.getProperties().keySet());
|
||||
assertEquals(originalOrder.get(0), "zebra");
|
||||
assertEquals(originalOrder.get(1), "apple");
|
||||
assertEquals(originalOrder.get(2), "mango");
|
||||
|
||||
// Apply normalizer with SORT_MODEL_PROPERTIES=true
|
||||
Map<String, String> options = new HashMap<>();
|
||||
options.put("SORT_MODEL_PROPERTIES", "true");
|
||||
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
|
||||
openAPINormalizer.normalize();
|
||||
|
||||
// Verify properties are now sorted alphabetically
|
||||
Schema normalizedSchema = openAPI.getComponents().getSchemas().get("TestModel");
|
||||
List<String> sortedOrder = new ArrayList<>(normalizedSchema.getProperties().keySet());
|
||||
assertEquals(sortedOrder.get(0), "apple");
|
||||
assertEquals(sortedOrder.get(1), "mango");
|
||||
assertEquals(sortedOrder.get(2), "zebra");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortModelPropertiesDisabledByDefault() {
|
||||
// Create a schema with properties in non-alphabetical order
|
||||
Schema schema = new ObjectSchema()
|
||||
.addProperty("zebra", new StringSchema())
|
||||
.addProperty("apple", new StringSchema())
|
||||
.addProperty("mango", new IntegerSchema());
|
||||
|
||||
OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("TestModel", schema);
|
||||
|
||||
// Apply normalizer without SORT_MODEL_PROPERTIES (default is false)
|
||||
Map<String, String> options = new HashMap<>();
|
||||
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
|
||||
openAPINormalizer.normalize();
|
||||
|
||||
// Verify properties retain original order
|
||||
Schema normalizedSchema = openAPI.getComponents().getSchemas().get("TestModel");
|
||||
List<String> order = new ArrayList<>(normalizedSchema.getProperties().keySet());
|
||||
assertEquals(order.get(0), "zebra");
|
||||
assertEquals(order.get(1), "apple");
|
||||
assertEquals(order.get(2), "mango");
|
||||
}
|
||||
|
||||
public static class RemoveRequiredNormalizer extends OpenAPINormalizer {
|
||||
|
||||
public RemoveRequiredNormalizer(OpenAPI openAPI, Map<String, String> inputRules) {
|
||||
|
||||
@@ -461,4 +461,27 @@ public class GoClientCodegenTest {
|
||||
TestUtils.assertFileContains(apiPath, defaultEnumArrayString);
|
||||
TestUtils.assertFileContains(apiPath, defaultValueString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEscapingInExamples() throws IOException {
|
||||
File output = Files.createTempDirectory("test").toFile();
|
||||
output.deleteOnExit();
|
||||
|
||||
final CodegenConfigurator configurator = new CodegenConfigurator()
|
||||
.setGeneratorName("go")
|
||||
.setInputSpec("src/test/resources/3_0/go/petstore-with-special-chars-in-examples.yaml")
|
||||
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
|
||||
|
||||
DefaultGenerator generator = new DefaultGenerator();
|
||||
List<File> files = generator.opts(configurator.toClientOptInput()).generate();
|
||||
files.forEach(File::deleteOnExit);
|
||||
|
||||
Path docPath = Paths.get(output + "/docs/TestAPI.md");
|
||||
// Verify that quotes are properly escaped in parameter examples
|
||||
TestUtils.assertFileContains(docPath, "stringWithQuotes := \"John \\\"Johnny\\\" Doe\"");
|
||||
// Verify that backslashes are properly escaped in parameter examples
|
||||
TestUtils.assertFileContains(docPath, "stringWithBackslash := \"C:\\\\path\\\\to\\\\file\"");
|
||||
// Verify that quotes are properly escaped in email parameter examples
|
||||
TestUtils.assertFileContains(docPath, "emailWithQuotes := \"test\\\"user@example.com\"");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,6 +649,10 @@ public class KotlinClientCodegenModelTest {
|
||||
// base properties are present
|
||||
TestUtils.assertFileContains(animalKt, "val id");
|
||||
TestUtils.assertFileContains(animalKt, "val optionalProperty");
|
||||
// base array with unique items = false is correctly handled as List
|
||||
TestUtils.assertFileContains(animalKt, "val stringArray: kotlin.collections.List<kotlin.String>");
|
||||
// base array with unique items = true is correctly handled as Set
|
||||
TestUtils.assertFileContains(animalKt, "val stringSet: kotlin.collections.Set<kotlin.String>");
|
||||
// base doesn't contain discriminator
|
||||
TestUtils.assertFileNotContains(animalKt, "val discriminator");
|
||||
|
||||
@@ -658,6 +662,10 @@ public class KotlinClientCodegenModelTest {
|
||||
// derived properties are overridden
|
||||
TestUtils.assertFileContains(birdKt, "override val id");
|
||||
TestUtils.assertFileContains(birdKt, "override val optionalProperty");
|
||||
// derived array with unique items = false is correctly handled as List and with override
|
||||
TestUtils.assertFileContains(birdKt, "override val stringArray: kotlin.collections.List<kotlin.String>");
|
||||
// derived array with unique items = true is correctly handled as Set and with override
|
||||
TestUtils.assertFileContains(birdKt, "override val stringSet: kotlin.collections.Set<kotlin.String>");
|
||||
// derived doesn't contain disciminator
|
||||
TestUtils.assertFileNotContains(birdKt, "val discriminator");
|
||||
}
|
||||
|
||||
@@ -89,6 +89,8 @@ public class KotlinSpringServerCodegenTest {
|
||||
// base properties are present
|
||||
TestUtils.assertFileContains(animalKt, "val id");
|
||||
TestUtils.assertFileContains(animalKt, "val optionalProperty");
|
||||
TestUtils.assertFileContains(animalKt, "val stringArray: kotlin.collections.List<kotlin.String>");
|
||||
TestUtils.assertFileContains(animalKt, "val stringSet: kotlin.collections.Set<kotlin.String>");
|
||||
// base doesn't contain discriminator
|
||||
TestUtils.assertFileNotContains(animalKt, "val discriminator");
|
||||
|
||||
@@ -98,6 +100,8 @@ public class KotlinSpringServerCodegenTest {
|
||||
// derived properties are overridden
|
||||
TestUtils.assertFileContains(birdKt, "override val id");
|
||||
TestUtils.assertFileContains(birdKt, "override val optionalProperty");
|
||||
TestUtils.assertFileContains(birdKt, "override val stringArray: kotlin.collections.List<kotlin.String>");
|
||||
TestUtils.assertFileContains(birdKt, "override val stringSet: kotlin.collections.Set<kotlin.String>");
|
||||
// derived doesn't contain disciminator
|
||||
TestUtils.assertFileNotContains(birdKt, "val discriminator");
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -146,4 +147,33 @@ public class PhpNextgenClientCodegenTest {
|
||||
Assert.assertListNotContains(modelContent, a -> a.equals("$color = self::COLOR_UNKNOWN_DEFAULT_OPEN_API;"), "");
|
||||
Assert.assertListContains(modelContent, a -> a.equalsIgnoreCase("\"Invalid value '%s' for 'color', must be one of '%s'\","), "");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDifferentResponseSchemasWithEmpty() throws IOException {
|
||||
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
|
||||
output.deleteOnExit();
|
||||
|
||||
OpenAPI openAPI = new OpenAPIParser()
|
||||
.readLocation("src/test/resources/bugs/issue_22817.yaml", null, new ParseOptions())
|
||||
.getOpenAPI();
|
||||
|
||||
|
||||
codegen.setOutputDir(output.getAbsolutePath());
|
||||
ClientOptInput input = new ClientOptInput()
|
||||
.openAPI(openAPI)
|
||||
.config(codegen);
|
||||
|
||||
DefaultGenerator generator = new DefaultGenerator();
|
||||
Map<String, File> files = generator.opts(input).generate().stream()
|
||||
.collect(Collectors.toMap(File::getName, Function.identity()));
|
||||
|
||||
List<String> modelContent = Files
|
||||
.readAllLines(files.get("DefaultApi.php").toPath())
|
||||
.stream()
|
||||
.map(String::trim)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Assert.assertListContains(modelContent, a -> a.equals("): int|string|null"), "Expected to find nullable return type declaration.");
|
||||
Assert.assertListNotContains(modelContent, a -> a.equals("): ?int|string"), "Expected to not find invalid union type with '?'.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ import java.util.Map;
|
||||
import static org.openapitools.codegen.TestUtils.createCodegenModelWrapper;
|
||||
import static org.openapitools.codegen.languages.ProtobufSchemaCodegen.USE_SIMPLIFIED_ENUM_NAMES;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.openapitools.codegen.languages.ProtobufSchemaCodegen.EXTRACT_ENUMS_TO_SEPARATE_FILES;
|
||||
import static org.openapitools.codegen.languages.ProtobufSchemaCodegen.START_ENUMS_WITH_UNSPECIFIED;
|
||||
|
||||
public class ProtobufSchemaCodegenTest {
|
||||
@@ -361,6 +362,73 @@ public class ProtobufSchemaCodegenTest {
|
||||
Assert.assertEquals(enumVars1.get(1).get("isString"), false);
|
||||
}
|
||||
|
||||
@Test(description = "Support multiple level of inheritance for discriminator - ensures properties from indirect children are included")
|
||||
public void testCodeGenWithAllOfDiscriminatorMultipleLevels() throws IOException {
|
||||
// set line break to \n across all platforms
|
||||
System.setProperty("line.separator", "\n");
|
||||
|
||||
File output = Files.createTempDirectory("test").toFile();
|
||||
|
||||
final CodegenConfigurator configurator = new CodegenConfigurator()
|
||||
.setGeneratorName("protobuf-schema")
|
||||
.setInputSpec("src/test/resources/3_0/allOf_composition_discriminator_multiple_inheritance.yaml")
|
||||
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
|
||||
|
||||
final ClientOptInput clientOptInput = configurator.toClientOptInput();
|
||||
DefaultGenerator generator = new DefaultGenerator();
|
||||
List<File> files = generator.opts(clientOptInput).generate();
|
||||
|
||||
// Verify Animal proto file (discriminator parent)
|
||||
TestUtils.ensureContainsFile(files, output, "models/animal.proto");
|
||||
Path animalPath = Paths.get(output + "/models/animal.proto");
|
||||
String animalContent = new String(Files.readAllBytes(animalPath), StandardCharsets.UTF_8);
|
||||
|
||||
// Properties from discriminated classes should be included in Animal:
|
||||
// From Dog (direct child in discriminator mapping): bark
|
||||
Assert.assertTrue(animalContent.contains("string bark"), "Animal should contain 'bark' property from Dog");
|
||||
// From Feline (intermediate parent, NOT in discriminator mapping): name, furColor
|
||||
Assert.assertTrue(animalContent.contains("string name"), "Animal should contain 'name' property from Feline");
|
||||
Assert.assertTrue(animalContent.contains("string fur_color"), "Animal should contain 'fur_color' property from Feline");
|
||||
// From Cat (indirect child through Feline, IN discriminator mapping): isIndoor, careDetails
|
||||
Assert.assertTrue(animalContent.contains("bool is_indoor"), "Animal should contain 'is_indoor' property from Cat (indirect child)");
|
||||
Assert.assertTrue(animalContent.contains("CareDetails care_details"), "Animal should contain 'care_details' property from Cat (indirect child)");
|
||||
|
||||
assertFileEquals(animalPath, Paths.get("src/test/resources/3_0/protobuf-schema/animal.proto"));
|
||||
|
||||
// Verify Cat proto file (indirect child in discriminator mapping)
|
||||
TestUtils.ensureContainsFile(files, output, "models/cat.proto");
|
||||
Path catPath = Paths.get(output + "/models/cat.proto");
|
||||
String catContent = new String(Files.readAllBytes(catPath), StandardCharsets.UTF_8);
|
||||
|
||||
// Cat should import Animal (the discriminator parent), not Feline (its immediate allOf parent)
|
||||
Assert.assertTrue(catContent.contains("import public \"models/animal.proto\";"),
|
||||
"Cat should import Animal (discriminator parent)");
|
||||
// Cat should also import CareDetails (its dependency)
|
||||
Assert.assertTrue(catContent.contains("import public \"models/care_details.proto\";"),
|
||||
"Cat should import CareDetails");
|
||||
|
||||
// Verify Feline proto file (intermediate parent, NOT in discriminator mapping)
|
||||
TestUtils.ensureContainsFile(files, output, "models/feline.proto");
|
||||
Path felinePath = Paths.get(output + "/models/feline.proto");
|
||||
String felineContent = new String(Files.readAllBytes(felinePath), StandardCharsets.UTF_8);
|
||||
|
||||
// According to requirements: Feline inherits from Animal but is NOT in discriminator mapping
|
||||
// So Feline should NOT import Animal
|
||||
Assert.assertFalse(felineContent.contains("import public \"models/animal.proto\";"),
|
||||
"Feline should NOT import Animal (not in discriminator mapping)");
|
||||
|
||||
// Verify Dog proto file (direct child in discriminator mapping)
|
||||
TestUtils.ensureContainsFile(files, output, "models/dog.proto");
|
||||
Path dogPath = Paths.get(output + "/models/dog.proto");
|
||||
String dogContent = new String(Files.readAllBytes(dogPath), StandardCharsets.UTF_8);
|
||||
|
||||
// Dog should import Animal (the discriminator parent)
|
||||
Assert.assertTrue(dogContent.contains("import public \"models/animal.proto\";"),
|
||||
"Dog should import Animal (discriminator parent)");
|
||||
|
||||
output.deleteOnExit();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test(description = "Validate that enums in arrays are treated as complex types")
|
||||
public void enumInArrayIsTreatedAsComplexType() {
|
||||
@@ -406,4 +474,591 @@ public class ProtobufSchemaCodegenTest {
|
||||
final CodegenProperty property = cm.vars.get(0);
|
||||
Assert.assertEquals(property.baseName, "colorMap");
|
||||
}
|
||||
|
||||
@Test(description = "Validate that a model referenced multiple times is imported only once in generated protobuf files")
|
||||
public void testModelImportedOnlyOnce() throws IOException {
|
||||
// set line break to \n across all platforms
|
||||
System.setProperty("line.separator", "\n");
|
||||
|
||||
File output = Files.createTempDirectory("test").toFile();
|
||||
|
||||
final CodegenConfigurator configurator = new CodegenConfigurator()
|
||||
.setGeneratorName("protobuf-schema")
|
||||
.setInputSpec("src/test/resources/3_0/protobuf-schema/model_imported_once.yaml")
|
||||
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
|
||||
|
||||
final ClientOptInput clientOptInput = configurator.toClientOptInput();
|
||||
DefaultGenerator generator = new DefaultGenerator();
|
||||
List<File> files = generator.opts(clientOptInput).generate();
|
||||
|
||||
// Check that the main model file was generated
|
||||
TestUtils.ensureContainsFile(files, output, "models/model.proto");
|
||||
Path modelPath = Paths.get(output + "/models/model.proto");
|
||||
String modelContent = new String(Files.readAllBytes(modelPath), StandardCharsets.UTF_8);
|
||||
|
||||
// Count occurrences of the import statement for model A (using "public" keyword as generated)
|
||||
String importStatement = "import public \"models/a.proto\";";
|
||||
int importCount = countOccurrences(modelContent, importStatement);
|
||||
|
||||
// Assert that model A is imported exactly once despite being used in:
|
||||
// - direct field in Model (directA)
|
||||
// - field in SubModel which is nested in Model (SubModel.aReference)
|
||||
Assert.assertEquals(importCount, 1, "Model A should be imported exactly once in model.proto");
|
||||
|
||||
// Check the SubModel file - it also references A directly
|
||||
TestUtils.ensureContainsFile(files, output, "models/sub_model.proto");
|
||||
Path subModelPath = Paths.get(output + "/models/sub_model.proto");
|
||||
String subModelContent = new String(Files.readAllBytes(subModelPath), StandardCharsets.UTF_8);
|
||||
int subModelImportCount = countOccurrences(subModelContent, importStatement);
|
||||
Assert.assertEquals(subModelImportCount, 1, "Model A should be imported exactly once in sub_model.proto");
|
||||
|
||||
// Check the ExtensibleModel file
|
||||
TestUtils.ensureContainsFile(files, output, "models/extensible_model.proto");
|
||||
Path extensiblePath = Paths.get(output + "/models/extensible_model.proto");
|
||||
String extensibleContent = new String(Files.readAllBytes(extensiblePath), StandardCharsets.UTF_8);
|
||||
|
||||
// Count occurrences in ExtensibleModel
|
||||
int extensibleImportCount = countOccurrences(extensibleContent, importStatement);
|
||||
|
||||
// Assert that model A is imported exactly once in ExtensibleModel despite being used in:
|
||||
// - direct field in ExtensibleModel (extensibleA)
|
||||
// - field in ChildModel_1 (childA) which extends ExtensibleModel via discriminator
|
||||
// - field in ChildModel_2 (directA) which also extends ExtensibleModel via discriminator
|
||||
Assert.assertEquals(extensibleImportCount, 1, "Model A should be imported exactly once in extensible_model.proto");
|
||||
|
||||
output.deleteOnExit();
|
||||
}
|
||||
|
||||
private int countOccurrences(String content, String substring) {
|
||||
int count = 0;
|
||||
int index = 0;
|
||||
while ((index = content.indexOf(substring, index)) != -1) {
|
||||
count++;
|
||||
index += substring.length();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Test(description = "Validate that enums are extracted to separate files when extractEnumsToSeparateFiles option is enabled")
|
||||
public void testExtractEnumsToSeparateFiles() throws IOException {
|
||||
// set line break to \n across all platforms
|
||||
System.setProperty("line.separator", "\n");
|
||||
|
||||
File output = Files.createTempDirectory("test").toFile();
|
||||
|
||||
final CodegenConfigurator configurator = new CodegenConfigurator()
|
||||
.setGeneratorName("protobuf-schema")
|
||||
.setInputSpec("src/test/resources/3_0/protobuf-schema/extracted_enum.yaml")
|
||||
.setOutputDir(output.getAbsolutePath().replace("\\", "/"))
|
||||
.addAdditionalProperty(EXTRACT_ENUMS_TO_SEPARATE_FILES, true);
|
||||
|
||||
final ClientOptInput clientOptInput = configurator.toClientOptInput();
|
||||
DefaultGenerator generator = new DefaultGenerator();
|
||||
List<File> files = generator.opts(clientOptInput).generate();
|
||||
|
||||
// Check that separate enum files were generated
|
||||
// With the new naming scheme, inline enum names include the parent model prefix
|
||||
TestUtils.ensureContainsFile(files, output, "models/separated_enum.proto");
|
||||
TestUtils.ensureContainsFile(files, output, "models/model_with_enums_inline_enum_property.proto");
|
||||
TestUtils.ensureContainsFile(files, output, "models/all_of_model_with_enums_another_inline_enum_property.proto");
|
||||
|
||||
// Check that the model file was generated
|
||||
TestUtils.ensureContainsFile(files, output, "models/model_with_enums.proto");
|
||||
Path modelPath = Paths.get(output + "/models/model_with_enums.proto");
|
||||
String modelContent = new String(Files.readAllBytes(modelPath), StandardCharsets.UTF_8);
|
||||
|
||||
// Verify that enums are NOT defined inline in the model
|
||||
Assert.assertFalse(modelContent.contains("enum Inline_enum_property"),
|
||||
"Inline enum should be extracted to separate file");
|
||||
|
||||
// Verify that the model imports the separated enum files
|
||||
Assert.assertTrue(modelContent.contains("import public \"models/separated_enum.proto\";"),
|
||||
"Model should import the separated enum file");
|
||||
Assert.assertTrue(modelContent.contains("import public \"models/model_with_enums_inline_enum_property.proto\";"),
|
||||
"Model should import the inline enum file with parent model prefix");
|
||||
|
||||
// Verify that the model uses the correct enum reference with .Enum suffix
|
||||
// With the new naming scheme, inline enums use ParentModelName_FieldName format
|
||||
Assert.assertTrue(modelContent.contains("ModelWithEnums_InlineEnumProperty.Enum"),
|
||||
"Model should reference extracted enum with .Enum suffix and parent model prefix");
|
||||
|
||||
// Check the AllOfModel file
|
||||
TestUtils.ensureContainsFile(files, output, "models/all_of_model_with_enums.proto");
|
||||
Path allOfModelPath = Paths.get(output + "/models/all_of_model_with_enums.proto");
|
||||
String allOfModelContent = new String(Files.readAllBytes(allOfModelPath), StandardCharsets.UTF_8);
|
||||
|
||||
// Verify that the allOf model imports the separated enum files
|
||||
Assert.assertTrue(allOfModelContent.contains("import public \"models/all_of_model_with_enums_another_inline_enum_property.proto\";"),
|
||||
"AllOf model should import its inline enum file with parent model prefix");
|
||||
|
||||
// Verify the separated enum file content
|
||||
Path separatedEnumPath = Paths.get(output + "/models/separated_enum.proto");
|
||||
String separatedEnumContent = new String(Files.readAllBytes(separatedEnumPath), StandardCharsets.UTF_8);
|
||||
Assert.assertTrue(separatedEnumContent.contains("package openapitools;"),
|
||||
"Separated enum file should contain a valid package declaration");
|
||||
Assert.assertTrue(separatedEnumContent.contains("message SeparatedEnum"),
|
||||
"Separated enum file should contain the message wrapper");
|
||||
Assert.assertTrue(separatedEnumContent.contains("enum Enum {"),
|
||||
"Separated enum file should contain inner Enum definition");
|
||||
Assert.assertTrue(separatedEnumContent.contains("SEPARATED_ENUM_VALUE1"),
|
||||
"Separated enum should contain SEPARATED_ENUM_VALUE1");
|
||||
Assert.assertTrue(separatedEnumContent.contains("SEPARATED_ENUM_VALUE2"),
|
||||
"Separated enum should contain SEPARATED_ENUM_VALUE2");
|
||||
|
||||
// Verify the inline enum file content - uses parent model name prefix
|
||||
Path inlineEnumPath = Paths.get(output + "/models/model_with_enums_inline_enum_property.proto");
|
||||
String inlineEnumContent = new String(Files.readAllBytes(inlineEnumPath), StandardCharsets.UTF_8);
|
||||
Assert.assertTrue(inlineEnumContent.contains("package openapitools;"),
|
||||
"Inline enum file should contain a valid package declaration");
|
||||
Assert.assertTrue(inlineEnumContent.contains("message ModelWithEnums_InlineEnumProperty"),
|
||||
"Inline enum file should contain the message wrapper with parent model prefix");
|
||||
Assert.assertTrue(inlineEnumContent.contains("enum Enum {"),
|
||||
"Inline enum file should contain inner Enum definition");
|
||||
// Note: Enum values keep the prefixes from the original field name, not parent+field
|
||||
// since they are already scoped within the message wrapper
|
||||
Assert.assertTrue(inlineEnumContent.contains("INLINE_ENUM_PROPERTY_VALUE2"),
|
||||
"Inline enum should contain enum value");
|
||||
Assert.assertTrue(inlineEnumContent.contains("INLINE_ENUM_PROPERTY_VALUE3"),
|
||||
"Inline enum should contain enum value");
|
||||
Assert.assertTrue(inlineEnumContent.contains("INLINE_ENUM_PROPERTY_VALUE4"),
|
||||
"Inline enum should contain enum value");
|
||||
|
||||
output.deleteOnExit();
|
||||
}
|
||||
|
||||
@Test(description = "Validate that enums are extracted to separate files when extractEnumsToSeparateFiles option is enabled")
|
||||
public void testExtractEnumsToSeparateFilesWithOtherEnumOptions() throws IOException {
|
||||
// set line break to \n across all platforms
|
||||
System.setProperty("line.separator", "\n");
|
||||
|
||||
File output = Files.createTempDirectory("test").toFile();
|
||||
|
||||
final CodegenConfigurator configurator = new CodegenConfigurator()
|
||||
.setGeneratorName("protobuf-schema")
|
||||
.setInputSpec("src/test/resources/3_0/protobuf-schema/extracted_enum.yaml")
|
||||
.setOutputDir(output.getAbsolutePath().replace("\\", "/"))
|
||||
.addAdditionalProperty("removeEnumValuePrefix", false)
|
||||
.addAdditionalProperty(START_ENUMS_WITH_UNSPECIFIED, true)
|
||||
.addAdditionalProperty(USE_SIMPLIFIED_ENUM_NAMES, true)
|
||||
.addAdditionalProperty(EXTRACT_ENUMS_TO_SEPARATE_FILES, true);
|
||||
|
||||
|
||||
final ClientOptInput clientOptInput = configurator.toClientOptInput();
|
||||
DefaultGenerator generator = new DefaultGenerator();
|
||||
List<File> files = generator.opts(clientOptInput).generate();
|
||||
|
||||
// Check that separate enum files were generated with parent model prefix
|
||||
TestUtils.ensureContainsFile(files, output, "models/separated_enum.proto");
|
||||
TestUtils.ensureContainsFile(files, output, "models/model_with_enums_inline_enum_property.proto");
|
||||
TestUtils.ensureContainsFile(files, output, "models/all_of_model_with_enums_another_inline_enum_property.proto");
|
||||
|
||||
// Check that the model file was generated
|
||||
TestUtils.ensureContainsFile(files, output, "models/model_with_enums.proto");
|
||||
Path modelPath = Paths.get(output + "/models/model_with_enums.proto");
|
||||
String modelContent = new String(Files.readAllBytes(modelPath), StandardCharsets.UTF_8);
|
||||
|
||||
// Verify that enums are NOT defined inline in the model
|
||||
Assert.assertFalse(modelContent.contains("enum Inline_enum_property"),
|
||||
"Inline enum should be extracted to separate file");
|
||||
|
||||
// Verify that the model imports the separated enum files
|
||||
Assert.assertTrue(modelContent.contains("import public \"models/separated_enum.proto\";"),
|
||||
"Model should import the separated enum file");
|
||||
Assert.assertTrue(modelContent.contains("import public \"models/model_with_enums_inline_enum_property.proto\";"),
|
||||
"Model should import the inline enum file with parent model prefix");
|
||||
|
||||
// Verify that the model uses the correct enum reference with .Enum suffix
|
||||
// With the new naming scheme, inline enums use ParentModelName_FieldName format
|
||||
Assert.assertTrue(modelContent.contains("ModelWithEnums_InlineEnumProperty.Enum"),
|
||||
"Model should reference extracted enum with .Enum suffix and parent model prefix");
|
||||
|
||||
// Check the AllOfModel file
|
||||
TestUtils.ensureContainsFile(files, output, "models/all_of_model_with_enums.proto");
|
||||
Path allOfModelPath = Paths.get(output + "/models/all_of_model_with_enums.proto");
|
||||
String allOfModelContent = new String(Files.readAllBytes(allOfModelPath), StandardCharsets.UTF_8);
|
||||
|
||||
// Verify that the allOf model imports the separated enum files
|
||||
Assert.assertTrue(allOfModelContent.contains("import public \"models/all_of_model_with_enums_another_inline_enum_property.proto\";"),
|
||||
"AllOf model should import its inline enum file with parent model prefix");
|
||||
|
||||
// Verify the separated enum file content
|
||||
Path separatedEnumPath = Paths.get(output + "/models/separated_enum.proto");
|
||||
String separatedEnumContent = new String(Files.readAllBytes(separatedEnumPath), StandardCharsets.UTF_8);
|
||||
Assert.assertTrue(separatedEnumContent.contains("package openapitools;"),
|
||||
"Separated enum file should contain a valid package declaration");
|
||||
Assert.assertTrue(separatedEnumContent.contains("message SeparatedEnum"),
|
||||
"Separated enum file should contain the message wrapper");
|
||||
Assert.assertTrue(separatedEnumContent.contains("enum Enum {"),
|
||||
"Separated enum file should contain inner Enum definition");
|
||||
Assert.assertTrue(separatedEnumContent.contains("UNSPECIFIED"),
|
||||
"Separated enum should contain UNSPECIFIED");
|
||||
Assert.assertTrue(separatedEnumContent.contains("VALUE1"),
|
||||
"Separated enum should contain VALUE1");
|
||||
Assert.assertTrue(separatedEnumContent.contains("VALUE2"),
|
||||
"Separated enum should contain VALUE2");
|
||||
|
||||
// Verify the inline enum file content - uses parent model name prefix
|
||||
Path inlineEnumPath = Paths.get(output + "/models/model_with_enums_inline_enum_property.proto");
|
||||
String inlineEnumContent = new String(Files.readAllBytes(inlineEnumPath), StandardCharsets.UTF_8);
|
||||
Assert.assertTrue(inlineEnumContent.contains("package openapitools;"),
|
||||
"Inline enum file should contain a valid package declaration");
|
||||
Assert.assertTrue(inlineEnumContent.contains("message ModelWithEnums_InlineEnumProperty"),
|
||||
"Inline enum file should contain the message wrapper with parent model prefix");
|
||||
Assert.assertTrue(inlineEnumContent.contains("enum Enum {"),
|
||||
"Inline enum file should contain inner Enum definition");
|
||||
Assert.assertTrue(inlineEnumContent.contains("UNSPECIFIED"),
|
||||
"Inline enum should contain UNSPECIFIED");
|
||||
// With simplified names, enum values don't have prefixes, just the base values
|
||||
Assert.assertTrue(inlineEnumContent.contains("VALUE2"),
|
||||
"Inline enum should contain enum value");
|
||||
Assert.assertTrue(inlineEnumContent.contains("VALUE3"),
|
||||
"Inline enum should contain enum value");
|
||||
Assert.assertTrue(inlineEnumContent.contains("VALUE4"),
|
||||
"Inline enum should contain enum value");
|
||||
|
||||
//output.deleteOnExit();
|
||||
}
|
||||
|
||||
@Test(description = "Test toModelImport with various input formats")
|
||||
public void testToModelImportVariations() {
|
||||
final ProtobufSchemaCodegen codegen = new ProtobufSchemaCodegen();
|
||||
codegen.setModelPackage("models");
|
||||
|
||||
// Normal case
|
||||
Assert.assertEquals(codegen.toModelImport("Pet"), "models/pet");
|
||||
|
||||
// Already prefixed - should not duplicate
|
||||
Assert.assertEquals(codegen.toModelImport("models/pet"), "models/pet");
|
||||
|
||||
// With different casing
|
||||
Assert.assertEquals(codegen.toModelImport("PetStore"), "models/pet_store");
|
||||
|
||||
// With numbers
|
||||
Assert.assertEquals(codegen.toModelImport("Pet123"), "models/pet123");
|
||||
|
||||
// Empty model package
|
||||
codegen.setModelPackage("");
|
||||
Assert.assertEquals(codegen.toModelImport("Pet"), "Pet");
|
||||
}
|
||||
|
||||
@Test(description = "Validate that enum imports are added to discriminator parent when extractEnumsToSeparateFiles is enabled")
|
||||
public void testDiscriminatorWithExtractedEnums() throws IOException {
|
||||
// set line break to \n across all platforms
|
||||
String originalLineSeparator = System.getProperty("line.separator");
|
||||
try {
|
||||
System.setProperty("line.separator", "\n");
|
||||
|
||||
File output = Files.createTempDirectory("test").toFile();
|
||||
|
||||
final CodegenConfigurator configurator = new CodegenConfigurator()
|
||||
.setGeneratorName("protobuf-schema")
|
||||
.setInputSpec("src/test/resources/3_0/protobuf-schema/extracted_enum.yaml")
|
||||
.setOutputDir(output.getAbsolutePath().replace("\\", "/"))
|
||||
.addAdditionalProperty(EXTRACT_ENUMS_TO_SEPARATE_FILES, true);
|
||||
|
||||
final ClientOptInput clientOptInput = configurator.toClientOptInput();
|
||||
DefaultGenerator generator = new DefaultGenerator();
|
||||
List<File> files = generator.opts(clientOptInput).generate();
|
||||
|
||||
// Check that the discriminator parent file was generated
|
||||
TestUtils.ensureContainsFile(files, output, "models/discriminated_model.proto");
|
||||
Path discriminatorPath = Paths.get(output + "/models/discriminated_model.proto");
|
||||
String discriminatorContent = new String(Files.readAllBytes(discriminatorPath), StandardCharsets.UTF_8);
|
||||
|
||||
// The discriminator parent should have properties from both children
|
||||
// From ModelTypeAWithInlineEnum: specificPropertyA, inlineEnumPropertyA
|
||||
Assert.assertTrue(discriminatorContent.contains("string specific_property_a"),
|
||||
"Discriminator parent should contain 'specific_property_a' from child A");
|
||||
|
||||
// The enum property should be present with the correct .Enum suffix
|
||||
// With the new naming scheme, inline enums use ParentModelName_FieldName format
|
||||
Assert.assertTrue(discriminatorContent.contains("ModelTypeAWithInlineEnum_InlineEnumProperty.Enum inline_enum_property"),
|
||||
"Discriminator parent should contain extracted enum property from child A with .Enum suffix and parent model prefix");
|
||||
|
||||
// CRITICAL: The discriminator parent should import the extracted enum file
|
||||
Assert.assertTrue(discriminatorContent.contains("import public \"models/model_type_a_with_inline_enum_inline_enum_property.proto\";"),
|
||||
"Discriminator parent MUST import the extracted enum from child A with parent model prefix");
|
||||
|
||||
// From ModelTypeBWithInlineEnum: specificPropertyB, referenceEnumPropertyB
|
||||
Assert.assertTrue(discriminatorContent.contains("string specific_property_b"),
|
||||
"Discriminator parent should contain 'specific_property_b' from child B");
|
||||
|
||||
// Child B references SeparatedEnum (not inline), so it should be imported
|
||||
Assert.assertTrue(discriminatorContent.contains("import public \"models/separated_enum.proto\";"),
|
||||
"Discriminator parent should import the separated enum referenced by child B");
|
||||
|
||||
// Verify the extracted enum file for inline enum from child A was created
|
||||
// The file name uses snake_case of the full wrapper message name
|
||||
TestUtils.ensureContainsFile(files, output, "models/model_type_a_with_inline_enum_inline_enum_property.proto");
|
||||
Path enumPath = Paths.get(output + "/models/model_type_a_with_inline_enum_inline_enum_property.proto");
|
||||
String enumContent = new String(Files.readAllBytes(enumPath), StandardCharsets.UTF_8);
|
||||
|
||||
Assert.assertTrue(enumContent.contains("message ModelTypeAWithInlineEnum_InlineEnumProperty"),
|
||||
"Extracted enum file should contain the message wrapper with parent model prefix");
|
||||
Assert.assertTrue(enumContent.contains("enum Enum {"),
|
||||
"Extracted enum file should contain inner Enum definition");
|
||||
// Note: Enum values keep the prefixes from the original field name, not parent+field
|
||||
// since they are already scoped within the message wrapper
|
||||
Assert.assertTrue(enumContent.contains("INLINE_ENUM_PROPERTY_VALUE7"),
|
||||
"Extracted enum should contain enum value");
|
||||
Assert.assertTrue(enumContent.contains("INLINE_ENUM_PROPERTY_VALUE8"),
|
||||
"Extracted enum should contain enum value");
|
||||
|
||||
output.deleteOnExit();
|
||||
} finally {
|
||||
// Restore original property to avoid side effects on other tests
|
||||
System.setProperty("line.separator", originalLineSeparator);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test(description = "Validate that referenced enum models are correctly wrapped with .Enum suffix when extractEnumsToSeparateFiles is enabled")
|
||||
public void testReferencedEnumsWithExtraction() throws IOException {
|
||||
// set line break to \n across all platforms
|
||||
String originalLineSeparator = System.getProperty("line.separator");
|
||||
try {
|
||||
System.setProperty("line.separator", "\n");
|
||||
|
||||
File output = Files.createTempDirectory("test").toFile();
|
||||
|
||||
final CodegenConfigurator configurator = new CodegenConfigurator()
|
||||
.setGeneratorName("protobuf-schema")
|
||||
.setInputSpec("src/test/resources/3_0/protobuf-schema/extracted_enum.yaml")
|
||||
.setOutputDir(output.getAbsolutePath().replace("\\", "/"))
|
||||
.addAdditionalProperty(EXTRACT_ENUMS_TO_SEPARATE_FILES, true);
|
||||
|
||||
final ClientOptInput clientOptInput = configurator.toClientOptInput();
|
||||
DefaultGenerator generator = new DefaultGenerator();
|
||||
List<File> files = generator.opts(clientOptInput).generate();
|
||||
|
||||
// Check that the model file was generated
|
||||
TestUtils.ensureContainsFile(files, output, "models/model_with_enums.proto");
|
||||
Path modelPath = Paths.get(output + "/models/model_with_enums.proto");
|
||||
String modelContent = new String(Files.readAllBytes(modelPath), StandardCharsets.UTF_8)
|
||||
.replace("\r\n", "\n").replace("\r", "\n");
|
||||
|
||||
// CRITICAL: The model should have the inline enum property with .Enum suffix
|
||||
// With the new naming scheme, inline enums use ParentModelName_FieldName format
|
||||
Assert.assertTrue(modelContent.contains("ModelWithEnums_InlineEnumProperty.Enum inline_enum_property"),
|
||||
"Model should reference inline extracted enum with .Enum suffix and parent model prefix");
|
||||
|
||||
// CRITICAL: The model should have the referenced enum property with .Enum suffix
|
||||
// This is the bug being fixed - referenced enums should also get wrapped
|
||||
Assert.assertTrue(modelContent.contains("SeparatedEnum.Enum reference_enum_property"),
|
||||
"Model should reference referenced enum with .Enum suffix (THIS IS THE FIX)");
|
||||
|
||||
// Verify that both enum files are imported
|
||||
Assert.assertTrue(modelContent.contains("import public \"models/separated_enum.proto\";"),
|
||||
"Model should import the separated enum file");
|
||||
Assert.assertTrue(modelContent.contains("import public \"models/model_with_enums_inline_enum_property.proto\";"),
|
||||
"Model should import the inline enum file with parent model prefix");
|
||||
|
||||
// Verify the separated enum file was correctly generated
|
||||
TestUtils.ensureContainsFile(files, output, "models/separated_enum.proto");
|
||||
Path separatedEnumPath = Paths.get(output + "/models/separated_enum.proto");
|
||||
String separatedEnumContent = new String(Files.readAllBytes(separatedEnumPath), StandardCharsets.UTF_8);
|
||||
|
||||
Assert.assertTrue(separatedEnumContent.contains("message SeparatedEnum"),
|
||||
"Separated enum file should contain the message wrapper");
|
||||
Assert.assertTrue(separatedEnumContent.contains("enum Enum {"),
|
||||
"Separated enum file should contain inner Enum definition");
|
||||
|
||||
// Verify the inline enum file was correctly generated with parent model prefix
|
||||
TestUtils.ensureContainsFile(files, output, "models/model_with_enums_inline_enum_property.proto");
|
||||
Path inlineEnumPath = Paths.get(output + "/models/model_with_enums_inline_enum_property.proto");
|
||||
String inlineEnumContent = new String(Files.readAllBytes(inlineEnumPath), StandardCharsets.UTF_8);
|
||||
|
||||
Assert.assertTrue(inlineEnumContent.contains("message ModelWithEnums_InlineEnumProperty"),
|
||||
"Inline enum file should contain the message wrapper with parent model prefix");
|
||||
Assert.assertTrue(inlineEnumContent.contains("enum Enum {"),
|
||||
"Inline enum file should contain inner Enum definition");
|
||||
|
||||
output.deleteOnExit();
|
||||
} finally {
|
||||
// Restore original property to avoid side effects on other tests
|
||||
System.setProperty("line.separator", originalLineSeparator);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test(description = "Validate backward compatibility: extracted_enum.yaml generates inline enums when EXTRACT_ENUMS_TO_SEPARATE_FILES is NOT enabled")
|
||||
public void testEnumsRemainsInlineWithoutExtraction() throws IOException {
|
||||
// set line break to \n across all platforms
|
||||
String originalLineSeparator = System.getProperty("line.separator");
|
||||
try {
|
||||
System.setProperty("line.separator", "\n");
|
||||
|
||||
File output = Files.createTempDirectory("test").toFile();
|
||||
|
||||
final CodegenConfigurator configurator = new CodegenConfigurator()
|
||||
.setGeneratorName("protobuf-schema")
|
||||
.setInputSpec("src/test/resources/3_0/protobuf-schema/extracted_enum.yaml")
|
||||
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
|
||||
|
||||
final ClientOptInput clientOptInput = configurator.toClientOptInput();
|
||||
DefaultGenerator generator = new DefaultGenerator();
|
||||
List<File> files = generator.opts(clientOptInput).generate();
|
||||
|
||||
// Check that the model file was generated
|
||||
TestUtils.ensureContainsFile(files, output, "models/model_with_enums.proto");
|
||||
Path modelPath = Paths.get(output + "/models/model_with_enums.proto");
|
||||
String modelContent = new String(Files.readAllBytes(modelPath), StandardCharsets.UTF_8)
|
||||
.replace("\r\n", "\n").replace("\r", "\n");
|
||||
|
||||
// WITHOUT extraction: Enums should be defined INLINE in the model file
|
||||
// Check for inline enum definitions (not extracted to separate files)
|
||||
// The inline enum should contain the enum values (not a message wrapper)
|
||||
Assert.assertTrue(modelContent.contains("enum") && modelContent.contains("VALUE2"),
|
||||
"Without extraction, inline enums should be defined inline in the model");
|
||||
|
||||
// Verify that inline enums use simple type references (NOT wrapped with .Enum)
|
||||
// When enums are inline, they're referenced as the enum name directly
|
||||
// (not as ParentModel_FieldName.Enum which is only used for extracted enums)
|
||||
Assert.assertTrue(!modelContent.contains(".Enum"),
|
||||
"Without extraction, enum properties should NOT reference .Enum suffix");
|
||||
|
||||
// Without extraction: Inline enum files should NOT be generated
|
||||
// (Note: SeparatedEnum.proto WILL be generated because SeparatedEnum is a top-level schema,
|
||||
// but inline_enum_property.proto should NOT be generated because it's an inline enum)
|
||||
List<Path> inlineEnumFiles = new java.util.ArrayList<>();
|
||||
if (Files.exists(Paths.get(output + "/models/inline_enum_property.proto"))) {
|
||||
inlineEnumFiles.add(Paths.get(output + "/models/inline_enum_property.proto"));
|
||||
}
|
||||
if (Files.exists(Paths.get(output + "/models/another_inline_enum_property.proto"))) {
|
||||
inlineEnumFiles.add(Paths.get(output + "/models/another_inline_enum_property.proto"));
|
||||
}
|
||||
Assert.assertTrue(inlineEnumFiles.isEmpty(),
|
||||
"Without extraction option enabled, inline enum files should NOT be created as separate files");
|
||||
|
||||
// Verify that imports for inline enum files are NOT present
|
||||
Assert.assertFalse(modelContent.contains("import public \"models/inline_enum_property.proto\""),
|
||||
"Without extraction, there should be no inline enum file imports");
|
||||
Assert.assertFalse(modelContent.contains("import public \"models/another_inline_enum_property.proto\""),
|
||||
"Without extraction, there should be no inline enum file imports");
|
||||
|
||||
// Check the AllOfModel file
|
||||
TestUtils.ensureContainsFile(files, output, "models/all_of_model_with_enums.proto");
|
||||
Path allOfModelPath = Paths.get(output + "/models/all_of_model_with_enums.proto");
|
||||
String allOfModelContent = new String(Files.readAllBytes(allOfModelPath), StandardCharsets.UTF_8);
|
||||
|
||||
// IMPORTANT: AllOf composition in protobuf has different semantics than OpenAPI allOf.
|
||||
// In protobuf, allOf models only contain direct properties of the model, not inherited/composed properties.
|
||||
// This differs from OpenAPI where allOf models would include properties from all composed schemas.
|
||||
// Therefore, we validate backward compatibility by ensuring:
|
||||
// 1. The model file is generated (proving structure is correct)
|
||||
// 2. No .Enum suffix is used (proving extraction mode is OFF - not using extracted enum references)
|
||||
// 3. Inline enums are defined inline (proving backward compatibility works with inline enums)
|
||||
Assert.assertTrue(!allOfModelContent.isEmpty(),
|
||||
"Without extraction option, the allOf model file should be generated");
|
||||
Assert.assertFalse(allOfModelContent.contains(".Enum"),
|
||||
"Without extraction option, enums should NOT use .Enum suffix (should be inline)");
|
||||
Assert.assertTrue(allOfModelContent.contains("enum") && allOfModelContent.contains("VALUE"),
|
||||
"Without extraction option, model should contain inline enum definitions");
|
||||
|
||||
output.deleteOnExit();
|
||||
} finally {
|
||||
// Restore original property to avoid side effects on other tests
|
||||
System.setProperty("line.separator", originalLineSeparator);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(description = "Validate that enums in arrays are correctly handled with .Enum suffix when extractEnumsToSeparateFiles is enabled")
|
||||
public void testEnumsInArraysWithExtraction() throws IOException {
|
||||
// set line break to \n across all platforms
|
||||
System.setProperty("line.separator", "\n");
|
||||
|
||||
File output = Files.createTempDirectory("test").toFile();
|
||||
System.out.println("EnumsInArrays Temporary output directory: " + output.getAbsolutePath());
|
||||
|
||||
final CodegenConfigurator configurator = new CodegenConfigurator()
|
||||
.setGeneratorName("protobuf-schema")
|
||||
.setInputSpec("src/test/resources/3_0/protobuf-schema/extracted_enum.yaml")
|
||||
.setOutputDir(output.getAbsolutePath().replace("\\", "/"))
|
||||
.addAdditionalProperty("removeEnumValuePrefix", false)
|
||||
.addAdditionalProperty(START_ENUMS_WITH_UNSPECIFIED, true)
|
||||
.addAdditionalProperty(USE_SIMPLIFIED_ENUM_NAMES, true)
|
||||
.addAdditionalProperty(EXTRACT_ENUMS_TO_SEPARATE_FILES, true);
|
||||
|
||||
final ClientOptInput clientOptInput = configurator.toClientOptInput();
|
||||
DefaultGenerator generator = new DefaultGenerator();
|
||||
List<File> files = generator.opts(clientOptInput).generate();
|
||||
|
||||
// Check that the model file was generated
|
||||
TestUtils.ensureContainsFile(files, output, "models/all_of_model_with_enums.proto");
|
||||
Path modelPath = Paths.get(output + "/models/all_of_model_with_enums.proto");
|
||||
String modelContent = new String(Files.readAllBytes(modelPath), StandardCharsets.UTF_8)
|
||||
.replace("\r\n", "\n").replace("\r", "\n");
|
||||
|
||||
// Test Case 1: Array with REFERENCED enum (listOfReferencedEnums)
|
||||
// Should have .Enum suffix: repeated SeparatedEnum.Enum
|
||||
// Use regex to validate structure without depending on specific field numbers
|
||||
java.util.regex.Pattern referencedEnumPattern = java.util.regex.Pattern.compile(
|
||||
"repeated\\s+SeparatedEnum\\.Enum\\s+list_of_referenced_enums\\s*=\\s*\\d+");
|
||||
Assert.assertTrue(
|
||||
referencedEnumPattern.matcher(modelContent).find(),
|
||||
"Array with referenced enum should use .Enum suffix with pattern: repeated SeparatedEnum.Enum list_of_referenced_enums = <number>");
|
||||
|
||||
// Test Case 2: Array with INLINE enum (listOfEnums) - in AllOfModelWithEnums
|
||||
// Should have .Enum suffix: repeated AllOfModelWithEnums_ListOfEnums.Enum
|
||||
// Use regex to validate structure without depending on specific field numbers
|
||||
java.util.regex.Pattern inlineEnumPattern = java.util.regex.Pattern.compile(
|
||||
"repeated\\s+AllOfModelWithEnums_ListOfEnums\\.Enum\\s+list_of_enums\\s*=\\s*\\d+");
|
||||
Assert.assertTrue(
|
||||
inlineEnumPattern.matcher(modelContent).find(),
|
||||
"Array with inline enum should use .Enum suffix with pattern: repeated AllOfModelWithEnums_ListOfEnums.Enum list_of_enums = <number>");
|
||||
|
||||
// Verify the imported enum file for referenced enum exists
|
||||
TestUtils.ensureContainsFile(files, output, "models/separated_enum.proto");
|
||||
|
||||
// Verify the extracted inline enum file for arrays exists
|
||||
TestUtils.ensureContainsFile(files, output, "models/all_of_model_with_enums_list_of_enums.proto");
|
||||
Path listOfEnumsPath = Paths.get(output + "/models/all_of_model_with_enums_list_of_enums.proto");
|
||||
String listOfEnumsContent = new String(Files.readAllBytes(listOfEnumsPath), StandardCharsets.UTF_8)
|
||||
.replace("\r\n", "\n").replace("\r", "\n");
|
||||
|
||||
// Verify the wrapper message structure
|
||||
Assert.assertTrue(listOfEnumsContent.contains("message AllOfModelWithEnums_ListOfEnums"),
|
||||
"Extracted inline enum file should contain wrapper message");
|
||||
Assert.assertTrue(listOfEnumsContent.contains("enum Enum"),
|
||||
"Wrapper message should contain inner Enum");
|
||||
Assert.assertTrue(listOfEnumsContent.contains("VALUE10") && listOfEnumsContent.contains("VALUE11"),
|
||||
"Wrapper message should contain enum values");
|
||||
|
||||
output.deleteOnExit();
|
||||
}
|
||||
|
||||
@Test(description = "Validate that enums in arrays remain inline when extractEnumsToSeparateFiles is NOT enabled")
|
||||
public void testEnumsInArraysWithoutExtraction() throws IOException {
|
||||
// set line break to \n across all platforms
|
||||
System.setProperty("line.separator", "\n");
|
||||
|
||||
File output = Files.createTempDirectory("test").toFile();
|
||||
|
||||
final CodegenConfigurator configurator = new CodegenConfigurator()
|
||||
.setGeneratorName("protobuf-schema")
|
||||
.setInputSpec("src/test/resources/3_0/protobuf-schema/extracted_enum.yaml")
|
||||
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
|
||||
// Note: EXTRACT_ENUMS_TO_SEPARATE_FILES is NOT enabled
|
||||
|
||||
final ClientOptInput clientOptInput = configurator.toClientOptInput();
|
||||
DefaultGenerator generator = new DefaultGenerator();
|
||||
List<File> files = generator.opts(clientOptInput).generate();
|
||||
|
||||
// Check that the model file was generated
|
||||
TestUtils.ensureContainsFile(files, output, "models/model_with_enums.proto");
|
||||
Path modelPath = Paths.get(output + "/models/model_with_enums.proto");
|
||||
String modelContent = new String(Files.readAllBytes(modelPath), StandardCharsets.UTF_8)
|
||||
.replace("\r\n", "\n").replace("\r", "\n");
|
||||
|
||||
// Without extraction: Arrays of enums should NOT use .Enum suffix
|
||||
// Instead, they should use the type directly or inline definition
|
||||
Assert.assertFalse(modelContent.contains(".Enum"),
|
||||
"Without extraction, enums should NOT use .Enum suffix");
|
||||
|
||||
// Extracted inline enum files should NOT exist
|
||||
Assert.assertFalse(Files.exists(Paths.get(output + "/models/model_with_enums_list_of_enums.proto")),
|
||||
"Without extraction, inline enum array files should NOT be generated");
|
||||
|
||||
output.deleteOnExit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.openapitools.codegen.rust;
|
||||
|
||||
import org.openapitools.codegen.DefaultGenerator;
|
||||
import org.openapitools.codegen.TestUtils;
|
||||
import org.openapitools.codegen.config.CodegenConfigurator;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tests for RustServerCodegen.
|
||||
*/
|
||||
public class RustServerCodegenTest {
|
||||
|
||||
/**
|
||||
* Test that integer parameters with minimum/maximum constraints are assigned appropriate Rust types.
|
||||
* This tests that integer parameter type fitting logic is applied to CodegenParameter objects.
|
||||
*/
|
||||
@Test
|
||||
public void testIntegerParameterTypeFitting() throws IOException {
|
||||
Path target = Files.createTempDirectory("test");
|
||||
final CodegenConfigurator configurator = new CodegenConfigurator()
|
||||
.setGeneratorName("rust-server")
|
||||
.setInputSpec("src/test/resources/3_0/rust-server/integer-params.yaml")
|
||||
.setSkipOverwrite(false)
|
||||
.setOutputDir(target.toAbsolutePath().toString().replace("\\", "/"));
|
||||
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
|
||||
files.forEach(File::deleteOnExit);
|
||||
|
||||
Path libPath = Path.of(target.toString(), "/src/lib.rs");
|
||||
TestUtils.assertFileExists(libPath);
|
||||
|
||||
// Verify that parameters with known min/max ranges get appropriate types
|
||||
// age: 0-150 should fit in u8
|
||||
TestUtils.assertFileContains(libPath, "age: u8");
|
||||
|
||||
// temperature: -50 to 50 should fit in i8
|
||||
TestUtils.assertFileContains(libPath, "temperature: i8");
|
||||
|
||||
// count: 0-65535 should fit in u16
|
||||
TestUtils.assertFileContains(libPath, "count: u16");
|
||||
|
||||
// offset: -32768 to 32767 should fit in i16
|
||||
TestUtils.assertFileContains(libPath, "offset: i16");
|
||||
|
||||
// large_unsigned: 0-4294967295 should be u32
|
||||
TestUtils.assertFileContains(libPath, "large_unsigned: u32");
|
||||
|
||||
// Verify integer with int32 format and minimum >= 0 becomes u32
|
||||
TestUtils.assertFileContains(libPath, "positive_int32: u32");
|
||||
|
||||
// Verify integer with int64 format and minimum >= 0 becomes u64
|
||||
TestUtils.assertFileContains(libPath, "positive_int64: u64");
|
||||
|
||||
// Clean up
|
||||
target.toFile().deleteOnExit();
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,8 @@ components:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
characteristics:
|
||||
$ref: '#/components/schemas/Characteristics'
|
||||
Reptile:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Pet'
|
||||
@@ -78,7 +80,7 @@ components:
|
||||
- $ref: '#/components/schemas/Lizard'
|
||||
discriminator:
|
||||
propertyName: petType
|
||||
# per https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#discriminatorObject
|
||||
# per https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#discriminator-object
|
||||
# this discriminator must be included to use it as a hint to pick a schema
|
||||
MyPetsNoDisc:
|
||||
oneOf:
|
||||
@@ -101,3 +103,8 @@ components:
|
||||
C:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/B'
|
||||
Characteristics:
|
||||
type: object
|
||||
properties:
|
||||
canHunt:
|
||||
type: boolean
|
||||
@@ -0,0 +1,58 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: OAI Specification example for Polymorphism
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/status:
|
||||
get:
|
||||
responses:
|
||||
'201':
|
||||
description: desc
|
||||
|
||||
components:
|
||||
schemas:
|
||||
Animal:
|
||||
type: object
|
||||
required:
|
||||
- petType
|
||||
properties:
|
||||
petType:
|
||||
type: string
|
||||
discriminator:
|
||||
propertyName: petType
|
||||
mapping:
|
||||
dog: '#/components/schemas/Dog'
|
||||
cat: '#/components/schemas/Cat'
|
||||
Feline:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Animal'
|
||||
- type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
furColor:
|
||||
type: string
|
||||
Cat:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Feline'
|
||||
- type: object
|
||||
properties:
|
||||
isIndoor:
|
||||
type: boolean
|
||||
careDetails:
|
||||
$ref: '#/components/schemas/CareDetails'
|
||||
Dog:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Animal'
|
||||
- type: object
|
||||
properties:
|
||||
bark:
|
||||
type: string
|
||||
CareDetails:
|
||||
type: object
|
||||
properties:
|
||||
veterinarian:
|
||||
type: string
|
||||
lastVetVisit:
|
||||
type: string
|
||||
format: date
|
||||
@@ -0,0 +1,52 @@
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: Test Escaping in Examples
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/test:
|
||||
get:
|
||||
operationId: testEscaping
|
||||
tags:
|
||||
- test
|
||||
parameters:
|
||||
- name: stringWithQuotes
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
example: 'John "Johnny" Doe'
|
||||
- name: stringWithBackslash
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
example: 'C:\path\to\file'
|
||||
- name: emailWithQuotes
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: email
|
||||
example: 'test"user@example.com'
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TestResponse'
|
||||
components:
|
||||
schemas:
|
||||
TestResponse:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
example: 'Name with "quotes" inside'
|
||||
path:
|
||||
type: string
|
||||
example: 'C:\Windows\System32'
|
||||
email:
|
||||
type: string
|
||||
format: email
|
||||
example: 'user"name@example.com'
|
||||
@@ -41,8 +41,19 @@ components:
|
||||
format: uuid
|
||||
optional_property:
|
||||
type: number
|
||||
stringSet:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
uniqueItems: true
|
||||
stringArray:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
- string_set
|
||||
- string_array
|
||||
discriminator:
|
||||
propertyName: discriminator
|
||||
mapping:
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
OAI Specification example for Polymorphism
|
||||
|
||||
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||
|
||||
The version of the OpenAPI document: 1.0.0
|
||||
|
||||
Generated by OpenAPI Generator: https://openapi-generator.tech
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package openapitools;
|
||||
|
||||
import public "models/care_details.proto";
|
||||
|
||||
message Animal {
|
||||
|
||||
string pet_type = 482112090;
|
||||
|
||||
string name = 3373707;
|
||||
|
||||
string fur_color = 478695002;
|
||||
|
||||
bool is_indoor = 183319801;
|
||||
|
||||
CareDetails care_details = 176721135;
|
||||
|
||||
string bark = 3016376;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: Enum extraction test
|
||||
version: '2.0'
|
||||
paths: {}
|
||||
components:
|
||||
schemas:
|
||||
SeparatedEnum:
|
||||
type: string
|
||||
enum:
|
||||
- VALUE1
|
||||
- VALUE2
|
||||
ModelWithEnums:
|
||||
type: object
|
||||
properties:
|
||||
referenceEnumProperty:
|
||||
$ref: "#/components/schemas/SeparatedEnum"
|
||||
inlineEnumProperty:
|
||||
type: string
|
||||
enum:
|
||||
- VALUE2
|
||||
- VALUE3
|
||||
- VALUE4
|
||||
listOfEnums:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
enum:
|
||||
- VALUE10
|
||||
- VALUE11
|
||||
AllOfModelWithEnums:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/ModelWithEnums"
|
||||
- type: object
|
||||
properties:
|
||||
anotherInlineEnumProperty:
|
||||
type: string
|
||||
enum:
|
||||
- VALUE5
|
||||
- VALUE6
|
||||
listOfReferencedEnums:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/SeparatedEnum'
|
||||
DiscriminatedModel:
|
||||
type: object
|
||||
properties:
|
||||
commonProperty:
|
||||
type: string
|
||||
modelType:
|
||||
type: string
|
||||
discriminator:
|
||||
propertyName: modelType
|
||||
mapping:
|
||||
typeA: "#/components/schemas/ModelTypeAWithInlineEnum"
|
||||
typeB: "#/components/schemas/ModelTypeBWithInlineEnum"
|
||||
ModelTypeAWithInlineEnum:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/DiscriminatedModel"
|
||||
- type: object
|
||||
properties:
|
||||
specificPropertyA:
|
||||
type: string
|
||||
inlineEnumProperty:
|
||||
type: string
|
||||
enum:
|
||||
- VALUE7
|
||||
- VALUE8
|
||||
ModelTypeBWithInlineEnum:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/DiscriminatedModel"
|
||||
- type: object
|
||||
properties:
|
||||
specificPropertyB:
|
||||
type: string
|
||||
referenceEnumPropertyB:
|
||||
$ref: "#/components/schemas/SeparatedEnum"
|
||||
@@ -0,0 +1,89 @@
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: Model Import Test
|
||||
version: 1.0.0
|
||||
paths: {}
|
||||
components:
|
||||
schemas:
|
||||
A:
|
||||
type: object
|
||||
description: Base model A that will be referenced multiple times
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
value:
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
|
||||
SubModel:
|
||||
type: object
|
||||
description: SubModel that uses model A
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/ExtensibleModel'
|
||||
- type: object
|
||||
properties:
|
||||
subField:
|
||||
type: string
|
||||
aReference:
|
||||
$ref: '#/components/schemas/A'
|
||||
required:
|
||||
- subField
|
||||
|
||||
Model:
|
||||
type: object
|
||||
description: Main model that uses A directly and via SubModel
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
directA:
|
||||
$ref: '#/components/schemas/A'
|
||||
nestedSubModel:
|
||||
$ref: '#/components/schemas/SubModel'
|
||||
required:
|
||||
- name
|
||||
|
||||
ExtensibleModel:
|
||||
type: object
|
||||
description: Extensible model with discriminator that uses model A
|
||||
discriminator:
|
||||
propertyName: type
|
||||
mapping:
|
||||
child1: '#/components/schemas/ChildModel_1'
|
||||
child2: '#/components/schemas/ChildModel_2'
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
extensibleA:
|
||||
$ref: '#/components/schemas/A'
|
||||
required:
|
||||
- type
|
||||
|
||||
ChildModel_1:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/ExtensibleModel'
|
||||
- type: object
|
||||
properties:
|
||||
childSpecificField:
|
||||
type: string
|
||||
childA:
|
||||
$ref: '#/components/schemas/A'
|
||||
ChildModel_2:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/SubExtensibleModel'
|
||||
- type: object
|
||||
properties:
|
||||
anotherChildSpecificField:
|
||||
type: integer
|
||||
directA:
|
||||
$ref: '#/components/schemas/A'
|
||||
|
||||
SubExtensibleModel:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/ExtensibleModel'
|
||||
- type: object
|
||||
properties:
|
||||
aSubExtensibleField:
|
||||
type: boolean
|
||||
subDirectA:
|
||||
$ref: '#/components/schemas/A'
|
||||
@@ -12,14 +12,20 @@ syntax = "proto3";
|
||||
|
||||
package openapitools;
|
||||
|
||||
import public "models/characteristics.proto";
|
||||
|
||||
message Pet {
|
||||
|
||||
string pet_type = 482112090;
|
||||
|
||||
string name = 3373707;
|
||||
|
||||
Characteristics characteristics = 455429578;
|
||||
|
||||
string bark = 3016376;
|
||||
|
||||
bool loves_rocks = 465093427;
|
||||
|
||||
bool has_legs = 140596906;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
|
||||
# Test that integer parameters are generated into the right
|
||||
# primitives in Rust code.
|
||||
openapi: 3.1.1
|
||||
info:
|
||||
title: Integer Parameter Type Fitting Test
|
||||
description: Test spec to verify that integer parameters with minimum/maximum constraints get appropriate Rust types
|
||||
version: 1.0.0
|
||||
servers:
|
||||
- url: http://localhost:8080
|
||||
paths:
|
||||
/test/integers:
|
||||
get:
|
||||
operationId: testIntegerParameters
|
||||
summary: Test integer parameter type fitting
|
||||
parameters:
|
||||
# age: 0-150 should fit in u8 (unsigned, 8-bit)
|
||||
- name: age
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 150
|
||||
# temperature: -50 to 50 should fit in i8 (signed, 8-bit)
|
||||
- name: temperature
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
minimum: -50
|
||||
maximum: 50
|
||||
# count: 0-65535 should fit in u16 (unsigned, 16-bit)
|
||||
- name: count
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 65535
|
||||
# offset: -32768 to 32767 should fit in i16 (signed, 16-bit)
|
||||
- name: offset
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
minimum: -32768
|
||||
maximum: 32767
|
||||
# large_unsigned: 0-4294967295 should be u32
|
||||
- name: large_unsigned
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 4294967295
|
||||
# positive_int32: format int32 with min >= 0 should become u32
|
||||
- name: positive_int32
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
minimum: 0
|
||||
# positive_int64: format int64 with min >= 0 should become u64
|
||||
- name: positive_int64
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
minimum: 0
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
@@ -0,0 +1,24 @@
|
||||
openapi: 3.0.4
|
||||
info:
|
||||
title: "Different response schemas including an empty one"
|
||||
version: "1.0.0"
|
||||
paths:
|
||||
/example:
|
||||
get:
|
||||
operationId: exampleGet
|
||||
responses:
|
||||
200:
|
||||
description: "A successful response with data"
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: integer
|
||||
400:
|
||||
description: "A bad request with a message"
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: string
|
||||
500:
|
||||
description: "An internal server error with no content"
|
||||
content: { }
|
||||
@@ -96,6 +96,7 @@ public class Bird {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
||||
@@ -96,6 +96,7 @@ public class Category {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
||||
@@ -132,6 +132,7 @@ public class DataQuery extends Query {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DataQuery id(@javax.annotation.Nullable Long id) {
|
||||
this.setId(id);
|
||||
|
||||
@@ -409,6 +409,7 @@ public class DefaultValue {
|
||||
this.stringNullable = JsonNullable.<String>of(stringNullable);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
||||
@@ -129,6 +129,7 @@ public class NumberPropertiesOnly {
|
||||
this._double = _double;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
||||
@@ -274,6 +274,7 @@ public class Pet {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
||||
@@ -144,6 +144,7 @@ public class Query {
|
||||
this.outcomes = outcomes;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
||||
@@ -96,6 +96,7 @@ public class Tag {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
||||
@@ -67,6 +67,7 @@ public class TestFormObjectMultipartRequestMarker {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
||||
@@ -157,6 +157,7 @@ public class TestQueryStyleDeepObjectExplodeTrueObjectAllOfQueryObjectParameter
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user