Merge branch '5.2.x'

This commit is contained in:
William Cheng 2021-05-07 12:09:21 +08:00
commit a0c3db1157
1271 changed files with 35139 additions and 2707 deletions

View File

@ -26,6 +26,7 @@ jobs:
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: ${{ matrix.java }}
- uses: actions/cache@v2.1.5

View File

@ -16,6 +16,7 @@ jobs:
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: 11
- name: Compile with Maven
run: mvn -B -q clean install jacoco:report

View File

@ -9,27 +9,17 @@
<div align="center">
[Master](https://github.com/OpenAPITools/openapi-generator/tree/master) (`5.1.1`):
[Master](https://github.com/OpenAPITools/openapi-generator/tree/master) (`5.2.0`):
[![Build Status](https://img.shields.io/travis/OpenAPITools/openapi-generator/master.svg?label=Integration%20Test)](https://travis-ci.org/OpenAPITools/openapi-generator)
[![Integration Test2](https://circleci.com/gh/OpenAPITools/openapi-generator.svg?style=shield)](https://circleci.com/gh/OpenAPITools/openapi-generator)
[![Run Status](https://api.shippable.com/projects/5af6bf74e790f4070084a115/badge?branch=master)](https://app.shippable.com/github/OpenAPITools/openapi-generator)
[![Windows Test](https://ci.appveyor.com/api/projects/status/github/openapitools/openapi-generator?branch=master&svg=true&passingText=Windows%20Test%20-%20OK&failingText=Windows%20Test%20-%20Fails)](https://ci.appveyor.com/project/WilliamCheng/openapi-generator-wh2wu)
[![JDK11 Build](https://cloud.drone.io/api/badges/OpenAPITools/openapi-generator/status.svg?ref=refs/heads/master)](https://cloud.drone.io/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)
[![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/openapitools/openapi-generator/Check%20Supported%20Java%20Versions/master?label=Check%20Supported%20Java%20Versions&logo=github&logoColor=green)](https://github.com/OpenAPITools/openapi-generator/actions?query=workflow%3A%22Check+Supported+Java+Versions%22)
[5.2.x](https://github.com/OpenAPITools/openapi-generator/tree/5.2.x) (`5.2.x`):
[![Build Status](https://img.shields.io/travis/OpenAPITools/openapi-generator/5.2.x.svg?label=Integration%20Test)](https://travis-ci.org/OpenAPITools/openapi-generator)
[![Integration Test2](https://circleci.com/gh/OpenAPITools/openapi-generator/tree/5.2.x.svg?style=shield)](https://circleci.com/gh/OpenAPITools/openapi-generator)
[![Run Status](https://api.shippable.com/projects/5af6bf74e790f4070084a115/badge?branch=5.2.x)](https://app.shippable.com/github/OpenAPITools/openapi-generator)
[![Windows Test](https://ci.appveyor.com/api/projects/status/github/openapitools/openapi-generator?branch=5.2.x&svg=true&passingText=Windows%20Test%20-%20OK&failingText=Windows%20Test%20-%20Fails)](https://ci.appveyor.com/project/WilliamCheng/openapi-generator-wh2wu)
[![JDK11 Build](https://cloud.drone.io/api/badges/OpenAPITools/openapi-generator/status.svg?ref=refs/heads/5.2.x)](https://cloud.drone.io/OpenAPITools/openapi-generator)
[![Bitrise](https://img.shields.io/bitrise/4a2b10a819d12b67/5.2.x?label=bitrise%3A%20Swift+4,5&token=859FMDR8QHwabCzwvZK6vQ)](https://app.bitrise.io/app/4a2b10a819d12b67)
[6.0.x](https://github.com/OpenAPITools/openapi-generator/tree/6.0.x) (`6.0.x`):
[![Build Status](https://img.shields.io/travis/OpenAPITools/openapi-generator/6.0.x.svg?label=Integration%20Test)](https://travis-ci.org/OpenAPITools/openapi-generator)
[![Integration Test2](https://circleci.com/gh/OpenAPITools/openapi-generator/tree/6.0.x.svg?style=shield)](https://circleci.com/gh/OpenAPITools/openapi-generator)
[![Run Status](https://api.shippable.com/projects/5af6bf74e790f4070084a115/badge?branch=6.0.x)](https://app.shippable.com/github/OpenAPITools/openapi-generator)
[![Windows Test](https://ci.appveyor.com/api/projects/status/github/openapitools/openapi-generator?branch=6.0.x&svg=true&passingText=Windows%20Test%20-%20OK&failingText=Windows%20Test%20-%20Fails)](https://ci.appveyor.com/project/WilliamCheng/openapi-generator-wh2wu)
[![JDK11 Build](https://cloud.drone.io/api/badges/OpenAPITools/openapi-generator/status.svg?ref=refs/heads/6.0.x)](https://cloud.drone.io/OpenAPITools/openapi-generator)
[![Bitrise](https://img.shields.io/bitrise/4a2b10a819d12b67/6.0.x?label=bitrise%3A%20Swift+4,5&token=859FMDR8QHwabCzwvZK6vQ)](https://app.bitrise.io/app/4a2b10a819d12b67)
@ -120,9 +110,8 @@ The OpenAPI Specification has undergone 3 revisions since initial creation in 20
| OpenAPI Generator Version | Release Date | Notes |
| --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------------------------------------------------- |
| 6.0.0 (upcoming major release) [SNAPSHOT](https://oss.sonatype.org/content/repositories/snapshots/org/openapitools/openapi-generator-cli/6.0.0-SNAPSHOT/) | Nov/Dec 2021 | Minor release with breaking changes (no fallback) |
| 5.2.0 (upcoming minor release) [SNAPSHOT](https://oss.sonatype.org/content/repositories/snapshots/org/openapitools/openapi-generator-cli/5.2.0-SNAPSHOT/) | May/Jun 2021 | Minor release with breaking changes (with fallback) |
| 5.1.1 (upcoming patch release) [SNAPSHOT](https://oss.sonatype.org/content/repositories/snapshots/org/openapitools/openapi-generator-cli/5.1.1-SNAPSHOT/) | Apr/May 2021 | Patch release (enhancements, bug fixes, etc) |
| [5.1.0](https://github.com/OpenAPITools/openapi-generator/releases/tag/v5.1.0) (latest stable release) | 20.03.2021 | Minor release with breaking changes (with fallback) |
| 5.2.0 (upcoming minor release) [SNAPSHOT](https://oss.sonatype.org/content/repositories/snapshots/org/openapitools/openapi-generator-cli/5.2.0-SNAPSHOT/) | Jun/Jul 2021 | Minor release with breaking changes (with fallback) |
| [5.1.1](https://github.com/OpenAPITools/openapi-generator/releases/tag/v5.1.1) (latest stable release) | 07.05.2021 | Patch release (enhancements, bug fixes, etc) |
| [4.3.1](https://github.com/OpenAPITools/openapi-generator/releases/tag/v4.3.1) | 06.05.2020 | Patch release (enhancements, bug fixes, etc) |
OpenAPI Spec compatibility: 1.0, 1.1, 1.2, 2.0, 3.0
@ -179,16 +168,16 @@ See the different versions of the [openapi-generator-cli](https://mvnrepository.
<!-- RELEASE_VERSION -->
If you're looking for the latest stable version, you can grab it directly from Maven.org (Java 8 runtime at a minimum):
JAR location: `https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/5.1.0/openapi-generator-cli-5.1.0.jar`
JAR location: `https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/5.1.1/openapi-generator-cli-5.1.1.jar`
For **Mac/Linux** users:
```sh
wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/5.1.0/openapi-generator-cli-5.1.0.jar -O openapi-generator-cli.jar
wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/5.1.1/openapi-generator-cli-5.1.1.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/5.1.0/openapi-generator-cli-5.1.0.jar
Invoke-WebRequest -OutFile openapi-generator-cli.jar https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/5.1.1/openapi-generator-cli-5.1.1.jar
```
After downloading the JAR, run `java -jar openapi-generator-cli.jar help` to show the usage.
@ -413,7 +402,7 @@ openapi-generator-cli version
To use a specific version of "openapi-generator-cli"
```sh
openapi-generator-cli version-manager set 5.1.0
openapi-generator-cli version-manager set 5.1.1
```
Or install it as dev-dependency:
@ -437,7 +426,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/5.1.0/openapi-generator-cli-5.1.0.jar)
You can also download the JAR (latest release) directly from [maven.org](https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/5.1.1/openapi-generator-cli-5.1.1.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,7 +1,10 @@
# this file exists because in this file we omit setting disallowAdditionalPropertiesIfNotPresent
# which makes it default to false
# that false setting is needed for composed schemas to work
# Composed schemas are schemas that contain the allOf/oneOf/anyOf keywords. v2 specs only support the allOf keyword.
generatorName: python
outputDir: samples/client/petstore/python
inputSpec: modules/openapi-generator/src/test/resources/2_0/python-client-experimental/petstore-with-fake-endpoints-models-for-testing.yaml
templateDir: modules/openapi-generator/src/main/resources/python
additionalProperties:
disallowAdditionalPropertiesIfNotPresent: "true"
packageName: petstore_api

View File

@ -0,0 +1,7 @@
generatorName: python
outputDir: samples/client/petstore/python_disallowAdditionalPropertiesIfNotPresent
inputSpec: modules/openapi-generator/src/test/resources/2_0/python-client-experimental/petstore-with-fake-endpoints-models-for-testing.yaml
templateDir: modules/openapi-generator/src/main/resources/python
additionalProperties:
disallowAdditionalPropertiesIfNotPresent: "true"
packageName: petstore_api

View File

@ -106,6 +106,7 @@ The following generators are available:
* [jaxrs-resteasy-eap](generators/jaxrs-resteasy-eap.md)
* [jaxrs-spec](generators/jaxrs-spec.md)
* [kotlin-server](generators/kotlin-server.md)
* [kotlin-server-deprecated (deprecated)](generators/kotlin-server-deprecated.md)
* [kotlin-spring](generators/kotlin-spring.md)
* [kotlin-vertx (beta)](generators/kotlin-vertx.md)
* [nodejs-express-server (beta)](generators/nodejs-express-server.md)

View File

@ -91,6 +91,7 @@ The following generators are available:
* [jaxrs-resteasy-eap](jaxrs-resteasy-eap.md)
* [jaxrs-spec](jaxrs-spec.md)
* [kotlin-server](kotlin-server.md)
* [kotlin-server-deprecated](kotlin-server-deprecated.md)
* [kotlin-spring](kotlin-spring.md)
* [kotlin-vertx (beta)](kotlin-vertx.md)
* [nodejs-express-server (beta)](nodejs-express-server.md)

View File

@ -0,0 +1,214 @@
---
title: Config Options for kotlin-server-deprecated
sidebar_label: kotlin-server-deprecated
---
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 |
| ------ | ----------- | ------ | ------- |
|apiSuffix|suffix for api classes| |Api|
|artifactId|Generated artifact id (name of jar).| |kotlin-server-deprecated|
|artifactVersion|Generated artifact's package version.| |1.0.0|
|enumPropertyNaming|Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', and 'original'| |camelCase|
|featureAutoHead|Automatically provide responses to HEAD requests for existing routes that have the GET verb defined.| |true|
|featureCORS|Ktor by default provides an interceptor for implementing proper support for Cross-Origin Resource Sharing (CORS). See enable-cors.org.| |false|
|featureCompression|Adds ability to compress outgoing content using gzip, deflate or custom encoder and thus reduce size of the response.| |true|
|featureConditionalHeaders|Avoid sending content if client already has same content, by checking ETag or LastModified properties.| |false|
|featureHSTS|Avoid sending content if client already has same content, by checking ETag or LastModified properties.| |true|
|groupId|Generated artifact package's organization (i.e. maven groupId).| |org.openapitools|
|library|library template (sub-template)|<dl><dt>**ktor**</dt><dd>ktor framework</dd></dl>|ktor|
|modelMutable|Create mutable models| |false|
|packageName|Generated artifact package name.| |org.openapitools.server|
|parcelizeModels|toggle &quot;@Parcelize&quot; for generated models| |null|
|serializableModel|boolean - toggle &quot;implements Serializable&quot; for generated models| |null|
|serializationLibrary|What serialization library to use: 'moshi' (default), or 'gson' or 'jackson'| |moshi|
|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |null|
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |null|
|sourceFolder|source folder for generated code| |src/main/kotlin|
## IMPORT MAPPING
| Type/Alias | Imports |
| ---------- | ------- |
|BigDecimal|java.math.BigDecimal|
|Date|java.time.LocalDate|
|DateTime|java.time.OffsetDateTime|
|File|java.io.File|
|LocalDate|java.time.LocalDate|
|LocalDateTime|java.time.LocalDateTime|
|LocalTime|java.time.LocalTime|
|Timestamp|java.sql.Timestamp|
|URI|java.net.URI|
|UUID|java.util.UUID|
## INSTANTIATION TYPES
| Type/Alias | Instantiated By |
| ---------- | --------------- |
|array|kotlin.collections.ArrayList|
|list|kotlin.collections.ArrayList|
|map|kotlin.collections.HashMap|
## LANGUAGE PRIMITIVES
<ul class="column-ul">
<li>kotlin.Array</li>
<li>kotlin.Boolean</li>
<li>kotlin.Byte</li>
<li>kotlin.ByteArray</li>
<li>kotlin.Char</li>
<li>kotlin.Double</li>
<li>kotlin.Float</li>
<li>kotlin.Int</li>
<li>kotlin.Long</li>
<li>kotlin.Short</li>
<li>kotlin.String</li>
<li>kotlin.collections.List</li>
<li>kotlin.collections.Map</li>
<li>kotlin.collections.Set</li>
</ul>
## RESERVED WORDS
<ul class="column-ul">
<li>as</li>
<li>break</li>
<li>class</li>
<li>continue</li>
<li>do</li>
<li>else</li>
<li>false</li>
<li>for</li>
<li>fun</li>
<li>if</li>
<li>in</li>
<li>interface</li>
<li>is</li>
<li>null</li>
<li>object</li>
<li>package</li>
<li>return</li>
<li>super</li>
<li>this</li>
<li>throw</li>
<li>true</li>
<li>try</li>
<li>typealias</li>
<li>typeof</li>
<li>val</li>
<li>var</li>
<li>when</li>
<li>while</li>
</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
|Array|✓|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
### 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
### Wire Format Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|JSON|✓|OAS2,OAS3
|XML|✓|OAS2,OAS3
|PROTOBUF|✗|ToolingExtension
|Custom|✗|OAS2,OAS3

View File

@ -16,6 +16,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|featureCompression|Adds ability to compress outgoing content using gzip, deflate or custom encoder and thus reduce size of the response.| |true|
|featureConditionalHeaders|Avoid sending content if client already has same content, by checking ETag or LastModified properties.| |false|
|featureHSTS|Avoid sending content if client already has same content, by checking ETag or LastModified properties.| |true|
|featureLocations|Generates routes in a typed way, for both: constructing URLs and reading the parameters.| |true|
|groupId|Generated artifact package's organization (i.e. maven groupId).| |org.openapitools|
|library|library template (sub-template)|<dl><dt>**ktor**</dt><dd>ktor framework</dd></dl>|ktor|
|modelMutable|Create mutable models| |false|

View File

@ -7,6 +7,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
| Option | Description | Values | Default |
| ------ | ----------- | ------ | ------- |
|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. NOTE: this option breaks composition and will be removed in 6.0.0</dd></dl>|false|
|generateSourceCodeOnly|Specifies that only a library source code is to be generated.| |false|
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true|
|library|library template (sub-template) to use: asyncio, tornado, urllib3| |urllib3|
@ -28,7 +29,6 @@ These options may be applied as additional-properties (cli) or configOptions (pl
| Type/Alias | Instantiated By |
| ---------- | --------------- |
|map|dict|
## LANGUAGE PRIMITIVES

View File

@ -4,7 +4,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-project</artifactId>
<!-- RELEASE_VERSION -->
<version>5.1.1</version>
<version>5.2.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<relativePath>../..</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<artifactId>openapi-generator-project</artifactId>
<groupId>org.openapitools</groupId>
<!-- RELEASE_VERSION -->
<version>5.1.1</version>
<version>5.2.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<relativePath>../..</relativePath>
</parent>

View File

@ -1,5 +1,5 @@
# RELEASE_VERSION
openApiGeneratorVersion=5.1.1
openApiGeneratorVersion=5.2.0-SNAPSHOT
# /RELEASE_VERSION
# BEGIN placeholders

View File

@ -4,7 +4,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-project</artifactId>
<!-- RELEASE_VERSION -->
<version>5.1.1</version>
<version>5.2.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<relativePath>../..</relativePath>
</parent>

View File

@ -1,3 +1,3 @@
# RELEASE_VERSION
openApiGeneratorVersion=5.1.1-SNAPSHOT
openApiGeneratorVersion=5.2.0-SNAPSHOT
# /RELEASE_VERSION

View File

@ -13,7 +13,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<!-- RELEASE_VERSION -->
<version>5.1.1</version>
<version>5.2.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>5.1.1-SNAPSHOT</version>
<version>5.2.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>5.1.1</version>
<version>5.2.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>5.1.1</version>
<version>5.2.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>5.1.1</version>
<version>5.2.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>5.1.1-SNAPSHOT</version>
<version>5.2.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>5.1.1</version>
<version>5.2.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<relativePath>../..</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-project</artifactId>
<!-- RELEASE_VERSION -->
<version>5.1.1</version>
<version>5.2.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<relativePath>../..</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-project</artifactId>
<!-- RELEASE_VERSION -->
<version>5.1.1</version>
<version>5.2.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<relativePath>../..</relativePath>
</parent>

View File

@ -42,15 +42,17 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen {
private Boolean hstsFeatureEnabled = true;
private Boolean corsFeatureEnabled = false;
private Boolean compressionFeatureEnabled = true;
private Boolean locationsFeatureEnabled = true;
// This is here to potentially warn the user when an option is not supoprted by the target framework.
// This is here to potentially warn the user when an option is not supported by the target framework.
private Map<String, List<String>> optionsSupportedPerFramework = new ImmutableMap.Builder<String, List<String>>()
.put(Constants.KTOR, Arrays.asList(
Constants.AUTOMATIC_HEAD_REQUESTS,
Constants.CONDITIONAL_HEADERS,
Constants.HSTS,
Constants.CORS,
Constants.COMPRESSION
Constants.COMPRESSION,
Constants.LOCATIONS
))
.build();
@ -85,6 +87,8 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen {
artifactId = "kotlin-server";
packageName = "org.openapitools.server";
typeMapping.put("array", "kotlin.collections.List");
// cliOptions default redefinition need to be updated
updateOption(CodegenConstants.ARTIFACT_ID, this.artifactId);
updateOption(CodegenConstants.PACKAGE_NAME, this.packageName);
@ -110,6 +114,7 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen {
addSwitch(Constants.HSTS, Constants.HSTS_DESC, getHstsFeatureEnabled());
addSwitch(Constants.CORS, Constants.CORS_DESC, getCorsFeatureEnabled());
addSwitch(Constants.COMPRESSION, Constants.COMPRESSION_DESC, getCompressionFeatureEnabled());
addSwitch(Constants.LOCATIONS, Constants.LOCATIONS_DESC, getLocationsFeatureEnabled());
}
public Boolean getAutoHeadFeatureEnabled() {
@ -156,6 +161,14 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen {
this.hstsFeatureEnabled = hstsFeatureEnabled;
}
public Boolean getLocationsFeatureEnabled() {
return locationsFeatureEnabled;
}
public void setLocationsFeatureEnabled(Boolean locationsFeatureEnabled) {
this.locationsFeatureEnabled = locationsFeatureEnabled;
}
public String getName() {
return "kotlin-server";
}
@ -209,6 +222,12 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen {
additionalProperties.put(Constants.COMPRESSION, getCompressionFeatureEnabled());
}
if (additionalProperties.containsKey(Constants.LOCATIONS)) {
setLocationsFeatureEnabled(convertPropertyToBooleanAndWriteBack(Constants.LOCATIONS));
} else {
additionalProperties.put(Constants.LOCATIONS, getLocationsFeatureEnabled());
}
boolean generateApis = additionalProperties.containsKey(CodegenConstants.GENERATE_APIS) && (Boolean) additionalProperties.get(CodegenConstants.GENERATE_APIS);
String packageFolder = (sourceFolder + File.separator + packageName).replace(".", File.separator);
String resourcesFolder = "src/main/resources"; // not sure this can be user configurable.
@ -223,7 +242,7 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen {
supportingFiles.add(new SupportingFile("AppMain.kt.mustache", packageFolder, "AppMain.kt"));
supportingFiles.add(new SupportingFile("Configuration.kt.mustache", packageFolder, "Configuration.kt"));
if (generateApis) {
if (generateApis && locationsFeatureEnabled) {
supportingFiles.add(new SupportingFile("Paths.kt.mustache", packageFolder, "Paths.kt"));
}
@ -247,6 +266,8 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen {
public final static String CORS_DESC = "Ktor by default provides an interceptor for implementing proper support for Cross-Origin Resource Sharing (CORS). See enable-cors.org.";
public final static String COMPRESSION = "featureCompression";
public final static String COMPRESSION_DESC = "Adds ability to compress outgoing content using gzip, deflate or custom encoder and thus reduce size of the response.";
public final static String LOCATIONS = "featureLocations";
public final static String LOCATIONS_DESC = "Generates routes in a typed way, for both: constructing URLs and reading the parameters.";
}
@Override

View File

@ -0,0 +1,270 @@
/*
* Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
* Copyright 2018 SmartBear Software
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openapitools.codegen.languages;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.CliOption;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.CodegenType;
import org.openapitools.codegen.SupportingFile;
import org.openapitools.codegen.meta.GeneratorMetadata;
import org.openapitools.codegen.meta.Stability;
import org.openapitools.codegen.meta.features.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
public class KotlinServerDeprecatedCodegen extends AbstractKotlinCodegen {
public static final String DEFAULT_LIBRARY = Constants.KTOR;
private final Logger LOGGER = LoggerFactory.getLogger(KotlinServerDeprecatedCodegen.class);
private Boolean autoHeadFeatureEnabled = true;
private Boolean conditionalHeadersFeatureEnabled = false;
private Boolean hstsFeatureEnabled = true;
private Boolean corsFeatureEnabled = false;
private Boolean compressionFeatureEnabled = true;
// This is here to potentially warn the user when an option is not supported by the target framework.
private Map<String, List<String>> optionsSupportedPerFramework = new ImmutableMap.Builder<String, List<String>>()
.put(Constants.KTOR, Arrays.asList(
Constants.AUTOMATIC_HEAD_REQUESTS,
Constants.CONDITIONAL_HEADERS,
Constants.HSTS,
Constants.CORS,
Constants.COMPRESSION
))
.build();
/**
* Constructs an instance of `KotlinServerDeprecatedCodegen`.
*/
public KotlinServerDeprecatedCodegen() {
super();
modifyFeatureSet(features -> features
.includeDocumentationFeatures(DocumentationFeature.Readme)
.wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.XML))
.securityFeatures(EnumSet.of(
SecurityFeature.BasicAuth,
SecurityFeature.ApiKey,
SecurityFeature.OAuth2_Implicit
))
.excludeGlobalFeatures(
GlobalFeature.XMLStructureDefinitions,
GlobalFeature.Callbacks,
GlobalFeature.LinkObjects,
GlobalFeature.ParameterStyling
)
.excludeSchemaSupportFeatures(
SchemaSupportFeature.Polymorphism
)
.excludeParameterFeatures(
ParameterFeature.Cookie
)
);
generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata)
.stability(Stability.DEPRECATED)
.build();
artifactId = "kotlin-server-deprecated";
packageName = "org.openapitools.server";
// cliOptions default redefinition need to be updated
updateOption(CodegenConstants.ARTIFACT_ID, this.artifactId);
updateOption(CodegenConstants.PACKAGE_NAME, this.packageName);
outputFolder = "generated-code" + File.separator + "kotlin-server-deprecated";
modelTemplateFiles.put("model.mustache", ".kt");
apiTemplateFiles.put("api.mustache", ".kt");
embeddedTemplateDir = templateDir = "kotlin-server-deprecated";
apiPackage = packageName + ".apis";
modelPackage = packageName + ".models";
supportedLibraries.put(Constants.KTOR, "ktor framework");
// TODO: Configurable server engine. Defaults to netty in build.gradle.
CliOption library = new CliOption(CodegenConstants.LIBRARY, CodegenConstants.LIBRARY_DESC);
library.setDefault(DEFAULT_LIBRARY);
library.setEnum(supportedLibraries);
cliOptions.add(library);
addSwitch(Constants.AUTOMATIC_HEAD_REQUESTS, Constants.AUTOMATIC_HEAD_REQUESTS_DESC, getAutoHeadFeatureEnabled());
addSwitch(Constants.CONDITIONAL_HEADERS, Constants.CONDITIONAL_HEADERS_DESC, getConditionalHeadersFeatureEnabled());
addSwitch(Constants.HSTS, Constants.HSTS_DESC, getHstsFeatureEnabled());
addSwitch(Constants.CORS, Constants.CORS_DESC, getCorsFeatureEnabled());
addSwitch(Constants.COMPRESSION, Constants.COMPRESSION_DESC, getCompressionFeatureEnabled());
}
public Boolean getAutoHeadFeatureEnabled() {
return autoHeadFeatureEnabled;
}
public void setAutoHeadFeatureEnabled(Boolean autoHeadFeatureEnabled) {
this.autoHeadFeatureEnabled = autoHeadFeatureEnabled;
}
public Boolean getCompressionFeatureEnabled() {
return compressionFeatureEnabled;
}
public void setCompressionFeatureEnabled(Boolean compressionFeatureEnabled) {
this.compressionFeatureEnabled = compressionFeatureEnabled;
}
public Boolean getConditionalHeadersFeatureEnabled() {
return conditionalHeadersFeatureEnabled;
}
public void setConditionalHeadersFeatureEnabled(Boolean conditionalHeadersFeatureEnabled) {
this.conditionalHeadersFeatureEnabled = conditionalHeadersFeatureEnabled;
}
public Boolean getCorsFeatureEnabled() {
return corsFeatureEnabled;
}
public void setCorsFeatureEnabled(Boolean corsFeatureEnabled) {
this.corsFeatureEnabled = corsFeatureEnabled;
}
public String getHelp() {
return "Generates a Kotlin server (Ktor v1.1.3). IMPORTANT: this generator has been deprecated." +
" Please migrate to `kotlin-server` which supports Ktor v1.5.2+.";
}
public Boolean getHstsFeatureEnabled() {
return hstsFeatureEnabled;
}
public void setHstsFeatureEnabled(Boolean hstsFeatureEnabled) {
this.hstsFeatureEnabled = hstsFeatureEnabled;
}
public String getName() {
return "kotlin-server-deprecated";
}
public CodegenType getTag() {
return CodegenType.SERVER;
}
@Override
public void processOpts() {
super.processOpts();
if (additionalProperties.containsKey(CodegenConstants.LIBRARY)) {
this.setLibrary((String) additionalProperties.get(CodegenConstants.LIBRARY));
}
// set default library to "ktor"
if (StringUtils.isEmpty(library)) {
this.setLibrary(DEFAULT_LIBRARY);
additionalProperties.put(CodegenConstants.LIBRARY, DEFAULT_LIBRARY);
LOGGER.info("`library` option is empty. Default to " + DEFAULT_LIBRARY);
}
if (additionalProperties.containsKey(Constants.AUTOMATIC_HEAD_REQUESTS)) {
setAutoHeadFeatureEnabled(convertPropertyToBooleanAndWriteBack(Constants.AUTOMATIC_HEAD_REQUESTS));
} else {
additionalProperties.put(Constants.AUTOMATIC_HEAD_REQUESTS, getAutoHeadFeatureEnabled());
}
if (additionalProperties.containsKey(Constants.CONDITIONAL_HEADERS)) {
setConditionalHeadersFeatureEnabled(convertPropertyToBooleanAndWriteBack(Constants.CONDITIONAL_HEADERS));
} else {
additionalProperties.put(Constants.CONDITIONAL_HEADERS, getConditionalHeadersFeatureEnabled());
}
if (additionalProperties.containsKey(Constants.HSTS)) {
setHstsFeatureEnabled(convertPropertyToBooleanAndWriteBack(Constants.HSTS));
} else {
additionalProperties.put(Constants.HSTS, getHstsFeatureEnabled());
}
if (additionalProperties.containsKey(Constants.CORS)) {
setCorsFeatureEnabled(convertPropertyToBooleanAndWriteBack(Constants.CORS));
} else {
additionalProperties.put(Constants.CORS, getCorsFeatureEnabled());
}
if (additionalProperties.containsKey(Constants.COMPRESSION)) {
setCompressionFeatureEnabled(convertPropertyToBooleanAndWriteBack(Constants.COMPRESSION));
} else {
additionalProperties.put(Constants.COMPRESSION, getCompressionFeatureEnabled());
}
boolean generateApis = additionalProperties.containsKey(CodegenConstants.GENERATE_APIS) && (Boolean) additionalProperties.get(CodegenConstants.GENERATE_APIS);
String packageFolder = (sourceFolder + File.separator + packageName).replace(".", File.separator);
String resourcesFolder = "src/main/resources"; // not sure this can be user configurable.
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
supportingFiles.add(new SupportingFile("Dockerfile.mustache", "", "Dockerfile"));
supportingFiles.add(new SupportingFile("build.gradle.mustache", "", "build.gradle"));
supportingFiles.add(new SupportingFile("settings.gradle.mustache", "", "settings.gradle"));
supportingFiles.add(new SupportingFile("gradle.properties", "", "gradle.properties"));
supportingFiles.add(new SupportingFile("AppMain.kt.mustache", packageFolder, "AppMain.kt"));
supportingFiles.add(new SupportingFile("Configuration.kt.mustache", packageFolder, "Configuration.kt"));
if (generateApis) {
supportingFiles.add(new SupportingFile("Paths.kt.mustache", packageFolder, "Paths.kt"));
}
supportingFiles.add(new SupportingFile("application.conf.mustache", resourcesFolder, "application.conf"));
supportingFiles.add(new SupportingFile("logback.xml", resourcesFolder, "logback.xml"));
final String infrastructureFolder = (sourceFolder + File.separator + packageName + File.separator + "infrastructure").replace(".", File.separator);
supportingFiles.add(new SupportingFile("ApiKeyAuth.kt.mustache", infrastructureFolder, "ApiKeyAuth.kt"));
}
public static class Constants {
public final static String KTOR = "ktor";
public final static String AUTOMATIC_HEAD_REQUESTS = "featureAutoHead";
public final static String AUTOMATIC_HEAD_REQUESTS_DESC = "Automatically provide responses to HEAD requests for existing routes that have the GET verb defined.";
public final static String CONDITIONAL_HEADERS = "featureConditionalHeaders";
public final static String CONDITIONAL_HEADERS_DESC = "Avoid sending content if client already has same content, by checking ETag or LastModified properties.";
public final static String HSTS = "featureHSTS";
public final static String HSTS_DESC = "Avoid sending content if client already has same content, by checking ETag or LastModified properties.";
public final static String CORS = "featureCORS";
public final static String CORS_DESC = "Ktor by default provides an interceptor for implementing proper support for Cross-Origin Resource Sharing (CORS). See enable-cors.org.";
public final static String COMPRESSION = "featureCompression";
public final static String COMPRESSION_DESC = "Adds ability to compress outgoing content using gzip, deflate or custom encoder and thus reduce size of the response.";
}
@Override
public void postProcess() {
System.out.println("################################################################################");
System.out.println("# Thanks for using OpenAPI Generator. #");
System.out.println("# Please consider donation to help us maintain this project \uD83D\uDE4F #");
System.out.println("# https://opencollective.com/openapi_generator/donate #");
System.out.println("# #");
System.out.println("# This generator's contributed by Jim Schubert (https://github.com/jimschubert)#");
System.out.println("# Please support his work directly via https://patreon.com/jimschubert \uD83D\uDE4F #");
System.out.println("################################################################################");
}
}

View File

@ -67,10 +67,6 @@ public class PythonClientCodegen extends PythonLegacyClientCodegen {
// in other code generators, support needs to be enabled on a case-by-case basis.
supportsAdditionalPropertiesWithComposedSchema = true;
// When the 'additionalProperties' keyword is not present in a OAS schema, allow
// undeclared properties. This is compliant with the JSON schema specification.
this.setDisallowAdditionalPropertiesIfNotPresent(false);
modifyFeatureSet(features -> features
.includeDocumentationFeatures(DocumentationFeature.Readme)
.wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.XML, WireFormatFeature.Custom))
@ -96,9 +92,8 @@ public class PythonClientCodegen extends PythonLegacyClientCodegen {
ParameterFeature.Cookie
)
);
// this may set datatype right for additional properties
instantiationTypes.put("map", "dict");
// needed for type object with additionalProperties: false
typeMapping.put("object", "dict");
languageSpecificPrimitives.add("file_type");
languageSpecificPrimitives.add("none_type");
@ -113,6 +108,20 @@ public class PythonClientCodegen extends PythonLegacyClientCodegen {
cliOptions.add(new CliOption(CodegenConstants.PYTHON_ATTR_NONE_IF_UNSET, CodegenConstants.PYTHON_ATTR_NONE_IF_UNSET_DESC)
.defaultValue(Boolean.FALSE.toString()));
// option to change how we process + set the data in the 'additionalProperties' keyword.
CliOption disallowAdditionalPropertiesIfNotPresentOpt = CliOption.newBoolean(
CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT,
CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT_DESC).defaultValue(Boolean.FALSE.toString());
Map<String, String> disallowAdditionalPropertiesIfNotPresentOpts = new HashMap<>();
disallowAdditionalPropertiesIfNotPresentOpts.put("false",
"The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.");
disallowAdditionalPropertiesIfNotPresentOpts.put("true",
"Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default. NOTE: "+
"this option breaks composition and will be removed in 6.0.0"
);
disallowAdditionalPropertiesIfNotPresentOpt.setEnum(disallowAdditionalPropertiesIfNotPresentOpts);
cliOptions.add(disallowAdditionalPropertiesIfNotPresentOpt);
generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata)
.stability(Stability.EXPERIMENTAL)
.build();
@ -162,6 +171,18 @@ public class PythonClientCodegen extends PythonLegacyClientCodegen {
}
additionalProperties.put("attrNoneIfUnset", attrNoneIfUnset);
// When the 'additionalProperties' keyword is not present in a OAS schema, allow
// undeclared properties. This is compliant with the JSON schema specification.
// setting this to false is required to have composed schemas work because:
// anyOf SchemaA + SchemaB, requires that props present only in A are accepted in B because in B
// they are additional properties
Boolean disallowAddProps = false;
if (additionalProperties.containsKey(CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT)) {
disallowAddProps = Boolean.valueOf(additionalProperties.get(CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT).toString());
}
this.setDisallowAdditionalPropertiesIfNotPresent(disallowAddProps);
// check library option to ensure only urllib3 is supported
if (!DEFAULT_LIBRARY.equals(getLibrary())) {
throw new RuntimeException("Only the `urllib3` library is supported in the refactored `python` client generator at the moment. Please fall back to `python-legacy` client generator for the time being. We welcome contributions to add back `asyncio`, `tornado` support to the `python` client generator.");
@ -730,6 +751,62 @@ public class PythonClientCodegen extends PythonLegacyClientCodegen {
return null;
}
@Override
protected Schema getAdditionalProperties(Schema schema) {
/*
Use cases:
1. addProps set to schema in spec: return that schema
2. addProps unset w/ getDisallowAdditionalPropertiesIfNotPresent -> null
3. addProps unset w/ getDisallowAdditionalPropertiesIfNotPresent=False -> new Schema()
4. addProps true -> new Schema() NOTE: v3 only
5. addprops false -> null NOTE: v3 only
*/
Object addProps = schema.getAdditionalProperties();
if (addProps instanceof Schema) {
return (Schema) addProps;
}
if (addProps == null) {
// When reaching this code path, this should indicate the 'additionalProperties' keyword is
// not present in the OAS schema. This is true for OAS 3.0 documents.
// However, the parsing logic is broken for OAS 2.0 documents because of the
// https://github.com/swagger-api/swagger-parser/issues/1369 issue.
// When OAS 2.0 documents are parsed, the swagger-v2-converter ignores the 'additionalProperties'
// keyword if the value is boolean. That means codegen is unable to determine whether
// additional properties are allowed or not.
//
// The original behavior was to assume additionalProperties had been set to false.
if (getDisallowAdditionalPropertiesIfNotPresent()) {
// If the 'additionalProperties' keyword is not present in a OAS schema,
// interpret as if the 'additionalProperties' keyword had been set to false.
// This is NOT compliant with the JSON schema specification. It is the original
// 'openapi-generator' behavior.
return null;
}
/*
// The disallowAdditionalPropertiesIfNotPresent CLI option has been set to true,
// but for now that only works with OAS 3.0 documents.
// The new behavior does not work with OAS 2.0 documents.
if (extensions == null || !extensions.containsKey(EXTENSION_OPENAPI_DOC_VERSION)) {
// Fallback to the legacy behavior.
return null;
}
// Get original swagger version from OAS extension.
// Note openAPI.getOpenapi() is always set to 3.x even when the document
// is converted from a OAS/Swagger 2.0 document.
// https://github.com/swagger-api/swagger-parser/pull/1374
SemVer version = new SemVer((String)extensions.get(EXTENSION_OPENAPI_DOC_VERSION));
if (version.major != 3) {
return null;
}
*/
}
if (addProps == null || (addProps instanceof Boolean && (Boolean) addProps)) {
// Return empty schema to allow any type
return new Schema();
}
return null;
}
/**
* Return a string representation of the Python types for the specified OAS schema.
* Primitive types in the OAS specification are implemented in Python using the corresponding
@ -769,16 +846,39 @@ public class PythonClientCodegen extends PythonLegacyClientCodegen {
}
}
if (isAnyTypeSchema(p)) {
// for v2 specs only, swagger-parser never generates an AnyType schemas even though it should generate them
// https://github.com/swagger-api/swagger-parser/issues/1378
// switch to v3 if you need AnyType to work
return prefix + "bool, date, datetime, dict, float, int, list, str, none_type" + suffix;
}
String originalSpecVersion = "X";
if (this.openAPI.getExtensions() != null && this.openAPI.getExtensions().containsKey("x-original-swagger-version")) {
originalSpecVersion = (String) this.openAPI.getExtensions().get("x-original-swagger-version");
originalSpecVersion = originalSpecVersion.substring(0, 1);
}
Boolean v2DisallowAdditionalPropertiesIfNotPresentAddPropsNullCase = (getAdditionalProperties(p) == null && this.getDisallowAdditionalPropertiesIfNotPresent() && originalSpecVersion.equals("2"));
Schema emptySchema = new Schema();
Boolean v2WithCompositionAddPropsAnyTypeSchemaCase = (getAdditionalProperties(p) != null && emptySchema.equals(getAdditionalProperties(p)) && originalSpecVersion.equals("2"));
if (isFreeFormObject(p) && (v2DisallowAdditionalPropertiesIfNotPresentAddPropsNullCase || v2WithCompositionAddPropsAnyTypeSchemaCase)) {
// for v2 specs only, input AnyType schemas (type unset) or schema {} results in FreeFromObject schemas
// per https://github.com/swagger-api/swagger-parser/issues/1378
// v2 spec uses cases
// 1. AnyType schemas
// 2. type object schema with no other info
// use case 1 + 2 -> both become use case 1
// switch to v3 if you need use cases 1 + 2 to work correctly
return prefix + "bool, date, datetime, dict, float, int, list, str, none_type" + fullSuffix;
}
// Resolve $ref because ModelUtils.isXYZ methods do not automatically resolve references.
if (ModelUtils.isNullable(ModelUtils.getReferencedSchema(this.openAPI, p))) {
fullSuffix = ", none_type" + suffix;
}
if (isFreeFormObject(p) && getAdditionalProperties(p) == null) {
return prefix + "bool, date, datetime, dict, float, int, list, str" + fullSuffix;
}
if ((ModelUtils.isMapSchema(p) || "object".equals(p.getType())) && getAdditionalProperties(p) != null) {
Boolean v3WithCompositionAddPropsAnyTypeSchemaCase = (getAdditionalProperties(p) != null && emptySchema.equals(getAdditionalProperties(p)) && originalSpecVersion.equals("3"));
if (isFreeFormObject(p) && v3WithCompositionAddPropsAnyTypeSchemaCase) {
// v3 code path, use case: type object schema with no other schema info
return prefix + "{str: (bool, date, datetime, dict, float, int, list, str, none_type)}" + fullSuffix;
} else if ((ModelUtils.isMapSchema(p) || "object".equals(p.getType())) && getAdditionalProperties(p) != null) {
Schema inner = getAdditionalProperties(p);
return prefix + "{str: " + getTypeString(inner, "(", ")", referencedModelNames) + "}" + fullSuffix;
} else if (ModelUtils.isArraySchema(p)) {
@ -837,7 +937,7 @@ public class PythonClientCodegen extends PythonLegacyClientCodegen {
// The 'addProps' may be a reference, getTypeDeclaration will resolve
// the reference.
List<String> referencedModelNames = new ArrayList<String>();
codegenModel.additionalPropertiesType = getTypeString(addProps, "", "", referencedModelNames);
getTypeString(addProps, "", "", referencedModelNames);
if (referencedModelNames.size() != 0) {
// Models that are referenced in the 'additionalPropertiesType' keyword
// must be added to the imports.

View File

@ -36,6 +36,7 @@ public class PythonLegacyClientCodegen extends AbstractPythonCodegen implements
// nose is a python testing framework, we use pytest if USE_NOSE is unset
public static final String USE_NOSE = "useNose";
public static final String RECURSION_LIMIT = "recursionLimit";
public static final String PYTHON_ATTR_NONE_IF_UNSET = "pythonAttrNoneIfUnset";
protected String packageUrl;
protected String apiDocPath = "docs/";

View File

@ -44,6 +44,7 @@ org.openapitools.codegen.languages.GraphQLNodeJSExpressServerCodegen
org.openapitools.codegen.languages.GroovyClientCodegen
org.openapitools.codegen.languages.KotlinClientCodegen
org.openapitools.codegen.languages.KotlinServerCodegen
org.openapitools.codegen.languages.KotlinServerDeprecatedCodegen
org.openapitools.codegen.languages.KotlinSpringServerCodegen
org.openapitools.codegen.languages.KotlinVertxServerCodegen
org.openapitools.codegen.languages.KtormSchemaCodegen

View File

@ -0,0 +1,84 @@
# {{packageName}} - Kotlin Server library for {{appName}}
## Requires
* Kotlin 1.1.2
* Gradle 3.3
## Build
First, create the gradle wrapper script:
```
gradle wrapper
```
Then, run:
```
./gradlew check assemble
```
This runs all tests and packages the library.
## Features/Implementation Notes
* Supports JSON inputs/outputs, File inputs, and Form inputs.
* Supports collection formats for query parameters: csv, tsv, ssv, pipes.
* Some Kotlin and Java types are fully qualified to avoid conflicts with types defined in OpenAPI definitions.
{{#generateApiDocs}}
<a name="documentation-for-api-endpoints"></a>
## Documentation for API Endpoints
All URIs are relative to *{{{basePath}}}*
Class | Method | HTTP request | Description
------------ | ------------- | ------------- | -------------
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{{summary}}}{{/summary}}
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
{{/generateApiDocs}}
{{#generateModelDocs}}
<a name="documentation-for-models"></a>
## Documentation for Models
{{#modelPackage}}
{{#models}}{{#model}} - [{{{modelPackage}}}.{{{classname}}}]({{modelDocPath}}{{{classname}}}.md)
{{/model}}{{/models}}
{{/modelPackage}}
{{^modelPackage}}
No model defined in this package
{{/modelPackage}}
{{/generateModelDocs}}
<a name="documentation-for-authorization"></a>{{! TODO: optional documentation for authorization? }}
## Documentation for Authorization
{{^authMethods}}
All endpoints do not require authorization.
{{/authMethods}}
{{#authMethods}}
{{#last}}
Authentication schemes defined for the API:
{{/last}}
{{/authMethods}}
{{#authMethods}}
<a name="{{name}}"></a>
### {{name}}
{{#isApiKey}}- **Type**: API key
- **API key parameter name**: {{keyParamName}}
- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}}
{{/isApiKey}}
{{#isBasic}}- **Type**: HTTP basic authentication
{{/isBasic}}
{{#isOAuth}}- **Type**: OAuth
- **Flow**: {{flow}}
- **Authorization URL**: {{authorizationUrl}}
- **Scopes**: {{^scopes}}N/A{{/scopes}}
{{#scopes}} - {{scope}}: {{description}}
{{/scopes}}
{{/isOAuth}}
{{/authMethods}}

View File

@ -0,0 +1,65 @@
# {{classname}}{{#description}}
{{description}}{{/description}}
All URIs are relative to *{{basePath}}*
Method | HTTP request | Description
------------- | ------------- | -------------
{{#operations}}{{#operation}}[**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}}
{{/operation}}{{/operations}}
{{#operations}}
{{#operation}}
<a name="{{operationId}}"></a>
# **{{operationId}}**
> {{#returnType}}{{returnType}} {{/returnType}}{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}})
{{summary}}{{#notes}}
{{notes}}{{/notes}}
### Example
```kotlin
// Import classes:
//import {{{packageName}}}.infrastructure.*
//import {{{modelPackage}}}.*
{{! TODO: Auth method documentation examples}}
val apiInstance = {{{classname}}}()
{{#allParams}}
val {{{paramName}}} : {{{dataType}}} = {{{example}}} // {{{dataType}}} | {{{description}}}
{{/allParams}}
try {
{{#returnType}}val result : {{{returnType}}} = {{/returnType}}apiInstance.{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}){{#returnType}}
println(result){{/returnType}}
} catch (e: ClientException) {
println("4xx response calling {{{classname}}}#{{{operationId}}}")
e.printStackTrace()
} catch (e: ServerException) {
println("5xx response calling {{{classname}}}#{{{operationId}}}")
e.printStackTrace()
}
```
### Parameters
{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}}
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}}
{{#allParams}} **{{paramName}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isFile}}**{{dataType}}**{{/isFile}}{{^isFile}}{{#generateModelDocs}}[**{{dataType}}**]({{baseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{dataType}}**{{/generateModelDocs}}{{/isFile}}{{/isPrimitiveType}}| {{description}} |{{^required}} [optional]{{/required}}{{#defaultValue}} [default to {{defaultValue}}]{{/defaultValue}}{{#allowableValues}} [enum: {{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}]{{/allowableValues}}
{{/allParams}}
### Return type
{{#returnType}}{{#returnTypeIsPrimitive}}**{{returnType}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}{{#generateModelDocs}}[**{{returnType}}**]({{returnBaseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{returnType}}**{{/generateModelDocs}}{{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}null (empty response body){{/returnType}}
### Authorization
{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{name}}](../README.md#{{name}}){{^-last}}, {{/-last}}{{/authMethods}}
### HTTP request headers
- **Content-Type**: {{#consumes}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}{{^consumes}}Not defined{{/consumes}}
- **Accept**: {{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}{{^produces}}Not defined{{/produces}}
{{/operation}}
{{/operations}}

View File

@ -0,0 +1,15 @@
# {{classname}}
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
{{#vars}}**{{name}}** | {{#isEnum}}[**inline**](#{{datatypeWithEnum}}){{/isEnum}}{{^isEnum}}{{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{dataType}}**]({{complexType}}.md){{/isPrimitiveType}}{{/isEnum}} | {{description}} | {{^required}} [optional]{{/required}}{{#isReadOnly}} [readonly]{{/isReadOnly}}
{{/vars}}
{{#vars}}{{#isEnum}}
<a name="{{{datatypeWithEnum}}}"></a>{{!NOTE: see java's resources "pojo_doc.mustache" once enums are fully implemented}}
## Enum: {{baseName}}
Name | Value
---- | -----{{#allowableValues}}
{{name}} | {{#values}}{{.}}{{^-last}}, {{/-last}}{{/values}}{{/allowableValues}}
{{/isEnum}}{{/vars}}

View File

@ -0,0 +1,52 @@
{{#parcelizeModels}}
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
{{/parcelizeModels}}
{{#serializableModel}}
import java.io.Serializable
{{/serializableModel}}
/**
* {{{description}}}
{{#vars}}
* @param {{name}} {{{description}}}
{{/vars}}
*/
{{#parcelizeModels}}
@Parcelize
{{/parcelizeModels}}
data class {{classname}} (
{{#requiredVars}}
{{>data_class_req_var}}{{^-last}},
{{/-last}}{{/requiredVars}}{{#hasRequired}}{{#hasOptional}},
{{/hasOptional}}{{/hasRequired}}{{#optionalVars}}{{>data_class_opt_var}}{{^-last}},
{{/-last}}{{/optionalVars}}
) {{^serializableModel}}{{#parcelizeModels}} : Parcelable{{/parcelizeModels}}{{/serializableModel}}{{^parcelizeModels}}{{#serializableModel}}: Serializable {{/serializableModel}}{{/parcelizeModels}}{{#parcelizeModels}}{{#serializableModel}} : Parcelable, Serializable {{/serializableModel}}{{/parcelizeModels}}
{{#vendorExtensions.x-has-data-class-body}}
{
{{/vendorExtensions.x-has-data-class-body}}
{{#serializableModel}}
companion object {
private const val serialVersionUID: Long = 123
}
{{/serializableModel}}
{{#hasEnums}}
{{#vars}}
{{#isEnum}}
/**
* {{{description}}}
* Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}}
*/
enum class {{nameInCamelCase}}(val value: {{dataType}}){
{{#allowableValues}}
{{#enumVars}}
{{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}}
{{/enumVars}}
{{/allowableValues}}
}
{{/isEnum}}
{{/vars}}
{{/hasEnums}}
{{#vendorExtensions.x-has-data-class-body}}
}
{{/vendorExtensions.x-has-data-class-body}}

View File

@ -0,0 +1,4 @@
{{#description}}
/* {{{description}}} */
{{/description}}
{{>modelMutable}} {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}? = {{#defaultvalue}}{{defaultvalue}}{{/defaultvalue}}{{^defaultvalue}}null{{/defaultvalue}}

View File

@ -0,0 +1,4 @@
{{#description}}
/* {{{description}}} */
{{/description}}
{{>modelMutable}} {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}

View File

@ -0,0 +1,9 @@
/**
* {{{description}}}
* Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}}
*/
enum class {{classname}}(val value: {{dataType}}){
{{#allowableValues}}{{#enumVars}}
{{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}}
{{/enumVars}}{{/allowableValues}}
}

View File

@ -0,0 +1,7 @@
# {{classname}}
## Enum
{{#allowableValues}}{{#enumVars}}
* `{{name}}` (value: `{{{value}}}`)
{{/enumVars}}{{/allowableValues}}

View File

@ -0,0 +1,85 @@
package {{packageName}}.infrastructure
import io.ktor.application.ApplicationCall
import io.ktor.application.call
import io.ktor.auth.Authentication
import io.ktor.auth.AuthenticationFailedCause
import io.ktor.auth.AuthenticationPipeline
import io.ktor.auth.AuthenticationProvider
import io.ktor.auth.Credential
import io.ktor.auth.Principal
import io.ktor.auth.UnauthorizedResponse
import io.ktor.http.auth.HeaderValueEncoding
import io.ktor.http.auth.HttpAuthHeader
import io.ktor.request.ApplicationRequest
import io.ktor.response.respond
enum class ApiKeyLocation(val location: String) {
QUERY("query"),
HEADER("header")
}
data class ApiKeyCredential(val value: String): Credential
data class ApiPrincipal(val apiKeyCredential: ApiKeyCredential?) : Principal
/**
* Represents a Api Key authentication provider
* @param name is the name of the provider, or `null` for a default provider
*/
class ApiKeyAuthenticationProvider(name: String?) : AuthenticationProvider(name) {
internal var authenticationFunction: suspend ApplicationCall.(ApiKeyCredential) -> Principal? = { null }
var apiKeyName: String = "";
var apiKeyLocation: ApiKeyLocation = ApiKeyLocation.QUERY;
/**
* Sets a validation function that will check given [ApiKeyCredential] instance and return [Principal],
* or null if credential does not correspond to an authenticated principal
*/
fun validate(body: suspend ApplicationCall.(ApiKeyCredential) -> Principal?) {
authenticationFunction = body
}
}
fun Authentication.Configuration.apiKeyAuth(name: String? = null, configure: ApiKeyAuthenticationProvider.() -> Unit) {
val provider = ApiKeyAuthenticationProvider(name).apply(configure)
val apiKeyName = provider.apiKeyName
val apiKeyLocation = provider.apiKeyLocation
val authenticate = provider.authenticationFunction
provider.pipeline.intercept(AuthenticationPipeline.RequestAuthentication) { context ->
val credentials = call.request.apiKeyAuthenticationCredentials(apiKeyName, apiKeyLocation)
val principal = credentials?.let { authenticate(call, it) }
val cause = when {
credentials == null -> AuthenticationFailedCause.NoCredentials
principal == null -> AuthenticationFailedCause.InvalidCredentials
else -> null
}
if (cause != null) {
context.challenge(apiKeyName, cause) {
// TODO: Verify correct response structure here.
call.respond(UnauthorizedResponse(HttpAuthHeader.Parameterized("API_KEY", mapOf("key" to apiKeyName), HeaderValueEncoding.QUOTED_ALWAYS)))
it.complete()
}
}
if (principal != null) {
context.principal(principal)
}
}
}
fun ApplicationRequest.apiKeyAuthenticationCredentials(apiKeyName: String, apiKeyLocation: ApiKeyLocation): ApiKeyCredential? {
val value: String? = when(apiKeyLocation) {
ApiKeyLocation.QUERY -> this.queryParameters[apiKeyName]
ApiKeyLocation.HEADER -> this.headers[apiKeyName]
}
when (value) {
null -> return null
else -> return ApiKeyCredential(value)
}
}

View File

@ -0,0 +1,145 @@
package {{packageName}}
import com.codahale.metrics.Slf4jReporter
import com.typesafe.config.ConfigFactory
import io.ktor.application.Application
import io.ktor.application.ApplicationStopping
import io.ktor.application.install
import io.ktor.application.log
import io.ktor.client.HttpClient
import io.ktor.client.engine.apache.Apache
import io.ktor.config.HoconApplicationConfig
{{#featureAutoHead}}
import io.ktor.features.AutoHeadResponse
{{/featureAutoHead}}
{{#featureCompression}}
import io.ktor.features.Compression
{{/featureCompression}}
{{#featureCORS}}
import io.ktor.features.CORS
{{/featureCORS}}
{{#featureConditionalHeaders}}
import io.ktor.features.ConditionalHeaders
{{/featureConditionalHeaders}}
import io.ktor.features.ContentNegotiation
import io.ktor.features.DefaultHeaders
{{#featureHSTS}}
import io.ktor.features.HSTS
{{/featureHSTS}}
import io.ktor.gson.GsonConverter
import io.ktor.http.ContentType
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Locations
import io.ktor.metrics.Metrics
import io.ktor.routing.Routing
import java.util.concurrent.TimeUnit
import io.ktor.util.KtorExperimentalAPI
{{#hasAuthMethods}}
import io.ktor.auth.Authentication
import io.ktor.auth.oauth
import org.openapitools.server.infrastructure.ApiKeyCredential
import org.openapitools.server.infrastructure.ApiPrincipal
import org.openapitools.server.infrastructure.apiKeyAuth
{{/hasAuthMethods}}
{{#generateApis}}{{#apiInfo}}{{#apis}}import {{apiPackage}}.{{classname}}
{{/apis}}{{/apiInfo}}{{/generateApis}}
@KtorExperimentalAPI
internal val settings = HoconApplicationConfig(ConfigFactory.defaultApplication(HTTP::class.java.classLoader))
object HTTP {
val client = HttpClient(Apache)
}
@KtorExperimentalAPI
@KtorExperimentalLocationsAPI
fun Application.main() {
install(DefaultHeaders)
install(Metrics) {
val reporter = Slf4jReporter.forRegistry(registry)
.outputTo(log)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build()
reporter.start(10, TimeUnit.SECONDS)
}
{{#generateApis}}
install(ContentNegotiation) {
register(ContentType.Application.Json, GsonConverter())
}
{{#featureAutoHead}}
install(AutoHeadResponse) // see http://ktor.io/features/autoheadresponse.html
{{/featureAutoHead}}
{{#featureConditionalHeaders}}
install(ConditionalHeaders) // see http://ktor.io/features/conditional-headers.html
{{/featureConditionalHeaders}}
{{#featureHSTS}}
install(HSTS, ApplicationHstsConfiguration()) // see http://ktor.io/features/hsts.html
{{/featureHSTS}}
{{#featureCORS}}
install(CORS, ApplicationCORSConfiguration()) // see http://ktor.io/features/cors.html
{{/featureCORS}}
{{#featureCompression}}
install(Compression, ApplicationCompressionConfiguration()) // see http://ktor.io/features/compression.html
{{/featureCompression}}
install(Locations) // see http://ktor.io/features/locations.html
{{#hasAuthMethods}}
install(Authentication) {
{{#authMethods}}
{{#isBasic}}
basic("{{{name}}}") {
validate { credentials ->
// TODO: "Apply your basic authentication functionality."
// Accessible in-method via call.principal<UserIdPrincipal>()
if (credentials.name == "Swagger" && "Codegen" == credentials.password) {
UserIdPrincipal(credentials.name)
} else {
null
}
}
{{/isBasic}}
{{#isApiKey}}
// "Implement API key auth ({{{name}}}) for parameter name '{{{keyParamName}}}'."
apiKeyAuth("{{{name}}}") {
validate { apikeyCredential: ApiKeyCredential ->
when {
apikeyCredential.value == "keyboardcat" -> ApiPrincipal(apikeyCredential)
else -> null
}
}
}
{{/isApiKey}}
{{#isOAuth}}
{{#bodyAllowed}}
{{/bodyAllowed}}
{{^bodyAllowed}}
oauth("{{name}}") {
client = HttpClient(Apache)
providerLookup = { ApplicationAuthProviders["{{{name}}}"] }
urlProvider = { _ ->
// TODO: define a callback url here.
"/"
}
}
{{/bodyAllowed}}
{{/isOAuth}}
{{/authMethods}}
}
{{/hasAuthMethods}}
install(Routing) {
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{classname}}()
{{/operations}}
{{/apis}}
{{/apiInfo}}
}
{{/generateApis}}
environment.monitor.subscribe(ApplicationStopping)
{
HTTP.client.close()
}
}

View File

@ -0,0 +1,114 @@
package {{packageName}}
// Use this file to hold package-level internal functions that return receiver object passed to the `install` method.
import io.ktor.auth.OAuthServerSettings
import io.ktor.features.Compression
import io.ktor.features.HSTS
import io.ktor.features.deflate
import io.ktor.features.gzip
import io.ktor.features.minimumSize
import io.ktor.http.HttpMethod
import io.ktor.util.KtorExperimentalAPI
import java.time.Duration
import java.util.concurrent.Executors
import {{packageName}}.settings
{{#featureCORS}}
/**
* Application block for [CORS] configuration.
*
* This file may be excluded in .openapi-generator-ignore,
* and application specific configuration can be applied in this function.
*
* See http://ktor.io/features/cors.html
*/
internal fun ApplicationCORSConfiguration(): CORS.Configuration.() -> Unit {
return {
// method(HttpMethod.Options)
// header(HttpHeaders.XForwardedProto)
// anyHost()
// host("my-host")
// host("my-host:80")
// host("my-host", subDomains = listOf("www"))
// host("my-host", schemes = listOf("http", "https"))
// allowCredentials = true
// maxAge = Duration.ofDays(1)
}
}
{{/featureCORS}}
{{#featureHSTS}}
/**
* Application block for [HSTS] configuration.
*
* This file may be excluded in .openapi-generator-ignore,
* and application specific configuration can be applied in this function.
*
* See http://ktor.io/features/hsts.html
*/
internal fun ApplicationHstsConfiguration(): HSTS.Configuration.() -> Unit {
return {
maxAge = Duration.ofDays(365)
includeSubDomains = true
preload = false
// You may also apply any custom directives supported by specific user-agent. For example:
// customDirectives.put("redirectHttpToHttps", "false")
}
}
{{/featureHSTS}}
{{#featureCompression}}
/**
* Application block for [Compression] configuration.
*
* This file may be excluded in .openapi-generator-ignore,
* and application specific configuration can be applied in this function.
*
* See http://ktor.io/features/compression.html
*/
internal fun ApplicationCompressionConfiguration(): Compression.Configuration.() -> Unit {
return {
gzip {
priority = 1.0
}
deflate {
priority = 10.0
minimumSize(1024) // condition
}
}
}
{{/featureCompression}}
// Defines authentication mechanisms used throughout the application.
@KtorExperimentalAPI
val ApplicationAuthProviders: Map<String, OAuthServerSettings> = listOf<OAuthServerSettings>(
{{#authMethods}}
{{#isOAuth}}
OAuthServerSettings.OAuth2ServerSettings(
name = "{{name}}",
authorizeUrl = "{{authorizationUrl}}",
accessTokenUrl = "{{tokenUrl}}",
requestMethod = HttpMethod.Get,
{{! TODO: flow, doesn't seem to be supported yet by ktor }}
clientId = settings.property("auth.oauth.{{name}}.clientId").getString(),
clientSecret = settings.property("auth.oauth.{{name}}.clientSecret").getString(),
defaultScopes = listOf({{#scopes}}"{{scope}}"{{^-last}}, {{/-last}}{{/scopes}})
){{^-last}},{{/-last}}
{{/isOAuth}}
{{/authMethods}}
// OAuthServerSettings.OAuth2ServerSettings(
// name = "facebook",
// authorizeUrl = "https://graph.facebook.com/oauth/authorize",
// accessTokenUrl = "https://graph.facebook.com/oauth/access_token",
// requestMethod = HttpMethod.Post,
//
// clientId = "settings.property("auth.oauth.facebook.clientId").getString()",
// clientSecret = "settings.property("auth.oauth.facebook.clientSecret").getString()",
// defaultScopes = listOf("public_profile")
// )
).associateBy { it.name }
// Provides an application-level fixed thread pool on which to execute coroutines (mainly)
internal val ApplicationExecutors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 4)

View File

@ -0,0 +1,7 @@
FROM openjdk:8-jre-alpine
COPY ./build/libs/{{artifactId}}.jar /root/{{artifactId}}.jar
WORKDIR /root
CMD ["java", "-server", "-Xms4g", "-Xmx4g", "-XX:+UseG1GC", "-XX:MaxGCPauseMillis=100", "-XX:+UseStringDeduplication", "-jar", "{{artifactId}}.jar"]

View File

@ -0,0 +1,28 @@
{{>licenseInfo}}
package {{packageName}}
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Location
{{#imports}}import {{import}}
{{/imports}}
{{#apiInfo}}
object Paths {
{{#apis}}
{{#operations}}
{{#operation}}
{{^bodyAllowed}}
/**
* {{summary}}
* {{#unescapedNotes}}{{.}}{{/unescapedNotes}}
{{#allParams}}* @param {{paramName}} {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}
{{/allParams}}*/
@KtorExperimentalLocationsAPI
@Location("{{path}}") class {{operationId}}({{#allParams}}val {{paramName}}: {{{dataType}}}{{^required}}? = null{{/required}}{{#required}}{{#isNullable}}?{{/isNullable}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}})
{{/bodyAllowed}}
{{/operation}}
{{/operations}}
{{/apis}}
}
{{/apiInfo}}

View File

@ -0,0 +1,101 @@
# {{packageName}} - Kotlin Server library for {{appName}}
{{#unescapedAppDescription}}
{{.}}
{{/unescapedAppDescription}}
Generated by OpenAPI Generator {{generatorVersion}}{{^hideGenerationTimestamp}} ({{generatedDate}}){{/hideGenerationTimestamp}}.
## Requires
* Kotlin 1.3.21
* Gradle 4.9
## Build
First, create the gradle wrapper script:
```
gradle wrapper
```
Then, run:
```
./gradlew check assemble
```
This runs all tests and packages the library.
## Running
The server builds as a fat jar with a main entrypoint. To start the service, run `java -jar ./build/libs/{{artifactId}}.jar`.
You may also run in docker:
```
docker build -t {{artifactId}} .
docker run -p 8080:8080 {{artifactId}}
```
## Features/Implementation Notes
* Supports JSON inputs/outputs, File inputs, and Form inputs (see ktor documentation for more info).
* ~Supports collection formats for query parameters: csv, tsv, ssv, pipes.~
* Some Kotlin and Java types are fully qualified to avoid conflicts with types defined in OpenAPI definitions.
{{#generateApiDocs}}
<a name="documentation-for-api-endpoints"></a>
## Documentation for API Endpoints
All URIs are relative to *{{{basePath}}}*
Class | Method | HTTP request | Description
------------ | ------------- | ------------- | -------------
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{{summary}}}{{/summary}}
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
{{/generateApiDocs}}
{{#generateModelDocs}}
<a name="documentation-for-models"></a>
## Documentation for Models
{{#modelPackage}}
{{#models}}{{#model}} - [{{{modelPackage}}}.{{{classname}}}]({{modelDocPath}}{{{classname}}}.md)
{{/model}}{{/models}}
{{/modelPackage}}
{{^modelPackage}}
No model defined in this package
{{/modelPackage}}
{{/generateModelDocs}}
<a name="documentation-for-authorization"></a>{{! TODO: optional documentation for authorization? }}
## Documentation for Authorization
{{^authMethods}}
All endpoints do not require authorization.
{{/authMethods}}
{{#authMethods}}
{{#last}}
Authentication schemes defined for the API:
{{/last}}
{{/authMethods}}
{{#authMethods}}
<a name="{{name}}"></a>
### {{name}}
{{#isApiKey}}- **Type**: API key
- **API key parameter name**: {{keyParamName}}
- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}}
{{/isApiKey}}
{{#isBasic}}- **Type**: HTTP basic authentication
{{/isBasic}}
{{#isOAuth}}- **Type**: OAuth
- **Flow**: {{flow}}
- **Authorization URL**: {{authorizationUrl}}
- **Scopes**: {{^scopes}}N/A{{/scopes}}
{{#scopes}} - {{scope}}: {{description}}
{{/scopes}}
{{/isOAuth}}
{{/authMethods}}

View File

@ -0,0 +1,25 @@
{{#hasAuthMethods}}
{{>libraries/ktor/_principal}}
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
{{#examples}}
{{#-first}}
{{#lambda.indented}}{{>_response}}{{/lambda.indented}}
{{/-first}}
{{/examples}}
{{^examples}}
call.respond(HttpStatusCode.NotImplemented)
{{/examples}}
}
{{/hasAuthMethods}}
{{^hasAuthMethods}}
{{#examples}}
{{#-first}}
{{>libraries/ktor/_response}}
{{/-first}}
{{/examples}}
{{^examples}}
call.respond(HttpStatusCode.NotImplemented)
{{/examples}}
{{/hasAuthMethods}}

View File

@ -0,0 +1,11 @@
{{#authMethods}}
{{#isBasic}}
val principal = call.authentication.principal<UserIdPrincipal>()
{{/isBasic}}
{{#isApiKey}}
val principal = call.authentication.principal<ApiPrincipal>()
{{/isApiKey}}
{{#isOAuth}}
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
{{/isOAuth}}
{{/authMethods}}

View File

@ -0,0 +1,8 @@
val exampleContentType = "{{{contentType}}}"
val exampleContentString = """{{&example}}"""
when(exampleContentType) {
"application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java))
"application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml)
else -> call.respondText(exampleContentString)
}

View File

@ -0,0 +1,62 @@
{{>licenseInfo}}
package {{apiPackage}}
import com.google.gson.Gson
import io.ktor.application.call
import io.ktor.auth.UserIdPrincipal
import io.ktor.auth.authentication
import io.ktor.auth.authenticate
import io.ktor.auth.OAuthAccessTokenResponse
import io.ktor.auth.OAuthServerSettings
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.delete
import io.ktor.locations.get
import io.ktor.response.respond
import io.ktor.response.respondText
import io.ktor.routing.Route
import io.ktor.routing.post
import io.ktor.routing.put
import io.ktor.routing.route
import {{packageName}}.Paths
import {{packageName}}.infrastructure.ApiPrincipal
{{#imports}}import {{import}}
{{/imports}}
{{#operations}}
@KtorExperimentalLocationsAPI
fun Route.{{classname}}() {
val gson = Gson()
val empty = mutableMapOf<String, Any?>()
{{#operation}}
{{#bodyAllowed}}
route("{{path}}") {
{{#hasAuthMethods}}
{{#authMethods}}
authenticate("{{{name}}}") {
{{/authMethods}}
{{/hasAuthMethods}}
{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}} {
{{#lambda.indented_12}}{{>libraries/ktor/_api_body}}{{/lambda.indented_12}}
}
{{#hasAuthMethods}}
}
{{/hasAuthMethods}}
}
{{/bodyAllowed}}
{{^bodyAllowed}}
{{! NOTE: Locations can be used on routes without body parameters.}}
{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}<Paths.{{operationId}}> { _: Paths.{{operationId}} ->
{{#lambda.indented_8}}{{>libraries/ktor/_api_body}}{{/lambda.indented_8}}
}
{{/bodyAllowed}}
{{/operation}}
}
{{/operations}}

View File

@ -0,0 +1,27 @@
ktor {
deployment {
environment = development
port = 8080
autoreload = true
watch = [ {{packageName}} ]
}
application {
modules = [ {{packageName}}.AppMainKt.main ]
}
}
# Typesafe config allows multiple ways to provide configuration values without hard-coding them here.
# Please see https://github.com/lightbend/config for details.
auth {
oauth {
{{#authMethods}}
{{#isOAuth}}
{{name}} {
clientId = ""
clientSecret = ""
}
{{/isOAuth}}
{{/authMethods}}
}
}

View File

@ -0,0 +1,68 @@
group '{{groupId}}'
version '{{artifactVersion}}'
wrapper {
gradleVersion = '4.9'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.3.21'
ext.ktor_version = '1.1.3'
ext.shadow_version = '2.0.3'
repositories {
maven { url "https://repo1.maven.org/maven2" }
maven {
url = "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.github.jengelman.gradle.plugins:shadow:$shadow_version"
}
}
apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName = "io.ktor.server.netty.DevelopmentEngine"
// Initialization order with shadow 2.0.1 and Gradle 4.3 is weird.
// See https://github.com/johnrengelman/shadow/issues/336#issuecomment-355402508
apply plugin: 'com.github.johnrengelman.shadow'
sourceCompatibility = 1.8
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
shadowJar {
baseName = '{{artifactId}}'
classifier = null
version = null
}
repositories {
maven { url "https://repo1.maven.org/maven2" }
maven { url "https://dl.bintray.com/kotlin/ktor" }
maven { url "https://dl.bintray.com/kotlin/kotlinx" }
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "io.ktor:ktor-server-netty:$ktor_version"
compile "io.ktor:ktor-metrics:$ktor_version"
compile "io.ktor:ktor-locations:$ktor_version"
compile "io.ktor:ktor-gson:$ktor_version"
compile "io.ktor:ktor-client-core:$ktor_version"
compile "io.ktor:ktor-client-apache:$ktor_version"
compile "ch.qos.logback:logback-classic:1.2.1"
testCompile group: 'junit', name: 'junit', version: '4.13'
}

View File

@ -0,0 +1,11 @@
/**
* {{{appName}}}
* {{{appDescription}}}
*
* {{#version}}The version of the OpenAPI document: {{{version}}}{{/version}}
* {{#infoEmail}}Contact: {{{infoEmail}}}{{/infoEmail}}
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@ -0,0 +1,15 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="trace">
<appender-ref ref="STDOUT"/>
</root>
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.netty" level="INFO"/>
</configuration>

View File

@ -0,0 +1,11 @@
/**
* {{{appName}}}
* {{{appDescription}}}
*
* {{#version}}The version of the OpenAPI document: {{{version}}}{{/version}}
* {{#infoEmail}}Contact: {{{infoEmail}}}{{/infoEmail}}
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@ -0,0 +1,11 @@
{{>licenseInfo}}
package {{modelPackage}}
{{#imports}}import {{import}}
{{/imports}}
{{#models}}
{{#model}}
{{#isEnum}}{{>enum_class}}{{/isEnum}}{{^isEnum}}{{>data_class}}{{/isEnum}}
{{/model}}
{{/models}}

View File

@ -0,0 +1 @@
{{#modelMutable}}var{{/modelMutable}}{{^modelMutable}}val{{/modelMutable}}

View File

@ -0,0 +1,3 @@
{{#models}}{{#model}}
{{#isEnum}}{{>enum_doc}}{{/isEnum}}{{^isEnum}}{{>class_doc}}{{/isEnum}}
{{/model}}{{/models}}

View File

@ -0,0 +1 @@
rootProject.name = '{{artifactId}}'

View File

@ -2,8 +2,8 @@
## Requires
* Kotlin 1.1.2
* Gradle 3.3
* Kotlin 1.4.31
* Gradle 6.8.2
## Build

View File

@ -1,57 +1,35 @@
package {{packageName}}.infrastructure
import io.ktor.application.ApplicationCall
import io.ktor.application.call
import io.ktor.auth.Authentication
import io.ktor.auth.AuthenticationFailedCause
import io.ktor.auth.AuthenticationPipeline
import io.ktor.auth.AuthenticationProvider
import io.ktor.auth.Credential
import io.ktor.auth.Principal
import io.ktor.auth.UnauthorizedResponse
import io.ktor.http.auth.HeaderValueEncoding
import io.ktor.http.auth.HttpAuthHeader
import io.ktor.request.ApplicationRequest
import io.ktor.response.respond
import io.ktor.application.*
import io.ktor.auth.*
import io.ktor.http.auth.*
import io.ktor.request.*
import io.ktor.response.*
enum class ApiKeyLocation(val location: String) {
QUERY("query"),
HEADER("header")
}
data class ApiKeyCredential(val value: String) : Credential
data class ApiPrincipal(val apiKeyCredential: ApiKeyCredential?) : Principal
/**
* Represents a Api Key authentication provider
* @param name is the name of the provider, or `null` for a default provider
*/
class ApiKeyAuthenticationProvider(name: String?) : AuthenticationProvider(name) {
internal var authenticationFunction: suspend ApplicationCall.(ApiKeyCredential) -> Principal? = { null }
class ApiKeyAuthenticationProvider(configuration: Configuration) : AuthenticationProvider(configuration) {
var apiKeyName: String = "";
private val authenticationFunction = configuration.authenticationFunction
var apiKeyLocation: ApiKeyLocation = ApiKeyLocation.QUERY;
private val apiKeyName: String = configuration.apiKeyName
/**
* Sets a validation function that will check given [ApiKeyCredential] instance and return [Principal],
* or null if credential does not correspond to an authenticated principal
*/
fun validate(body: suspend ApplicationCall.(ApiKeyCredential) -> Principal?) {
authenticationFunction = body
}
}
private val apiKeyLocation: ApiKeyLocation = configuration.apiKeyLocation
fun Authentication.Configuration.apiKeyAuth(name: String? = null, configure: ApiKeyAuthenticationProvider.() -> Unit) {
val provider = ApiKeyAuthenticationProvider(name).apply(configure)
val apiKeyName = provider.apiKeyName
val apiKeyLocation = provider.apiKeyLocation
val authenticate = provider.authenticationFunction
provider.pipeline.intercept(AuthenticationPipeline.RequestAuthentication) { context ->
internal fun install() {
pipeline.intercept(AuthenticationPipeline.RequestAuthentication) { context ->
val credentials = call.request.apiKeyAuthenticationCredentials(apiKeyName, apiKeyLocation)
val principal = credentials?.let { authenticate(call, it) }
val principal = credentials?.let { authenticationFunction(call, it) }
val cause = when {
credentials == null -> AuthenticationFailedCause.NoCredentials
@ -61,8 +39,15 @@ fun Authentication.Configuration.apiKeyAuth(name: String? = null, configure: Api
if (cause != null) {
context.challenge(apiKeyName, cause) {
// TODO: Verify correct response structure here.
call.respond(UnauthorizedResponse(HttpAuthHeader.Parameterized("API_KEY", mapOf("key" to apiKeyName), HeaderValueEncoding.QUOTED_ALWAYS)))
call.respond(
UnauthorizedResponse(
HttpAuthHeader.Parameterized(
"API_KEY",
mapOf("key" to apiKeyName),
HeaderValueEncoding.QUOTED_ALWAYS
)
)
)
it.complete()
}
}
@ -73,13 +58,48 @@ fun Authentication.Configuration.apiKeyAuth(name: String? = null, configure: Api
}
}
fun ApplicationRequest.apiKeyAuthenticationCredentials(apiKeyName: String, apiKeyLocation: ApiKeyLocation): ApiKeyCredential? {
class Configuration internal constructor(name: String?) : AuthenticationProvider.Configuration(name) {
internal var authenticationFunction: suspend ApplicationCall.(ApiKeyCredential) -> Principal? = {
throw NotImplementedError(
"Api Key auth validate function is not specified. Use apiKeyAuth { validate { ... } } to fix."
)
}
var apiKeyName: String = ""
var apiKeyLocation: ApiKeyLocation = ApiKeyLocation.QUERY
/**
* Sets a validation function that will check given [ApiKeyCredential] instance and return [Principal],
* or null if credential does not correspond to an authenticated principal
*/
fun validate(body: suspend ApplicationCall.(ApiKeyCredential) -> Principal?) {
authenticationFunction = body
}
}
}
fun Authentication.Configuration.apiKeyAuth(
name: String? = null,
configure: ApiKeyAuthenticationProvider.Configuration.() -> Unit
) {
val configuration = ApiKeyAuthenticationProvider.Configuration(name).apply(configure)
val provider = ApiKeyAuthenticationProvider(configuration)
provider.install()
register(provider)
}
fun ApplicationRequest.apiKeyAuthenticationCredentials(
apiKeyName: String,
apiKeyLocation: ApiKeyLocation
): ApiKeyCredential? {
val value: String? = when (apiKeyLocation) {
ApiKeyLocation.QUERY -> this.queryParameters[apiKeyName]
ApiKeyLocation.HEADER -> this.headers[apiKeyName]
}
when (value) {
null -> return null
else -> return ApiKeyCredential(value)
return when (value) {
null -> null
else -> ApiKeyCredential(value)
}
}

View File

@ -28,15 +28,16 @@ import io.ktor.features.HSTS
{{/featureHSTS}}
import io.ktor.gson.GsonConverter
import io.ktor.http.ContentType
{{#featureLocations}}
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Locations
import io.ktor.metrics.Metrics
{{/featureLocations}}
import io.ktor.routing.Routing
import java.util.concurrent.TimeUnit
import io.ktor.util.KtorExperimentalAPI
{{#hasAuthMethods}}
import io.ktor.auth.Authentication
import io.ktor.auth.oauth
import io.ktor.metrics.dropwizard.DropwizardMetrics
import org.openapitools.server.infrastructure.ApiKeyCredential
import org.openapitools.server.infrastructure.ApiPrincipal
import org.openapitools.server.infrastructure.apiKeyAuth
@ -44,18 +45,18 @@ import org.openapitools.server.infrastructure.apiKeyAuth
{{#generateApis}}{{#apiInfo}}{{#apis}}import {{apiPackage}}.{{classname}}
{{/apis}}{{/apiInfo}}{{/generateApis}}
@KtorExperimentalAPI
internal val settings = HoconApplicationConfig(ConfigFactory.defaultApplication(HTTP::class.java.classLoader))
object HTTP {
val client = HttpClient(Apache)
}
@KtorExperimentalAPI
{{#featureLocations}}
@KtorExperimentalLocationsAPI
{{/featureLocations}}
fun Application.main() {
install(DefaultHeaders)
install(Metrics) {
install(DropwizardMetrics) {
val reporter = Slf4jReporter.forRegistry(registry)
.outputTo(log)
.convertRatesTo(TimeUnit.SECONDS)
@ -82,7 +83,9 @@ fun Application.main() {
{{#featureCompression}}
install(Compression, ApplicationCompressionConfiguration()) // see http://ktor.io/features/compression.html
{{/featureCompression}}
{{#featureLocations}}
install(Locations) // see http://ktor.io/features/locations.html
{{/featureLocations}}
{{#hasAuthMethods}}
install(Authentication) {
{{#authMethods}}
@ -138,8 +141,7 @@ fun Application.main() {
{{/generateApis}}
environment.monitor.subscribe(ApplicationStopping)
{
environment.monitor.subscribe(ApplicationStopping) {
HTTP.client.close()
}
}

View File

@ -8,11 +8,7 @@ import io.ktor.features.deflate
import io.ktor.features.gzip
import io.ktor.features.minimumSize
import io.ktor.http.HttpMethod
import io.ktor.util.KtorExperimentalAPI
import java.time.Duration
import java.util.concurrent.Executors
import {{packageName}}.settings
import java.util.concurrent.TimeUnit
{{#featureCORS}}
/**
@ -49,7 +45,7 @@ internal fun ApplicationCORSConfiguration(): CORS.Configuration.() -> Unit {
*/
internal fun ApplicationHstsConfiguration(): HSTS.Configuration.() -> Unit {
return {
maxAge = Duration.ofDays(365)
maxAgeInSeconds = TimeUnit.DAYS.toSeconds(365)
includeSubDomains = true
preload = false
@ -82,7 +78,6 @@ internal fun ApplicationCompressionConfiguration(): Compression.Configuration.()
{{/featureCompression}}
// Defines authentication mechanisms used throughout the application.
@KtorExperimentalAPI
val ApplicationAuthProviders: Map<String, OAuthServerSettings> = listOf<OAuthServerSettings>(
{{#authMethods}}
{{#isOAuth}}
@ -109,6 +104,3 @@ val ApplicationAuthProviders: Map<String, OAuthServerSettings> = listOf<OAuthSer
// defaultScopes = listOf("public_profile")
// )
).associateBy { it.name }
// Provides an application-level fixed thread pool on which to execute coroutines (mainly)
internal val ApplicationExecutors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 4)

View File

@ -3,6 +3,7 @@ package {{packageName}}
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Location
import {{packageName}}.models.*
{{#imports}}import {{import}}
{{/imports}}
@ -11,7 +12,6 @@ object Paths {
{{#apis}}
{{#operations}}
{{#operation}}
{{^bodyAllowed}}
/**
* {{summary}}
* {{#unescapedNotes}}{{.}}{{/unescapedNotes}}
@ -20,7 +20,6 @@ object Paths {
@KtorExperimentalLocationsAPI
@Location("{{path}}") class {{operationId}}({{#allParams}}val {{paramName}}: {{{dataType}}}{{^required}}? = null{{/required}}{{#required}}{{#isNullable}}?{{/isNullable}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}})
{{/bodyAllowed}}
{{/operation}}
{{/operations}}
{{/apis}}

View File

@ -8,8 +8,8 @@ Generated by OpenAPI Generator {{generatorVersion}}{{^hideGenerationTimestamp}}
## Requires
* Kotlin 1.3.21
* Gradle 4.9
* Kotlin 1.4.31
* Gradle 6.8.2
## Build

View File

@ -1,8 +1,5 @@
{{#hasAuthMethods}}
{{>libraries/ktor/_principal}}
if (principal == null) {
call.respond(HttpStatusCode.Unauthorized)
} else {
{{#examples}}
{{#-first}}
{{#lambda.indented}}{{>_response}}{{/lambda.indented}}
@ -11,7 +8,6 @@ if (principal == null) {
{{^examples}}
call.respond(HttpStatusCode.NotImplemented)
{{/examples}}
}
{{/hasAuthMethods}}
{{^hasAuthMethods}}
{{#examples}}

View File

@ -1,11 +1,11 @@
{{#authMethods}}
{{#isBasic}}
val principal = call.authentication.principal<UserIdPrincipal>()
val principal = call.authentication.principal<UserIdPrincipal>()!!
{{/isBasic}}
{{#isApiKey}}
val principal = call.authentication.principal<ApiPrincipal>()
val principal = call.authentication.principal<ApiPrincipal>()!!
{{/isApiKey}}
{{#isOAuth}}
val principal = call.authentication.principal<OAuthAccessTokenResponse>()
val principal = call.authentication.principal<OAuthAccessTokenResponse>()!!
{{/isOAuth}}
{{/authMethods}}

View File

@ -10,17 +10,29 @@ import io.ktor.auth.OAuthAccessTokenResponse
import io.ktor.auth.OAuthServerSettings
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.delete
import io.ktor.locations.get
import io.ktor.response.respond
import io.ktor.response.respondText
import io.ktor.routing.Route
{{#featureLocations}}
import {{packageName}}.Paths
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.delete
import io.ktor.locations.get
import io.ktor.locations.post
import io.ktor.locations.put
import io.ktor.locations.options
import io.ktor.locations.head
{{/featureLocations}}
{{^featureLocations}}
import io.ktor.routing.delete
import io.ktor.routing.get
import io.ktor.routing.post
import io.ktor.routing.put
import io.ktor.routing.options
import io.ktor.routing.head
import io.ktor.routing.route
{{/featureLocations}}
import {{packageName}}.Paths
import {{packageName}}.infrastructure.ApiPrincipal
@ -28,34 +40,33 @@ import {{packageName}}.infrastructure.ApiPrincipal
{{/imports}}
{{#operations}}
{{#featureLocations}}
@KtorExperimentalLocationsAPI
{{/featureLocations}}
fun Route.{{classname}}() {
val gson = Gson()
val empty = mutableMapOf<String, Any?>()
{{#operation}}
{{#bodyAllowed}}
route("{{path}}") {
{{#hasAuthMethods}}
{{#authMethods}}
authenticate("{{{name}}}") {
{{/authMethods}}
{{/hasAuthMethods}}
{{^featureLocations}}
route("{{path}}") {
{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}} {
{{#lambda.indented_12}}{{>libraries/ktor/_api_body}}{{/lambda.indented_12}}
}
}
{{/featureLocations}}
{{#featureLocations}}
{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}<Paths.{{operationId}}> {
{{#lambda.indented_8}}{{>libraries/ktor/_api_body}}{{/lambda.indented_8}}
}
{{/featureLocations}}
{{#hasAuthMethods}}
}
{{/hasAuthMethods}}
}
{{/bodyAllowed}}
{{^bodyAllowed}}
{{! NOTE: Locations can be used on routes without body parameters.}}
{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}<Paths.{{operationId}}> { _: Paths.{{operationId}} ->
{{#lambda.indented_8}}{{>libraries/ktor/_api_body}}{{/lambda.indented_8}}
}
{{/bodyAllowed}}
{{/operation}}
}

View File

@ -2,13 +2,13 @@ group '{{groupId}}'
version '{{artifactVersion}}'
wrapper {
gradleVersion = '4.9'
gradleVersion = '6.8.2'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
ext.kotlin_version = '1.3.21'
ext.ktor_version = '1.1.3'
ext.kotlin_version = '1.4.31'
ext.ktor_version = '1.5.2'
ext.shadow_version = '2.0.3'
repositories {
@ -29,7 +29,7 @@ apply plugin: 'application'
mainClassName = "io.ktor.server.netty.DevelopmentEngine"
// Initialization order with shadow 2.0.1 and Gradle 4.3 is weird.
// Initialization order with shadow 2.0.1 and Gradle 6.8.2 is weird.
// See https://github.com/johnrengelman/shadow/issues/336#issuecomment-355402508
apply plugin: 'com.github.johnrengelman.shadow'
@ -59,7 +59,10 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "io.ktor:ktor-server-netty:$ktor_version"
compile "io.ktor:ktor-metrics:$ktor_version"
compile "io.ktor:ktor-auth:$ktor_version"
{{#featureLocations}}
compile "io.ktor:ktor-locations:$ktor_version"
{{/featureLocations}}
compile "io.ktor:ktor-gson:$ktor_version"
compile "io.ktor:ktor-client-core:$ktor_version"
compile "io.ktor:ktor-client-apache:$ktor_version"

View File

@ -28,9 +28,9 @@ Name | Type | Description | Notes
{{#optionalVars}}
**{{name}}** | {{^complexType}}**{{dataType}}**{{/complexType}}{{#complexType}}[**{{dataType}}**]({{complexType}}.md){{/complexType}} | {{description}} | [optional] {{#isReadOnly}}[readonly] {{/isReadOnly}}{{#defaultValue}} if omitted the server will use the default value of {{{.}}}{{/defaultValue}}
{{/optionalVars}}
{{#additionalPropertiesType}}
**any string name** | **{{additionalPropertiesType}}** | any string name can be used but the value must be the correct type | [optional]
{{/additionalPropertiesType}}
{{#additionalProperties}}
**any string name** | {{^complexType}}**{{dataType}}**{{/complexType}}{{#complexType}}[**{{dataType}}**]({{complexType}}.md){{/complexType}} | any string name can be used but the value must be the correct type | [optional]
{{/additionalProperties}}
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@ -60,7 +60,7 @@
{{/optionalVars}}
}
{{#additionalPropertiesType}}
{{#additionalProperties}}
@cached_property
def additional_properties_type():
"""
@ -72,11 +72,11 @@
lazy_import()
{{/-first}}
{{/imports}}
return ({{{additionalPropertiesType}}},) # noqa: E501
{{/additionalPropertiesType}}
{{^additionalPropertiesType}}
return ({{{dataType}}},) # noqa: E501
{{/additionalProperties}}
{{^additionalProperties}}
additional_properties_type = None
{{/additionalPropertiesType}}
{{/additionalProperties}}
_nullable = {{#isNullable}}True{{/isNullable}}{{^isNullable}}False{{/isNullable}}

View File

@ -10,7 +10,52 @@
'_additional_properties_model_instances',
])
{{> model_templates/method_init_shared }}
@convert_js_args_to_python_args
def __init__(self, *args, **kwargs): # noqa: E501
"""{{classname}} - a model defined in OpenAPI
Keyword Args:
{{#requiredVars}}
{{#defaultValue}}
{{name}} ({{{dataType}}}):{{#description}} {{{description}}}.{{/description}} defaults to {{{defaultValue}}}{{#allowableValues}}, must be one of [{{#enumVars}}{{{value}}}, {{/enumVars}}]{{/allowableValues}} # noqa: E501
{{/defaultValue}}
{{^defaultValue}}
{{name}} ({{{dataType}}}):{{#description}} {{{description}}}{{/description}}
{{/defaultValue}}
{{/requiredVars}}
{{> model_templates/docstring_init_required_kwargs }}
{{#optionalVars}}
{{name}} ({{{dataType}}}):{{#description}} {{{description}}}.{{/description}} [optional]{{#defaultValue}} if omitted the server will use the default value of {{{defaultValue}}}{{/defaultValue}} # noqa: E501
{{/optionalVars}}
"""
{{#requiredVars}}
{{#defaultValue}}
{{name}} = kwargs.get('{{name}}', {{{defaultValue}}})
{{/defaultValue}}
{{/requiredVars}}
_check_type = kwargs.pop('_check_type', True)
_spec_property_naming = kwargs.pop('_spec_property_naming', False)
_path_to_item = kwargs.pop('_path_to_item', ())
_configuration = kwargs.pop('_configuration', None)
_visited_composed_classes = kwargs.pop('_visited_composed_classes', ())
if args:
raise ApiTypeError(
"Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % (
args,
self.__class__.__name__,
),
path_to_item=_path_to_item,
valid_classes=(self.__class__,),
)
self._data_store = {}
self._check_type = _check_type
self._spec_property_naming = _spec_property_naming
self._path_to_item = _path_to_item
self._configuration = _configuration
self._visited_composed_classes = _visited_composed_classes + (self.__class__,)
constant_args = {
'_check_type': _check_type,
@ -19,28 +64,18 @@
'_configuration': _configuration,
'_visited_composed_classes': self._visited_composed_classes,
}
required_args = {
{{#requiredVars}}
'{{name}}': {{name}},
{{/requiredVars}}
}
model_args = {}
model_args.update(required_args)
model_args.update(kwargs)
composed_info = validate_get_composed_info(
constant_args, model_args, self)
constant_args, kwargs, self)
self._composed_instances = composed_info[0]
self._var_name_to_model_instances = composed_info[1]
self._additional_properties_model_instances = composed_info[2]
unused_args = composed_info[3]
discarded_args = composed_info[3]
for var_name, var_value in required_args.items():
setattr(self, var_name, var_value)
for var_name, var_value in kwargs.items():
if var_name in unused_args and \
if var_name in discarded_args and \
self._configuration is not None and \
self._configuration.discard_unknown_keys and \
not self._additional_properties_model_instances:
self._additional_properties_model_instances:
# discard variable.
continue
setattr(self, var_name, var_value)

View File

@ -4,27 +4,43 @@
self.__dict__[name] = value
return
# set the attribute on the correct instance
model_instances = self._var_name_to_model_instances.get(
name, self._additional_properties_model_instances)
if model_instances:
for model_instance in model_instances:
if model_instance == self:
self.set_attribute(name, value)
else:
setattr(model_instance, name, value)
if name not in self._var_name_to_model_instances:
# we assigned an additional property
self.__dict__['_var_name_to_model_instances'][name] = (
model_instance
)
return None
"""
Use cases:
1. additional_properties_type is None (additionalProperties == False in spec)
Check for property presence in self.openapi_types
if not present then throw an error
if present set in self, set attribute
always set on composed schemas
2. additional_properties_type exists
set attribute on self
always set on composed schemas
"""
if self.additional_properties_type is None:
"""
For an attribute to exist on a composed schema it must:
- fulfill schema_requirements in the self composed schema not considering oneOf/anyOf/allOf schemas AND
- fulfill schema_requirements in each oneOf/anyOf/allOf schemas
schema_requirements:
For an attribute to exist on a schema it must:
- be present in properties at the schema OR
- have additionalProperties unset (defaults additionalProperties = any type) OR
- have additionalProperties set
"""
if name not in self.openapi_types:
raise ApiAttributeError(
"{0} has no attribute '{1}'".format(
type(self).__name__, name),
[e for e in [self._path_to_item, name] if e]
)
# attribute must be set on self and composed instances
self.set_attribute(name, value)
for model_instance in self._composed_instances:
setattr(model_instance, name, value)
if name not in self._var_name_to_model_instances:
# we assigned an additional property
self.__dict__['_var_name_to_model_instances'][name] = self._composed_instances + [self]
return None
__unset_attribute_value__ = object()
@ -34,13 +50,12 @@
return self.__dict__[name]
# get the attribute from the correct instance
model_instances = self._var_name_to_model_instances.get(
name, self._additional_properties_model_instances)
model_instances = self._var_name_to_model_instances.get(name)
values = []
# A composed model stores child (oneof/anyOf/allOf) models under
# self._var_name_to_model_instances. A named property can exist in
# multiple child models. If the property is present in more than one
# child model, the value must be the same across all the child models.
# A composed model stores self and child (oneof/anyOf/allOf) models under
# self._var_name_to_model_instances.
# Any property must exist in self and all model instances
# The value stored in all model instances must be the same
if model_instances:
for model_instance in model_instances:
if name in model_instance._data_store:

View File

@ -1302,8 +1302,13 @@ def get_allof_instances(self, model_args, constant_args):
self: the class we are handling
model_args (dict): var_name to var_value
used to make instances
constant_args (dict): var_name to var_value
used to make instances
constant_args (dict):
metadata arguments:
_check_type
_path_to_item
_spec_property_naming
_configuration
_visited_composed_classes
Returns
composed_instances (list)
@ -1311,20 +1316,8 @@ def get_allof_instances(self, model_args, constant_args):
composed_instances = []
for allof_class in self._composed_schemas['allOf']:
# no need to handle changing js keys to python because
# for composed schemas, allof parameters are included in the
# composed schema and were changed to python keys in __new__
# extract a dict of only required keys from fixed_model_args
kwargs = {}
var_names = set(allof_class.openapi_types.keys())
for var_name in var_names:
if var_name in model_args:
kwargs[var_name] = model_args[var_name]
# and use it to make the instance
kwargs.update(constant_args)
try:
allof_instance = allof_class(**kwargs)
allof_instance = allof_class(**model_args, **constant_args)
composed_instances.append(allof_instance)
except Exception as ex:
raise ApiValueError(
@ -1384,31 +1377,9 @@ def get_oneof_instance(cls, model_kwargs, constant_kwargs, model_arg=None):
single_value_input = allows_single_value_input(oneof_class)
if not single_value_input:
# transform js keys from input data to python keys in fixed_model_args
fixed_model_args = change_keys_js_to_python(
model_kwargs, oneof_class)
# Extract a dict with the properties that are declared in the oneOf schema.
# Undeclared properties (e.g. properties that are allowed because of the
# additionalProperties attribute in the OAS document) are not added to
# the dict.
kwargs = {}
var_names = set(oneof_class.openapi_types.keys())
for var_name in var_names:
if var_name in fixed_model_args:
kwargs[var_name] = fixed_model_args[var_name]
# do not try to make a model with no input args
if len(kwargs) == 0:
continue
# and use it to make the instance
kwargs.update(constant_kwargs)
try:
if not single_value_input:
oneof_instance = oneof_class(**kwargs)
oneof_instance = oneof_class(**model_kwargs, **constant_kwargs)
else:
if issubclass(oneof_class, ModelSimple):
oneof_instance = oneof_class(model_arg, **constant_kwargs)
@ -1465,24 +1436,8 @@ def get_anyof_instances(self, model_args, constant_args):
# none_type deserialization is handled in the __new__ method
continue
# transform js keys to python keys in fixed_model_args
fixed_model_args = change_keys_js_to_python(model_args, anyof_class)
# extract a dict of only required keys from these_model_vars
kwargs = {}
var_names = set(anyof_class.openapi_types.keys())
for var_name in var_names:
if var_name in fixed_model_args:
kwargs[var_name] = fixed_model_args[var_name]
# do not try to make a model with no input args
if len(kwargs) == 0:
continue
# and use it to make the instance
kwargs.update(constant_args)
try:
anyof_instance = anyof_class(**kwargs)
anyof_instance = anyof_class(**model_args, **constant_args)
anyof_instances.append(anyof_instance)
except Exception:
pass
@ -1495,47 +1450,34 @@ def get_anyof_instances(self, model_args, constant_args):
return anyof_instances
def get_additional_properties_model_instances(
composed_instances, self):
additional_properties_model_instances = []
all_instances = [self]
all_instances.extend(composed_instances)
for instance in all_instances:
if instance.additional_properties_type is not None:
additional_properties_model_instances.append(instance)
return additional_properties_model_instances
def get_var_name_to_model_instances(self, composed_instances):
var_name_to_model_instances = {}
all_instances = [self]
all_instances.extend(composed_instances)
for instance in all_instances:
for var_name in instance.openapi_types:
if var_name not in var_name_to_model_instances:
var_name_to_model_instances[var_name] = [instance]
else:
var_name_to_model_instances[var_name].append(instance)
return var_name_to_model_instances
def get_unused_args(self, composed_instances, model_args):
unused_args = dict(model_args)
# arguments apssed to self were already converted to python names
def get_discarded_args(self, composed_instances, model_args):
"""
Gathers the args that were discarded by configuration.discard_unknown_keys
"""
model_arg_keys = model_args.keys()
discarded_args = set()
# arguments passed to self were already converted to python names
# before __init__ was called
for var_name_py in self.attribute_map:
if var_name_py in unused_args:
del unused_args[var_name_py]
for instance in composed_instances:
if instance.__class__ in self._composed_schemas['allOf']:
for var_name_py in instance.attribute_map:
if var_name_py in unused_args:
del unused_args[var_name_py]
try:
keys = instance.to_dict().keys()
discarded_keys = model_args - keys
discarded_args.update(discarded_keys)
except Exception:
# allOf integer schema will throw exception
pass
else:
for var_name_js in instance.attribute_map.values():
if var_name_js in unused_args:
del unused_args[var_name_js]
return unused_args
try:
all_keys = set(model_to_dict(instance, serialize=False).keys())
js_keys = model_to_dict(instance, serialize=True).keys()
all_keys.update(js_keys)
discarded_keys = model_arg_keys - all_keys
discarded_args.update(discarded_keys)
except Exception:
# allOf integer schema will throw exception
pass
return discarded_args
def validate_get_composed_info(constant_args, model_args, self):
@ -1579,36 +1521,42 @@ def validate_get_composed_info(constant_args, model_args, self):
composed_instances.append(oneof_instance)
anyof_instances = get_anyof_instances(self, model_args, constant_args)
composed_instances.extend(anyof_instances)
"""
set additional_properties_model_instances
additional properties must be evaluated at the schema level
so self's additional properties are most important
If self is a composed schema with:
- no properties defined in self
- additionalProperties: False
Then for object payloads every property is an additional property
and they are not allowed, so only empty dict is allowed
Properties must be set on all matching schemas
so when a property is assigned toa composed instance, it must be set on all
composed instances regardless of additionalProperties presence
keeping it to prevent breaking changes in v5.0.1
TODO remove cls._additional_properties_model_instances in 6.0.0
"""
additional_properties_model_instances = []
if self.additional_properties_type is not None:
additional_properties_model_instances = [self]
"""
no need to set properties on self in here, they will be set in __init__
By here all composed schema oneOf/anyOf/allOf instances have their properties set using
model_args
"""
discarded_args = get_discarded_args(self, composed_instances, model_args)
# map variable names to composed_instances
var_name_to_model_instances = get_var_name_to_model_instances(
self, composed_instances)
# set additional_properties_model_instances
additional_properties_model_instances = (
get_additional_properties_model_instances(composed_instances, self)
)
# set any remaining values
unused_args = get_unused_args(self, composed_instances, model_args)
if len(unused_args) > 0 and \
len(additional_properties_model_instances) == 0 and \
(self._configuration is None or
not self._configuration.discard_unknown_keys):
raise ApiValueError(
"Invalid input arguments input when making an instance of "
"class %s. Not all inputs were used. The unused input data "
"is %s" % (self.__class__.__name__, unused_args)
)
# no need to add additional_properties to var_name_to_model_instances here
# because additional_properties_model_instances will direct us to that
# instance when we use getattr or setattr
# and we update var_name_to_model_instances in setattr
var_name_to_model_instances = {}
for prop_name in model_args:
if prop_name not in discarded_args:
var_name_to_model_instances[prop_name] = [self] + composed_instances
return [
composed_instances,
var_name_to_model_instances,
additional_properties_model_instances,
unused_args
discarded_args
]

View File

@ -9,6 +9,7 @@ import org.openapitools.codegen.DefaultGenerator;
import org.openapitools.codegen.languages.AbstractKotlinCodegen;
import org.openapitools.codegen.languages.KotlinClientCodegen;
import org.openapitools.codegen.languages.KotlinServerCodegen;
import org.openapitools.codegen.languages.KotlinServerDeprecatedCodegen;
import org.openapitools.codegen.languages.KotlinSpringServerCodegen;
import org.openapitools.codegen.languages.KotlinVertxServerCodegen;
import org.testng.annotations.DataProvider;
@ -28,6 +29,7 @@ public class KotlinModelCodegenTest {
return new Object[][]{
{new KotlinClientCodegen()},
{new KotlinServerCodegen()},
{new KotlinServerDeprecatedCodegen()},
{new KotlinSpringServerCodegen()},
{new KotlinVertxServerCodegen()},
};

View File

@ -19,7 +19,7 @@ package org.openapitools.codegen.options;
import com.google.common.collect.ImmutableMap;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.languages.PythonLegacyClientCodegen;
import org.openapitools.codegen.languages.PythonClientCodegen;
import java.util.Map;
@ -30,6 +30,8 @@ public class PythonClientOptionsProvider implements OptionsProvider {
public static final String PACKAGE_URL_VALUE = "";
public static final String USE_NOSE_VALUE = "false";
public static final String RECURSION_LIMIT = "1200";
public static final String DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT = "false";
public static final String PYTHON_ATTR_NONE_IF_UNSET = "false";
@Override
public String getLanguage() {
@ -39,16 +41,17 @@ public class PythonClientOptionsProvider implements OptionsProvider {
@Override
public Map<String, String> createOptions() {
ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<String, String>();
return builder.put(PythonLegacyClientCodegen.PACKAGE_URL, PACKAGE_URL_VALUE)
return builder.put(PythonClientCodegen.PACKAGE_URL, PACKAGE_URL_VALUE)
.put(CodegenConstants.PACKAGE_NAME, PACKAGE_NAME_VALUE)
.put(CodegenConstants.PROJECT_NAME, PROJECT_NAME_VALUE)
.put(CodegenConstants.PACKAGE_VERSION, PACKAGE_VERSION_VALUE)
.put(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG, "true")
.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, "true")
.put(CodegenConstants.SOURCECODEONLY_GENERATION, "false")
.put(CodegenConstants.LIBRARY, "urllib3")
.put(PythonLegacyClientCodegen.USE_NOSE, USE_NOSE_VALUE)
.put(PythonLegacyClientCodegen.RECURSION_LIMIT, RECURSION_LIMIT)
.put(CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT, DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT)
.put(PythonClientCodegen.USE_NOSE, USE_NOSE_VALUE)
.put(PythonClientCodegen.RECURSION_LIMIT, RECURSION_LIMIT)
.put(PythonClientCodegen.PYTHON_ATTR_NONE_IF_UNSET, PYTHON_ATTR_NONE_IF_UNSET)
.build();
}

View File

@ -0,0 +1,59 @@
/*
* Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
* Copyright 2018 SmartBear Software
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openapitools.codegen.options;
import com.google.common.collect.ImmutableMap;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.languages.PythonLegacyClientCodegen;
import java.util.Map;
public class PythonLegacyClientOptionsProvider implements OptionsProvider {
public static final String PACKAGE_NAME_VALUE = "swagger_client_python";
public static final String PROJECT_NAME_VALUE = "swagger-client-python";
public static final String PACKAGE_VERSION_VALUE = "1.0.0-SNAPSHOT";
public static final String PACKAGE_URL_VALUE = "";
public static final String USE_NOSE_VALUE = "false";
public static final String RECURSION_LIMIT = "1200";
@Override
public String getLanguage() {
return "python";
}
@Override
public Map<String, String> createOptions() {
ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<String, String>();
return builder.put(PythonLegacyClientCodegen.PACKAGE_URL, PACKAGE_URL_VALUE)
.put(CodegenConstants.PACKAGE_NAME, PACKAGE_NAME_VALUE)
.put(CodegenConstants.PROJECT_NAME, PROJECT_NAME_VALUE)
.put(CodegenConstants.PACKAGE_VERSION, PACKAGE_VERSION_VALUE)
.put(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG, "true")
.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, "true")
.put(CodegenConstants.SOURCECODEONLY_GENERATION, "false")
.put(CodegenConstants.LIBRARY, "urllib3")
.put(PythonLegacyClientCodegen.USE_NOSE, USE_NOSE_VALUE)
.put(PythonLegacyClientCodegen.RECURSION_LIMIT, RECURSION_LIMIT)
.build();
}
@Override
public boolean isServer() {
return false;
}
}

View File

@ -19,7 +19,7 @@ package org.openapitools.codegen.python;
import org.openapitools.codegen.AbstractOptionsTest;
import org.openapitools.codegen.CodegenConfig;
import org.openapitools.codegen.languages.PythonLegacyClientCodegen;
import org.openapitools.codegen.languages.PythonClientCodegen;
import org.openapitools.codegen.options.PythonClientOptionsProvider;
import org.testng.Assert;
@ -29,7 +29,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class PythonClientOptionsTest extends AbstractOptionsTest {
private PythonLegacyClientCodegen clientCodegen = mock(PythonLegacyClientCodegen.class, mockSettings);
private PythonClientCodegen clientCodegen = mock(PythonClientCodegen.class, mockSettings);
public PythonClientOptionsTest() {
super(new PythonClientOptionsProvider());

View File

@ -0,0 +1,54 @@
/*
* Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
* Copyright 2018 SmartBear Software
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openapitools.codegen.python;
import org.openapitools.codegen.AbstractOptionsTest;
import org.openapitools.codegen.CodegenConfig;
import org.openapitools.codegen.languages.PythonLegacyClientCodegen;
import org.openapitools.codegen.options.PythonLegacyClientOptionsProvider;
import org.testng.Assert;
import java.io.File;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class PythonLegacyClientOptionsTest extends AbstractOptionsTest {
private PythonLegacyClientCodegen clientCodegen = mock(PythonLegacyClientCodegen.class, mockSettings);
public PythonLegacyClientOptionsTest() {
super(new PythonLegacyClientOptionsProvider());
}
@Override
protected CodegenConfig getCodegenConfig() {
return clientCodegen;
}
@SuppressWarnings("unused")
@Override
protected void verifyOptions() {
Assert.assertEquals(clientCodegen.packagePath(), PythonLegacyClientOptionsProvider.PACKAGE_NAME_VALUE.replace('.', File.separatorChar));
verify(clientCodegen).setPackageName(PythonLegacyClientOptionsProvider.PACKAGE_NAME_VALUE);
verify(clientCodegen).setProjectName(PythonLegacyClientOptionsProvider.PROJECT_NAME_VALUE);
verify(clientCodegen).setPackageVersion(PythonLegacyClientOptionsProvider.PACKAGE_VERSION_VALUE);
verify(clientCodegen).setPackageUrl(PythonLegacyClientOptionsProvider.PACKAGE_URL_VALUE);
verify(clientCodegen).setUseNose(PythonLegacyClientOptionsProvider.USE_NOSE_VALUE);
}
}

View File

@ -1373,8 +1373,6 @@ definitions:
properties:
breed:
type: string
additionalProperties: false
additionalProperties: false
Cat:
allOf:
- $ref: '#/definitions/Animal'
@ -1523,8 +1521,7 @@ definitions:
type: object
additionalProperties:
type: array
items:
type: object
items: {}
map_map_string:
type: object
additionalProperties:
@ -1535,14 +1532,15 @@ definitions:
type: object
additionalProperties:
type: object
additionalProperties:
type: object
anytype_1:
type: object
anytype_2: {}
additionalProperties: {}
anytype_1: {}
anytype_2:
description: no type is set for this
anytype_3:
type: object
properties: {}
description: 'because of a bug in swagger-parser, this should have values {str: (str, int, float...)}
but instead we get any type. See https://github.com/swagger-api/swagger-parser/issues/1378'
AdditionalPropertiesString:
type: object
properties:
@ -2091,7 +2089,6 @@ definitions:
properties:
interNet:
type: boolean
additionalProperties: false
GrandparentAnimal:
type: object
required:

View File

@ -1646,7 +1646,6 @@ components:
Cat:
allOf:
- $ref: '#/components/schemas/Animal'
- $ref: '#/components/schemas/Address'
- type: object
properties:
declawed:
@ -2107,6 +2106,9 @@ components:
items:
type: object
nullable: true
object_nullable:
type: object
nullable: true
object_nullable_prop:
type: object
nullable: true
@ -2124,18 +2126,27 @@ components:
type: object
nullable: true
additionalProperties:
type: object
nullable: true
ComposedSchemaWithPropsAndNoAddProps:
properties:
color:
type: string
allOf:
- $ref: '#/components/schemas/Tag'
# Below additionalProperties is set to false to validate the use
# case when a composed schema has additionalProperties set to false.
# This definition will only allow in object payloads that set color and no other properties because
# additionalProperties are evaluated at the schema level and do not include composed schema
# properties. Only color is defined here, all others are additional
additionalProperties: false
fruit:
description: a schema that tests oneOf and includes a schema level property
properties:
color:
type: string
oneOf:
- $ref: '#/components/schemas/apple'
- $ref: '#/components/schemas/banana'
# Below additionalProperties is set to false to validate the use
# case when a composed schema has additionalProperties set to false.
additionalProperties: false
apple:
type: object
properties:
@ -2145,12 +2156,16 @@ components:
origin:
type: string
pattern: /^[A-Z\s]*$/i
required:
- cultivar
nullable: true
banana:
type: object
properties:
lengthCm:
type: number
required:
- lengthCm
mammal:
oneOf:
- $ref: '#/components/schemas/whale'
@ -2210,13 +2225,13 @@ components:
anyOf:
- $ref: '#/components/schemas/apple'
- $ref: '#/components/schemas/banana'
additionalProperties: false
fruitReq:
description: a schema where additionalProperties is on in the composed schema and off in the oneOf object schemas
also, this schem accepts null as a value
oneOf:
- type: 'null'
- $ref: '#/components/schemas/appleReq'
- $ref: '#/components/schemas/bananaReq'
additionalProperties: false
appleReq:
type: object
properties:
@ -2320,7 +2335,6 @@ components:
allOf:
- $ref: '#/components/schemas/ShapeInterface'
- $ref: '#/components/schemas/TriangleInterface'
additionalProperties: false
ScaleneTriangle:
allOf:
- $ref: '#/components/schemas/ShapeInterface'

View File

@ -10,7 +10,7 @@
<packaging>pom</packaging>
<name>openapi-generator-project</name>
<!-- RELEASE_VERSION -->
<version>5.1.1</version>
<version>5.2.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<url>https://github.com/openapitools/openapi-generator</url>
<scm>
@ -1206,6 +1206,7 @@
<!--<module>samples/client/petstore/javascript-apollo</module>-->
<module>samples/client/petstore/javascript-flowtyped</module>
<module>samples/client/petstore/python</module>
<module>samples/client/petstore/python_disallowAdditionalPropertiesIfNotPresent</module>
<module>samples/client/petstore/python-legacy</module>
<module>samples/client/petstore/python-asyncio</module>
<module>samples/client/petstore/python-tornado</module>

View File

@ -1 +1 @@
5.1.1
5.2.0-SNAPSHOT

View File

@ -1 +1 @@
5.1.1
5.2.0-SNAPSHOT

View File

@ -1 +1 @@
5.1.1
5.2.0-SNAPSHOT

View File

@ -1 +1 @@
5.1.1
5.2.0-SNAPSHOT

View File

@ -1 +1 @@
5.1.1
5.2.0-SNAPSHOT

View File

@ -1 +1 @@
5.1.1
5.2.0-SNAPSHOT

View File

@ -4,7 +4,7 @@
*
* The version of the OpenAPI document: 1.0.0
*
* NOTE: This class is auto generated by OpenAPI-Generator 5.1.1.
* NOTE: This class is auto generated by OpenAPI-Generator 5.2.0-SNAPSHOT.
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@ -4,7 +4,7 @@
*
* The version of the OpenAPI document: 1.0.0
*
* NOTE: This class is auto generated by OpenAPI-Generator 5.1.1.
* NOTE: This class is auto generated by OpenAPI-Generator 5.2.0-SNAPSHOT.
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@ -4,7 +4,7 @@
*
* The version of the OpenAPI document: 1.0.0
*
* NOTE: This class is auto generated by OpenAPI-Generator 5.1.1.
* NOTE: This class is auto generated by OpenAPI-Generator 5.2.0-SNAPSHOT.
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@ -4,7 +4,7 @@
*
* The version of the OpenAPI document: 1.0.0
*
* NOTE: This class is auto generated by OpenAPI-Generator 5.1.1.
* NOTE: This class is auto generated by OpenAPI-Generator 5.2.0-SNAPSHOT.
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@ -4,7 +4,7 @@
*
* The version of the OpenAPI document: 1.0.0
*
* NOTE: This class is auto generated by OpenAPI-Generator 5.1.1.
* NOTE: This class is auto generated by OpenAPI-Generator 5.2.0-SNAPSHOT.
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@ -4,7 +4,7 @@
*
* The version of the OpenAPI document: 1.0.0
*
* NOTE: This class is auto generated by OpenAPI-Generator 5.1.1.
* NOTE: This class is auto generated by OpenAPI-Generator 5.2.0-SNAPSHOT.
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@ -4,7 +4,7 @@
*
* The version of the OpenAPI document: 1.0.0
*
* NOTE: This class is auto generated by OpenAPI-Generator 5.1.1.
* NOTE: This class is auto generated by OpenAPI-Generator 5.2.0-SNAPSHOT.
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@ -4,7 +4,7 @@
*
* The version of the OpenAPI document: 1.0.0
*
* NOTE: This class is auto generated by OpenAPI-Generator 5.1.1.
* NOTE: This class is auto generated by OpenAPI-Generator 5.2.0-SNAPSHOT.
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@ -4,7 +4,7 @@
*
* The version of the OpenAPI document: 1.0.0
*
* NOTE: This class is auto generated by OpenAPI-Generator 5.1.1.
* NOTE: This class is auto generated by OpenAPI-Generator 5.2.0-SNAPSHOT.
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@ -4,7 +4,7 @@
*
* The version of the OpenAPI document: 1.0.0
*
* NOTE: This class is auto generated by OpenAPI-Generator 5.1.1.
* NOTE: This class is auto generated by OpenAPI-Generator 5.2.0-SNAPSHOT.
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@ -4,7 +4,7 @@
*
* The version of the OpenAPI document: 1.0.0
*
* NOTE: This class is auto generated by OpenAPI-Generator 5.1.1.
* NOTE: This class is auto generated by OpenAPI-Generator 5.2.0-SNAPSHOT.
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@ -4,7 +4,7 @@
*
* The version of the OpenAPI document: 1.0.0
*
* NOTE: This class is auto generated by OpenAPI-Generator 5.1.1.
* NOTE: This class is auto generated by OpenAPI-Generator 5.2.0-SNAPSHOT.
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@ -4,7 +4,7 @@
*
* The version of the OpenAPI document: 1.0.0
*
* NOTE: This class is auto generated by OpenAPI-Generator 5.1.1.
* NOTE: This class is auto generated by OpenAPI-Generator 5.2.0-SNAPSHOT.
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@ -4,7 +4,7 @@
*
* The version of the OpenAPI document: 1.0.0
*
* NOTE: This class is auto generated by OpenAPI-Generator 5.1.1.
* NOTE: This class is auto generated by OpenAPI-Generator 5.2.0-SNAPSHOT.
* https://openapi-generator.tech
* Do not edit the class manually.
*/

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