Compare commits

..

18 Commits

Author SHA1 Message Date
William Cheng
3d25263e32 use localhost 2025-09-27 16:33:24 +08:00
William Cheng
9a2d997398 skip host table update 2025-09-27 16:19:59 +08:00
William Cheng
d920f72020 test with ruby 3.4 2025-09-27 16:17:40 +08:00
William Cheng
152c87ee18 Revert "test with mac"
This reverts commit 3acc76a48b.
2025-09-27 15:53:48 +08:00
William Cheng
3acc76a48b test with mac 2025-09-27 15:32:08 +08:00
William Cheng
efd2fe60ad Merge remote-tracking branch 'origin/master' into ruby-github-test 2025-09-24 16:24:15 +08:00
William Cheng
43c78856f5 add new files 2025-09-21 17:51:01 +08:00
William Cheng
564f4e023d cat hosts 2025-09-21 17:34:07 +08:00
William Cheng
117773f14c update samples 2025-09-21 17:29:03 +08:00
William Cheng
260787b0a1 update 2025-09-21 17:07:40 +08:00
William Cheng
ef0aef28d7 use 127.0.0.1 2025-09-21 17:02:14 +08:00
William Cheng
324d773123 update tests 2025-09-21 16:56:52 +08:00
William Cheng
2760903274 update config to use ruby petstore fake spec 2025-09-21 16:48:43 +08:00
William Cheng
9d29eb98bc localhost 2025-09-21 16:44:59 +08:00
William Cheng
c2a8e11b45 add petstore, update host table 2025-09-21 16:39:50 +08:00
William Cheng
2b3947ea1a fix workflow 2025-09-21 16:38:06 +08:00
William Cheng
33fdad9554 add echo client tests 2025-09-21 16:35:55 +08:00
William Cheng
dceb03a8f0 migrate ruby tests to github workflow 2025-09-21 16:33:18 +08:00
8340 changed files with 8919 additions and 61453 deletions

View File

@@ -6,13 +6,13 @@ on:
- 'samples/server/petstore/go-echo-server/**'
- 'samples/server/petstore/go-api-server/**'
- 'samples/server/petstore/go-chi-server/**'
- 'samples/server/others/go-server/**'
- 'samples/server/others/go-server/no-body-path-params/**'
pull_request:
paths:
- 'samples/server/petstore/go-echo-server/**'
- 'samples/server/petstore/go-api-server/**'
- 'samples/server/petstore/go-chi-server/**'
- 'samples/server/others/go-server/**'
- 'samples/server/others/go-server/no-body-path-params/**'
jobs:
build:
@@ -26,7 +26,6 @@ jobs:
- samples/server/petstore/go-api-server/
- samples/server/petstore/go-chi-server/
- samples/server/others/go-server/no-body-path-params/
- samples/server/others/go-server/optional-body/
steps:
- uses: actions/checkout@v5
- uses: actions/setup-go@v6
@@ -59,4 +58,4 @@ jobs:
go mod tidy
- name: Run tests
working-directory: ${{ matrix.sample }}
run: go test ./samples_tests -v
run: go test ./samples_tests -v

View File

@@ -5,7 +5,6 @@ on:
paths:
- 'samples/client/petstore/java/**'
- samples/client/petstore/jaxrs-cxf-client/**
- samples/client/petstore/jaxrs-cxf-client-swagger2/**
- samples/client/petstore/java-micronaut-client/**
- samples/openapi3/client/petstore/java/jersey2-java8-special-characters/**
- samples/openapi3/client/petstore/java/jersey2-java8-swagger1/**
@@ -28,7 +27,6 @@ on:
paths:
- 'samples/client/petstore/java/**'
- samples/client/petstore/jaxrs-cxf-client/**
- samples/client/petstore/jaxrs-cxf-client-swagger2/**
- samples/client/petstore/java-micronaut-client/**
- samples/openapi3/client/petstore/java/jersey2-java8-special-characters/**
- samples/openapi3/client/petstore/java/jersey2-java8-swagger1/**
@@ -56,7 +54,6 @@ jobs:
matrix:
sample:
# clients
- samples/client/petstore/jaxrs-cxf-client-swagger2
- samples/client/petstore/jaxrs-cxf-client
- samples/client/petstore/java/native
- samples/client/petstore/java/native-async

View File

@@ -34,7 +34,6 @@ jobs:
# server
- samples/server/petstore/kotlin-server-required-and-nullable-properties
- samples/server/petstore/kotlin-springboot-3
- samples/server/petstore/kotlin-springboot-additionalproperties
- samples/server/petstore/kotlin-springboot-delegate-nodefaults
- samples/server/petstore/kotlin-springboot-request-cookie
- samples/server/petstore/kotlin-server/jaxrs-spec
@@ -68,4 +67,4 @@ jobs:
arguments: wrapper
- name: Build
working-directory: ${{ matrix.sample }}
run: ./gradlew build -x test
run: ./gradlew build -x test

View File

@@ -76,4 +76,4 @@ jobs:
arguments: wrapper
- name: Build
working-directory: ${{ matrix.sample }}
run: ./gradlew build -x test
run: ./gradlew build -x test

View File

@@ -34,7 +34,6 @@ jobs:
- "3.13"
sample:
- samples/openapi3/client/petstore/python-aiohttp
- samples/openapi3/client/petstore/python-httpx
- samples/openapi3/client/petstore/python
- samples/openapi3/client/petstore/python-lazyImports
services:

View File

@@ -0,0 +1,43 @@
name: Samples Ruby
on:
push:
paths:
- 'samples/client/echo_api/ruby-httpx/**'
- 'samples/client/echo_api/ruby-faraday/**'
- 'samples/client/echo_api/ruby-typhoeus/**'
pull_request:
paths:
- 'samples/client/echo_api/ruby-httpx/**'
- 'samples/client/echo_api/ruby-faraday/**'
- 'samples/client/echo_api/ruby-typhoeus/**'
jobs:
build:
name: Build Ruby
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
sample:
- 'samples/client/echo_api/ruby-httpx/'
- 'samples/client/echo_api/ruby-faraday/'
- 'samples/client/echo_api/ruby-typhoeus/'
steps:
- uses: actions/checkout@v5
- name: Setup node.js
uses: actions/setup-node@v5
- name: Run echo server
run: |
git clone https://github.com/wing328/http-echo-server -b openapi-generator-test-server
(cd http-echo-server && npm install && npm start &)
- uses: actions/setup-ruby@v1
with:
ruby-version: 3.2
bundler-cache: true
- name: Install bundle
working-directory: ${{ matrix.sample }}
run: bundle install
- name: Run rspec
working-directory: ${{ matrix.sample }}
run: rspec

View File

@@ -1,16 +1,18 @@
name: Samples Ruby
name: Samples Ruby
on:
push:
paths:
- 'samples/client/echo_api/ruby-httpx/**'
- 'samples/client/echo_api/ruby-faraday/**'
- 'samples/client/echo_api/ruby-typhoeus/**'
- samples/client/petstore/ruby/**
- samples/client/petstore/ruby-faraday/**
- samples/client/petstore/ruby-httpx/**
- samples/client/petstore/ruby-autoload/**
pull_request:
paths:
- 'samples/client/echo_api/ruby-httpx/**'
- 'samples/client/echo_api/ruby-faraday/**'
- 'samples/client/echo_api/ruby-typhoeus/**'
- samples/client/petstore/ruby/**
- samples/client/petstore/ruby-faraday/**
- samples/client/petstore/ruby-httpx/**
- samples/client/petstore/ruby-autoload/**
jobs:
build:
@@ -20,21 +22,28 @@ jobs:
fail-fast: false
matrix:
sample:
- 'samples/client/echo_api/ruby-httpx/'
- 'samples/client/echo_api/ruby-faraday/'
- 'samples/client/echo_api/ruby-typhoeus/'
- samples/client/petstore/ruby/
#- samples/client/petstore/ruby-faraday/
#- samples/client/petstore/ruby-httpx/
#- samples/client/petstore/ruby-autoload/
services:
petstore-api:
image: swaggerapi/petstore
ports:
- 80:8080
env:
SWAGGER_HOST: http://petstore.swagger.io
SWAGGER_BASE_PATH: /v2
steps:
- uses: actions/checkout@v5
- name: Setup node.js
uses: actions/setup-node@v5
- name: Run echo server
run: |
git clone https://github.com/wing328/http-echo-server -b openapi-generator-test-server
(cd http-echo-server && npm install && npm start &)
- uses: actions/setup-ruby@v1
#- name: Add hosts to /etc/hosts
# run: |
# sudo echo "127.0.0.1 petstore.swagger.io" | sudo tee -a /etc/hosts
# cat /etc/hosts
- uses: ruby/setup-ruby@v1
with:
ruby-version: 3.0
bundler-cache: true
ruby-version: '3.4' # Not needed with a .ruby-version, .tool-versions or mise.toml
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- name: Install bundle
working-directory: ${{ matrix.sample }}
run: bundle install

View File

@@ -15,7 +15,7 @@
<div align="center">
[Master](https://github.com/OpenAPITools/openapi-generator/tree/master) (`7.17.0`):
[Master](https://github.com/OpenAPITools/openapi-generator/tree/master) (`7.16.0`):
[![Integration Test2](https://circleci.com/gh/OpenAPITools/openapi-generator.svg?style=shield)](https://circleci.com/gh/OpenAPITools/openapi-generator)
[![Bitrise](https://img.shields.io/bitrise/4a2b10a819d12b67/master?label=bitrise%3A%20Swift+4,5&token=859FMDR8QHwabCzwvZK6vQ)](https://app.bitrise.io/app/4a2b10a819d12b67)
@@ -148,8 +148,8 @@ The OpenAPI Specification has undergone 3 revisions since initial creation in 20
| OpenAPI Generator Version | Release Date | Notes |
| --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------------------------------------------------- |
| 7.17.0 (upcoming minor release) [SNAPSHOT](https://github.com/OpenAPITools/openapi-generator/wiki/FAQ#how-to-test-with-the-latest-master-of-openapi-generator) | 24.10.2025 | Minor release with breaking changes (with fallback) |
| [7.16.0](https://github.com/OpenAPITools/openapi-generator/releases/tag/v7.16.0) (latest stable release) | 28.09.2025 | Minor release with breaking changes (with fallback) |
| 7.16.0 (upcoming minor release) [SNAPSHOT](https://oss.sonatype.org/content/repositories/snapshots/org/openapitools/openapi-generator-cli/7.16.0-SNAPSHOT/) | 22.09.2025 | Minor release with breaking changes (with fallback) |
| [7.15.0](https://github.com/OpenAPITools/openapi-generator/releases/tag/v7.15.0) (latest stable release) | 22.08.2025 | Minor release with breaking changes (with fallback) |
| [6.6.0](https://github.com/OpenAPITools/openapi-generator/releases/tag/v6.6.0) | 11.05.2023 | Minor release with breaking changes (with fallback) |
| [5.4.0](https://github.com/OpenAPITools/openapi-generator/releases/tag/v5.4.0) | 31.01.2022 | Minor release with breaking changes (with fallback) |
| [4.3.1](https://github.com/OpenAPITools/openapi-generator/releases/tag/v4.3.1) | 06.05.2020 | Patch release (enhancements, bug fixes, etc) |
@@ -212,16 +212,16 @@ See the different versions of the [openapi-generator-cli](https://search.maven.o
<!-- RELEASE_VERSION -->
If you're looking for the latest stable version, you can grab it directly from Maven.org (Java 11 runtime at a minimum):
JAR location: `https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.16.0/openapi-generator-cli-7.16.0.jar`
JAR location: `https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.15.0/openapi-generator-cli-7.15.0.jar`
For **Mac/Linux** users:
```sh
wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.16.0/openapi-generator-cli-7.16.0.jar -O openapi-generator-cli.jar
wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.15.0/openapi-generator-cli-7.15.0.jar -O openapi-generator-cli.jar
```
For **Windows** users, you will need to install [wget](http://gnuwin32.sourceforge.net/packages/wget.htm) or you can use Invoke-WebRequest in PowerShell (3.0+), e.g.
```
Invoke-WebRequest -OutFile openapi-generator-cli.jar https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.16.0/openapi-generator-cli-7.16.0.jar
Invoke-WebRequest -OutFile openapi-generator-cli.jar https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.15.0/openapi-generator-cli-7.15.0.jar
```
After downloading the JAR, run `java -jar openapi-generator-cli.jar help` to show the usage.
@@ -456,7 +456,7 @@ openapi-generator-cli version
To use a specific version of "openapi-generator-cli"
```sh
openapi-generator-cli version-manager set 7.16.0
openapi-generator-cli version-manager set 7.15.0
```
Or install it as dev-dependency:
@@ -480,7 +480,7 @@ pip install openapi-generator-cli
To install a specific version
```
pip install openapi-generator-cli==7.16.0
pip install openapi-generator-cli==7.15.0
```
You can also install with [jdk4py](https://github.com/activeviam/jdk4py) instead of java binary. (python>=3.10 is required)
@@ -506,7 +506,7 @@ java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generat
(if you're on Windows, replace the last command with `java -jar modules\openapi-generator-cli\target\openapi-generator-cli.jar generate -i https://raw.githubusercontent.com/openapitools/openapi-generator/master/modules/openapi-generator/src/test/resources/3_0/petstore.yaml -g php -o c:\temp\php_api_client`)
<!-- RELEASE_VERSION -->
You can also download the JAR (latest release) directly from [maven.org](https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.16.0/openapi-generator-cli-7.16.0.jar)
You can also download the JAR (latest release) directly from [maven.org](https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.15.0/openapi-generator-cli-7.15.0.jar)
<!-- /RELEASE_VERSION -->
To get a list of **general** options available, please run `java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar help generate`

View File

@@ -1,8 +0,0 @@
generatorName: go-server
outputDir: samples/server/others/go-server/optional-body
inputSpec: modules/openapi-generator/src/test/resources/3_0/optional_body.yaml
templateDir: modules/openapi-generator/src/main/resources/go-server
additionalProperties:
hideGenerationTimestamp: "true"
packageName: petstoreserver
addResponseHeaders: true

View File

@@ -1,8 +0,0 @@
generatorName: jaxrs-cxf-client
outputDir: samples/client/petstore/jaxrs-cxf-client-swagger2
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/JavaJaxRS/cxf
additionalProperties:
artifactId: jaxrs-cxf-petstore-swagger2
documentationProvider: swagger2
annotationLibrary: swagger2

View File

@@ -1,14 +0,0 @@
generatorName: kotlin-spring
outputDir: samples/server/petstore/kotlin-springboot-additionalproperties
library: spring-boot
inputSpec: samples/server/petstore/kotlin-springboot-additionalproperties/src/main/resources/openapi.yaml
templateDir: modules/openapi-generator/src/main/resources/kotlin-spring
additionalProperties:
documentationProvider: none
annotationLibrary: none
useSwaggerUI: "false"
serviceImplementation: "true"
serializableModel: "true"
beanValidations: "true"
useSpringBoot3: "true"
requestMappingMode: api_interface

View File

@@ -7,7 +7,6 @@ additionalProperties:
packageName: petstore_api
mapNumberTo: float
poetry1: true
enumUnknownDefaultCase: true
nameMappings:
_type: underscore_type
type_: type_with_underscore

View File

@@ -1,15 +0,0 @@
generatorName: python
outputDir: samples/openapi3/client/petstore/python-httpx
inputSpec: modules/openapi-generator/src/test/resources/3_0/python/petstore-with-fake-endpoints-models-for-testing.yaml
templateDir: modules/openapi-generator/src/main/resources/python
library: httpx
additionalProperties:
packageName: petstore_api
mapNumberTo: float
poetry1: true
nameMappings:
_type: underscore_type
type_: type_with_underscore
modelNameMappings:
# The OpenAPI spec ApiResponse conflicts with the internal ApiResponse
ApiResponse: ModelApiResponse

View File

@@ -61,4 +61,4 @@
- filename: "samples/server/petstore/rust-axum/output/rust-axum-oneof/tests/oneof_with_discriminator.rs"
sha256: 2d4f5a069fdcb3057bb078d5e75b3de63cd477b97725e457079df24bd2c30600
- filename: "samples/server/petstore/rust-axum/output/openapi-v3/tests/oneof_untagged.rs"
sha256: 1d3fb01f65e98290b1d3eece28014c7d3e3f2fdf18e7110249d3c591cc4642ab
sha256: e72fbf81a9849dc7abb7e2169f2fc355c8b1cf991c0e2ffc083126abd9e966e7

167
docs/generators/postman.md Normal file
View File

@@ -0,0 +1,167 @@
---
title: Documentation for the postman Generator
---
## METADATA
| Property | Value | Notes |
| -------- | ----- | ----- |
| generator name | postman | pass this to the generate command after -g |
| generator stability | BETA | |
| generator type | DOCUMENTATION | |
| generator default templating engine | mustache | |
| helpTxt | Generates a postman JSON file | |
## CONFIG OPTIONS
These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details.
| Option | Description | Values | Default |
| ------ | ----------- | ------ | ------- |
|folderStrategy|whether to create folders according to the spec&rsquo;s paths or tags| |null|
|pathParamsAsVariables|whether to create Postman variables for path parameters| |false|
|postmanGuid|whether to convert placeholders (i.e. {{UNIQUE_REFERENCE}}) into Postman formula {{$guid}}| |null|
|postmanGuidPlaceholderName|name of the placeholder (i.e. {{UNIQUE_REFERENCE}}) to replace with Postman formula {{$guid}}| |null|
|postmanIsoTimestamp|whether to convert placeholders (i.e. {{ISO_TIMESTAMP}}) into Postman formula {{$isoTimestamp}}| |null|
|postmanIsoTimestampPlaceholderName|name of the placeholder (i.e. {{ISO_TIMESTAMP}}) to replace with Postman formula {{$isoTimestamp}}| |null|
|postmanVariables|whether to convert placeholders (i.e. {{VAR_1}}) into Postman variables| |null|
|requestParameterGeneration|whether to generate the request parameters based on the schema or the examples| |null|
## IMPORT MAPPING
| Type/Alias | Imports |
| ---------- | ------- |
## INSTANTIATION TYPES
| Type/Alias | Instantiated By |
| ---------- | --------------- |
## LANGUAGE PRIMITIVES
<ul class="column-ul">
</ul>
## RESERVED WORDS
<ul class="column-ul">
</ul>
## FEATURE SET
### Client Modification Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|BasePath|✗|ToolingExtension
|Authorizations|✗|ToolingExtension
|UserAgent|✗|ToolingExtension
|MockServer|✗|ToolingExtension
### Data Type Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Custom|✗|OAS2,OAS3
|Int32|✓|OAS2,OAS3
|Int64|✓|OAS2,OAS3
|Float|✓|OAS2,OAS3
|Double|✓|OAS2,OAS3
|Decimal|✓|ToolingExtension
|String|✓|OAS2,OAS3
|Byte|✓|OAS2,OAS3
|Binary|✓|OAS2,OAS3
|Boolean|✓|OAS2,OAS3
|Date|✓|OAS2,OAS3
|DateTime|✓|OAS2,OAS3
|Password|✓|OAS2,OAS3
|File|✓|OAS2
|Uuid|✗|
|Array|✓|OAS2,OAS3
|Null|✗|OAS3
|AnyType|✗|OAS2,OAS3
|Object|✓|OAS2,OAS3
|Maps|✓|ToolingExtension
|CollectionFormat|✓|OAS2
|CollectionFormatMulti|✓|OAS2
|Enum|✓|OAS2,OAS3
|ArrayOfEnum|✓|ToolingExtension
|ArrayOfModel|✓|ToolingExtension
|ArrayOfCollectionOfPrimitives|✓|ToolingExtension
|ArrayOfCollectionOfModel|✓|ToolingExtension
|ArrayOfCollectionOfEnum|✓|ToolingExtension
|MapOfEnum|✓|ToolingExtension
|MapOfModel|✓|ToolingExtension
|MapOfCollectionOfPrimitives|✓|ToolingExtension
|MapOfCollectionOfModel|✓|ToolingExtension
|MapOfCollectionOfEnum|✓|ToolingExtension
### Documentation Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Readme|✗|ToolingExtension
|Model|✓|ToolingExtension
|Api|✓|ToolingExtension
### Global Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Host|✓|OAS2,OAS3
|BasePath|✓|OAS2,OAS3
|Info|✓|OAS2,OAS3
|Schemes|✗|OAS2,OAS3
|PartialSchemes|✓|OAS2,OAS3
|Consumes|✓|OAS2
|Produces|✓|OAS2
|ExternalDocumentation|✓|OAS2,OAS3
|Examples|✓|OAS2,OAS3
|XMLStructureDefinitions|✗|OAS2,OAS3
|MultiServer|✗|OAS3
|ParameterizedServer|✗|OAS3
|ParameterStyling|✗|OAS3
|Callbacks|✓|OAS3
|LinkObjects|✗|OAS3
### Parameter Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Path|✓|OAS2,OAS3
|Query|✓|OAS2,OAS3
|Header|✓|OAS2,OAS3
|Body|✓|OAS2
|FormUnencoded|✓|OAS2
|FormMultipart|✓|OAS2
|Cookie|✓|OAS3
### Schema Support Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Simple|✓|OAS2,OAS3
|Composite|✓|OAS2,OAS3
|Polymorphism|✓|OAS2,OAS3
|Union|✗|OAS3
|allOf|✗|OAS2,OAS3
|anyOf|✗|OAS3
|oneOf|✗|OAS3
|not|✗|OAS3
### Security Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|BasicAuth|✓|OAS2,OAS3
|ApiKey|✓|OAS2,OAS3
|OpenIDConnect|✗|OAS3
|BearerToken|✓|OAS3
|OAuth2_Implicit|✓|OAS2,OAS3
|OAuth2_Password|✓|OAS2,OAS3
|OAuth2_ClientCredentials|✓|OAS2,OAS3
|OAuth2_AuthorizationCode|✓|OAS2,OAS3
|SignatureAuth|✗|OAS3
### Wire Format Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|JSON|✓|OAS2,OAS3
|XML|✓|OAS2,OAS3
|PROTOBUF|✗|ToolingExtension
|Custom|✗|OAS2,OAS3

View File

@@ -22,11 +22,10 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|dateFormat|date format for query parameters| |%Y-%m-%d|
|datetimeFormat|datetime format for query parameters| |%Y-%m-%dT%H:%M:%S%z|
|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|
|enumUnknownDefaultCase|If the server adds new enum cases, that are unknown by an old spec/client, the client will fail to parse the network response.With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the server sends an enum case that is not known by the client/spec, they can safely fallback to this case.| |false|
|generateSourceCodeOnly|Specifies that only a library source code is to be generated.| |false|
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true|
|lazyImports|Enable lazy imports.| |false|
|library|library template (sub-template) to use: asyncio, tornado (deprecated), urllib3, httpx| |urllib3|
|library|library template (sub-template) to use: asyncio, tornado (deprecated), urllib3| |urllib3|
|mapNumberTo|Map number to Union[StrictFloat, StrictInt], StrictStr or float.| |Union[StrictFloat, StrictInt]|
|packageName|python package name (convention: snake_case).| |openapi_client|
|packageUrl|python package URL.| |null|

View File

@@ -77,7 +77,6 @@ These options may be applied as additional-properties (cli) or configOptions (pl
<li>dyn</li>
<li>else</li>
<li>enum</li>
<li>errors</li>
<li>extern</li>
<li>false</li>
<li>final</li>
@@ -208,8 +207,8 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|Composite|✓|OAS2,OAS3
|Polymorphism|✗|OAS2,OAS3
|Union|✗|OAS3
|allOf||OAS2,OAS3
|anyOf||OAS3
|allOf||OAS2,OAS3
|anyOf||OAS3
|oneOf|✓|OAS3
|not|✗|OAS3

View File

@@ -22,7 +22,7 @@ npm install @openapitools/openapi-generator-cli -g
To install a specific version of the tool, pass the version during installation:
<!-- RELEASE_VERSION -->
```bash
openapi-generator-cli version-manager set 7.16.0
openapi-generator-cli version-manager set 7.15.0
```
<!-- /RELEASE_VERSION -->
To install the tool as a dev dependency in your current project:
@@ -119,18 +119,18 @@ docker run --rm \
<!-- RELEASE_VERSION -->
If you're looking for the latest stable version, you can grab it directly from Maven.org (Java 11 runtime at a minimum):
JAR location: `https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.16.0/openapi-generator-cli-7.16.0.jar`
JAR location: `https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.15.0/openapi-generator-cli-7.15.0.jar`
For **Mac/Linux** users:
```bash
wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.16.0/openapi-generator-cli-7.16.0.jar -O openapi-generator-cli.jar
wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.15.0/openapi-generator-cli-7.15.0.jar -O openapi-generator-cli.jar
```
For **Windows** users, you will need to install [wget](http://gnuwin32.sourceforge.net/packages/wget.htm) or you can use Invoke-WebRequest in PowerShell (3.0+), e.g.
```powershell
Invoke-WebRequest -OutFile openapi-generator-cli.jar https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.16.0/openapi-generator-cli-7.16.0.jar
Invoke-WebRequest -OutFile openapi-generator-cli.jar https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.15.0/openapi-generator-cli-7.15.0.jar
```
<!-- /RELEASE_VERSION -->

View File

@@ -16,7 +16,7 @@ Add to your `build->plugins` section (default phase is `generate-sources` phase)
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>7.16.0</version>
<version>7.15.0</version>
<executions>
<execution>
<goals>

View File

@@ -4,7 +4,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-project</artifactId>
<!-- RELEASE_VERSION -->
<version>7.17.0-SNAPSHOT</version>
<version>7.16.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@@ -6,7 +6,7 @@
<artifactId>openapi-generator-project</artifactId>
<groupId>org.openapitools</groupId>
<!-- RELEASE_VERSION -->
<version>7.17.0-SNAPSHOT</version>
<version>7.16.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@@ -97,7 +97,7 @@ task validateGoodSpec(type: org.openapitools.generator.gradle.plugin.tasks.Valid
[source,group]
----
plugins {
id "org.openapi.generator" version "7.16.0"
id "org.openapi.generator" version "7.15.0"
}
----
@@ -113,7 +113,7 @@ buildscript {
// url "https://plugins.gradle.org/m2/"
}
dependencies {
classpath "org.openapitools:openapi-generator-gradle-plugin:7.16.0"
classpath "org.openapitools:openapi-generator-gradle-plugin:7.15.0"
}
}
@@ -759,7 +759,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath('org.openapitools:openapi-generator-gradle-plugin:7.16.0') {
classpath('org.openapitools:openapi-generator-gradle-plugin:7.15.0') {
exclude group: 'com.google.guava'
}
}

View File

@@ -1,5 +1,5 @@
# RELEASE_VERSION
openApiGeneratorVersion=7.17.0-SNAPSHOT
openApiGeneratorVersion=7.16.0-SNAPSHOT
# /RELEASE_VERSION
# BEGIN placeholders

View File

@@ -4,7 +4,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-project</artifactId>
<!-- RELEASE_VERSION -->
<version>7.17.0-SNAPSHOT</version>
<version>7.16.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@@ -19,5 +19,5 @@ gradle generateGoWithInvalidSpec # expected outcome: BUILD FAILED
The samples can be tested against other versions of the plugin using the `openApiGeneratorVersion` property. For example:
```bash
gradle -PopenApiGeneratorVersion=7.16.0 openApiValidate
gradle -PopenApiGeneratorVersion=7.15.0 openApiValidate
```

View File

@@ -1,3 +1,3 @@
# RELEASE_VERSION
openApiGeneratorVersion=7.17.0-SNAPSHOT
openApiGeneratorVersion=7.16.0-SNAPSHOT
# /RELEASE_VERSION

View File

@@ -12,7 +12,7 @@ Add to your `build->plugins` section (default phase is `generate-sources` phase)
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<!-- RELEASE_VERSION -->
<version>7.16.0</version>
<version>7.15.0</version>
<!-- /RELEASE_VERSION -->
<executions>
<execution>

View File

@@ -13,7 +13,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<!-- RELEASE_VERSION -->
<version>7.17.0-SNAPSHOT</version>
<version>7.16.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<executions>
<execution>

View File

@@ -15,7 +15,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<!-- RELEASE_VERSION -->
<version>7.17.0-SNAPSHOT</version>
<version>7.16.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<executions>
<execution>

View File

@@ -19,7 +19,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<!-- RELEASE_VERSION -->
<version>7.17.0-SNAPSHOT</version>
<version>7.16.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<dependencies>
<dependency>

View File

@@ -13,7 +13,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<!-- RELEASE_VERSION -->
<version>7.17.0-SNAPSHOT</version>
<version>7.16.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<executions>
<execution>

View File

@@ -13,7 +13,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<!-- RELEASE_VERSION -->
<version>7.17.0-SNAPSHOT</version>
<version>7.16.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<executions>
<execution>

View File

@@ -20,7 +20,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<!-- RELEASE_VERSION -->
<version>7.17.0-SNAPSHOT</version>
<version>7.16.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<executions>
<execution>

View File

@@ -5,7 +5,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-project</artifactId>
<!-- RELEASE_VERSION -->
<version>7.17.0-SNAPSHOT</version>
<version>7.16.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@@ -4,7 +4,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-project</artifactId>
<!-- RELEASE_VERSION -->
<version>7.17.0-SNAPSHOT</version>
<version>7.16.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@@ -4,7 +4,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-project</artifactId>
<!-- RELEASE_VERSION -->
<version>7.17.0-SNAPSHOT</version>
<version>7.16.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@@ -115,15 +115,6 @@ public class CodegenOperation {
return nonEmpty(bodyParams);
}
/**
* Check if there's at least one optional body parameter
*
* @return true if optional body parameter exists, false otherwise
*/
public boolean getHasOptionalBodyParam() {
return nonEmpty(bodyParams) && nonEmpty(optionalParams) && bodyParams.stream().anyMatch(optionalParams::contains);
}
/**
* Check if there's at least one query parameter
*

View File

@@ -410,7 +410,6 @@ public class GoServerCodegen extends AbstractGoCodegen {
private void addConditionalImportInformation(OperationsMap operations) {
boolean hasPathParams = false;
boolean hasBodyParams = false;
boolean hasOptionalBodyParams = false;
for (CodegenOperation op : operations.getOperations().getOperation()) {
if (op.getHasPathParams()) {
@@ -419,14 +418,10 @@ public class GoServerCodegen extends AbstractGoCodegen {
if (op.getHasBodyParam()) {
hasBodyParams = true;
}
if (op.getHasOptionalBodyParam()) {
hasOptionalBodyParams = true;
}
}
additionalProperties.put("hasPathParams", hasPathParams);
additionalProperties.put("hasBodyParams", hasBodyParams);
additionalProperties.put("hasOptionalBodyParams", hasOptionalBodyParams);
}

View File

@@ -996,7 +996,6 @@ public class KotlinClientCodegen extends AbstractKotlinCodegen {
}
return objs;
}
private Stream<List<CodegenProperty>> getAllVarProperties(CodegenModel model) {
return Stream.of(model.vars, model.allVars, model.optionalVars, model.requiredVars, model.readOnlyVars, model.readWriteVars);
}
@@ -1114,27 +1113,6 @@ public class KotlinClientCodegen extends AbstractKotlinCodegen {
return false;
}
@Override
public void postProcessParameter(CodegenParameter parameter) {
super.postProcessParameter(parameter);
adjustEnumRefDefault(parameter);
}
/**
* Properly set the default value for enum (reference).
*
* @param param codegen parameter
*/
private void adjustEnumRefDefault(CodegenParameter param) {
if (StringUtils.isEmpty(param.defaultValue) || !(param.isEnum || param.isEnumRef)) {
return;
}
String type = StringUtils.defaultIfEmpty(param.datatypeWithEnum, param.dataType);
param.enumDefaultValue = toEnumVarName(param.defaultValue, type);
param.defaultValue = type + "." + param.enumDefaultValue;
}
@Override
public void postProcess() {
System.out.println("################################################################################");

View File

@@ -153,13 +153,11 @@ public class PythonClientCodegen extends AbstractPythonCodegen implements Codege
cliOptions.add(new CliOption(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP, CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP_DESC).defaultValue("false"));
cliOptions.add(new CliOption(POETRY1_FALLBACK, "Fallback to formatting pyproject.toml to Poetry 1.x format."));
cliOptions.add(new CliOption(LAZY_IMPORTS, "Enable lazy imports.").defaultValue(Boolean.FALSE.toString()));
cliOptions.add(new CliOption(CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE, CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE_DESC).defaultValue("false"));
supportedLibraries.put("urllib3", "urllib3-based client");
supportedLibraries.put("asyncio", "asyncio-based client");
supportedLibraries.put("tornado", "tornado-based client (deprecated)");
supportedLibraries.put("httpx", "httpx-based client");
CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "library template (sub-template) to use: asyncio, tornado (deprecated), urllib3, httpx");
CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "library template (sub-template) to use: asyncio, tornado (deprecated), urllib3");
libraryOption.setDefault(DEFAULT_LIBRARY);
cliOptions.add(libraryOption);
setLibrary(DEFAULT_LIBRARY);
@@ -272,10 +270,6 @@ public class PythonClientCodegen extends AbstractPythonCodegen implements Codege
additionalProperties.put(LAZY_IMPORTS, Boolean.valueOf(additionalProperties.get(LAZY_IMPORTS).toString()));
}
if (additionalProperties.containsKey(CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE)) {
setEnumUnknownDefaultCase(Boolean.parseBoolean(additionalProperties.get(CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE).toString()));
}
String modelPath = packagePath() + File.separatorChar + modelPackage.replace('.', File.separatorChar);
String apiPath = packagePath() + File.separatorChar + apiPackage.replace('.', File.separatorChar);
@@ -336,15 +330,10 @@ public class PythonClientCodegen extends AbstractPythonCodegen implements Codege
if ("asyncio".equals(getLibrary())) {
supportingFiles.add(new SupportingFile("asyncio/rest.mustache", packagePath(), "rest.py"));
additionalProperties.put("async", "true");
additionalProperties.put("asyncio", "true");
} else if ("tornado".equals(getLibrary())) {
supportingFiles.add(new SupportingFile("tornado/rest.mustache", packagePath(), "rest.py"));
additionalProperties.put("tornado", "true");
} else if ("httpx".equals(getLibrary())) {
supportingFiles.add(new SupportingFile("httpx/rest.mustache", packagePath(), "rest.py"));
additionalProperties.put("async", "true");
additionalProperties.put("httpx", "true");
} else {
supportingFiles.add(new SupportingFile("rest.mustache", packagePath(), "rest.py"));
}

View File

@@ -99,18 +99,6 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
public RustAxumServerCodegen() {
super();
// The `#[validate(nested)]` macro relies on an internal field named `errors` to accumulate validation results. Therefore, defining a struct like this will fail:
//
// ```rust
// struct A {
// #[validate(nested)]
// errors: B,
// }
// ```
//
// To avoid this, either rename the field to something other than "errors", or reserve it.
this.reservedWords.add("errors");
modifyFeatureSet(features -> features
.wireFormatFeatures(EnumSet.of(
WireFormatFeature.JSON,
@@ -124,9 +112,7 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
.schemaSupportFeatures(EnumSet.of(
SchemaSupportFeature.Simple,
SchemaSupportFeature.Composite,
SchemaSupportFeature.oneOf,
SchemaSupportFeature.anyOf,
SchemaSupportFeature.allOf
SchemaSupportFeature.oneOf
))
.excludeGlobalFeatures(
GlobalFeature.Info,
@@ -647,163 +633,105 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
return op;
}
private void postProcessPolymorphism(final List<ModelMap> allModels) {
final HashMap<String, List<String>> discriminatorsForModel = new HashMap<>();
private void postProcessOneOfModels(List<ModelMap> allModels) {
final HashMap<String, List<String>> oneOfMapDiscriminator = new HashMap<>();
for (final ModelMap mo : allModels) {
for (ModelMap mo : allModels) {
final CodegenModel cm = mo.getModel();
final CodegenComposedSchemas cs = cm.getComposedSchemas();
if (cs != null) {
final List<CodegenProperty> csOneOf = cs.getOneOf();
if (csOneOf != null) {
processPolymorphismDataType(csOneOf);
for (CodegenProperty model : csOneOf) {
// Generate a valid name for the enum variant.
// Mainly needed for primitive types.
model.datatypeWithEnum = camelize(model.dataType.replaceAll("(?:\\w+::)+(\\w+)", "$1")
.replace("<", "Of").replace(">", ""));
// Primitive type is not properly set, this overrides it to guarantee adequate model generation.
if (!model.getDataType().matches(String.format(Locale.ROOT, ".*::%s", model.getDatatypeWithEnum()))) {
model.isPrimitiveType = true;
}
}
cs.setOneOf(csOneOf);
cm.setComposedSchemas(cs);
}
final List<CodegenProperty> csAnyOf = cs.getAnyOf();
if (csAnyOf != null) {
processPolymorphismDataType(csAnyOf);
cs.setAnyOf(csAnyOf);
cm.setComposedSchemas(cs);
}
}
if (cm.discriminator != null) {
for (final String model : cm.oneOf) {
final List<String> discriminators = discriminatorsForModel.getOrDefault(model, new ArrayList<>());
for (String model : cm.oneOf) {
List<String> discriminators = oneOfMapDiscriminator.getOrDefault(model, new ArrayList<>());
discriminators.add(cm.discriminator.getPropertyName());
discriminatorsForModel.put(model, discriminators);
}
for (final String model : cm.anyOf) {
final List<String> discriminators = discriminatorsForModel.getOrDefault(model, new ArrayList<>());
discriminators.add(cm.discriminator.getPropertyName());
discriminatorsForModel.put(model, discriminators);
oneOfMapDiscriminator.put(model, discriminators);
}
}
}
final var blocking = new HashSet<String>();
for (ModelMap mo : allModels) {
final CodegenModel cm = mo.getModel();
final List<String> discriminators = discriminatorsForModel.get(cm.getSchemaName());
if (discriminators != null) {
// If the discriminator field is not a defined attribute in the variant structure, create it.
if (!discriminating(discriminators, cm)) {
final String discriminator = discriminators.get(0);
for (CodegenProperty var : cm.vars) {
var.isDiscriminator = false;
}
CodegenProperty property = new CodegenProperty();
final List<String> discriminatorsForModel = oneOfMapDiscriminator.get(cm.getSchemaName());
// Static attributes
// Only strings are supported by serde for tag field types, so it's the only one we'll deal with
property.openApiType = "string";
property.complexType = "string";
property.dataType = "String";
property.datatypeWithEnum = "String";
property.baseType = "string";
property.required = true;
property.isPrimitiveType = true;
property.isString = true;
property.isDiscriminator = true;
if (discriminatorsForModel != null) {
for (String discriminator : discriminatorsForModel) {
boolean hasDiscriminatorDefined = false;
// Attributes based on the discriminator value
property.baseName = discriminator;
property.name = discriminator;
property.nameInCamelCase = camelize(discriminator);
property.nameInPascalCase = property.nameInCamelCase.substring(0, 1).toUpperCase(Locale.ROOT) + property.nameInCamelCase.substring(1);
property.nameInSnakeCase = underscore(discriminator).toUpperCase(Locale.ROOT);
property.getter = String.format(Locale.ROOT, "get%s", property.nameInPascalCase);
property.setter = String.format(Locale.ROOT, "set%s", property.nameInPascalCase);
property.defaultValueWithParam = String.format(Locale.ROOT, " = data.%s;", property.name);
for (CodegenProperty var : cm.vars) {
if (var.baseName.equals(discriminator)) {
var.isDiscriminator = true;
hasDiscriminatorDefined = true;
break;
}
}
// Attributes based on the model name
property.defaultValue = String.format(Locale.ROOT, "r#\"%s\"#.to_string()", cm.getSchemaName());
property.jsonSchema = String.format(Locale.ROOT, "{ \"default\":\"%s\"; \"type\":\"string\" }", cm.getSchemaName());
// If the discriminator field is not a defined attribute in the variant structure, create it.
if (!hasDiscriminatorDefined) {
CodegenProperty property = new CodegenProperty();
cm.vars.add(property);
// Static attributes
// Only strings are supported by serde for tag field types, so it's the only one we'll deal with
property.openApiType = "string";
property.complexType = "string";
property.dataType = "String";
property.datatypeWithEnum = "String";
property.baseType = "string";
property.required = true;
property.isPrimitiveType = true;
property.isString = true;
property.isDiscriminator = true;
// Attributes based on the discriminator value
property.baseName = discriminator;
property.name = discriminator;
property.nameInCamelCase = camelize(discriminator);
property.nameInPascalCase = property.nameInCamelCase.substring(0, 1).toUpperCase(Locale.ROOT) + property.nameInCamelCase.substring(1);
property.nameInSnakeCase = underscore(discriminator).toUpperCase(Locale.ROOT);
property.getter = String.format(Locale.ROOT, "get%s", property.nameInPascalCase);
property.setter = String.format(Locale.ROOT, "set%s", property.nameInPascalCase);
property.defaultValueWithParam = String.format(Locale.ROOT, " = data.%s;", property.name);
// Attributes based on the model name
property.defaultValue = String.format(Locale.ROOT, "r#\"%s\"#.to_string()", cm.getSchemaName());
property.jsonSchema = String.format(Locale.ROOT, "{ \"default\":\"%s\"; \"type\":\"string\" }", cm.getSchemaName());
cm.vars.add(property);
}
}
}
if (cm.vars.stream().noneMatch(v -> v.isDiscriminator)) {
blocking.add(cm.getSchemaName());
}
}
for (final ModelMap mo : allModels) {
final CodegenModel cm = mo.getModel();
if (cm.discriminator != null) {
// if no discriminator in any of variant -> disable discriminator
if (cm.oneOf.stream().anyMatch(blocking::contains) || cm.anyOf.stream().anyMatch(blocking::contains)) {
cm.discriminator = null;
}
}
}
}
private static boolean discriminating(final List<String> discriminatorsForModel, final CodegenModel cm) {
resetDiscriminatorProperty(cm);
// Discriminator will be presented as enum tag -> One and only one tag is allowed
int countString = 0;
int countNonString = 0;
for (final CodegenProperty var : cm.vars) {
if (discriminatorsForModel.stream().anyMatch(discriminator -> var.baseName.equals(discriminator) || var.name.equals(discriminator))) {
if (var.isString) {
var.isDiscriminator = true;
++countString;
} else
++countNonString;
}
}
if (countString > 0 && (countNonString > 0 || countString > 1)) {
// at least two discriminator, one of them is string -> should not render serde tag
resetDiscriminatorProperty(cm);
}
return countNonString > 0 || countString > 0;
}
private static void resetDiscriminatorProperty(final CodegenModel cm) {
for (final CodegenProperty var : cm.vars) {
var.isDiscriminator = false;
}
}
private static void processPolymorphismDataType(final List<CodegenProperty> cp) {
final HashSet<String> dedupDataTypeWithEnum = new HashSet<>();
final HashMap<String, Integer> dedupDataType = new HashMap<>();
int idx = 0;
for (CodegenProperty model : cp) {
// Generate a valid name for the enum variant.
// Mainly needed for primitive types.
model.datatypeWithEnum = camelize(model.dataType.replaceAll("(?:\\w+::)+(\\w+)", "$1")
.replace("<", "Of").replace(">", "")).replace(" ", "").replace(",", "");
if (!dedupDataTypeWithEnum.add(model.datatypeWithEnum)) {
model.datatypeWithEnum += ++idx;
}
dedupDataType.put(model.getDataType(), dedupDataType.getOrDefault(model.getDataType(), 0) + 1);
if (!model.getDataType().matches(String.format(Locale.ROOT, ".*::%s", model.getDatatypeWithEnum()))) {
model.isPrimitiveType = true;
}
}
for (CodegenProperty model : cp) {
if (dedupDataType.get(model.getDataType()) == 1) {
model.vendorExtensions.put("x-from-trait", true);
}
}
}
@Override
public OperationsMap postProcessOperationsWithModels(final OperationsMap operationsMap, List<ModelMap> allModels) {
postProcessPolymorphism(allModels);
postProcessOneOfModels(allModels);
final OperationMap operations = operationsMap.getOperations();
operations.put("classnamePascalCase", camelize(operations.getClassname()));
@@ -973,7 +901,7 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
// restore things to sensible values.
@Override
public CodegenParameter fromRequestBody(RequestBody body, Set<String> imports, String bodyParameterName) {
final var original_schema = ModelUtils.getSchemaFromRequestBody(body);
final Schema original_schema = ModelUtils.getSchemaFromRequestBody(body);
CodegenParameter codegenParameter = super.fromRequestBody(body, imports, bodyParameterName);
if (StringUtils.isNotBlank(original_schema.get$ref())) {
@@ -992,10 +920,10 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
@Override
public String toInstantiationType(final Schema p) {
if (ModelUtils.isArraySchema(p)) {
final var inner = ModelUtils.getSchemaItems(p);
final Schema inner = ModelUtils.getSchemaItems(p);
return instantiationTypes.get("array") + "<" + getSchemaType(inner) + ">";
} else if (ModelUtils.isMapSchema(p)) {
final var inner = ModelUtils.getAdditionalProperties(p);
final Schema inner = ModelUtils.getAdditionalProperties(p);
return instantiationTypes.get("map") + "<" + typeMapping.get("string") + ", " + getSchemaType(inner) + ">";
} else {
return null;
@@ -1024,10 +952,6 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
@Override
public String toDefaultValue(final Schema p) {
String defaultValue = null;
if (ModelUtils.isEnumSchema(p))
return null;
if ((ModelUtils.isNullable(p)) && (p.getDefault() != null) && ("null".equalsIgnoreCase(p.getDefault().toString())))
return "Nullable::Null";
@@ -1041,9 +965,6 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
} else if (ModelUtils.isNumberSchema(p)) {
if (p.getDefault() != null) {
defaultValue = p.getDefault().toString();
if (!defaultValue.contains(".")) {
defaultValue += ".0";
}
}
} else if (ModelUtils.isIntegerSchema(p)) {
if (p.getDefault() != null) {
@@ -1160,7 +1081,7 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
String cmd = System.getenv("RUST_POST_PROCESS_FILE");
if (StringUtils.isEmpty(cmd)) {
cmd = "rustfmt";
command = new String[]{cmd, "--edition", "2024", fileName};
command = new String[]{cmd, "--edition", "2021", fileName};
} else {
command = new String[]{cmd, fileName};
}
@@ -1172,7 +1093,7 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
}
@Override
protected void updateParameterForString(CodegenParameter codegenParameter, final Schema parameterSchema) {
protected void updateParameterForString(CodegenParameter codegenParameter, Schema parameterSchema) {
if (ModelUtils.isEmailSchema(parameterSchema)) {
codegenParameter.isEmail = true;
} else if (ModelUtils.isUUIDSchema(parameterSchema)) {
@@ -1199,7 +1120,7 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
codegenParameter.isDecimal = true;
codegenParameter.isPrimitiveType = true;
}
if (codegenParameter.isString) {
if (Boolean.TRUE.equals(codegenParameter.isString)) {
codegenParameter.isPrimitiveType = true;
}
}
@@ -1231,16 +1152,6 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
return null;
}
@Override
public String toVarName(String name) {
final var varName = super.toVarName(name);
if (varName.startsWith("r#"))
return "r_" + varName.substring(2);
return varName;
}
static class PathMethodOperations {
public String path;
public ArrayList<MethodOperation> methodOperations;

View File

@@ -307,6 +307,69 @@ public class RustClientCodegen extends AbstractRustCodegen implements CodegenCon
mdl.getComposedSchemas().setOneOf(newOneOfs);
}
// Handle anyOf schemas similarly to oneOf
// This is pragmatic since Rust's untagged enum will deserialize to the first matching variant
if (mdl.getComposedSchemas() != null && mdl.getComposedSchemas().getAnyOf() != null
&& !mdl.getComposedSchemas().getAnyOf().isEmpty()) {
List<CodegenProperty> newAnyOfs = mdl.getComposedSchemas().getAnyOf().stream()
.map(CodegenProperty::clone)
.collect(Collectors.toList());
List<Schema> schemas = ModelUtils.getInterfaces(model);
if (newAnyOfs.size() != schemas.size()) {
// For safety reasons, this should never happen unless there is an error in the code
throw new RuntimeException("anyOf size does not match the model");
}
Map<String, String> refsMapping = Optional.ofNullable(model.getDiscriminator())
.map(Discriminator::getMapping).orElse(Collections.emptyMap());
// Reverse mapped references to use as baseName for anyOf, but different keys may point to the same $ref.
// Thus, we group them by the value
Map<String, List<String>> mappedNamesByRef = refsMapping.entrySet().stream()
.collect(Collectors.groupingBy(Map.Entry::getValue,
Collectors.mapping(Map.Entry::getKey, Collectors.toList())
));
for (int i = 0; i < newAnyOfs.size(); i++) {
CodegenProperty anyOf = newAnyOfs.get(i);
Schema schema = schemas.get(i);
if (mappedNamesByRef.containsKey(schema.get$ref())) {
// prefer mapped names if present
// remove mapping not in order not to reuse for the next occurrence of the ref
List<String> names = mappedNamesByRef.get(schema.get$ref());
String mappedName = names.remove(0);
anyOf.setBaseName(mappedName);
anyOf.setName(toModelName(mappedName));
} else if (!org.apache.commons.lang3.StringUtils.isEmpty(schema.get$ref())) {
// use $ref if it's reference
String refName = ModelUtils.getSimpleRef(schema.get$ref());
if (refName != null) {
String modelName = toModelName(refName);
anyOf.setName(modelName);
anyOf.setBaseName(refName);
}
} else if (anyOf.isArray) {
// If the type is an array, extend the name with the inner type to prevent name collisions
// in case multiple arrays with different types are defined. If the user has manually specified
// a name, use that name instead.
String collectionWithTypeName = toModelName(schema.getType()) + anyOf.containerTypeMapped + anyOf.items.dataType;
String anyOfName = Optional.ofNullable(schema.getTitle()).orElse(collectionWithTypeName);
anyOf.setName(anyOfName);
}
else {
// In-placed type (primitive), because there is no mapping or ref for it.
// use camelized `title` if present, otherwise use `type`
String anyOfName = Optional.ofNullable(schema.getTitle()).orElseGet(schema::getType);
anyOf.setName(toModelName(anyOfName));
}
}
// Set anyOf as oneOf for template processing since we want the same output
mdl.getComposedSchemas().setOneOf(newAnyOfs);
}
return mdl;
}

View File

@@ -1,7 +1,4 @@
{{>additionalOneOfTypeAnnotations}}{{>generatedAnnotation}}{{>typeInfoAnnotation}}{{>xmlAnnotation}}
{{#vendorExtensions.x-class-extra-annotation}}
{{{vendorExtensions.x-class-extra-annotation}}}
{{/vendorExtensions.x-class-extra-annotation}}
public {{>sealed}}interface {{classname}} {{#vendorExtensions.x-implements}}{{#-first}}extends {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}}{{>permits}}{
{{#discriminator}}
public {{propertyType}} {{propertyGetter}}();

View File

@@ -8,9 +8,7 @@ import org.junit.Test;
import org.junit.Before;
import static org.junit.Assert.*;
{{#useBeanValidation}}
import {{javaxPackage}}.validation.Valid;
{{/useBeanValidation}}
import {{javaxPackage}}.ws.rs.core.Response;
import org.apache.cxf.jaxrs.client.JAXRSClientFactory;
import org.apache.cxf.jaxrs.client.ClientConfiguration;

View File

@@ -13,8 +13,7 @@
@JsonSubTypes.Type(value = {{classname}}.class){{^-last}}, {{/-last}}
{{/interfaceModels}}
})
{{/useDeductionForOneOfInterfaces}}{{#vendorExtensions.x-class-extra-annotation}}{{{vendorExtensions.x-class-extra-annotation}}}
{{/vendorExtensions.x-class-extra-annotation}}
{{/useDeductionForOneOfInterfaces}}
{{/discriminator}}
{{>generatedAnnotation}}

View File

@@ -6,10 +6,12 @@ import (
{{#hasBodyParams}}
"encoding/json"
{{/hasBodyParams}}
{{#hasOptionalBodyParams}}
{{#isBodyParam}}
{{^required}}
"errors"
"io"
{{/hasOptionalBodyParams}}
{{/required}}
{{/isBodyParam}}
"net/http"
"strings"
{{#imports}} "{{import}}"

View File

@@ -18,7 +18,7 @@
{{/hasOptional}}{{/hasRequired}}{{#optionalVars}}{{>dataClassOptVar}}{{^-last}},
{{/-last}}{{/optionalVars}}
){{/discriminator}}{{! no newline
}}{{#parent}} : {{{.}}}{{#isMap}}(){{/isMap}}{{! no newline
}}{{#parent}} : {{{.}}}{{! no newline
}}{{#serializableModel}}{{! no newline
}}{{^vendorExtensions.x-kotlin-implements}}, Serializable{{/vendorExtensions.x-kotlin-implements}}{{! no newline
}}{{#vendorExtensions.x-kotlin-implements}}, Serializable, {{! no newline

View File

@@ -28,10 +28,10 @@ To be able to use it, you will need these dependencies in your own package that
* urllib3 >= 2.1.0, < 3.0.0
* python-dateutil >= 2.8.2
{{#async}}
{{#asyncio}}
* aiohttp >= 3.8.4
* aiohttp-retry >= 2.8.3
{{/async}}
{{/asyncio}}
{{#tornado}}
* tornado >= 4.2, < 5
{{/tornado}}

View File

@@ -32,14 +32,14 @@ class {{classname}}:
@validate_call
{{#async}}async {{/async}}def {{operationId}}{{>partial_api_args}} -> {{{returnType}}}{{^returnType}}None{{/returnType}}:
{{#asyncio}}async {{/asyncio}}def {{operationId}}{{>partial_api_args}} -> {{{returnType}}}{{^returnType}}None{{/returnType}}:
{{>partial_api}}
response_data = {{#async}}await {{/async}}self.api_client.call_api(
response_data = {{#asyncio}}await {{/asyncio}}self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)
{{#async}}await {{/async}}response_data.read()
{{#asyncio}}await {{/asyncio}}response_data.read()
return self.api_client.response_deserialize(
response_data=response_data,
response_types_map=_response_types_map,
@@ -47,14 +47,14 @@ class {{classname}}:
@validate_call
{{#async}}async {{/async}}def {{operationId}}_with_http_info{{>partial_api_args}} -> ApiResponse[{{{returnType}}}{{^returnType}}None{{/returnType}}]:
{{#asyncio}}async {{/asyncio}}def {{operationId}}_with_http_info{{>partial_api_args}} -> ApiResponse[{{{returnType}}}{{^returnType}}None{{/returnType}}]:
{{>partial_api}}
response_data = {{#async}}await {{/async}}self.api_client.call_api(
response_data = {{#asyncio}}await {{/asyncio}}self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)
{{#async}}await {{/async}}response_data.read()
{{#asyncio}}await {{/asyncio}}response_data.read()
return self.api_client.response_deserialize(
response_data=response_data,
response_types_map=_response_types_map,
@@ -62,10 +62,10 @@ class {{classname}}:
@validate_call
{{#async}}async {{/async}}def {{operationId}}_without_preload_content{{>partial_api_args}} -> RESTResponseType:
{{#asyncio}}async {{/asyncio}}def {{operationId}}_without_preload_content{{>partial_api_args}} -> RESTResponseType:
{{>partial_api}}
response_data = {{#async}}await {{/async}}self.api_client.call_api(
response_data = {{#asyncio}}await {{/asyncio}}self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)

View File

@@ -88,7 +88,7 @@ class ApiClient:
self.user_agent = '{{{httpUserAgent}}}{{^httpUserAgent}}OpenAPI-Generator/{{{packageVersion}}}/python{{/httpUserAgent}}'
self.client_side_validation = configuration.client_side_validation
{{#async}}
{{#asyncio}}
async def __aenter__(self):
return self
@@ -97,14 +97,14 @@ class ApiClient:
async def close(self):
await self.rest_client.close()
{{/async}}
{{^async}}
{{/asyncio}}
{{^asyncio}}
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
{{/async}}
{{/asyncio}}
@property
def user_agent(self):
@@ -257,7 +257,7 @@ class ApiClient:
{{#tornado}}
@tornado.gen.coroutine
{{/tornado}}
{{#async}}async {{/async}}def call_api(
{{#asyncio}}async {{/asyncio}}def call_api(
self,
method,
url,
@@ -280,7 +280,7 @@ class ApiClient:
try:
# perform request and return response
response_data = {{#async}}await {{/async}}{{#tornado}}yield {{/tornado}}self.rest_client.request(
response_data = {{#asyncio}}await {{/asyncio}}{{#tornado}}yield {{/tornado}}self.rest_client.request(
method, url,
headers=header_params,
body=body, post_params=post_params,

View File

@@ -10,7 +10,7 @@ from pprint import pprint
{{> python_doc_auth_partial}}
# Enter a context with an instance of the API client
{{#async}}async {{/async}}with {{{packageName}}}.ApiClient(configuration) as api_client:
{{#asyncio}}async {{/asyncio}}with {{{packageName}}}.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = {{{packageName}}}.{{{classname}}}(api_client)
{{#allParams}}
@@ -21,7 +21,7 @@ from pprint import pprint
{{#summary}}
# {{{.}}}
{{/summary}}
{{#returnType}}api_response = {{/returnType}}{{#async}}await {{/async}}api_instance.{{{operationId}}}({{#allParams}}{{#required}}{{paramName}}{{/required}}{{^required}}{{paramName}}={{paramName}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}})
{{#returnType}}api_response = {{/returnType}}{{#asyncio}}await {{/asyncio}}api_instance.{{{operationId}}}({{#allParams}}{{#required}}{{paramName}}{{/required}}{{^required}}{{paramName}}={{paramName}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}})
{{#returnType}}
print("The response of {{classname}}->{{operationId}}:\n")
pprint(api_response)

View File

@@ -8,31 +8,31 @@ import unittest
from {{apiPackage}}.{{classFilename}} import {{classname}}
class {{#operations}}Test{{classname}}(unittest.{{#async}}IsolatedAsyncio{{/async}}TestCase):
class {{#operations}}Test{{classname}}(unittest.{{#asyncio}}IsolatedAsyncio{{/asyncio}}TestCase):
"""{{classname}} unit test stubs"""
{{#async}}
{{#asyncio}}
async def asyncSetUp(self) -> None:
self.api = {{classname}}()
async def asyncTearDown(self) -> None:
await self.api.api_client.close()
{{/async}}
{{^async}}
{{/asyncio}}
{{^asyncio}}
def setUp(self) -> None:
self.api = {{classname}}()
def tearDown(self) -> None:
pass
{{/async}}
{{/asyncio}}
{{#operation}}
{{#async}}
{{#asyncio}}
async def test_{{operationId}}(self) -> None:
{{/async}}
{{^async}}
{{/asyncio}}
{{^asyncio}}
def test_{{operationId}}(self) -> None:
{{/async}}
{{/asyncio}}
"""Test case for {{{operationId}}}
{{#summary}}

View File

@@ -8,7 +8,7 @@ from pprint import pprint
# Enter a context with an instance of the API client
{{#async}}async {{/async}}with {{{packageName}}}.ApiClient(configuration) as api_client:
{{#asyncio}}async {{/asyncio}}with {{{packageName}}}.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = {{{packageName}}}.{{{classname}}}(api_client)
{{#allParams}}
@@ -19,7 +19,7 @@ from pprint import pprint
{{#summary}}
# {{{.}}}
{{/summary}}
{{#returnType}}api_response = {{/returnType}}{{#async}}await {{/async}}api_instance.{{{operationId}}}({{#allParams}}{{#required}}{{paramName}}{{/required}}{{^required}}{{paramName}}={{paramName}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}})
{{#returnType}}api_response = {{/returnType}}{{#asyncio}}await {{/asyncio}}api_instance.{{{operationId}}}({{#allParams}}{{#required}}{{paramName}}{{/required}}{{^required}}{{paramName}}={{paramName}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}})
{{#returnType}}
print("The response of {{classname}}->{{operationId}}:\n")
pprint(api_response)

View File

@@ -7,9 +7,9 @@ import copy
import http.client as httplib
import logging
from logging import FileHandler
{{^async}}
{{^asyncio}}
import multiprocessing
{{/async}}
{{/asyncio}}
import sys
from typing import Any, ClassVar, Dict, List, Literal, Optional, TypedDict, Union
from typing_extensions import NotRequired, Self
@@ -395,13 +395,13 @@ conf = {{{packageName}}}.Configuration(
Set this to the SNI value expected by the server.
"""
{{#async}}
{{#asyncio}}
self.connection_pool_maxsize = 100
"""This value is passed to the aiohttp to limit simultaneous connections.
Default values is 100, None means no-limit.
"""
{{/async}}
{{^async}}
{{/asyncio}}
{{^asyncio}}
self.connection_pool_maxsize = multiprocessing.cpu_count() * 5
"""urllib3 connection pool's maximum number of connections saved
per pool. urllib3 uses 1 connection as default value, but this is
@@ -409,7 +409,7 @@ conf = {{{packageName}}}.Configuration(
requests to the same host, which is often the case here.
cpu_count * 5 is used as default value to increase performance.
"""
{{/async}}
{{/asyncio}}
self.proxy: Optional[str] = None
"""Proxy URL

View File

@@ -1,185 +0,0 @@
# coding: utf-8
{{>partial_header}}
import io
import json
import re
import ssl
from typing import Optional, Union
import httpx
from {{packageName}}.exceptions import ApiException, ApiValueError
RESTResponseType = httpx.Response
class RESTResponse(io.IOBase):
def __init__(self, resp) -> None:
self.response = resp
self.status = resp.status_code
self.reason = resp.reason_phrase
self.data = None
async def read(self):
if self.data is None:
self.data = await self.response.aread()
return self.data
def getheaders(self):
"""Returns a CIMultiDictProxy of the response headers."""
return self.response.headers
def getheader(self, name, default=None):
"""Returns a given response header."""
return self.response.headers.get(name, default)
class RESTClientObject:
def __init__(self, configuration) -> None:
# maxsize is number of requests to host that are allowed in parallel
self.maxsize = configuration.connection_pool_maxsize
self.ssl_context = ssl.create_default_context(
cafile=configuration.ssl_ca_cert,
cadata=configuration.ca_cert_data,
)
if configuration.cert_file:
self.ssl_context.load_cert_chain(
configuration.cert_file, keyfile=configuration.key_file
)
if not configuration.verify_ssl:
self.ssl_context.check_hostname = False
self.ssl_context.verify_mode = ssl.CERT_NONE
self.proxy = configuration.proxy
self.proxy_headers = configuration.proxy_headers
self.pool_manager: Optional[httpx.AsyncClient] = None
async def close(self):
if self.pool_manager is not None:
await self.pool_manager.aclose()
async def request(
self,
method,
url,
headers=None,
body=None,
post_params=None,
_request_timeout=None):
"""Execute request
:param method: http request method
:param url: http request url
:param headers: http request headers
:param body: request json body, for `application/json`
:param post_params: request post parameters,
`application/x-www-form-urlencoded`
and `multipart/form-data`
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
"""
method = method.upper()
assert method in [
'GET',
'HEAD',
'DELETE',
'POST',
'PUT',
'PATCH',
'OPTIONS'
]
if post_params and body:
raise ApiValueError(
"body parameter cannot be used with post_params parameter."
)
post_params = post_params or {}
headers = headers or {}
timeout = _request_timeout or 5 * 60
if 'Content-Type' not in headers:
headers['Content-Type'] = 'application/json'
args = {
"method": method,
"url": url,
"timeout": timeout,
"headers": headers
}
# For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE`
if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']:
if re.search('json', headers['Content-Type'], re.IGNORECASE):
if body is not None:
args["json"] = body
elif headers['Content-Type'] == 'application/x-www-form-urlencoded': # noqa: E501
args["data"] = dict(post_params)
elif headers['Content-Type'] == 'multipart/form-data':
# must del headers['Content-Type'], or the correct
# Content-Type which generated by httpx
del headers['Content-Type']
files = []
data = {}
for param in post_params:
k, v = param
if isinstance(v, tuple) and len(v) == 3:
files.append((k, v))
else:
# Ensures that dict objects are serialized
if isinstance(v, dict):
v = json.dumps(v)
elif isinstance(v, int):
v = str(v)
data[k] = v
if files:
args["files"] = files
if data:
args["data"] = data
# Pass a `bytes` parameter directly in the body to support
# other content types than Json when `body` argument is provided
# in serialized form
elif isinstance(body, str) or isinstance(body, bytes):
args["data"] = body
else:
# Cannot generate the request from given parameters
msg = """Cannot prepare a request message for provided
arguments. Please check that your arguments match
declared content type."""
raise ApiException(status=0, reason=msg)
if self.pool_manager is None:
self.pool_manager = self._create_pool_manager()
r = await self.pool_manager.request(**args)
return RESTResponse(r)
def _create_pool_manager(self) -> httpx.AsyncClient:
limits = httpx.Limits(max_connections=self.maxsize)
proxy = None
if self.proxy:
proxy = httpx.Proxy(
url=self.proxy,
headers=self.proxy_headers
)
return httpx.AsyncClient(
limits=limits,
proxy=proxy,
verify=self.ssl_context,
trust_env=True
)

View File

@@ -24,13 +24,6 @@ class {{classname}}({{vendorExtensions.x-py-enum-type}}, Enum):
def from_json(cls, json_str: str) -> Self:
"""Create an instance of {{classname}} from a JSON string"""
return cls(json.loads(json_str))
{{#enumUnknownDefaultCase}}
@classmethod
def _missing_(cls, value):
"""Handle unknown enum values"""
return cls.UNKNOWN_DEFAULT_OPEN_API
{{/enumUnknownDefaultCase}}
{{#defaultValue}}

View File

@@ -39,9 +39,6 @@ python-dateutil = ">= 2.8.2"
aiohttp = ">= 3.8.4"
aiohttp-retry = ">= 2.8.3"
{{/asyncio}}
{{#httpx}}
httpx = ">= 0.28.1"
{{/httpx}}
{{#tornado}}
tornado = ">=4.2, <5"
{{/tornado}}
@@ -61,10 +58,10 @@ requires-python = ">=3.9"
dependencies = [
"urllib3 (>=2.1.0,<3.0.0)",
"python-dateutil (>=2.8.2)",
{{#async}}
{{#asyncio}}
"aiohttp (>=3.8.4)",
"aiohttp-retry (>=2.8.3)",
{{/async}}
{{/asyncio}}
{{#tornado}}
"tornado (>=4.2,<5)",
{{/tornado}}

View File

@@ -4,9 +4,6 @@ python_dateutil >= 2.8.2
aiohttp >= 3.8.4
aiohttp-retry >= 2.8.3
{{/asyncio}}
{{#httpx}}
httpx = ">= 0.28.1"
{{/httpx}}
{{#tornado}}
tornado = ">= 4.2, < 5"
{{/tornado}}

View File

@@ -21,9 +21,6 @@ REQUIRES = [
"aiohttp >= 3.8.4",
"aiohttp-retry >= 2.8.3",
{{/asyncio}}
{{#httpx}}
"httpx >= 0.28.1",
{{/httpx}}
{{#tornado}}
"tornado>=4.2, < 5",
{{/tornado}}

View File

@@ -524,7 +524,7 @@ pub fn check_xss_map<T>(v: &std::collections::HashMap<String, T>) -> std::result
/// Enumeration of values.
/// Since this enum's variants do not hold data, we can easily define them as `#[repr(C)]`
/// which helps with FFI.
#[allow(non_camel_case_types, clippy::large_enum_variant)]
#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "conversion", derive(frunk_enum_derive::LabelledGenericEnum))]
@@ -751,38 +751,17 @@ impl std::str::FromStr for {{{classname}}} {
{{^arrayModelType}}
{{! general struct}}
{{#anyOf.size}}
{{#discriminator}}
#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
#[serde(tag = "{{{propertyBaseName}}}")]
{{/discriminator}}
{{^discriminator}}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
{{/discriminator}}
#[allow(non_camel_case_types, clippy::large_enum_variant)]
pub enum {{{classname}}} {
{{#composedSchemas}}
{{#anyOf}}
{{{datatypeWithEnum}}}({{{dataType}}}),
{{/anyOf}}
{{/composedSchemas}}
}
/// Any of:
{{#anyOf}}
/// - {{{.}}}
{{/anyOf}}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct {{{classname}}}(Box<serde_json::value::RawValue>);
impl validator::Validate for {{{classname}}}
{
fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> {
match self {
{{#composedSchemas}}
{{#anyOf}}
{{^isModel}}
Self::{{{datatypeWithEnum}}}(_) => std::result::Result::Ok(()),
{{/isModel}}
{{#isModel}}
Self::{{{datatypeWithEnum}}}(v) => v.validate(),
{{/isModel}}
{{/anyOf}}
{{/composedSchemas}}
}
std::result::Result::Ok(())
}
}
@@ -797,32 +776,11 @@ impl std::str::FromStr for {{{classname}}} {
}
}
{{#discriminator}}
impl serde::Serialize for {{{classname}}} {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer {
match self {
{{#composedSchemas}}
{{#anyOf}}
Self::{{{datatypeWithEnum}}}(x) => x.serialize(serializer),
{{/anyOf}}
{{/composedSchemas}}
}
impl PartialEq for {{{classname}}} {
fn eq(&self, other: &Self) -> bool {
self.0.get() == other.0.get()
}
}
{{/discriminator}}
{{#composedSchemas}}
{{#anyOf}}
{{#vendorExtensions.x-from-trait}}
impl From<{{{dataType}}}> for {{{classname}}} {
fn from(value: {{{dataType}}}) -> Self {
Self::{{{datatypeWithEnum}}}(value)
}
}
{{/vendorExtensions.x-from-trait}}
{{/anyOf}}
{{/composedSchemas}}
{{/anyOf.size}}
{{#oneOf.size}}
@@ -834,11 +792,11 @@ impl From<{{{dataType}}}> for {{{classname}}} {
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
{{/discriminator}}
#[allow(non_camel_case_types, clippy::large_enum_variant)]
#[allow(non_camel_case_types)]
pub enum {{{classname}}} {
{{#composedSchemas}}
{{#oneOf}}
{{{datatypeWithEnum}}}({{{dataType}}}),
{{{datatypeWithEnum}}}(Box<{{{dataType}}}>),
{{/oneOf}}
{{/composedSchemas}}
}
@@ -849,29 +807,18 @@ impl validator::Validate for {{{classname}}}
match self {
{{#composedSchemas}}
{{#oneOf}}
{{^isModel}}
{{#isPrimitiveType}}
Self::{{{datatypeWithEnum}}}(_) => std::result::Result::Ok(()),
{{/isModel}}
{{#isModel}}
Self::{{{datatypeWithEnum}}}(v) => v.validate(),
{{/isModel}}
{{/isPrimitiveType}}
{{^isPrimitiveType}}
Self::{{{datatypeWithEnum}}}(x) => x.validate(),
{{/isPrimitiveType}}
{{/oneOf}}
{{/composedSchemas}}
}
}
}
/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value
/// as specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde deserializer
impl std::str::FromStr for {{{classname}}} {
type Err = serde_json::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
serde_json::from_str(s)
}
}
{{#discriminator}}
impl serde::Serialize for {{{classname}}} {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@@ -887,18 +834,29 @@ impl serde::Serialize for {{{classname}}} {
}
{{/discriminator}}
{{#composedSchemas}}
{{#oneOf}}
{{#vendorExtensions.x-from-trait}}
impl From<{{{dataType}}}> for {{{classname}}} {
fn from(value: {{{dataType}}}) -> Self {
Self::{{{datatypeWithEnum}}}(value)
Self::{{{datatypeWithEnum}}}(Box::new(value))
}
}
{{/vendorExtensions.x-from-trait}}
{{/oneOf}}
{{/composedSchemas}}
/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value
/// as specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde deserializer
impl std::str::FromStr for {{{classname}}} {
type Err = serde_json::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
serde_json::from_str(s)
}
}
{{/oneOf.size}}
{{^anyOf.size}}
{{^oneOf.size}}
@@ -913,10 +871,8 @@ pub struct {{{classname}}} {
/// Note: inline enums are not fully supported by openapi-generator
{{/isEnum}}
{{#isDiscriminator}}
{{#isString}}
#[serde(default = "{{{classname}}}::_name_for_{{{name}}}")]
#[serde(serialize_with = "{{{classname}}}::_serialize_{{{name}}}")]
{{/isString}}
{{/isDiscriminator}}
#[serde(rename = "{{{baseName}}}")]
{{#hasValidation}}
@@ -1033,9 +989,9 @@ pub struct {{{classname}}} {
{{/vars}}
}
{{#vars}}
{{#isDiscriminator}}
{{#isString}}
impl {{{classname}}} {
fn _name_for_{{{name}}}() -> String {
String::from("{{{classname}}}")
@@ -1048,10 +1004,10 @@ impl {{{classname}}} {
s.serialize_str(&Self::_name_for_{{{name}}}())
}
}
{{/isString}}
{{/isDiscriminator}}
{{/vars}}
{{#vars}}
{{#hasValidation}}
{{#pattern}}
@@ -1079,9 +1035,9 @@ fn validate_byte_{{#lambda.lowercase}}{{{classname}}}_{{{name}}}{{/lambda.lowerc
impl {{{classname}}} {
#[allow(clippy::new_without_default, clippy::too_many_arguments)]
pub fn new({{#vars}}{{^isDiscriminator}}{{^defaultValue}}{{{name}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}, {{/defaultValue}}{{/isDiscriminator}}{{#isDiscriminator}}{{^isString}}{{^defaultValue}}{{{name}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}, {{/defaultValue}}{{/isString}}{{/isDiscriminator}}{{/vars}}) -> {{{classname}}} {
pub fn new({{#vars}}{{^defaultValue}}{{{name}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}, {{/defaultValue}}{{/vars}}) -> {{{classname}}} {
{{{classname}}} {
{{#vars}} {{^isDiscriminator}}{{#defaultValue}}{{{name}}}: {{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}{{{name}}}{{/defaultValue}}{{/isDiscriminator}}{{#isDiscriminator}}{{#isString}}{{{name}}}: Self::_name_for_{{{name}}}(){{/isString}}{{^isString}}{{#defaultValue}}{{{name}}}: {{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}{{{name}}}{{/defaultValue}}{{/isString}}{{/isDiscriminator}},
{{#vars}} {{#defaultValue}}{{{name}}}: {{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}{{{name}}}{{/defaultValue}},
{{/vars}}
}
}
@@ -1119,7 +1075,7 @@ impl std::fmt::Display for {{{classname}}} {
{{/isArray}}
{{#isArray}}
{{#isNullable}}
Some(self.{{{name}}}.as_ref().map_or("null".to_string(), |x| x.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(","))),
Some(self.{{{name}}}.as_ref().map_or(vec!["null".to_string()], |x| x.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(","))),
{{/isNullable}}
{{^isNullable}}
Some(self.{{{name}}}.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(",")),
@@ -1270,6 +1226,7 @@ impl std::convert::TryFrom<HeaderValue> for header::IntoHeaderValue<{{{classname
}
}
}
{{/oneOf.size}}
{{/anyOf.size}}

View File

@@ -121,8 +121,40 @@ impl Default for {{classname}} {
{{!-- for non-enum schemas --}}
{{^isEnum}}
{{^discriminator}}
{{#composedSchemas}}
{{#oneOf}}
{{#-first}}
{{! Model with composedSchemas.oneOf - generate enum}}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum {{classname}} {
{{/-first}}
{{/oneOf}}
{{/composedSchemas}}
{{#composedSchemas}}
{{#oneOf}}
{{#description}}
/// {{{.}}}
{{/description}}
{{{name}}}({{#isModel}}{{^avoidBoxedModels}}Box<{{/avoidBoxedModels}}{{/isModel}}{{{dataType}}}{{#isModel}}{{^avoidBoxedModels}}>{{/avoidBoxedModels}}{{/isModel}}),
{{/oneOf}}
{{/composedSchemas}}
{{#composedSchemas}}
{{#oneOf}}
{{#-last}}
}
impl Default for {{classname}} {
fn default() -> Self {
{{#oneOf}}{{#-first}}Self::{{{name}}}(Default::default()){{/-first}}{{/oneOf}}
}
}
{{/-last}}
{{/oneOf}}
{{^oneOf}}
{{! composedSchemas exists but no oneOf - generate normal struct}}
{{#vendorExtensions.x-rust-has-byte-array}}#[serde_as]
{{/vendorExtensions.x-rust-has-byte-array}}{{#oneOf.isEmpty}}#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
{{/vendorExtensions.x-rust-has-byte-array}}#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct {{{classname}}} {
{{#vars}}
{{#description}}
@@ -172,29 +204,62 @@ impl {{{classname}}} {
}
}
}
{{/oneOf.isEmpty}}
{{^oneOf.isEmpty}}
{{! TODO: add other vars that are not part of the oneOf}}
{{#description}}
/// {{{.}}}
{{/description}}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum {{classname}} {
{{#composedSchemas.oneOf}}
{{/oneOf}}
{{/composedSchemas}}
{{^composedSchemas}}
{{! Normal struct without composedSchemas}}
{{#vendorExtensions.x-rust-has-byte-array}}#[serde_as]
{{/vendorExtensions.x-rust-has-byte-array}}#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct {{{classname}}} {
{{#vars}}
{{#description}}
/// {{{.}}}
{{/description}}
{{{name}}}({{#isModel}}{{^avoidBoxedModels}}Box<{{/avoidBoxedModels}}{{/isModel}}{{{dataType}}}{{#isModel}}{{^avoidBoxedModels}}>{{/avoidBoxedModels}}{{/isModel}}),
{{/composedSchemas.oneOf}}
{{#isByteArray}}
{{#vendorExtensions.isMandatory}}#[serde_as(as = "serde_with::base64::Base64")]{{/vendorExtensions.isMandatory}}{{^vendorExtensions.isMandatory}}#[serde_as(as = "{{^serdeAsDoubleOption}}Option{{/serdeAsDoubleOption}}{{#serdeAsDoubleOption}}super::DoubleOption{{/serdeAsDoubleOption}}<serde_with::base64::Base64>")]{{/vendorExtensions.isMandatory}}
{{/isByteArray}}
#[serde(rename = "{{{baseName}}}"{{^required}}{{#isNullable}}, default{{^isByteArray}}, with = "::serde_with::rust::double_option"{{/isByteArray}}{{/isNullable}}{{/required}}{{^required}}, skip_serializing_if = "Option::is_none"{{/required}}{{#required}}{{#isNullable}}, deserialize_with = "Option::deserialize"{{/isNullable}}{{/required}})]
pub {{{name}}}: {{!
### Option Start
}}{{#isNullable}}Option<{{/isNullable}}{{^required}}Option<{{/required}}{{!
### Enums
}}{{#isEnum}}{{#isArray}}{{#uniqueItems}}std::collections::HashSet<{{/uniqueItems}}{{^uniqueItems}}Vec<{{/uniqueItems}}{{/isArray}}{{{enumName}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{!
### Non-Enums Start
}}{{^isEnum}}{{!
### Models
}}{{#isModel}}{{^avoidBoxedModels}}Box<{{/avoidBoxedModels}}{{{dataType}}}{{^avoidBoxedModels}}>{{/avoidBoxedModels}}{{/isModel}}{{!
### Primative datatypes
}}{{^isModel}}{{#isByteArray}}Vec<u8>{{/isByteArray}}{{^isByteArray}}{{{dataType}}}{{/isByteArray}}{{/isModel}}{{!
### Non-Enums End
}}{{/isEnum}}{{!
### Option End (and trailing comma)
}}{{#isNullable}}>{{/isNullable}}{{^required}}>{{/required}},
{{/vars}}
}
impl Default for {{classname}} {
fn default() -> Self {
{{#composedSchemas.oneOf}}{{#-first}}Self::{{{name}}}(Default::default()){{/-first}}{{/composedSchemas.oneOf}}
impl {{{classname}}} {
{{#description}}
/// {{{.}}}
{{/description}}
pub fn new({{#requiredVars}}{{{name}}}: {{!
### Option Start
}}{{#isNullable}}Option<{{/isNullable}}{{!
### Enums
}}{{#isEnum}}{{#isArray}}{{#uniqueItems}}std::collections::HashSet<{{/uniqueItems}}{{^uniqueItems}}Vec<{{/uniqueItems}}{{/isArray}}{{{enumName}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{!
### Non-Enums
}}{{^isEnum}}{{#isByteArray}}Vec<u8>{{/isByteArray}}{{^isByteArray}}{{{dataType}}}{{/isByteArray}}{{/isEnum}}{{!
### Option End
}}{{#isNullable}}>{{/isNullable}}{{!
### Comma for next arguement
}}{{^-last}}, {{/-last}}{{/requiredVars}}) -> {{{classname}}} {
{{{classname}}} {
{{#vars}}
{{{name}}}{{^required}}: None{{/required}}{{#required}}{{#isModel}}{{^avoidBoxedModels}}: {{^isNullable}}Box::new({{{name}}}){{/isNullable}}{{#isNullable}}if let Some(x) = {{{name}}} {Some(Box::new(x))} else {None}{{/isNullable}}{{/avoidBoxedModels}}{{/isModel}}{{/required}},
{{/vars}}
}
}
}
{{/oneOf.isEmpty}}
{{/composedSchemas}}
{{/discriminator}}
{{/isEnum}}
{{!-- for properties that are of enum type --}}

View File

@@ -22,7 +22,7 @@ import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObj
import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '{{apiRelativeToRoot}}base{{importFileExtension}}';
{{#imports}}
// @ts-ignore
import type { {{classname}} } from '{{apiRelativeToRoot}}{{tsModelPackage}}{{#importFileExtension}}/index{{importFileExtension}}{{/importFileExtension}}';
import type { {{classname}} } from '{{apiRelativeToRoot}}{{tsModelPackage}}{{importFileExtension}}';
{{/imports}}
{{/withSeparateModelsAndApi}}
{{^withSeparateModelsAndApi}}

View File

@@ -5,4 +5,4 @@
export * from "./api{{importFileExtension}}";
export * from "./configuration{{importFileExtension}}";
{{#withSeparateModelsAndApi}}export * from "./{{tsModelPackage}}{{#importFileExtension}}/index{{importFileExtension}}{{/importFileExtension}}";{{/withSeparateModelsAndApi}}
{{#withSeparateModelsAndApi}}export * from "./{{tsModelPackage}}{{importFileExtension}}";{{/withSeparateModelsAndApi}}

View File

@@ -15,7 +15,7 @@
{{/packageAsSourceOnlyLibrary}}
{{^packageAsSourceOnlyLibrary}}
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"typings": "./dist/index.d.ts",
{{#supportsES6}}
"module": "./dist/esm/index.js",
"sideEffects": false,

View File

@@ -3982,21 +3982,4 @@ public class JavaClientCodegenTest {
}
assertTrue(speciesSeen);
}
@Test
public void testOneOfClassWithAnnotation() throws IOException {
final Map<String, File> files = generateFromContract("src/test/resources/3_0/java/oneOf-with-annotations.yaml", RESTCLIENT);
JavaFileAssert.assertThat(files.get("Fruit.java"))
.isNormalClass()
.assertTypeAnnotations().containsWithName("SuppressWarnings");
}
@Test
public void testOneOfInterfaceWithAnnotation() throws IOException {
final Map<String, File> files = generateFromContract("src/test/resources/3_0/java/oneOf-with-annotations.yaml", RESTCLIENT,
Map.of(USE_ONE_OF_INTERFACES, "true"));
JavaFileAssert.assertThat(files.get("Fruit.java"))
.isInterface()
.assertTypeAnnotations().containsWithName("SuppressWarnings");
}
}

View File

@@ -5730,12 +5730,4 @@ public class SpringCodegenTest {
javaFileAssert
.hasImports("java.util.concurrent.atomic.AtomicInteger");
}
@Test
public void testOneOfInterfaceWithAnnotation() throws IOException {
final Map<String, File> files = generateFromContract("src/test/resources/3_0/java/oneOf-with-annotations.yaml", SPRING_BOOT);
JavaFileAssert.assertThat(files.get("Fruit.java"))
.isInterface()
.assertTypeAnnotations().containsWithName("SuppressWarnings");
}
}

View File

@@ -91,24 +91,6 @@ public class KotlinClientCodegenApiTest {
assertFileContainsLine(lines, "suspend fun deletePet(@Path(\"petId\") petId: kotlin.Long, @Header(\"api_key\") apiKey: kotlin.String? = null)" + expectedUnitResponse);
}
@Test
public void testEnumDefaultForReferencedSchemaParameterJvmOkhttp4() throws IOException {
OpenAPI openAPI = readOpenAPI("3_0/kotlin/enum-default-query.yaml");
KotlinClientCodegen codegen = createCodegen(ClientLibrary.JVM_OKHTTP4);
codegen.additionalProperties().put("enumPropertyNaming", "UPPERCASE");
ClientOptInput input = createClientOptInput(openAPI, codegen);
DefaultGenerator generator = new DefaultGenerator();
enableOnlyApiGeneration(generator);
List<File> files = generator.opts(input).generate();
File statusApi = files.stream().filter(file -> file.getName().equals("StatusApi.kt")).findAny().orElseThrow();
assertFileContains(statusApi.toPath(), "state: PetStatus? = PetStatus.AVAILABLE");
}
private static void assertFileContainsLine(List<String> lines, String line) {
Assert.assertListContains(lines, s -> s.equals(line), line);
}

View File

@@ -26,8 +26,6 @@ import io.swagger.v3.parser.core.models.ParseOptions;
import io.swagger.v3.parser.util.SchemaTypeUtil;
import org.openapitools.codegen.*;
import org.openapitools.codegen.config.CodegenConfigurator;
import org.openapitools.codegen.model.ModelMap;
import org.openapitools.codegen.model.ModelsMap;
import org.openapitools.codegen.languages.PythonClientCodegen;
import org.openapitools.codegen.languages.features.CXFServerFeatures;
import org.testng.Assert;
@@ -40,8 +38,6 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
@@ -51,7 +47,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.openapitools.codegen.TestUtils.assertFileContains;
import static org.openapitools.codegen.TestUtils.assertFileExists;
import static org.openapitools.codegen.TestUtils.assertFileNotContains;
public class PythonClientCodegenTest {
@@ -690,120 +685,4 @@ public class PythonClientCodegenTest {
// Verify it does NOT use the legacy string format
TestUtils.assertFileNotContains(pyprojectPath, "license = \"BSD-3-Clause\"");
}
@Test(description = "test enumUnknownDefaultCase option")
public void testEnumUnknownDefaultCaseOption() {
final PythonClientCodegen codegen = new PythonClientCodegen();
// Test default value is false
codegen.processOpts();
Assert.assertEquals(codegen.getEnumUnknownDefaultCase(), Boolean.FALSE);
// Test setting via additionalProperties
codegen.additionalProperties().put(CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE, "true");
codegen.processOpts();
Assert.assertEquals(codegen.getEnumUnknownDefaultCase(), Boolean.TRUE);
}
@Test(description = "test enum model generation with enumUnknownDefaultCase")
public void testEnumModelWithUnknownDefaultCase() {
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/enum_unknown_default_case.yaml");
final PythonClientCodegen codegen = new PythonClientCodegen();
// Enable enumUnknownDefaultCase
codegen.additionalProperties().put(CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE, "true");
codegen.setOpenAPI(openAPI);
codegen.processOpts();
// Verify that enumUnknownDefaultCase is set
Assert.assertEquals(codegen.getEnumUnknownDefaultCase(), Boolean.TRUE);
// Process all models to trigger enum processing
Map<String, Schema> schemas = openAPI.getComponents().getSchemas();
Map<String, ModelsMap> allModels = new HashMap<>();
for (String modelName : schemas.keySet()) {
Schema schema = schemas.get(modelName);
CodegenModel cm = codegen.fromModel(modelName, schema);
ModelsMap modelsMap = new ModelsMap();
modelsMap.setModels(Collections.singletonList(new ModelMap(Collections.singletonMap("model", cm))));
allModels.put(modelName, modelsMap);
}
// Post-process to add enumVars
allModels = codegen.postProcessAllModels(allModels);
// Get the ColorEnum model
CodegenModel colorEnum = null;
for (Map.Entry<String, ModelsMap> entry : allModels.entrySet()) {
if ("ColorEnum".equals(entry.getKey())) {
colorEnum = entry.getValue().getModels().get(0).getModel();
break;
}
}
Assert.assertNotNull(colorEnum);
Assert.assertNotNull(colorEnum.allowableValues);
List<Map<String, Object>> enumVars = (List<Map<String, Object>>) colorEnum.allowableValues.get("enumVars");
Assert.assertNotNull(enumVars);
// Check that we have the expected enum values including UNKNOWN_DEFAULT_OPEN_API
Assert.assertTrue(enumVars.stream().anyMatch(var -> "'RED'".equals(var.get("value"))));
Assert.assertTrue(enumVars.stream().anyMatch(var -> "'GREEN'".equals(var.get("value"))));
Assert.assertTrue(enumVars.stream().anyMatch(var -> "'BLUE'".equals(var.get("value"))));
Assert.assertTrue(enumVars.stream().anyMatch(var -> "'YELLOW'".equals(var.get("value"))));
Assert.assertTrue(enumVars.stream().anyMatch(var -> "'unknown_default_open_api'".equals(var.get("value"))));
}
@Test(description = "test enum generation with enumUnknownDefaultCase enabled")
public void testEnumGenerationWithUnknownDefaultCase() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
String outputPath = output.getAbsolutePath().replace('\\', '/');
final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("python")
.setInputSpec("src/test/resources/3_0/enum_unknown_default_case.yaml")
.setOutputDir(outputPath)
.addAdditionalProperty(CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE, "true");
DefaultGenerator generator = new DefaultGenerator();
List<File> files = generator.opts(configurator.toClientOptInput()).generate();
files.forEach(File::deleteOnExit);
Path enumFile = Paths.get(outputPath, "openapi_client", "models", "color_enum.py");
// Check that UNKNOWN_DEFAULT_OPEN_API is added (with single quotes as Python generates)
TestUtils.assertFileContains(enumFile, "UNKNOWN_DEFAULT_OPEN_API = 'unknown_default_open_api'");
// Check that _missing_ method is added
TestUtils.assertFileContains(enumFile, "@classmethod");
TestUtils.assertFileContains(enumFile, "def _missing_(cls, value):");
TestUtils.assertFileContains(enumFile, "return cls.UNKNOWN_DEFAULT_OPEN_API");
}
@Test(description = "test enum generation with enumUnknownDefaultCase disabled")
public void testEnumGenerationWithoutUnknownDefaultCase() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
String outputPath = output.getAbsolutePath().replace('\\', '/');
final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("python")
.setInputSpec("src/test/resources/3_0/enum_unknown_default_case.yaml")
.setOutputDir(outputPath)
.addAdditionalProperty(CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE, "false");
DefaultGenerator generator = new DefaultGenerator();
List<File> files = generator.opts(configurator.toClientOptInput()).generate();
files.forEach(File::deleteOnExit);
Path enumFile = Paths.get(outputPath, "openapi_client", "models", "color_enum.py");
// Check that UNKNOWN_DEFAULT_OPEN_API is NOT added
TestUtils.assertFileNotContains(enumFile, "UNKNOWN_DEFAULT_OPEN_API");
// Check that _missing_ method is NOT added
TestUtils.assertFileNotContains(enumFile, "def _missing_(cls, value):");
}
}

View File

@@ -271,4 +271,37 @@ public class RustClientCodegenTest {
TestUtils.assertFileExists(outputPath);
TestUtils.assertFileContains(outputPath, enumSpec);
}
@Test
public void testAnyOfSupport() throws IOException {
Path target = Files.createTempDirectory("test-anyof");
final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("rust")
.setInputSpec("src/test/resources/3_0/rust/rust-anyof-test.yaml")
.setSkipOverwrite(false)
.setOutputDir(target.toAbsolutePath().toString().replace("\\", "/"));
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
files.forEach(File::deleteOnExit);
// Test that ModelIdentifier generates an untagged enum, not an empty struct
Path modelIdentifierPath = Path.of(target.toString(), "/src/models/model_identifier.rs");
TestUtils.assertFileExists(modelIdentifierPath);
// Should generate an untagged enum
TestUtils.assertFileContains(modelIdentifierPath, "#[serde(untagged)]");
TestUtils.assertFileContains(modelIdentifierPath, "pub enum ModelIdentifier");
// Should have String variant (for anyOf with string types)
TestUtils.assertFileContains(modelIdentifierPath, "String(String)");
// Should NOT generate an empty struct
TestUtils.assertFileNotContains(modelIdentifierPath, "pub struct ModelIdentifier {");
TestUtils.assertFileNotContains(modelIdentifierPath, "pub fn new()");
// Test AnotherAnyOfTest with mixed types
Path anotherTestPath = Path.of(target.toString(), "/src/models/another_any_of_test.rs");
TestUtils.assertFileExists(anotherTestPath);
TestUtils.assertFileContains(anotherTestPath, "#[serde(untagged)]");
TestUtils.assertFileContains(anotherTestPath, "pub enum AnotherAnyOfTest");
}
}

View File

@@ -1,56 +0,0 @@
openapi: 3.0.0
info:
title: Enum Test API
description: API for testing enum generation with enumUnknownDefaultCase
version: 1.0.0
paths:
/colors:
get:
summary: Get color
operationId: getColor
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/ColorResponse'
components:
schemas:
ColorResponse:
type: object
required:
- color
- status
properties:
color:
$ref: '#/components/schemas/ColorEnum'
status:
$ref: '#/components/schemas/StatusEnum'
priority:
$ref: '#/components/schemas/PriorityEnum'
ColorEnum:
type: string
description: Available colors
enum:
- RED
- GREEN
- BLUE
- YELLOW
StatusEnum:
type: string
description: Status values
enum:
- PENDING
- APPROVED
- REJECTED
- IN_PROGRESS
PriorityEnum:
type: integer
description: Priority levels
enum:
- 1
- 2
- 3
- 4
- 5

View File

@@ -1,44 +0,0 @@
openapi: 3.0.1
info:
title: fruity
version: 0.0.1
paths:
/:
get:
responses:
'200':
description: desc
content:
application/json:
schema:
$ref: '#/components/schemas/fruit'
components:
schemas:
fruit:
title: fruit
x-class-extra-annotation: '@SuppressWarnings("unchecked")'
properties:
color:
type: string
oneOf:
- $ref: '#/components/schemas/apple'
- $ref: '#/components/schemas/banana'
- $ref: '#/components/schemas/orange'
apple:
title: apple
type: object
properties:
kind:
type: string
banana:
title: banana
type: object
properties:
count:
type: number
orange:
title: orange
type: object
properties:
sweet:
type: boolean

View File

@@ -1,26 +0,0 @@
openapi: 3.0.1
info:
title: Enum default test
version: 1.0.0
paths:
/status:
get:
tags:
- Status
operationId: getStatus
parameters:
- name: state
in: query
schema:
$ref: '#/components/schemas/PetStatus'
responses:
'200':
description: OK
components:
schemas:
PetStatus:
type: string
enum:
- available
- pending
default: available

View File

@@ -1,35 +0,0 @@
openapi: 3.0.3
info:
title: optional body
version: 1.0.1
servers:
- url: https://api.123.com/api/v1
tags:
- name: just-api
description: Everything about API functions
paths:
/silly:
post:
tags:
- just-api
operationId: send_optional_payload
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/Payload"
responses:
'200':
description: Successful operation
content:
application/text:
schema:
type: string
components:
schemas:
Payload:
type: object
properties:
token:
type: string
description: Some kind of token - usually received by Email

View File

@@ -31,6 +31,7 @@ paths:
$ref: '#/components/schemas/Foo'
/pet:
servers:
- url: 'http://127.0.0.1/v2'
- url: 'http://petstore.swagger.io/v2'
- url: 'http://path-server-test.petstore.local/v2'
- url: 'http://{server}.swagger.io:{port}/v2'
@@ -188,6 +189,7 @@ paths:
deprecated: true
'/pet/{petId}':
servers:
- url: 'http://127.0.0.1/v2'
- url: 'http://petstore.swagger.io/v2'
- url: 'http://path-server-test.petstore.local/v2'
- url: 'http://{server}.swagger.io:{port}/v2'
@@ -1358,6 +1360,8 @@ paths:
200:
description: OK
servers:
- url: https://127.0.0.1/v2
description: The local server without variables
- url: 'http://{server}.swagger.io:{port}/v2'
description: petstore server
variables:
@@ -1380,8 +1384,6 @@ servers:
- 'v1'
- 'v2'
default: 'v2'
- url: https://127.0.0.1/no_varaible
description: The local server without variables
- url: http://server.{version}.openapi-generator.tech
description: The openapi-generator test server
variables:

View File

@@ -0,0 +1,42 @@
openapi: 3.0.0
info:
title: Rust anyOf Test
version: 1.0.0
paths:
/model:
get:
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/TestResponse'
components:
schemas:
TestResponse:
type: object
properties:
model:
$ref: '#/components/schemas/ModelIdentifier'
status:
type: string
ModelIdentifier:
description: Model identifier that can be a string or specific enum value
anyOf:
- type: string
description: Any model name as string
- type: string
enum:
- gpt-4
- gpt-3.5-turbo
- dall-e-3
description: Known model enum values
AnotherAnyOfTest:
description: Another test case with different types
anyOf:
- type: string
- type: integer
- type: array
items:
type: string

View File

@@ -15,7 +15,7 @@
<packaging>pom</packaging>
<name>openapi-generator-project</name>
<!-- RELEASE_VERSION -->
<version>7.17.0-SNAPSHOT</version>
<version>7.16.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<url>https://github.com/openapitools/openapi-generator</url>
<scm>

View File

@@ -1 +1 @@
7.17.0-SNAPSHOT
7.16.0-SNAPSHOT

View File

@@ -6,7 +6,7 @@ This C# SDK is automatically generated by the [OpenAPI Generator](https://openap
- API version: 0.1.0
- SDK version: 1.0.0
- Generator version: 7.17.0-SNAPSHOT
- Generator version: 7.16.0-SNAPSHOT
- Build package: org.openapitools.codegen.languages.CSharpClientCodegen
<a id="frameworks-supported"></a>

View File

@@ -1 +1 @@
7.17.0-SNAPSHOT
7.16.0-SNAPSHOT

View File

@@ -7,7 +7,7 @@ This API client was generated by the [OpenAPI Generator](https://openapi-generat
- API version: 0.1.0
- Package version: 1.0.0
- Generator version: 7.17.0-SNAPSHOT
- Generator version: 7.16.0-SNAPSHOT
- Build package: org.openapitools.codegen.languages.GoClientCodegen
## Installation

View File

@@ -1 +1 @@
7.17.0-SNAPSHOT
7.16.0-SNAPSHOT

View File

@@ -7,7 +7,7 @@ This API client was generated by the [OpenAPI Generator](https://openapi-generat
- API version: 0.1.0
- Package version: 1.0.0
- Generator version: 7.17.0-SNAPSHOT
- Generator version: 7.16.0-SNAPSHOT
- Build package: org.openapitools.codegen.languages.GoClientCodegen
## Installation

View File

@@ -1 +1 @@
7.17.0-SNAPSHOT
7.16.0-SNAPSHOT

View File

@@ -4,7 +4,7 @@ Echo Server API
- API version: 0.1.0
- Generator version: 7.17.0-SNAPSHOT
- Generator version: 7.16.0-SNAPSHOT
Echo Server API

View File

@@ -81,7 +81,7 @@ import org.openapitools.client.auth.Authentication;
import org.openapitools.client.auth.HttpBasicAuth;
import org.openapitools.client.auth.HttpBearerAuth;
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.17.0-SNAPSHOT")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.16.0-SNAPSHOT")
public class ApiClient extends JavaTimeFormatter {
protected Map<String, String> defaultHeaderMap = new HashMap<String, String>();
protected Map<String, String> defaultCookieMap = new HashMap<String, String>();

View File

@@ -16,7 +16,7 @@ package org.openapitools.client;
import java.util.Map;
import java.util.List;
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.17.0-SNAPSHOT")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.16.0-SNAPSHOT")
public class ApiException extends Exception {
private static final long serialVersionUID = 1L;

View File

@@ -17,7 +17,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
import java.util.Collections;
import java.util.Map;
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.17.0-SNAPSHOT")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.16.0-SNAPSHOT")
public abstract class BaseApi {
protected ApiClient apiClient;

View File

@@ -17,7 +17,7 @@ import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.17.0-SNAPSHOT")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.16.0-SNAPSHOT")
public class Configuration {
public static final String VERSION = "0.1.0";

View File

@@ -20,7 +20,7 @@ import java.time.format.DateTimeParseException;
* Class that add parsing/formatting support for Java 8+ {@code OffsetDateTime} class.
* It's generated for java clients when {@code AbstractJavaCodegen#dateLibrary} specified as {@code java8}.
*/
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.17.0-SNAPSHOT")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.16.0-SNAPSHOT")
public class JavaTimeFormatter {
private DateTimeFormatter offsetDateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;

View File

@@ -13,7 +13,7 @@
package org.openapitools.client;
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.17.0-SNAPSHOT")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.16.0-SNAPSHOT")
public class Pair {
private final String name;
private final String value;

View File

@@ -22,7 +22,7 @@ import java.text.DecimalFormat;
import java.util.GregorianCalendar;
import java.util.TimeZone;
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.17.0-SNAPSHOT")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.16.0-SNAPSHOT")
public class RFC3339DateFormat extends DateFormat {
private static final long serialVersionUID = 1L;
private static final TimeZone TIMEZONE_Z = TimeZone.getTimeZone("UTC");

View File

@@ -28,7 +28,7 @@ import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeFeature;
import com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer;
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.17.0-SNAPSHOT")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.16.0-SNAPSHOT")
public class RFC3339InstantDeserializer<T extends Temporal> extends InstantDeserializer<T> {
private static final long serialVersionUID = 1L;
private final static boolean DEFAULT_NORMALIZE_ZONE_ID = JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID.enabledByDefault();

View File

@@ -19,7 +19,7 @@ import java.time.ZonedDateTime;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.Module.SetupContext;
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.17.0-SNAPSHOT")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.16.0-SNAPSHOT")
public class RFC3339JavaTimeModule extends SimpleModule {
private static final long serialVersionUID = 1L;

View File

@@ -18,7 +18,7 @@ import java.util.Map;
/**
* Representing a Server configuration.
*/
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.17.0-SNAPSHOT")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.16.0-SNAPSHOT")
public class ServerConfiguration {
public String URL;
public String description;

View File

@@ -18,7 +18,7 @@ import java.util.HashSet;
/**
* Representing a Server Variable for server URL template substitution.
*/
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.17.0-SNAPSHOT")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.16.0-SNAPSHOT")
public class ServerVariable {
public String description;
public String defaultValue;

View File

@@ -16,7 +16,7 @@ package org.openapitools.client;
import java.util.Collection;
import java.util.Iterator;
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.17.0-SNAPSHOT")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.16.0-SNAPSHOT")
public class StringUtil {
/**
* Check if the given array contains the given value (with case-insensitive comparison).

View File

@@ -30,7 +30,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.StringJoiner;
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.17.0-SNAPSHOT")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.16.0-SNAPSHOT")
public class AuthApi extends BaseApi {
public AuthApi() {

View File

@@ -34,7 +34,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.StringJoiner;
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.17.0-SNAPSHOT")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.16.0-SNAPSHOT")
public class BodyApi extends BaseApi {
public BodyApi() {

View File

@@ -31,7 +31,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.StringJoiner;
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.17.0-SNAPSHOT")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.16.0-SNAPSHOT")
public class FormApi extends BaseApi {
public FormApi() {

View File

@@ -31,7 +31,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.StringJoiner;
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.17.0-SNAPSHOT")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.16.0-SNAPSHOT")
public class HeaderApi extends BaseApi {
public HeaderApi() {

Some files were not shown because too many files have changed in this diff Show More