forked from loafle/openapi-generator-original
Merge remote-tracking branch 'origin/master' into 6.0.x
This commit is contained in:
commit
5e29d61639
@ -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
|
||||
|
1
.github/workflows/sonar.yml
vendored
1
.github/workflows/sonar.yml
vendored
@ -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
|
||||
|
@ -1,6 +1,5 @@
|
||||
kind: pipeline
|
||||
name: default
|
||||
|
||||
steps:
|
||||
# test Java 11 HTTP client
|
||||
- name: java11-test
|
||||
@ -53,4 +52,12 @@ steps:
|
||||
- name: haskell-client-test
|
||||
image: haskell:8.6.5
|
||||
commands:
|
||||
- (cd samples/client/petstore/haskell-http-client/ && stack --install-ghc --no-haddock-deps haddock --fast && stack test --fast)
|
||||
- (cd samples/client/petstore/haskell-http-client/ && stack --allow-different-user --install-ghc --no-haddock-deps haddock --fast && stack --allow-different-user test --fast)
|
||||
# test erlang client and server
|
||||
- name: erlang
|
||||
image: erlang:alpine
|
||||
commands:
|
||||
- (cd samples/client/petstore/erlang-client && rebar3 compile)
|
||||
- (cd samples/client/petstore/erlang-proper && rebar3 compile)
|
||||
# comment out as the tests pass locally but not in the CI
|
||||
#- (cd samples/server/petstore/erlang-server && rebar3 compile)
|
||||
|
@ -48,6 +48,7 @@ Code change should conform to the programming style guide of the respective lang
|
||||
- C#: https://msdn.microsoft.com/en-us/library/vstudio/ff926074.aspx
|
||||
- C++: https://google.github.io/styleguide/cppguide.html
|
||||
- C++ (Tizen): https://wiki.tizen.org/Native_Platform_Coding_Idiom_and_Style_Guide#C.2B.2B_Coding_Style
|
||||
- C++ (Unreal Engine 4): https://docs.unrealengine.com/en-US/ProductionPipelines/DevelopmentSetup/CodingStandard/index.html
|
||||
- Clojure: https://github.com/bbatsov/clojure-style-guide
|
||||
- Crystal: https://crystal-lang.org/reference/conventions/coding_style.html
|
||||
- Dart: https://www.dartlang.org/guides/language/effective-dart/style
|
||||
|
34
README.md
34
README.md
@ -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`):
|
||||
[](https://travis-ci.org/OpenAPITools/openapi-generator)
|
||||
[](https://circleci.com/gh/OpenAPITools/openapi-generator)
|
||||
[](https://app.shippable.com/github/OpenAPITools/openapi-generator)
|
||||
[](https://ci.appveyor.com/project/WilliamCheng/openapi-generator-wh2wu)
|
||||
[](https://cloud.drone.io/OpenAPITools/openapi-generator)
|
||||
[](https://app.bitrise.io/app/4a2b10a819d12b67)
|
||||
[](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`):
|
||||
[](https://travis-ci.org/OpenAPITools/openapi-generator)
|
||||
[](https://circleci.com/gh/OpenAPITools/openapi-generator)
|
||||
[](https://app.shippable.com/github/OpenAPITools/openapi-generator)
|
||||
[](https://ci.appveyor.com/project/WilliamCheng/openapi-generator-wh2wu)
|
||||
[](https://cloud.drone.io/OpenAPITools/openapi-generator)
|
||||
[](https://app.bitrise.io/app/4a2b10a819d12b67)
|
||||
|
||||
[6.0.x](https://github.com/OpenAPITools/openapi-generator/tree/6.0.x) (`6.0.x`):
|
||||
[](https://travis-ci.org/OpenAPITools/openapi-generator)
|
||||
[](https://circleci.com/gh/OpenAPITools/openapi-generator)
|
||||
[](https://app.shippable.com/github/OpenAPITools/openapi-generator)
|
||||
[](https://ci.appveyor.com/project/WilliamCheng/openapi-generator-wh2wu)
|
||||
[](https://cloud.drone.io/OpenAPITools/openapi-generator)
|
||||
[](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`
|
||||
@ -562,7 +551,7 @@ When code is generated from this project, it shall be considered **AS IS** and o
|
||||
|
||||
### [3.5 - IDE Integration](#table-of-contents)
|
||||
|
||||
Here is a list of community-conitributed IDE plug-ins that integrate with OpenAPI Generator:
|
||||
Here is a list of community-contributed IDE plug-ins that integrate with OpenAPI Generator:
|
||||
|
||||
- Eclipse: [Codewind OpenAPI Tools for Eclipse](https://www.eclipse.org/codewind/open-api-tools-for-eclipse.html) by [IBM](https://www.ibm.com)
|
||||
- IntelliJ IDEA: [OpenAPI Generator](https://plugins.jetbrains.com/plugin/8433-openapi-generator) by [Jim Schubert](https://jimschubert.us/#/)
|
||||
@ -666,6 +655,7 @@ Here are some companies/projects (alphabetical order) using OpenAPI Generator in
|
||||
- [Twitter](https://twitter.com)
|
||||
- [unblu inc.](https://www.unblu.com/)
|
||||
- [Veamly](https://www.veamly.com/)
|
||||
- [VMWare](https://www.vmware.com/)
|
||||
- [wbt-solutions](https://www.wbt-solutions.de/)
|
||||
- [Woleet](https://www.woleet.io/)
|
||||
- [WSO2](https://wso2.com/)
|
||||
@ -821,8 +811,8 @@ Here are some companies/projects (alphabetical order) using OpenAPI Generator in
|
||||
- 2021-03-28 - [Trying out NestJS part 4: Generate Typescript clients from OpenAPI documents](https://dev.to/arnaudcortisse/trying-out-nestjs-part-4-generate-typescript-clients-from-openapi-documents-28mk) by [Arnaud Cortisse](https://dev.to/arnaudcortisse)
|
||||
- 2021-03-31 - [Open API Server Implementation Using OpenAPI Generator](https://www.baeldung.com/java-openapi-generator-server) at [Baeldung](https://www.baeldung.com/)
|
||||
- 2021-03-31 - [使用OpenAPI Generator實現Open API Server](https://www.1ju.org/article/java-openapi-generator-server) at [億聚網](https://www.1ju.org/)
|
||||
- 2022-04-19 - [Introducing Twilio’s OpenAPI Specification Beta](https://www.twilio.com/blog/introducing-twilio-open-api-specification-beta) by [GARETH PAUL JONES](https://www.twilio.com/blog/author/gpj) at [Twilio Blog](https://www.twilio.com/blog)
|
||||
- 2022-04-22 - [Leveraging OpenApi strengths in a Micro-Service environment](https://medium.com/unibuddy-technology-blog/leveraging-openapi-strengths-in-a-micro-service-environment-3d7f9e7c26ff) by Nicolas Jellab at [Unibuddy Technology Blog](https://medium.com/unibuddy-technology-blog)
|
||||
- 2021-04-19 - [Introducing Twilio’s OpenAPI Specification Beta](https://www.twilio.com/blog/introducing-twilio-open-api-specification-beta) by [GARETH PAUL JONES](https://www.twilio.com/blog/author/gpj) at [Twilio Blog](https://www.twilio.com/blog)
|
||||
- 2021-04-22 - [Leveraging OpenApi strengths in a Micro-Service environment](https://medium.com/unibuddy-technology-blog/leveraging-openapi-strengths-in-a-micro-service-environment-3d7f9e7c26ff) by Nicolas Jellab at [Unibuddy Technology Blog](https://medium.com/unibuddy-technology-blog)
|
||||
|
||||
## [6 - About Us](#table-of-contents)
|
||||
|
||||
|
@ -5,3 +5,4 @@ templateDir: modules/openapi-generator/src/main/resources/kotlin-client
|
||||
additionalProperties:
|
||||
requestDateConverter: toString
|
||||
artifactId: kotlin-petstore-json-request-string
|
||||
parcelizeModels: true
|
||||
|
@ -1,6 +1,6 @@
|
||||
generatorName: ktorm-schema
|
||||
outputDir: samples/schema/petstore/ktorm
|
||||
inputSpec: modules/openapi-generator/src/test/resources/2_0/petstore.yaml
|
||||
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
|
||||
templateDir: modules/openapi-generator/src/main/resources/ktorm-schema
|
||||
additionalProperties:
|
||||
hideGenerationTimestamp: true
|
||||
|
@ -1,6 +1,6 @@
|
||||
generatorName: nim
|
||||
outputDir: samples/client/petstore/nim
|
||||
inputSpec: modules/openapi-generator/src/test/resources/2_0/petstore.yaml
|
||||
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
|
||||
templateDir: modules/openapi-generator/src/main/resources/nim-client
|
||||
additionalProperties:
|
||||
packageName: petstore
|
||||
|
@ -52,6 +52,7 @@ Code change should conform to the programming style guide of the respective lang
|
||||
- C#: https://msdn.microsoft.com/en-us/library/vstudio/ff926074.aspx
|
||||
- C++: https://google.github.io/styleguide/cppguide.html
|
||||
- C++ (Tizen): https://wiki.tizen.org/Native_Platform_Coding_Idiom_and_Style_Guide#C.2B.2B_Coding_Style
|
||||
- C++ (Unreal Engine 4): https://docs.unrealengine.com/en-US/ProductionPipelines/DevelopmentSetup/CodingStandard/index.html
|
||||
- Clojure: https://github.com/bbatsov/clojure-style-guide
|
||||
- Crystal: https://crystal-lang.org/reference/conventions/coding_style.html
|
||||
- Dart: https://www.dartlang.org/guides/language/effective-dart/style
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -7,9 +7,9 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|
||||
| Option | Description | Values | Default |
|
||||
| ------ | ----------- | ------ | ------- |
|
||||
|aspnetCoreVersion|ASP.NET Core version: 5.0, 3.1, 3.0, 2.2, 2.1, 2.0 (deprecated)| |3.1|
|
||||
|buildTarget|Target to build an application or library| |program|
|
||||
|classModifier|Class Modifier for controller classes: Empty string or abstract.| ||
|
||||
|aspnetCoreVersion|ASP.NET Core version: 5.0, 3.1, 3.0, 2.2, 2.1, 2.0 (deprecated)|<dl><dt>**2.0**</dt><dd>ASP.NET Core 2.0</dd><dt>**2.1**</dt><dd>ASP.NET Core 2.1</dd><dt>**2.2**</dt><dd>ASP.NET Core 2.2</dd><dt>**3.0**</dt><dd>ASP.NET Core 3.0</dd><dt>**3.1**</dt><dd>ASP.NET Core 3.1</dd><dt>**5.0**</dt><dd>ASP.NET Core 5.0</dd></dl>|3.1|
|
||||
|buildTarget|Target to build an application or library|<dl><dt>**program**</dt><dd>Generate code for a standalone server</dd><dt>**library**</dt><dd>Generate code for a server abstract class library</dd></dl>|program|
|
||||
|classModifier|Class Modifier for controller classes: Empty string or abstract.|<dl><dt>****</dt><dd>Keep class default with no modifier</dd><dt>**abstract**</dt><dd>Make class abstract</dd></dl>||
|
||||
|compatibilityVersion|ASP.Net Core CompatibilityVersion| |Version_2_2|
|
||||
|enumNameSuffix|Suffix that will be appended to all enum names.| |Enum|
|
||||
|enumValueSuffix|Suffix that will be appended to all enum values.| |Enum|
|
||||
@ -17,10 +17,10 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|isLibrary|Is the build a library| |false|
|
||||
|licenseName|The name of the license| |NoLicense|
|
||||
|licenseUrl|The URL of the license| |http://localhost|
|
||||
|modelClassModifier|Model Class Modifier can be nothing or partial| |partial|
|
||||
|modelClassModifier|Model Class Modifier can be nothing or partial|<dl><dt>****</dt><dd>Keep model class default with no modifier</dd><dt>**partial**</dt><dd>Make model class partial</dd></dl>|partial|
|
||||
|newtonsoftVersion|Version for Microsoft.AspNetCore.Mvc.NewtonsoftJson for ASP.NET Core 3.0+| |3.0.0|
|
||||
|operationIsAsync|Set methods to async or sync (default).| |false|
|
||||
|operationModifier|Operation Modifier can be virtual or abstract| |virtual|
|
||||
|operationModifier|Operation Modifier can be virtual or abstract|<dl><dt>**virtual**</dt><dd>Keep method virtual</dd><dt>**abstract**</dt><dd>Make method abstract</dd></dl>|virtual|
|
||||
|operationResultTask|Set methods result to Task<>.| |false|
|
||||
|packageAuthors|Specifies Authors property in the .NET Core project file.| |OpenAPI|
|
||||
|packageCopyright|Specifies an AssemblyCopyright for the .NET Framework global assembly attributes stored in the AssemblyInfo file.| |No Copyright|
|
||||
@ -32,7 +32,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|returnICollection|Return ICollection<T> instead of the concrete type.| |false|
|
||||
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
|
||||
|sourceFolder|source folder for generated code| |src|
|
||||
|swashbuckleVersion|Swashbuckle version: 3.0.0, 4.0.0, 5.0.0| |3.0.0|
|
||||
|swashbuckleVersion|Swashbuckle version: 3.0.0, 4.0.0, 5.0.0|<dl><dt>**3.0.0**</dt><dd>Swashbuckle 3.0.0</dd><dt>**4.0.0**</dt><dd>Swashbuckle 4.0.0</dd><dt>**5.0.0**</dt><dd>Swashbuckle 5.0.0</dd></dl>|3.0.0|
|
||||
|useCollection|Deserialize array types to Collection<T> instead of List<T>.| |false|
|
||||
|useDateTimeOffset|Use DateTimeOffset to model date-time properties| |false|
|
||||
|useDefaultRouting|Use default routing for the ASP.NET Core version.| |true|
|
||||
|
214
docs/generators/kotlin-server-deprecated.md
Normal file
214
docs/generators/kotlin-server-deprecated.md
Normal 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 "@Parcelize" for generated models| |null|
|
||||
|serializableModel|boolean - toggle "implements Serializable" 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
|
@ -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|
|
||||
|
@ -16,6 +16,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|groupId|Generated artifact package's organization (i.e. maven groupId).| |org.openapitools|
|
||||
|library|Library template (sub-template) to use|<dl><dt>**jvm-okhttp4**</dt><dd>[DEFAULT] Platform: Java Virtual Machine. HTTP client: OkHttp 4.2.0 (Android 5.0+ and Java 8+). JSON processing: Moshi 1.8.0.</dd><dt>**jvm-okhttp3**</dt><dd>Platform: Java Virtual Machine. HTTP client: OkHttp 3.12.4 (Android 2.3+ and Java 7+). JSON processing: Moshi 1.8.0.</dd><dt>**jvm-retrofit2**</dt><dd>Platform: Java Virtual Machine. HTTP client: Retrofit 2.6.2.</dd><dt>**multiplatform**</dt><dd>Platform: Kotlin multiplatform. HTTP client: Ktor 1.2.4. JSON processing: Kotlinx Serialization: 0.12.0.</dd></dl>|jvm-okhttp4|
|
||||
|modelMutable|Create mutable models| |false|
|
||||
|moshiCodeGen|Whether to enable codegen with the Moshi library. Refer to the [official Moshi doc](https://github.com/square/moshi#codegen) for more info.| |false|
|
||||
|packageName|Generated artifact package name.| |org.openapitools.client|
|
||||
|parcelizeModels|toggle "@Parcelize" for generated models| |null|
|
||||
|requestDateConverter|JVM-Option. Defines in how to handle date-time objects that are used for a request (as query or parameter)|<dl><dt>**toJson**</dt><dd>[DEFAULT] Date formater option using a json converter.</dd><dt>**toString**</dt><dd>Use the 'toString'-method of the date-time object to retrieve the related string representation.</dd></dl>|toJson|
|
||||
|
@ -13,8 +13,8 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (prefered for JDK 1.8+)</dd></dl>|java8|
|
||||
|disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|<dl><dt>**false**</dt><dd>The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.</dd><dt>**true**</dt><dd>Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.</dd></dl>|true|
|
||||
|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true|
|
||||
|jodaTimeVersion|The version of joda-time library| |2.10.6|
|
||||
|json4sVersion|The version of json4s library| |3.6.8|
|
||||
|jodaTimeVersion|The version of joda-time library| |2.10.10|
|
||||
|json4sVersion|The version of json4s library| |3.6.11|
|
||||
|jsonLibrary|Json library to use. Possible values are: json4s and circe.| |json4s|
|
||||
|legacyDiscriminatorBehavior|Set to false for generators with better support for discriminators. (Python, Java, Go, PowerShell, C#have this enabled by default).|<dl><dt>**true**</dt><dd>The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.</dd><dt>**false**</dt><dd>The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.</dd></dl>|true|
|
||||
|mainPackage|Top-level package name, which defines 'apiPackage', 'modelPackage', 'invokerPackage'| |org.openapitools.client|
|
||||
@ -25,7 +25,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true|
|
||||
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
|
||||
|sourceFolder|source folder for generated code| |null|
|
||||
|sttpClientVersion|The version of sttp client| |2.2.0|
|
||||
|sttpClientVersion|The version of sttp client| |2.2.9|
|
||||
|
||||
## IMPORT MAPPING
|
||||
|
||||
|
@ -199,14 +199,14 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
|
||||
aspnetCoreVersion.addEnum("5.0", "ASP.NET Core 5.0");
|
||||
aspnetCoreVersion.setDefault("3.1");
|
||||
aspnetCoreVersion.setOptValue(aspnetCoreVersion.getDefault());
|
||||
addOption(aspnetCoreVersion.getOpt(), aspnetCoreVersion.getDescription(), aspnetCoreVersion.getOptValue());
|
||||
cliOptions.add(aspnetCoreVersion);
|
||||
|
||||
swashbuckleVersion.addEnum("3.0.0", "Swashbuckle 3.0.0");
|
||||
swashbuckleVersion.addEnum("4.0.0", "Swashbuckle 4.0.0");
|
||||
swashbuckleVersion.addEnum("5.0.0", "Swashbuckle 5.0.0");
|
||||
swashbuckleVersion.setDefault("3.0.0");
|
||||
swashbuckleVersion.setOptValue(swashbuckleVersion.getDefault());
|
||||
addOption(swashbuckleVersion.getOpt(), swashbuckleVersion.getDescription(), swashbuckleVersion.getOptValue());
|
||||
cliOptions.add(swashbuckleVersion);
|
||||
|
||||
// CLI Switches
|
||||
addSwitch(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG,
|
||||
@ -261,19 +261,19 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
|
||||
classModifier.addEnum("abstract", "Make class abstract");
|
||||
classModifier.setDefault("");
|
||||
classModifier.setOptValue(classModifier.getDefault());
|
||||
addOption(classModifier.getOpt(), classModifier.getDescription(), classModifier.getOptValue());
|
||||
cliOptions.add(classModifier);
|
||||
|
||||
operationModifier.addEnum("virtual", "Keep method virtual");
|
||||
operationModifier.addEnum("abstract", "Make method abstract");
|
||||
operationModifier.setDefault("virtual");
|
||||
operationModifier.setOptValue(operationModifier.getDefault());
|
||||
addOption(operationModifier.getOpt(), operationModifier.getDescription(), operationModifier.getOptValue());
|
||||
cliOptions.add(operationModifier);
|
||||
|
||||
buildTarget.addEnum("program", "Generate code for a standalone server");
|
||||
buildTarget.addEnum("library", "Generate code for a server abstract class library");
|
||||
buildTarget.setDefault("program");
|
||||
buildTarget.setOptValue(buildTarget.getDefault());
|
||||
addOption(buildTarget.getOpt(), buildTarget.getDescription(), buildTarget.getOptValue());
|
||||
cliOptions.add(buildTarget);
|
||||
|
||||
addSwitch(GENERATE_BODY,
|
||||
"Generates method body.",
|
||||
@ -292,7 +292,7 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
|
||||
modelClassModifier.addEnum("partial", "Make model class partial");
|
||||
modelClassModifier.setDefault("partial");
|
||||
modelClassModifier.setOptValue(modelClassModifier.getDefault());
|
||||
addOption(modelClassModifier.getOpt(), modelClassModifier.getDescription(), modelClassModifier.getOptValue());
|
||||
cliOptions.add(modelClassModifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -592,6 +592,7 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
|
||||
private void setBuildTarget() {
|
||||
setCliOption(buildTarget);
|
||||
if ("library".equals(buildTarget.getOptValue())) {
|
||||
LOGGER.warn("buildTarget is {} so changing default isLibrary to true", buildTarget.getOptValue());
|
||||
isLibrary = true;
|
||||
projectSdk = SDK_LIB;
|
||||
additionalProperties.put(CLASS_MODIFIER, "abstract");
|
||||
@ -636,7 +637,7 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
|
||||
|
||||
private void setUseSwashbuckle() {
|
||||
if (isLibrary) {
|
||||
LOGGER.warn("buildTarget is " + buildTarget.getOptValue() + " so changing default isLibrary to false ");
|
||||
LOGGER.warn("isLibrary is true so changing default useSwashbuckle to false");
|
||||
useSwashbuckle = false;
|
||||
} else {
|
||||
useSwashbuckle = true;
|
||||
|
@ -128,6 +128,7 @@ public class CppPistacheServerCodegen extends AbstractCppCodegen {
|
||||
typeMapping.put("boolean", "bool");
|
||||
typeMapping.put("array", "std::vector");
|
||||
typeMapping.put("map", "std::map");
|
||||
typeMapping.put("set", "std::vector");
|
||||
typeMapping.put("file", "std::string");
|
||||
typeMapping.put("object", "Object");
|
||||
typeMapping.put("binary", "std::string");
|
||||
|
@ -61,6 +61,8 @@ public class KotlinClientCodegen extends AbstractKotlinCodegen {
|
||||
public static final String REQUEST_DATE_CONVERTER = "requestDateConverter";
|
||||
public static final String COLLECTION_TYPE = "collectionType";
|
||||
|
||||
public static final String MOSHI_CODE_GEN = "moshiCodeGen";
|
||||
|
||||
protected static final String VENDOR_EXTENSION_BASE_NAME_LITERAL = "x-base-name-literal";
|
||||
|
||||
protected String dateLibrary = DateLibrary.JAVA8.value;
|
||||
@ -204,6 +206,8 @@ public class KotlinClientCodegen extends AbstractKotlinCodegen {
|
||||
cliOptions.add(CliOption.newBoolean(USE_RX_JAVA2, "Whether to use the RxJava2 adapter with the retrofit2 library."));
|
||||
cliOptions.add(CliOption.newBoolean(USE_RX_JAVA3, "Whether to use the RxJava3 adapter with the retrofit2 library."));
|
||||
cliOptions.add(CliOption.newBoolean(USE_COROUTINES, "Whether to use the Coroutines adapter with the retrofit2 library."));
|
||||
|
||||
cliOptions.add(CliOption.newBoolean(MOSHI_CODE_GEN, "Whether to enable codegen with the Moshi library. Refer to the [official Moshi doc](https://github.com/square/moshi#codegen) for more info."));
|
||||
}
|
||||
|
||||
public CodegenType getTag() {
|
||||
|
@ -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
|
||||
|
@ -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("################################################################################");
|
||||
}
|
||||
}
|
@ -166,8 +166,8 @@ public class ScalaAkkaClientCodegen extends AbstractScalaCodegen implements Code
|
||||
additionalProperties.put(CodegenConstants.INVOKER_PACKAGE, invokerPackage);
|
||||
|
||||
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
|
||||
supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml"));
|
||||
supportingFiles.add(new SupportingFile("build.sbt.mustache", "", "build.sbt"));
|
||||
supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml"));
|
||||
supportingFiles.add(new SupportingFile("reference.mustache", resourcesFolder, "reference.conf"));
|
||||
final String invokerFolder = (sourceFolder + File.separator + invokerPackage).replace(".", File.separator);
|
||||
supportingFiles.add(new SupportingFile("apiRequest.mustache", invokerFolder, "ApiRequest.scala"));
|
||||
@ -175,6 +175,7 @@ public class ScalaAkkaClientCodegen extends AbstractScalaCodegen implements Code
|
||||
supportingFiles.add(new SupportingFile("requests.mustache", invokerFolder, "requests.scala"));
|
||||
supportingFiles.add(new SupportingFile("apiSettings.mustache", invokerFolder, "ApiSettings.scala"));
|
||||
final String apiFolder = (sourceFolder + File.separator + apiPackage).replace(".", File.separator);
|
||||
supportingFiles.add(new SupportingFile("project/build.properties.mustache", "project", "build.properties"));
|
||||
supportingFiles.add(new SupportingFile("enumsSerializers.mustache", apiFolder, "EnumsSerializers.scala"));
|
||||
supportingFiles.add(new SupportingFile("serializers.mustache", invokerFolder, "Serializers.scala"));
|
||||
}
|
||||
|
@ -37,20 +37,22 @@ import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.openapitools.codegen.utils.StringUtils.camelize;
|
||||
|
||||
public class ScalaSttpClientCodegen extends AbstractScalaCodegen implements CodegenConfig {
|
||||
private static final StringProperty STTP_CLIENT_VERSION = new StringProperty("sttpClientVersion", "The version of " +
|
||||
"sttp client", "2.2.0");
|
||||
"sttp client", "2.2.9");
|
||||
private static final BooleanProperty USE_SEPARATE_ERROR_CHANNEL = new BooleanProperty("separateErrorChannel",
|
||||
"Whether to return response as " +
|
||||
"F[Either[ResponseError[ErrorType], ReturnType]]] or to flatten " +
|
||||
"response's error raising them through enclosing monad (F[ReturnType]).", true);
|
||||
private static final StringProperty JODA_TIME_VERSION = new StringProperty("jodaTimeVersion", "The version of " +
|
||||
"joda-time library", "2.10.6");
|
||||
"joda-time library", "2.10.10");
|
||||
private static final StringProperty JSON4S_VERSION = new StringProperty("json4sVersion", "The version of json4s " +
|
||||
"library", "3.6.8");
|
||||
"library", "3.6.11");
|
||||
private static final StringProperty CIRCE_VERSION = new StringProperty("circeVersion", "The version of circe " +
|
||||
"library", "0.13.0");
|
||||
private static final JsonLibraryProperty JSON_LIBRARY_PROPERTY = new JsonLibraryProperty();
|
||||
@ -179,8 +181,17 @@ public class ScalaSttpClientCodegen extends AbstractScalaCodegen implements Code
|
||||
|
||||
@Override
|
||||
public String encodePath(String input) {
|
||||
String result = super.encodePath(input);
|
||||
return result.replace("{", "${");
|
||||
String path = super.encodePath(input);
|
||||
|
||||
// The parameter names in the URI must be converted to the same case as
|
||||
// the method parameter.
|
||||
StringBuffer buf = new StringBuffer(path.length());
|
||||
Matcher matcher = Pattern.compile("[{](.*?)[}]").matcher(path);
|
||||
while (matcher.find()) {
|
||||
matcher.appendReplacement(buf, "\\${" + toParamName(matcher.group(0)) + "}");
|
||||
}
|
||||
matcher.appendTail(buf);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -10,8 +10,8 @@ lazy val root = (project in file(".")).
|
||||
resolvers += Resolver.mavenLocal,
|
||||
libraryDependencies ++= Seq(
|
||||
"io.swagger" % "swagger-annotations" % "1.5.24",
|
||||
"com.squareup.okhttp3" % "okhttp" % "3.14.7",
|
||||
"com.squareup.okhttp3" % "logging-interceptor" % "3.14.7",
|
||||
"com.squareup.okhttp3" % "okhttp" % "4.9.1",
|
||||
"com.squareup.okhttp3" % "logging-interceptor" % "4.9.1",
|
||||
"com.google.code.gson" % "gson" % "2.8.6",
|
||||
"org.apache.commons" % "commons-lang3" % "3.10",
|
||||
{{#hasOAuthMethods}}
|
||||
|
@ -59,6 +59,9 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
{{#jsr310}}
|
||||
{{#threetenbp}}
|
||||
import org.threeten.bp.OffsetDateTime;
|
||||
@ -98,25 +101,22 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
|
||||
|
||||
private final WebClient webClient;
|
||||
private final DateFormat dateFormat;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
private Map<String, Authentication> authentications;
|
||||
|
||||
|
||||
public ApiClient() {
|
||||
this.dateFormat = createDefaultDateFormat();
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setDateFormat(dateFormat);
|
||||
mapper.registerModule(new JavaTimeModule());
|
||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
{{#openApiNullable}}
|
||||
JsonNullableModule jnm = new JsonNullableModule();
|
||||
mapper.registerModule(jnm);
|
||||
{{/openApiNullable}}
|
||||
|
||||
this.webClient = buildWebClient(mapper);
|
||||
this.objectMapper = createDefaultObjectMapper(this.dateFormat);
|
||||
this.webClient = buildWebClient(this.objectMapper);
|
||||
this.init();
|
||||
}
|
||||
|
||||
public ApiClient(WebClient webClient) {
|
||||
this(Optional.ofNullable(webClient).orElseGet(() -> buildWebClient()), createDefaultDateFormat());
|
||||
}
|
||||
|
||||
public ApiClient(ObjectMapper mapper, DateFormat format) {
|
||||
this(buildWebClient(mapper.copy()), format);
|
||||
}
|
||||
@ -128,15 +128,31 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
|
||||
private ApiClient(WebClient webClient, DateFormat format) {
|
||||
this.webClient = webClient;
|
||||
this.dateFormat = format;
|
||||
this.objectMapper = createDefaultObjectMapper(format);
|
||||
this.init();
|
||||
}
|
||||
|
||||
public DateFormat createDefaultDateFormat() {
|
||||
public static DateFormat createDefaultDateFormat() {
|
||||
DateFormat dateFormat = new RFC3339DateFormat();
|
||||
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
return dateFormat;
|
||||
}
|
||||
|
||||
public static ObjectMapper createDefaultObjectMapper(@Nullable DateFormat dateFormat) {
|
||||
if (null == dateFormat) {
|
||||
dateFormat = createDefaultDateFormat();
|
||||
}
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setDateFormat(dateFormat);
|
||||
mapper.registerModule(new JavaTimeModule());
|
||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
{{#openApiNullable}}
|
||||
JsonNullableModule jnm = new JsonNullableModule();
|
||||
mapper.registerModule(jnm);
|
||||
{{/openApiNullable}}
|
||||
return mapper;
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
// Setup authentications (key: authentication name, value: authentication).
|
||||
authentications = new HashMap<String, Authentication>();{{#authMethods}}{{#isBasic}}{{#isBasicBasic}}
|
||||
@ -149,20 +165,45 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the WebClient used to make HTTP requests.
|
||||
* Build the WebClientBuilder used to make WebClient.
|
||||
* @param mapper ObjectMapper used for serialize/deserialize
|
||||
* @return WebClient
|
||||
*/
|
||||
public static WebClient buildWebClient(ObjectMapper mapper) {
|
||||
public static WebClient.Builder buildWebClientBuilder(ObjectMapper mapper) {
|
||||
ExchangeStrategies strategies = ExchangeStrategies
|
||||
.builder()
|
||||
.codecs(clientDefaultCodecsConfigurer -> {
|
||||
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(mapper, MediaType.APPLICATION_JSON));
|
||||
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(mapper, MediaType.APPLICATION_JSON));
|
||||
}).build();
|
||||
WebClient.Builder webClient = WebClient.builder().exchangeStrategies(strategies);
|
||||
return webClient.build();
|
||||
WebClient.Builder webClientBuilder = WebClient.builder().exchangeStrategies(strategies);
|
||||
return webClientBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the WebClientBuilder used to make WebClient.
|
||||
* @return WebClient
|
||||
*/
|
||||
public static WebClient.Builder buildWebClientBuilder() {
|
||||
return buildWebClientBuilder(createDefaultObjectMapper(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the WebClient used to make HTTP requests.
|
||||
* @param mapper ObjectMapper used for serialize/deserialize
|
||||
* @return WebClient
|
||||
*/
|
||||
public static WebClient buildWebClient(ObjectMapper mapper) {
|
||||
return buildWebClientBuilder(mapper).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the WebClient used to make HTTP requests.
|
||||
* @return WebClient
|
||||
*/
|
||||
public static WebClient buildWebClient() {
|
||||
return buildWebClientBuilder(createDefaultObjectMapper(null)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current base path
|
||||
@ -352,6 +393,22 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
|
||||
return dateFormat.format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ObjectMapper used to make HTTP requests.
|
||||
* @return ObjectMapper objectMapper
|
||||
*/
|
||||
public ObjectMapper getObjectMapper() {
|
||||
return objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the WebClient used to make HTTP requests.
|
||||
* @return WebClient webClient
|
||||
*/
|
||||
public WebClient getWebClient() {
|
||||
return webClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the given parameter object into string.
|
||||
* @param param the object to convert
|
||||
|
@ -36,7 +36,6 @@ export type FetchArgs = {
|
||||
options: {};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
@ -79,12 +78,12 @@ export const {{classname}}FetchParamCreator = function (configuration?: Configur
|
||||
{{/summary}}
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
{{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options: RequestOptions): FetchArgs {
|
||||
{{operationId}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options: RequestOptions): FetchArgs {
|
||||
{{#allParams}}
|
||||
{{#required}}
|
||||
// verify required parameter '{{paramName}}' is not null or undefined
|
||||
if ({{paramName}} === null || {{paramName}} === undefined) {
|
||||
throw new RequiredError('{{paramName}}','Required parameter {{paramName}} was null or undefined when calling {{nickname}}.');
|
||||
throw new RequiredError('{{paramName}}','Required parameter {{paramName}} was null or undefined when calling {{operationId}}.');
|
||||
}
|
||||
{{/required}}
|
||||
{{/allParams}}
|
||||
@ -142,7 +141,7 @@ export const {{classname}}FetchParamCreator = function (configuration?: Configur
|
||||
localVarQueryParameter['{{baseName}}'] = {{paramName}};
|
||||
{{/isCollectionFormatMulti}}
|
||||
{{^isCollectionFormatMulti}}
|
||||
localVarQueryParameter['{{baseName}}'] = {{paramName}}.join(COLLECTION_FORMATS["{{collectionFormat}}"]);
|
||||
localVarQueryParameter['{{baseName}}'] = {{#uniqueItems}}Array.from({{/uniqueItems}}{{paramName}}{{#uniqueItems}}){{/uniqueItems}}.join(COLLECTION_FORMATS["{{collectionFormat}}"]);
|
||||
{{/isCollectionFormatMulti}}
|
||||
}
|
||||
{{/isArray}}
|
||||
@ -166,7 +165,7 @@ export const {{classname}}FetchParamCreator = function (configuration?: Configur
|
||||
{{#headerParams}}
|
||||
{{#isArray}}
|
||||
if ({{paramName}}) {
|
||||
localVarHeaderParameter['{{baseName}}'] = {{paramName}}.join(COLLECTION_FORMATS["{{collectionFormat}}"]);
|
||||
localVarHeaderParameter['{{baseName}}'] = {{#uniqueItems}}Array.from({{/uniqueItems}}{{paramName}}{{#uniqueItems}}){{/uniqueItems}}.join(COLLECTION_FORMATS["{{collectionFormat}}"]);
|
||||
}
|
||||
{{/isArray}}
|
||||
{{^isArray}}
|
||||
@ -185,7 +184,7 @@ export const {{classname}}FetchParamCreator = function (configuration?: Configur
|
||||
})
|
||||
{{/isCollectionFormatMulti}}
|
||||
{{^isCollectionFormatMulti}}
|
||||
localVarFormParams.set('{{baseName}}', {{paramName}}.join(COLLECTION_FORMATS["{{collectionFormat}}"]));
|
||||
localVarFormParams.set('{{baseName}}', {{#uniqueItems}}Array.from({{/uniqueItems}}{{paramName}}{{#uniqueItems}}){{/uniqueItems}}.join(COLLECTION_FORMATS["{{collectionFormat}}"]));
|
||||
{{/isCollectionFormatMulti}}
|
||||
}
|
||||
{{/isArray}}
|
||||
@ -227,7 +226,7 @@ export const {{classname}}FetchParamCreator = function (configuration?: Configur
|
||||
};
|
||||
|
||||
export type {{classname}}Type = { {{#operation}}
|
||||
{{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: RequestOptions): Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Response{{/returnType}}>,
|
||||
{{operationId}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: RequestOptions): Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Response{{/returnType}}>,
|
||||
{{/operation}}
|
||||
}
|
||||
|
||||
@ -247,8 +246,8 @@ export const {{classname}} = function(configuration?: Configuration, fetch: Fetc
|
||||
{{/summary}}
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
{{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: RequestOptions = {}): Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Response{{/returnType}}> {
|
||||
const localVarFetchArgs = {{classname}}FetchParamCreator(configuration).{{nickname}}({{#allParams}}{{paramName}}, {{/allParams}}options);
|
||||
{{operationId}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: RequestOptions = {}): Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Response{{/returnType}}> {
|
||||
const localVarFetchArgs = {{classname}}FetchParamCreator(configuration).{{operationId}}({{#allParams}}{{paramName}}, {{/allParams}}options);
|
||||
return fetch(basePath + localVarFetchArgs.url, localVarFetchArgs.options).then((response) => {
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
return response{{#returnType}}.json(){{/returnType}};
|
||||
@ -260,6 +259,7 @@ export const {{classname}} = function(configuration?: Configuration, fetch: Fetc
|
||||
{{/operation}}
|
||||
}
|
||||
};
|
||||
|
||||
{{/operations}}{{/apis}}{{/apiInfo}}
|
||||
export type ApiTypes = { {{#apiInfo}}{{#apis}}{{#operations}}
|
||||
{{classname}}: {{classname}}Type,
|
||||
|
@ -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
|
||||
|
@ -979,7 +979,7 @@ case $key in
|
||||
body_parameters[${body_key}]=${body_value}
|
||||
fi
|
||||
;;
|
||||
+\([^=]\):*)
|
||||
+([^=]):*)
|
||||
# Parse header arguments and convert them into curl
|
||||
# only after the operation argument
|
||||
if [[ "$operation" ]]; then
|
||||
|
@ -14,24 +14,21 @@
|
||||
#include <pistache/http_headers.h>
|
||||
#include <pistache/optional.h>
|
||||
{{^hasModelImport}}#include <nlohmann/json.hpp>{{/hasModelImport}}
|
||||
#include <utility>
|
||||
|
||||
{{#imports}}{{{import}}}
|
||||
{{/imports}}
|
||||
|
||||
{{#apiNamespaceDeclarations}}
|
||||
namespace {{this}} {
|
||||
{{/apiNamespaceDeclarations}}
|
||||
|
||||
{{#hasModelImport}}
|
||||
using namespace {{modelNamespace}};{{/hasModelImport}}
|
||||
namespace {{apiNamespace}}
|
||||
{
|
||||
|
||||
class {{declspec}} {{classname}} {
|
||||
public:
|
||||
{{classname}}(std::shared_ptr<Pistache::Rest::Router>);
|
||||
virtual ~{{classname}}() {}
|
||||
explicit {{classname}}(const std::shared_ptr<Pistache::Rest::Router>& rtr);
|
||||
virtual ~{{classname}}() = default;
|
||||
void init();
|
||||
|
||||
const std::string base = "{{basePathWithoutHost}}";
|
||||
static const std::string base;
|
||||
|
||||
private:
|
||||
void setupRoutes();
|
||||
@ -41,9 +38,21 @@ private:
|
||||
{{/operation}}
|
||||
void {{classnameSnakeLowerCase}}_default_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
|
||||
std::shared_ptr<Pistache::Rest::Router> router;
|
||||
{{#operation}}
|
||||
const std::shared_ptr<Pistache::Rest::Router> router;
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to handle unexpected Exceptions during Parameter parsing and validation.
|
||||
/// May be overriden to return custom error formats.
|
||||
/// </summary>
|
||||
virtual std::pair<Pistache::Http::Code, std::string> handleParsingException(const std::exception& ex) const noexcept;
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to handle unexpected Exceptions during processing of the request in handler functions.
|
||||
/// May be overriden to return custom error formats.
|
||||
/// </summary>
|
||||
virtual std::pair<Pistache::Http::Code, std::string> handleOperationException(const std::exception& ex) const noexcept;
|
||||
|
||||
{{#operation}}
|
||||
/// <summary>
|
||||
/// {{summary}}
|
||||
/// </summary>
|
||||
@ -54,7 +63,7 @@ private:
|
||||
{{#allParams}}
|
||||
/// <param name="{{paramName}}">{{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}</param>
|
||||
{{/allParams}}
|
||||
virtual void {{operationIdSnakeCase}}({{#allParams}}const {{{dataType}}} &{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}Pistache::Http::ResponseWriter &response) = 0;
|
||||
virtual void {{operationIdSnakeCase}}({{#allParams}}const {{#isModel}}{{modelNamespace}}::{{/isModel}}{{{dataType}}} &{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}Pistache::Http::ResponseWriter &response) = 0;
|
||||
{{/vendorExtensions.x-codegen-pistache-is-parsing-supported}}
|
||||
{{^vendorExtensions.x-codegen-pistache-is-parsing-supported}}
|
||||
virtual void {{operationIdSnakeCase}}(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter &response) = 0;
|
||||
@ -63,9 +72,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
{{#apiNamespaceDeclarations}}
|
||||
}
|
||||
{{/apiNamespaceDeclarations}}
|
||||
} // namespace {{apiNamespace}}
|
||||
|
||||
#endif /* {{classname}}_H_ */
|
||||
|
||||
|
@ -22,17 +22,16 @@
|
||||
{{#imports}}{{{import}}}
|
||||
{{/imports}}
|
||||
|
||||
{{#apiNamespaceDeclarations}}
|
||||
namespace {{this}} {
|
||||
{{/apiNamespaceDeclarations}}
|
||||
namespace {{apiNamespace}}
|
||||
{
|
||||
|
||||
{{#hasModelImport}}
|
||||
using namespace {{modelNamespace}};{{/hasModelImport}}
|
||||
|
||||
class {{classname}}Impl : public {{apiNamespace}}::{{classname}} {
|
||||
public:
|
||||
{{classname}}Impl(std::shared_ptr<Pistache::Rest::Router>);
|
||||
~{{classname}}Impl() {}
|
||||
explicit {{classname}}Impl(const std::shared_ptr<Pistache::Rest::Router>& rtr);
|
||||
~{{classname}}Impl() override = default;
|
||||
|
||||
{{#operation}}
|
||||
{{#vendorExtensions.x-codegen-pistache-is-parsing-supported}}
|
||||
@ -45,9 +44,7 @@ public:
|
||||
|
||||
};
|
||||
|
||||
{{#apiNamespaceDeclarations}}
|
||||
}
|
||||
{{/apiNamespaceDeclarations}}
|
||||
} // namespace {{apiNamespace}}
|
||||
|
||||
{{/operations}}
|
||||
|
||||
|
@ -10,9 +10,10 @@ namespace {{this}} {
|
||||
{{#hasModelImport}}
|
||||
using namespace {{modelNamespace}};{{/hasModelImport}}
|
||||
|
||||
{{classname}}Impl::{{classname}}Impl(std::shared_ptr<Pistache::Rest::Router> rtr)
|
||||
{{classname}}Impl::{{classname}}Impl(const std::shared_ptr<Pistache::Rest::Router>& rtr)
|
||||
: {{classname}}(rtr)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
{{#operation}}
|
||||
{{#vendorExtensions.x-codegen-pistache-is-parsing-supported}}
|
||||
|
@ -4,16 +4,18 @@
|
||||
#include "{{classname}}.h"
|
||||
#include "{{prefix}}Helpers.h"
|
||||
|
||||
{{#apiNamespaceDeclarations}}
|
||||
namespace {{this}} {
|
||||
{{/apiNamespaceDeclarations}}
|
||||
namespace {{apiNamespace}}
|
||||
{
|
||||
|
||||
using namespace {{helpersNamespace}};
|
||||
{{#hasModelImport}}
|
||||
using namespace {{modelNamespace}};{{/hasModelImport}}
|
||||
|
||||
{{classname}}::{{classname}}(std::shared_ptr<Pistache::Rest::Router> rtr) {
|
||||
router = rtr;
|
||||
const std::string {{classname}}::base = "{{basePathWithoutHost}}";
|
||||
|
||||
{{classname}}::{{classname}}(const std::shared_ptr<Pistache::Rest::Router>& rtr)
|
||||
: router(rtr)
|
||||
{
|
||||
}
|
||||
|
||||
void {{classname}}::init() {
|
||||
@ -31,8 +33,26 @@ void {{classname}}::setupRoutes() {
|
||||
router->addCustomHandler(Routes::bind(&{{classname}}::{{classnameSnakeLowerCase}}_default_handler, this));
|
||||
}
|
||||
|
||||
std::pair<Pistache::Http::Code, std::string> {{classname}}::handleParsingException(const std::exception& ex) const noexcept
|
||||
{
|
||||
try {
|
||||
throw ex;
|
||||
} catch (nlohmann::detail::exception &e) {
|
||||
return std::make_pair(Pistache::Http::Code::Bad_Request, e.what());
|
||||
} catch ({{helpersNamespace}}::ValidationException &e) {
|
||||
return std::make_pair(Pistache::Http::Code::Bad_Request, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<Pistache::Http::Code, std::string> {{classname}}::handleOperationException(const std::exception& ex) const noexcept
|
||||
{
|
||||
return std::make_pair(Pistache::Http::Code::Internal_Server_Error, ex.what());
|
||||
}
|
||||
|
||||
{{#operation}}
|
||||
void {{classname}}::{{operationIdSnakeCase}}_handler(const Pistache::Rest::Request &{{#hasParams}}request{{/hasParams}}, Pistache::Http::ResponseWriter response) {
|
||||
try {
|
||||
|
||||
{{#vendorExtensions.x-codegen-pistache-is-parsing-supported}}
|
||||
{{#hasPathParams}}
|
||||
// Getting the path params
|
||||
@ -72,10 +92,18 @@ void {{classname}}::{{operationIdSnakeCase}}_handler(const Pistache::Rest::Reque
|
||||
{{#bodyParam}}
|
||||
{{^isPrimitiveType}}
|
||||
nlohmann::json::parse(request.body()).get_to({{paramName}});
|
||||
{{paramName}}.validate();
|
||||
{{/isPrimitiveType}}
|
||||
{{#isPrimitiveType}}
|
||||
{{paramName}} = request.body();
|
||||
{{/isPrimitiveType}}
|
||||
} catch (std::exception &e) {
|
||||
const std::pair<Pistache::Http::Code, std::string> errorInfo = this->handleParsingException(e);
|
||||
response.send(errorInfo.first, errorInfo.second);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
{{/bodyParam}}
|
||||
{{/hasBodyParam}}
|
||||
this->{{operationIdSnakeCase}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}response);
|
||||
@ -84,19 +112,19 @@ void {{classname}}::{{operationIdSnakeCase}}_handler(const Pistache::Rest::Reque
|
||||
try {
|
||||
this->{{operationIdSnakeCase}}(request, response);
|
||||
{{/vendorExtensions.x-codegen-pistache-is-parsing-supported}}
|
||||
} catch (nlohmann::detail::exception &e) {
|
||||
//send a 400 error
|
||||
response.send(Pistache::Http::Code::Bad_Request, e.what());
|
||||
return;
|
||||
} catch (Pistache::Http::HttpError &e) {
|
||||
response.send(static_cast<Pistache::Http::Code>(e.code()), e.what());
|
||||
return;
|
||||
} catch (std::exception &e) {
|
||||
//send a 500 error
|
||||
response.send(Pistache::Http::Code::Internal_Server_Error, e.what());
|
||||
const std::pair<Pistache::Http::Code, std::string> errorInfo = this->handleOperationException(e);
|
||||
response.send(errorInfo.first, errorInfo.second);
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (std::exception &e) {
|
||||
response.send(Pistache::Http::Code::Internal_Server_Error, e.what());
|
||||
}
|
||||
|
||||
}
|
||||
{{/operation}}
|
||||
|
||||
@ -104,8 +132,6 @@ void {{classname}}::{{classnameSnakeLowerCase}}_default_handler(const Pistache::
|
||||
response.send(Pistache::Http::Code::Not_Found, "The requested method does not exist");
|
||||
}
|
||||
|
||||
{{#apiNamespaceDeclarations}}
|
||||
}
|
||||
{{/apiNamespaceDeclarations}}
|
||||
} // namespace {{apiNamespace}}
|
||||
|
||||
{{/operations}}
|
||||
|
@ -14,16 +14,80 @@
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
{{#helpersNamespaceDeclarations}}
|
||||
namespace {{this}} {
|
||||
{{/helpersNamespaceDeclarations}}
|
||||
namespace {{helpersNamespace}}
|
||||
{
|
||||
|
||||
class ValidationException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit ValidationException(const std::string& what)
|
||||
: std::runtime_error(what)
|
||||
{ }
|
||||
~ValidationException() override = default;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Validate a string against the full-date definition of RFC 3339, section 5.6.
|
||||
/// </summary>
|
||||
bool validateRfc3339_date(const std::string& str);
|
||||
|
||||
/// <summary>
|
||||
/// Validate a string against the date-time definition of RFC 3339, section 5.6.
|
||||
/// </summary>
|
||||
bool validateRfc3339_date_time(const std::string& str);
|
||||
|
||||
namespace sfinae_helpers
|
||||
{
|
||||
struct NoType {};
|
||||
template <typename T1, typename T2> NoType operator==(const T1&, const T2&);
|
||||
|
||||
template <typename T1, typename T2> class EqualsOperatorAvailable
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
value = !std::is_same< decltype(std::declval<T1>() == std::declval<T2>()), NoType >::value
|
||||
};
|
||||
};
|
||||
} // namespace sfinae_helpers
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determine if the given vector<T> only has unique elements. T must provide the == operator.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
bool hasOnlyUniqueItems(const std::vector<T>& vec)
|
||||
{
|
||||
static_assert(sfinae_helpers::EqualsOperatorAvailable<T, T>::value,
|
||||
"hasOnlyUniqueItems<T> cannot be called, passed template type does not provide == operator.");
|
||||
if (vec.size() <= 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Compare every element of vec to every other element of vec.
|
||||
// This isn't an elegant way to do this, since it's O(n^2),
|
||||
// but it's the best solution working only with the == operator.
|
||||
// This could be greatly improved if our models provided a valid hash
|
||||
// and/or the < operator
|
||||
for (size_t i = 0; i < vec.size() - 1; i++)
|
||||
{
|
||||
for (size_t j = i + 1; j < vec.size(); j++)
|
||||
{
|
||||
if (vec[i] == vec[j])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string toStringValue(const std::string &value);
|
||||
std::string toStringValue(const int32_t &value);
|
||||
std::string toStringValue(const int64_t &value);
|
||||
std::string toStringValue(const bool &value);
|
||||
std::string toStringValue(const float &value);
|
||||
std::string toStringValue(const double &value);
|
||||
std::string toStringValue(const int32_t value);
|
||||
std::string toStringValue(const int64_t value);
|
||||
std::string toStringValue(const bool value);
|
||||
std::string toStringValue(const float value);
|
||||
std::string toStringValue(const double value);
|
||||
|
||||
bool fromStringValue(const std::string &inStr, std::string &value);
|
||||
bool fromStringValue(const std::string &inStr, int32_t &value);
|
||||
@ -57,8 +121,6 @@ namespace {{this}} {
|
||||
return fromStringValue(inStrings, value);
|
||||
}
|
||||
|
||||
{{#helpersNamespaceDeclarations}}
|
||||
}
|
||||
{{/helpersNamespaceDeclarations}}
|
||||
} // namespace {{helpersNamespace}}
|
||||
|
||||
#endif // {{prefix}}Helpers_H_
|
@ -1,32 +1,74 @@
|
||||
{{>licenseInfo}}
|
||||
#include "{{prefix}}Helpers.h"
|
||||
#include <regex>
|
||||
|
||||
{{#helpersNamespaceDeclarations}}
|
||||
namespace {{this}} {
|
||||
{{/helpersNamespaceDeclarations}}
|
||||
namespace {{helpersNamespace}}
|
||||
{
|
||||
|
||||
const std::regex regexRfc3339_date(R"(^(\d{4})\-(\d{2})\-(\d{2})$)");
|
||||
const std::regex regexRfc3339_date_time(
|
||||
R"(^(\d{4})\-(\d{2})\-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(\.\d+)?([Zz]|([\+\-])(\d{2}):(\d{2}))$)"
|
||||
);
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
// Determine if given year is a leap year
|
||||
// See RFC 3339, Appendix C https://tools.ietf.org/html/rfc3339#appendix-C
|
||||
bool isLeapYear(const uint16_t year) {
|
||||
return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
|
||||
}
|
||||
|
||||
bool validateDateValues(const uint16_t year, const uint16_t month, const uint16_t day) {
|
||||
return !(
|
||||
(month == 0 || month > 12)
|
||||
|| (day == 0)
|
||||
|| (month == 2 && day > (28 + (isLeapYear(year) ? 1 : 0)))
|
||||
|| (month <= 7 && day > (30 + month % 2))
|
||||
|| (month >= 8 && day > (31 - month % 2))
|
||||
);
|
||||
}
|
||||
|
||||
bool validateTimeValues(const uint16_t hours, const uint16_t minutes, const uint16_t seconds) {
|
||||
return (hours <= 23) && (minutes <= 59) && (seconds <= 60);
|
||||
}
|
||||
}
|
||||
|
||||
bool validateRfc3339_date(const std::string& str) {
|
||||
std::smatch match;
|
||||
const bool found = std::regex_search(str, match, regexRfc3339_date);
|
||||
return found && validateDateValues(std::stoi(match[1]), std::stoi(match[2]), std::stoi(match[3]));
|
||||
}
|
||||
|
||||
bool validateRfc3339_date_time(const std::string& str) {
|
||||
std::smatch match;
|
||||
const bool found = std::regex_search(str, match, regexRfc3339_date_time);
|
||||
return found
|
||||
&& validateDateValues(std::stoi(match[1]), std::stoi(match[2]), std::stoi(match[3]))
|
||||
&& validateTimeValues(std::stoi(match[4]), std::stoi(match[5]), std::stoi(match[6]));
|
||||
}
|
||||
|
||||
std::string toStringValue(const std::string &value){
|
||||
return std::string(value);
|
||||
}
|
||||
|
||||
std::string toStringValue(const int32_t &value){
|
||||
std::string toStringValue(const int32_t value){
|
||||
return std::to_string(value);
|
||||
}
|
||||
|
||||
std::string toStringValue(const int64_t &value){
|
||||
std::string toStringValue(const int64_t value){
|
||||
return std::to_string(value);
|
||||
}
|
||||
|
||||
std::string toStringValue(const bool &value){
|
||||
std::string toStringValue(const bool value){
|
||||
return value ? std::string("true") : std::string("false");
|
||||
}
|
||||
|
||||
std::string toStringValue(const float &value){
|
||||
std::string toStringValue(const float value){
|
||||
return std::to_string(value);
|
||||
}
|
||||
|
||||
std::string toStringValue(const double &value){
|
||||
std::string toStringValue(const double value){
|
||||
return std::to_string(value);
|
||||
}
|
||||
|
||||
@ -56,9 +98,15 @@ bool fromStringValue(const std::string &inStr, int64_t &value){
|
||||
}
|
||||
|
||||
bool fromStringValue(const std::string &inStr, bool &value){
|
||||
bool result = true;
|
||||
inStr == "true"?value = true: inStr == "false"?value = false: result = false;
|
||||
return result;
|
||||
if (inStr == "true") {
|
||||
value = true;
|
||||
return true;
|
||||
}
|
||||
if (inStr == "false") {
|
||||
value = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fromStringValue(const std::string &inStr, float &value){
|
||||
@ -81,6 +129,4 @@ bool fromStringValue(const std::string &inStr, double &value){
|
||||
return true;
|
||||
}
|
||||
|
||||
{{#helpersNamespaceDeclarations}}
|
||||
}
|
||||
{{/helpersNamespaceDeclarations}}
|
||||
} // namespace {{helpersNamespace}}
|
||||
|
@ -13,9 +13,8 @@
|
||||
{{/imports}}
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
{{#modelNamespaceDeclarations}}
|
||||
namespace {{this}} {
|
||||
{{/modelNamespaceDeclarations}}
|
||||
namespace {{modelNamespace}}
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// {{description}}
|
||||
@ -24,7 +23,7 @@ class {{declspec}} {{classname}}
|
||||
{
|
||||
public:
|
||||
{{classname}}();
|
||||
virtual ~{{classname}}();
|
||||
virtual ~{{classname}}() = default;
|
||||
{{#isEnum}}{{#allowableValues}}
|
||||
enum class e{{classname}} {
|
||||
// To have a valid default value.
|
||||
@ -35,7 +34,20 @@ public:
|
||||
{{{name}}}{{^-last}}, {{/-last}}
|
||||
{{/enumVars}}
|
||||
};{{/allowableValues}}{{/isEnum}}
|
||||
void validate();
|
||||
|
||||
/// <summary>
|
||||
/// Validate the current data in the model. Throws a ValidationException on failure.
|
||||
/// </summary>
|
||||
void validate() const;
|
||||
|
||||
/// <summary>
|
||||
/// Validate the current data in the model. Returns false on error and writes an error
|
||||
/// message into the given stringstream.
|
||||
/// </summary>
|
||||
bool validate(std::stringstream& msg) const;
|
||||
|
||||
bool operator==(const {{classname}}& rhs) const;
|
||||
bool operator!=(const {{classname}}& rhs) const;
|
||||
|
||||
/////////////////////////////////////////////
|
||||
/// {{classname}} members
|
||||
@ -44,7 +56,7 @@ public:
|
||||
/// <summary>
|
||||
/// {{description}}
|
||||
/// </summary>
|
||||
{{{dataType}}}{{#isContainer}}&{{/isContainer}} {{getter}}(){{^isContainer}} const{{/isContainer}};
|
||||
{{{dataType}}} {{getter}}() const;
|
||||
void {{setter}}({{{dataType}}} const{{^isPrimitiveType}}&{{/isPrimitiveType}} value);{{^required}}
|
||||
bool {{nameInCamelCase}}IsSet() const;
|
||||
void unset{{name}}();{{/required}}
|
||||
@ -65,11 +77,12 @@ protected:
|
||||
{{#isEnum}}
|
||||
{{classname}}::e{{classname}} m_value = {{classname}}::e{{classname}}::INVALID_VALUE_OPENAPI_GENERATED;
|
||||
{{/isEnum}}
|
||||
|
||||
// Helper overload for validate. Used when one model stores another model and calls it's validate.
|
||||
bool validate(std::stringstream& msg, const std::string& pathPrefix) const;
|
||||
};
|
||||
|
||||
{{#modelNamespaceDeclarations}}
|
||||
}
|
||||
{{/modelNamespaceDeclarations}}
|
||||
} // namespace {{modelNamespace}}
|
||||
|
||||
#endif /* {{classname}}_H_ */
|
||||
{{/model}}
|
||||
|
@ -2,12 +2,12 @@
|
||||
{{#models}}{{#model}}
|
||||
|
||||
#include "{{classname}}.h"
|
||||
{{#isEnum}}#include <stdexcept>
|
||||
#include <sstream>{{/isEnum}}
|
||||
#include "{{prefix}}Helpers.h"
|
||||
{{#isEnum}}#include <stdexcept>{{/isEnum}}
|
||||
#include <sstream>
|
||||
|
||||
{{#modelNamespaceDeclarations}}
|
||||
namespace {{this}} {
|
||||
{{/modelNamespaceDeclarations}}
|
||||
namespace {{modelNamespace}}
|
||||
{
|
||||
|
||||
{{classname}}::{{classname}}()
|
||||
{
|
||||
@ -18,13 +18,69 @@ namespace {{this}} {
|
||||
{{/required}}{{/vars}}
|
||||
}
|
||||
|
||||
{{classname}}::~{{classname}}()
|
||||
void {{classname}}::validate() const
|
||||
{
|
||||
std::stringstream msg;
|
||||
if (!validate(msg))
|
||||
{
|
||||
throw {{helpersNamespace}}::ValidationException(msg.str());
|
||||
}
|
||||
}
|
||||
|
||||
void {{classname}}::validate()
|
||||
bool {{classname}}::validate(std::stringstream& msg) const
|
||||
{
|
||||
// TODO: implement validation
|
||||
return validate(msg, "");
|
||||
}
|
||||
|
||||
bool {{classname}}::validate(std::stringstream& msg, const std::string& pathPrefix) const
|
||||
{
|
||||
bool success = true;
|
||||
const std::string _pathPrefix = pathPrefix.empty() ? "{{classname}}" : pathPrefix;
|
||||
|
||||
{{#isEnum}}{{! Special case for enum types }}
|
||||
if (m_value == {{classname}}::e{{classname}}::INVALID_VALUE_OPENAPI_GENERATED)
|
||||
{
|
||||
success = false;
|
||||
msg << _pathPrefix << ": has no value;";
|
||||
}
|
||||
{{/isEnum}}
|
||||
{{^isEnum}}
|
||||
{{#vars}}
|
||||
{{#isArray}} {{! Always generate validation body for array types }}
|
||||
{{^required}}if ({{nameInCamelCase}}IsSet()){{/required}}
|
||||
{{#required}}/* {{name}} */ {{/required}}{
|
||||
const {{{dataType}}}& value = m_{{name}};
|
||||
const std::string currentValuePath = _pathPrefix + ".{{nameInCamelCase}}";
|
||||
{{> model-validation-body }}
|
||||
}
|
||||
{{/isArray}}{{^isArray}}{{#hasValidation}} {{! Only generate validation if necessary }}
|
||||
{{^required}}if ({{nameInCamelCase}}IsSet()){{/required}}
|
||||
{{#required}}/* {{name}} */ {{/required}}{
|
||||
const {{{dataType}}}& value = m_{{name}};
|
||||
const std::string currentValuePath = _pathPrefix + ".{{nameInCamelCase}}";
|
||||
{{> model-validation-body }}
|
||||
}
|
||||
{{/hasValidation}}{{/isArray}}
|
||||
{{/vars}}
|
||||
{{/isEnum}}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool {{classname}}::operator==(const {{classname}}& rhs) const
|
||||
{
|
||||
return
|
||||
{{#isEnum}}getValue() == rhs.getValue(){{/isEnum}}
|
||||
{{^isEnum}}{{#vars}}
|
||||
{{#required}}({{getter}}() == rhs.{{getter}}()){{/required}}
|
||||
{{^required}}((!{{nameInCamelCase}}IsSet() && !rhs.{{nameInCamelCase}}IsSet()) || ({{nameInCamelCase}}IsSet() && rhs.{{nameInCamelCase}}IsSet() && {{getter}}() == rhs.{{getter}}())){{/required}}{{^-last}} &&{{/-last}}
|
||||
{{/vars}}{{/isEnum}}
|
||||
;
|
||||
}
|
||||
|
||||
bool {{classname}}::operator!=(const {{classname}}& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json& j, const {{classname}}& o)
|
||||
@ -74,7 +130,7 @@ void from_json(const nlohmann::json& j, {{classname}}& o)
|
||||
{{/enumVars}}{{/allowableValues}}{{/isEnum}}
|
||||
}
|
||||
|
||||
{{#vars}}{{{dataType}}}{{#isContainer}}&{{/isContainer}} {{classname}}::{{getter}}(){{^isContainer}} const{{/isContainer}}
|
||||
{{#vars}}{{{dataType}}} {{classname}}::{{getter}}() const
|
||||
{
|
||||
return m_{{name}};
|
||||
}
|
||||
@ -102,9 +158,7 @@ void {{classname}}::setValue({{classname}}::e{{classname}} value)
|
||||
m_value = value;
|
||||
}{{/isEnum}}
|
||||
|
||||
{{#modelNamespaceDeclarations}}
|
||||
}
|
||||
{{/modelNamespaceDeclarations}}
|
||||
} // namespace {{modelNamespace}}
|
||||
|
||||
{{/model}}
|
||||
{{/models}}
|
||||
|
@ -14,9 +14,8 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
{{#hasOptional}}#include <pistache/optional.h>{{/hasOptional}}
|
||||
|
||||
{{#modelNamespaceDeclarations}}
|
||||
namespace {{this}} {
|
||||
{{/modelNamespaceDeclarations}}
|
||||
namespace {{modelNamespace}}
|
||||
{
|
||||
|
||||
struct {{classname}}
|
||||
{
|
||||
@ -34,9 +33,9 @@ struct {{classname}}
|
||||
void to_json(nlohmann::json& j, const {{classname}}& o);
|
||||
void from_json(const nlohmann::json& j, {{classname}}& o);
|
||||
|
||||
{{#modelNamespaceDeclarations}}
|
||||
} // {{this}}
|
||||
{{/modelNamespaceDeclarations}}
|
||||
|
||||
} // namespace {{modelNamespace}}
|
||||
|
||||
|
||||
#endif /* {{classname}}_H_ */
|
||||
{{/model}}
|
||||
|
@ -3,9 +3,8 @@
|
||||
|
||||
#include "{{classname}}.h"
|
||||
|
||||
{{#modelNamespaceDeclarations}}
|
||||
namespace {{this}} {
|
||||
{{/modelNamespaceDeclarations}}
|
||||
namespace {{modelNamespace}}
|
||||
{
|
||||
|
||||
nlohmann::json {{classname}}::to_json() const
|
||||
{
|
||||
@ -51,9 +50,7 @@ void from_json(const nlohmann::json& j, {{classname}}& o)
|
||||
{{/vars}}
|
||||
}
|
||||
|
||||
{{#modelNamespaceDeclarations}}
|
||||
} // {{this}}
|
||||
{{/modelNamespaceDeclarations}}
|
||||
} // namespace {{modelNamespace}}
|
||||
|
||||
{{/model}}
|
||||
{{/models}}
|
||||
|
124
modules/openapi-generator/src/main/resources/cpp-pistache-server/model-validation-body.mustache
vendored
Normal file
124
modules/openapi-generator/src/main/resources/cpp-pistache-server/model-validation-body.mustache
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
{{!
|
||||
This template generates the validation logic for a model.
|
||||
This template file calls itself recursively for arrays.
|
||||
}}
|
||||
{{! Check for eunm properties that are their own schema }}
|
||||
{{! These are in their own class with a validate function, the way to check for this is quite a hack
|
||||
since isEnum, isString and isModel are all false. }}
|
||||
{{^isString}}{{#allowableValues.enumVars.0.value}}
|
||||
success = value.validate(msg, currentValuePath) && success;
|
||||
{{/allowableValues.enumVars.0.value}}{{/isString}}
|
||||
{{#isModel}}success = value.validate(msg, currentValuePath + ".{{nameInCamelCase}}") && success;{{/isModel}}
|
||||
{{! Date validation }}
|
||||
{{#isDate}}
|
||||
if (!{{helpersNamespace}}::validateRfc3339_date(value))
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must be a valid RFC 3339 date-full string;";
|
||||
}
|
||||
{{/isDate}}
|
||||
{{! Date-Time validation }}
|
||||
{{#isDateTime}}
|
||||
if (!{{helpersNamespace}}::validateRfc3339_date_time(value))
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must be a valid RFC 3339 date-time string;";
|
||||
}
|
||||
{{/isDateTime}}
|
||||
{{! string validation }}
|
||||
{{#isString}}
|
||||
{{#minLength}}
|
||||
if (value.length() < {{minLength}})
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must be at least {{minLength}} characters long;";
|
||||
}
|
||||
{{/minLength}}
|
||||
{{#maxLength}}
|
||||
if (value.length() > {{maxLength}})
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must be at most {{maxLength}} characters long;";
|
||||
}
|
||||
{{/maxLength}}
|
||||
{{!
|
||||
TODO validate regex of string using pattern variable. This has two challenges
|
||||
- Is compatibility with the given regex pattern guaranteed?
|
||||
- Creating the std::regex on every validation would be rather slow. Ideally one would
|
||||
initialize them for the class once as a static const and use them.
|
||||
}}
|
||||
{{! string encoded enum validation }}
|
||||
{{#isEnum}}
|
||||
{{#allowableValues}}
|
||||
if ({{#enumVars}}
|
||||
value != "{{value}}"{{^-last}} &&{{/-last}}{{/enumVars}}
|
||||
) {
|
||||
success = false;
|
||||
msg << currentValuePath << ": has invalid value \"" << value << "\";";
|
||||
}
|
||||
{{/allowableValues}}
|
||||
{{/isEnum}}
|
||||
{{/isString}}
|
||||
{{! numeric validation }}
|
||||
{{#isNumeric}}
|
||||
{{#minimum}}
|
||||
if (value <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{#isFloat}}static_cast<float>({{/isFloat}}{{minimum}}{{#isFloat}}){{/isFloat}}{{#isLong}}ll{{/isLong}})
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must be greater than{{^exclusiveMinimum}} or equal to{{/exclusiveMinimum}} {{minimum}};";
|
||||
}
|
||||
{{/minimum}}
|
||||
{{#maximum}}
|
||||
if (value >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{#isFloat}}static_cast<float>({{/isFloat}}{{maximum}}{{#isFloat}}){{/isFloat}}{{#isLong}}ll{{/isLong}})
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must be less than{{^exclusiveMaximum}} or equal to{{/exclusiveMaximum}} {{maximum}};";
|
||||
}
|
||||
{{/maximum}}
|
||||
{{#multipleOf}}
|
||||
{{#isInteger}}if (value % {{multipleOf}}{{#isLong}}ll{{/isLong}} != 0){{/isInteger}}
|
||||
{{#isFloat}}if (std::fmod(value, static_cast<float>({{multipleOf}})) != 0){{/isFloat}}
|
||||
{{#isDouble}}if (std::fmod(value, {{multipleOf}}) != 0){{/isDouble}}
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must be a multiple of {{multipleOf}};";
|
||||
}
|
||||
{{/multipleOf}}
|
||||
{{/isNumeric}}
|
||||
{{! Array validation }}
|
||||
{{#isArray}}
|
||||
{{#minItems}}
|
||||
if (value.size() < {{minItems}})
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must have at least {{minItems}} elements;";
|
||||
}
|
||||
{{/minItems}}
|
||||
{{#maxItems}}
|
||||
if (value.size() > {{maxItems}})
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must have at most {{maxItems}} elements;";
|
||||
}
|
||||
{{/maxItems}}
|
||||
{{#uniqueItems}}
|
||||
if (!{{helpersNamespace}}::hasOnlyUniqueItems(value))
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": may not contain the same item more than once;";
|
||||
}
|
||||
{{/uniqueItems}}
|
||||
{ // Recursive validation of array elements
|
||||
const std::string oldValuePath = currentValuePath;
|
||||
int i = 0;
|
||||
{{! the element var has the same name as the vector, so that the recursive template works - what a wonderful hack }}
|
||||
for (const {{{items.dataType}}}& value : value)
|
||||
{ {{! and I do a similar hack with currentValuePath... }}
|
||||
const std::string currentValuePath = oldValuePath + "[" + std::to_string(i) + "]";
|
||||
{{#items}}
|
||||
{{> model-validation-body }} {{! Recursively apply template to array - this is where things will probbaly go wrong }}
|
||||
{{/items}}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
{{/isArray}}
|
@ -15,10 +15,20 @@ public:
|
||||
{{classname}}();
|
||||
~{{classname}}();
|
||||
|
||||
/* Sets the URL Endpoint.
|
||||
* Note: several fallback endpoints can be configured in request retry policies, see Request::SetShouldRetry */
|
||||
void SetURL(const FString& Url);
|
||||
|
||||
/* Adds global header params to all requests */
|
||||
void AddHeaderParam(const FString& Key, const FString& Value);
|
||||
void ClearHeaderParams();
|
||||
|
||||
/* Sets the retry manager to the user-defined retry manager. User must manage the lifetime of the retry manager.
|
||||
* If no retry manager is specified and a request needs retries, a default retry manager will be used.
|
||||
* See also: Request::SetShouldRetry */
|
||||
void SetHttpRetryManager(FHttpRetrySystem::FManager& RetryManager);
|
||||
FHttpRetrySystem::FManager& GetHttpRetryManager();
|
||||
|
||||
{{#operations}}{{#operation}}class {{operationIdCamelCase}}Request;
|
||||
class {{operationIdCamelCase}}Response;
|
||||
{{/operation}}{{/operations}}
|
||||
@ -27,15 +37,17 @@ public:
|
||||
{{#operations}}{{#operation}}{{#description}}/* {{{description}}} */
|
||||
{{/description}}bool {{operationIdCamelCase}}(const {{operationIdCamelCase}}Request& Request, const F{{operationIdCamelCase}}Delegate& Delegate = F{{operationIdCamelCase}}Delegate()) const;
|
||||
{{/operation}}{{/operations}}
|
||||
|
||||
private:
|
||||
{{#operations}}{{#operation}}void On{{operationIdCamelCase}}Response(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded, F{{operationIdCamelCase}}Delegate Delegate, int AutoRetryCount) const;
|
||||
{{#operations}}{{#operation}}void On{{operationIdCamelCase}}Response(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded, F{{operationIdCamelCase}}Delegate Delegate) const;
|
||||
{{/operation}}{{/operations}}
|
||||
FHttpRequestRef CreateHttpRequest(const Request& Request) const;
|
||||
bool IsValid() const;
|
||||
void HandleResponse(FHttpResponsePtr HttpResponse, bool bSucceeded, Response& InOutResponse) const;
|
||||
|
||||
FString Url;
|
||||
TMap<FString,FString> AdditionalHeaderParams;
|
||||
mutable FHttpRetrySystem::FManager* RetryManager = nullptr;
|
||||
mutable TUniquePtr<HttpRetryManager> DefaultRetryManager;
|
||||
};
|
||||
|
||||
{{#cppNamespaceDeclarations}}
|
||||
|
@ -45,6 +45,40 @@ bool {{classname}}::IsValid() const
|
||||
return true;
|
||||
}
|
||||
|
||||
void {{classname}}::SetHttpRetryManager(FHttpRetrySystem::FManager& InRetryManager)
|
||||
{
|
||||
if(RetryManager != &GetHttpRetryManager())
|
||||
{
|
||||
DefaultRetryManager.Reset();
|
||||
RetryManager = &InRetryManager;
|
||||
}
|
||||
}
|
||||
|
||||
FHttpRetrySystem::FManager& {{classname}}::GetHttpRetryManager()
|
||||
{
|
||||
return *RetryManager;
|
||||
}
|
||||
|
||||
FHttpRequestRef {{classname}}::CreateHttpRequest(const Request& Request) const
|
||||
{
|
||||
if (!Request.GetRetryParams().IsSet())
|
||||
{
|
||||
return FHttpModule::Get().CreateRequest();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!RetryManager)
|
||||
{
|
||||
// Create default retry manager if none was specified
|
||||
DefaultRetryManager = MakeUnique<HttpRetryManager>(6, 60);
|
||||
RetryManager = DefaultRetryManager.Get();
|
||||
}
|
||||
|
||||
const HttpRetryParams& Params = Request.GetRetryParams().GetValue();
|
||||
return RetryManager->CreateRequest(Params.RetryLimitCountOverride, Params.RetryTimeoutRelativeSecondsOverride, Params.RetryResponseCodes, Params.RetryVerbs, Params.RetryDomains);
|
||||
}
|
||||
}
|
||||
|
||||
void {{classname}}::HandleResponse(FHttpResponsePtr HttpResponse, bool bSucceeded, Response& InOutResponse) const
|
||||
{
|
||||
InOutResponse.SetHttpResponse(HttpResponse);
|
||||
@ -96,7 +130,7 @@ bool {{classname}}::{{operationIdCamelCase}}(const {{operationIdCamelCase}}Reque
|
||||
if (!IsValid())
|
||||
return false;
|
||||
|
||||
FHttpRequestRef HttpRequest = FHttpModule::Get().CreateRequest();
|
||||
FHttpRequestRef HttpRequest = CreateHttpRequest(Request);
|
||||
HttpRequest->SetURL(*(Url + Request.ComputePath()));
|
||||
|
||||
for(const auto& It : AdditionalHeaderParams)
|
||||
@ -106,27 +140,16 @@ bool {{classname}}::{{operationIdCamelCase}}(const {{operationIdCamelCase}}Reque
|
||||
|
||||
Request.SetupHttpRequest(HttpRequest);
|
||||
|
||||
HttpRequest->OnProcessRequestComplete().BindRaw(this, &{{classname}}::On{{operationIdCamelCase}}Response, Delegate, Request.GetAutoRetryCount());
|
||||
HttpRequest->OnProcessRequestComplete().BindRaw(this, &{{classname}}::On{{operationIdCamelCase}}Response, Delegate);
|
||||
return HttpRequest->ProcessRequest();
|
||||
}
|
||||
|
||||
void {{classname}}::On{{operationIdCamelCase}}Response(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded, F{{operationIdCamelCase}}Delegate Delegate, int AutoRetryCount) const
|
||||
void {{classname}}::On{{operationIdCamelCase}}Response(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded, F{{operationIdCamelCase}}Delegate Delegate) const
|
||||
{
|
||||
{{operationIdCamelCase}}Response Response;
|
||||
Response.SetHttpRequest(HttpRequest);
|
||||
|
||||
HandleResponse(HttpResponse, bSucceeded, Response);
|
||||
|
||||
if(!Response.IsSuccessful() && AutoRetryCount > 0)
|
||||
{
|
||||
HttpRequest->OnProcessRequestComplete().BindRaw(this, &{{classname}}::On{{operationIdCamelCase}}Response, Delegate, AutoRetryCount - 1);
|
||||
Response.AsyncRetry();
|
||||
}
|
||||
else
|
||||
{
|
||||
Delegate.ExecuteIfBound(Response);
|
||||
}
|
||||
}
|
||||
|
||||
{{/operation}}
|
||||
{{/operations}}
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "Interfaces/IHttpResponse.h"
|
||||
#include "Serialization/JsonWriter.h"
|
||||
#include "Dom/JsonObject.h"
|
||||
#include "HttpRetrySystem.h"
|
||||
#include "Containers/Ticker.h"
|
||||
|
||||
{{#cppNamespaceDeclarations}}
|
||||
namespace {{this}}
|
||||
@ -12,6 +14,31 @@ namespace {{this}}
|
||||
{{/cppNamespaceDeclarations}}
|
||||
|
||||
typedef TSharedRef<TJsonWriter<>> JsonWriter;
|
||||
using namespace FHttpRetrySystem;
|
||||
|
||||
struct {{dllapi}} HttpRetryManager : public FManager, public FTickerObjectBase
|
||||
{
|
||||
using FManager::FManager;
|
||||
|
||||
bool Tick(float DeltaTime) final;
|
||||
};
|
||||
|
||||
struct {{dllapi}} HttpRetryParams
|
||||
{
|
||||
HttpRetryParams(
|
||||
const FRetryLimitCountSetting& InRetryLimitCountOverride = FRetryLimitCountSetting(),
|
||||
const FRetryTimeoutRelativeSecondsSetting& InRetryTimeoutRelativeSecondsOverride = FRetryTimeoutRelativeSecondsSetting(),
|
||||
const FRetryResponseCodes& InRetryResponseCodes = FRetryResponseCodes(),
|
||||
const FRetryVerbs& InRetryVerbs = FRetryVerbs(),
|
||||
const FRetryDomainsPtr& InRetryDomains = FRetryDomainsPtr()
|
||||
);
|
||||
|
||||
FRetryLimitCountSetting RetryLimitCountOverride;
|
||||
FRetryTimeoutRelativeSecondsSetting RetryTimeoutRelativeSecondsOverride;
|
||||
FRetryResponseCodes RetryResponseCodes;
|
||||
FRetryVerbs RetryVerbs;
|
||||
FRetryDomainsPtr RetryDomains;
|
||||
};
|
||||
|
||||
class {{dllapi}} Model
|
||||
{
|
||||
@ -28,11 +55,12 @@ public:
|
||||
virtual void SetupHttpRequest(const FHttpRequestRef& HttpRequest) const = 0;
|
||||
virtual FString ComputePath() const = 0;
|
||||
|
||||
void SetAutoRetryCount(int InCount) { AutoRetryCount = InCount; }
|
||||
int GetAutoRetryCount() const { return AutoRetryCount; }
|
||||
/* Enables retry and optionally sets a retry policy for this request */
|
||||
void SetShouldRetry(const HttpRetryParams& Params = HttpRetryParams()) { RetryParams = Params; }
|
||||
const TOptional<HttpRetryParams>& GetRetryParams() const { return RetryParams; }
|
||||
|
||||
private:
|
||||
int AutoRetryCount = 0;
|
||||
TOptional<HttpRetryParams> RetryParams;
|
||||
};
|
||||
|
||||
class {{dllapi}} Response
|
||||
@ -44,8 +72,6 @@ public:
|
||||
void SetSuccessful(bool InSuccessful) { Successful = InSuccessful; }
|
||||
bool IsSuccessful() const { return Successful; }
|
||||
|
||||
void AsyncRetry() const;
|
||||
|
||||
virtual void SetHttpResponseCode(EHttpResponseCodes::Type InHttpResponseCode);
|
||||
EHttpResponseCodes::Type GetHttpResponseCode() const { return ResponseCode; }
|
||||
|
||||
@ -55,15 +81,11 @@ public:
|
||||
void SetHttpResponse(const FHttpResponsePtr& InHttpResponse) { HttpResponse = InHttpResponse; }
|
||||
const FHttpResponsePtr& GetHttpResponse() const { return HttpResponse; }
|
||||
|
||||
void SetHttpRequest(const FHttpRequestPtr& InHttpRequest) { HttpRequest = InHttpRequest; }
|
||||
const FHttpRequestPtr& GetHttpRequest() const { return HttpRequest; }
|
||||
|
||||
private:
|
||||
bool Successful;
|
||||
EHttpResponseCodes::Type ResponseCode;
|
||||
FString ResponseString;
|
||||
FHttpResponsePtr HttpResponse;
|
||||
FHttpRequestPtr HttpRequest;
|
||||
};
|
||||
|
||||
{{#cppNamespaceDeclarations}}
|
||||
|
@ -1,13 +1,30 @@
|
||||
{{>licenseInfo}}
|
||||
#include "{{modelNamePrefix}}BaseModel.h"
|
||||
|
||||
#include "Async/Async.h"
|
||||
|
||||
{{#cppNamespaceDeclarations}}
|
||||
namespace {{this}}
|
||||
{
|
||||
{{/cppNamespaceDeclarations}}
|
||||
|
||||
bool HttpRetryManager::Tick(float DeltaTime)
|
||||
{
|
||||
FManager::Update();
|
||||
return true;
|
||||
}
|
||||
|
||||
HttpRetryParams::HttpRetryParams(const FRetryLimitCountSetting& InRetryLimitCountOverride /*= FRetryLimitCountSetting()*/,
|
||||
const FRetryTimeoutRelativeSecondsSetting& InRetryTimeoutRelativeSecondsOverride /*= FRetryTimeoutRelativeSecondsSetting()*/,
|
||||
const FRetryResponseCodes& InRetryResponseCodes /*= FRetryResponseCodes()*/,
|
||||
const FRetryVerbs& InRetryVerbs /*= FRetryVerbs()*/,
|
||||
const FRetryDomainsPtr& InRetryDomains /*= FRetryDomainsPtr() */)
|
||||
: RetryLimitCountOverride(InRetryLimitCountOverride)
|
||||
, RetryTimeoutRelativeSecondsOverride(InRetryTimeoutRelativeSecondsOverride)
|
||||
, RetryResponseCodes(InRetryResponseCodes)
|
||||
, RetryVerbs(InRetryVerbs)
|
||||
, RetryDomains(InRetryDomains)
|
||||
{
|
||||
}
|
||||
|
||||
void Response::SetHttpResponseCode(EHttpResponseCodes::Type InHttpResponseCode)
|
||||
{
|
||||
ResponseCode = InHttpResponseCode;
|
||||
@ -18,15 +35,6 @@ void Response::SetHttpResponseCode(EHttpResponseCodes::Type InHttpResponseCode)
|
||||
}
|
||||
}
|
||||
|
||||
void Response::AsyncRetry() const
|
||||
{
|
||||
// Unfortunately, it is currently usafe to call ProcessRequest() directly here.
|
||||
// This is because the HttpManager will remove all references to this HttpRequest in FHttpManager::Tick including the new request we just added, instead of removing just one.
|
||||
// This will lead to the request's destruction and eventually a crash.
|
||||
// The only solution is therefore to ensure we are taking an extra reference to the request, and that the request is added after the queue is flushed.
|
||||
Async(EAsyncExecution::TaskGraph, [AddRef = FHttpRequestPtr(GetHttpRequest())](){ AddRef->ProcessRequest(); });
|
||||
}
|
||||
|
||||
{{#cppNamespaceDeclarations}}
|
||||
}
|
||||
{{/cppNamespaceDeclarations}}
|
@ -197,7 +197,10 @@ module {{moduleName}}
|
||||
|
||||
# Returns Auth Settings hash for api client.
|
||||
def auth_settings
|
||||
Hash{ {{#authMethods}}{{#isApiKey}}"{{name}}" => {
|
||||
Hash{
|
||||
{{#authMethods}}
|
||||
{{#isApiKey}}
|
||||
"{{name}}" => {
|
||||
type: "api_key",
|
||||
in: {{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{#isKeyInQuery}}"query"{{/isKeyInQuery}},
|
||||
key: "{{keyParamName}}",
|
||||
@ -206,8 +209,7 @@ module {{moduleName}}
|
||||
{{/isApiKey}}
|
||||
{{#isBasic}}
|
||||
{{#isBasicBasic}}
|
||||
"{{name}}" =>
|
||||
{
|
||||
"{{name}}" => {
|
||||
type: "basic",
|
||||
in: "header",
|
||||
key: "Authorization",
|
||||
@ -215,8 +217,7 @@ module {{moduleName}}
|
||||
},
|
||||
{{/isBasicBasic}}
|
||||
{{#isBasicBearer}}
|
||||
"{{name}}" =>
|
||||
{
|
||||
"{{name}}" => {
|
||||
type: "bearer",
|
||||
in: "header",
|
||||
{{#bearerFormat}}
|
||||
@ -228,8 +229,7 @@ module {{moduleName}}
|
||||
{{/isBasicBearer}}
|
||||
{{/isBasic}}
|
||||
{{#isOAuth}}
|
||||
"{{name}}" =>
|
||||
{
|
||||
"{{name}}" => {
|
||||
type: "oauth2",
|
||||
in: "header",
|
||||
key: "Authorization",
|
||||
|
@ -1,5 +1,6 @@
|
||||
# {{#lambdaPrefixWithHash}}{{> api_info}}{{/lambdaPrefixWithHash}}
|
||||
|
||||
require "json"
|
||||
require "time"
|
||||
|
||||
module {{moduleName}}
|
||||
|
@ -1,8 +1,10 @@
|
||||
{{#description}}
|
||||
# {{{description}}}
|
||||
{{/description}}
|
||||
class {{classname}}{{#parent}} < {{{.}}}{{/parent}} include JSON::Serializable
|
||||
include JSON::Serializable {{#vars}}
|
||||
class {{classname}}{{#parent}} < {{{.}}}{{/parent}}
|
||||
include JSON::Serializable
|
||||
|
||||
{{#vars}}
|
||||
{{#description}}
|
||||
# {{{description}}}
|
||||
{{/description}}
|
||||
|
@ -1,4 +1,4 @@
|
||||
name: {{{moduleName}}}
|
||||
name: {{{shardName}}}
|
||||
version: {{{shardVersion}}}
|
||||
authors:
|
||||
- {{{shardAuthors}}}
|
||||
|
@ -137,6 +137,9 @@ services.AddHttpClient<YourApiClass>(httpClient =>
|
||||
```csharp
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
{{#useHttpClient}}
|
||||
using System.Net.Http;
|
||||
{{/useHttpClient}}
|
||||
using {{packageName}}.{{apiPackage}};
|
||||
using {{packageName}}.Client;
|
||||
using {{packageName}}.{{modelPackage}};
|
||||
@ -174,7 +177,15 @@ namespace Example
|
||||
{{/authMethods}}
|
||||
|
||||
{{/hasAuthMethods}}
|
||||
{{#useHttpClient}}
|
||||
// create instances of HttpClient, HttpClientHandler to be reused later with different Api classes
|
||||
HttpClient httpClient = new HttpClient();
|
||||
HttpClientHandler httpClientHandler = new HttpClientHandler();
|
||||
var apiInstance = new {{classname}}(httpClient, config, httpClientHandler);
|
||||
{{/useHttpClient}}
|
||||
{{^useHttpClient}}
|
||||
var apiInstance = new {{classname}}(config);
|
||||
{{/useHttpClient}}
|
||||
{{#allParams}}
|
||||
{{#isPrimitiveType}}
|
||||
var {{paramName}} = {{{example}}}; // {{{dataType}}} | {{{description}}}{{^required}} (optional) {{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}
|
||||
|
@ -22,6 +22,9 @@ Method | HTTP request | Description
|
||||
```csharp
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
{{#useHttpClient}}
|
||||
using System.Net.Http;
|
||||
{{/useHttpClient}}
|
||||
using {{packageName}}.{{apiPackage}};
|
||||
using {{packageName}}.Client;
|
||||
using {{packageName}}.{{modelPackage}};
|
||||
@ -58,7 +61,15 @@ namespace Example
|
||||
{{/authMethods}}
|
||||
|
||||
{{/hasAuthMethods}}
|
||||
{{#useHttpClient}}
|
||||
// create instances of HttpClient, HttpClientHandler to be reused later with different Api classes
|
||||
HttpClient httpClient = new HttpClient();
|
||||
HttpClientHandler httpClientHandler = new HttpClientHandler();
|
||||
var apiInstance = new {{classname}}(httpClient, config, httpClientHandler);
|
||||
{{/useHttpClient}}
|
||||
{{^useHttpClient}}
|
||||
var apiInstance = new {{classname}}(config);
|
||||
{{/useHttpClient}}
|
||||
{{#allParams}}
|
||||
{{#isPrimitiveType}}
|
||||
var {{paramName}} = {{{example}}}; // {{{dataType}}} | {{{description}}}{{^required}} (optional) {{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}
|
||||
|
@ -98,13 +98,13 @@ namespace {{packageName}}.Client
|
||||
|
||||
if (type == typeof(byte[])) // return byte array
|
||||
{
|
||||
return response.Content.ReadAsByteArrayAsync().Result;
|
||||
return response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
// TODO: ? if (type.IsAssignableFrom(typeof(Stream)))
|
||||
if (type == typeof(Stream))
|
||||
{
|
||||
var bytes = response.Content.ReadAsByteArrayAsync().Result;
|
||||
var bytes = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult();
|
||||
if (headers != null)
|
||||
{
|
||||
var filePath = String.IsNullOrEmpty(_configuration.TempFolderPath)
|
||||
@ -128,18 +128,18 @@ namespace {{packageName}}.Client
|
||||
|
||||
if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object
|
||||
{
|
||||
return DateTime.Parse(response.Content.ReadAsStringAsync().Result, null, System.Globalization.DateTimeStyles.RoundtripKind);
|
||||
return DateTime.Parse(response.Content.ReadAsStringAsync().GetAwaiter().GetResult(), null, System.Globalization.DateTimeStyles.RoundtripKind);
|
||||
}
|
||||
|
||||
if (type == typeof(String) || type.Name.StartsWith("System.Nullable")) // return primitive type
|
||||
{
|
||||
return Convert.ChangeType(response.Content.ReadAsStringAsync().Result, type);
|
||||
return Convert.ChangeType(response.Content.ReadAsStringAsync().GetAwaiter().GetResult(), type);
|
||||
}
|
||||
|
||||
// at this point, it must be a model (json)
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result, type, _serializerSettings);
|
||||
return JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().GetAwaiter().GetResult(), type, _serializerSettings);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -191,8 +191,9 @@ namespace {{packageName}}.Client
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ApiClient" />, defaulting to the global configurations' base url.
|
||||
/// **IMPORTANT** This will also create an istance of HttpClient, which is less than ideal.
|
||||
/// It's better to reuse the <see href="https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#issues-with-the-original-httpclient-class-available-in-net">HttpClient and HttpClientHander</see>.
|
||||
/// </summary>
|
||||
[Obsolete("Constructors without HttpClient have non-trivial drawbacks and are thus considered deprecated. Check README.md for details.")]
|
||||
public ApiClient() :
|
||||
this({{packageName}}.Client.GlobalConfiguration.Instance.BasePath)
|
||||
{
|
||||
@ -200,10 +201,11 @@ namespace {{packageName}}.Client
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ApiClient" />.
|
||||
/// **IMPORTANT** This will also create an istance of HttpClient, which is less than ideal.
|
||||
/// It's better to reuse the <see href="https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#issues-with-the-original-httpclient-class-available-in-net">HttpClient and HttpClientHander</see>.
|
||||
/// </summary>
|
||||
/// <param name="basePath">The target service's base path in URL format.</param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
[Obsolete("Constructors without HttpClient have non-trivial drawbacks and are thus considered deprecated. Check README.md for details.")]
|
||||
public ApiClient(String basePath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(basePath)) throw new ArgumentException("basePath cannot be empty");
|
||||
@ -399,10 +401,10 @@ namespace {{packageName}}.Client
|
||||
partial void InterceptRequest(HttpRequestMessage req);
|
||||
partial void InterceptResponse(HttpRequestMessage req, HttpResponseMessage response);
|
||||
|
||||
private ApiResponse<T> ToApiResponse<T>(HttpResponseMessage response, object responseData, Uri uri)
|
||||
private async Task<ApiResponse<T>> ToApiResponse<T>(HttpResponseMessage response, object responseData, Uri uri)
|
||||
{
|
||||
T result = (T) responseData;
|
||||
string rawContent = response.Content.ToString();
|
||||
string rawContent = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var transformed = new ApiResponse<T>(response.StatusCode, new Multimap<string, string>({{#caseInsensitiveResponseHeaders}}StringComparer.OrdinalIgnoreCase{{/caseInsensitiveResponseHeaders}}), result, rawContent)
|
||||
{
|
||||
@ -444,7 +446,7 @@ namespace {{packageName}}.Client
|
||||
|
||||
private ApiResponse<T> Exec<T>(HttpRequestMessage req, IReadableConfiguration configuration)
|
||||
{
|
||||
return ExecAsync<T>(req, configuration).Result;
|
||||
return ExecAsync<T>(req, configuration).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private async Task<ApiResponse<T>> ExecAsync<T>(HttpRequestMessage req,
|
||||
@ -509,6 +511,11 @@ namespace {{packageName}}.Client
|
||||
}
|
||||
{{/supportsRetry}}
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
return await ToApiResponse<T>(response, default(T), req.RequestUri);
|
||||
}
|
||||
|
||||
object responseData = deserializer.Deserialize<T>(response);
|
||||
|
||||
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data
|
||||
@ -523,9 +530,7 @@ namespace {{packageName}}.Client
|
||||
|
||||
InterceptResponse(req, response);
|
||||
|
||||
var result = ToApiResponse<T>(response, responseData, req.RequestUri);
|
||||
|
||||
return result;
|
||||
return await ToApiResponse<T>(response, responseData, req.RequestUri);
|
||||
}
|
||||
|
||||
{{#supportsAsync}}
|
||||
|
@ -106,20 +106,22 @@ namespace {{packageName}}.{{apiPackage}}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="{{classname}}"/> class.
|
||||
/// **IMPORTANT** This will also create an istance of HttpClient, which is less than ideal.
|
||||
/// It's better to reuse the <see href="https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#issues-with-the-original-httpclient-class-available-in-net">HttpClient and HttpClientHander</see>.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Constructors without HttpClient have non-trivial drawbacks and are thus considered deprecated. Check README.md for details.")]
|
||||
public {{classname}}() : this((string)null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="{{classname}}"/> class.
|
||||
/// **IMPORTANT** This will also create an istance of HttpClient, which is less than ideal.
|
||||
/// It's better to reuse the <see href="https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#issues-with-the-original-httpclient-class-available-in-net">HttpClient and HttpClientHander</see>.
|
||||
/// </summary>
|
||||
/// <param name="basePath">The target service's base path in URL format.</param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Constructors without HttpClient have non-trivial drawbacks and are thus considered deprecated. Check README.md for details.")]
|
||||
public {{classname}}(String basePath)
|
||||
{
|
||||
this.Configuration = {{packageName}}.Client.Configuration.MergeConfigurations(
|
||||
@ -136,11 +138,12 @@ namespace {{packageName}}.{{apiPackage}}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="{{classname}}"/> class using Configuration object.
|
||||
/// **IMPORTANT** This will also create an istance of HttpClient, which is less than ideal.
|
||||
/// It's better to reuse the <see href="https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#issues-with-the-original-httpclient-class-available-in-net">HttpClient and HttpClientHander</see>.
|
||||
/// </summary>
|
||||
/// <param name="configuration">An instance of Configuration.</param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Constructors without HttpClient have non-trivial drawbacks and are thus considered deprecated. Check README.md for details.")]
|
||||
public {{classname}}({{packageName}}.Client.Configuration configuration)
|
||||
{
|
||||
if (configuration == null) throw new ArgumentNullException("configuration");
|
||||
|
@ -84,7 +84,7 @@ class ApiClient {
|
||||
? '?${urlEncodedQueryParams.join('&')}'
|
||||
: '';
|
||||
|
||||
final url = '$basePath$path$queryString';
|
||||
final Uri uri = Uri.parse('$basePath$path$queryString');
|
||||
|
||||
if (nullableContentType != null) {
|
||||
headerParams['Content-Type'] = nullableContentType;
|
||||
@ -96,7 +96,7 @@ class ApiClient {
|
||||
body is MultipartFile && (nullableContentType == null ||
|
||||
!nullableContentType.toLowerCase().startsWith('multipart/form-data'))
|
||||
) {
|
||||
final request = StreamedRequest(method, Uri.parse(url));
|
||||
final request = StreamedRequest(method, uri);
|
||||
request.headers.addAll(headerParams);
|
||||
request.contentLength = body.length;
|
||||
body.finalize().listen(
|
||||
@ -110,7 +110,7 @@ class ApiClient {
|
||||
}
|
||||
|
||||
if (body is MultipartRequest) {
|
||||
final request = MultipartRequest(method, Uri.parse(url));
|
||||
final request = MultipartRequest(method, uri);
|
||||
request.fields.addAll(body.fields);
|
||||
request.files.addAll(body.files);
|
||||
request.headers.addAll(body.headers);
|
||||
@ -125,12 +125,12 @@ class ApiClient {
|
||||
final nullableHeaderParams = headerParams.isEmpty ? null : headerParams;
|
||||
|
||||
switch(method) {
|
||||
case 'POST': return await _client.post(url, headers: nullableHeaderParams, body: msgBody,);
|
||||
case 'PUT': return await _client.put(url, headers: nullableHeaderParams, body: msgBody,);
|
||||
case 'DELETE': return await _client.delete(url, headers: nullableHeaderParams,);
|
||||
case 'PATCH': return await _client.patch(url, headers: nullableHeaderParams, body: msgBody,);
|
||||
case 'HEAD': return await _client.head(url, headers: nullableHeaderParams,);
|
||||
case 'GET': return await _client.get(url, headers: nullableHeaderParams,);
|
||||
case 'POST': return await _client.post(uri, headers: nullableHeaderParams, body: msgBody,);
|
||||
case 'PUT': return await _client.put(uri, headers: nullableHeaderParams, body: msgBody,);
|
||||
case 'DELETE': return await _client.delete(uri, headers: nullableHeaderParams,);
|
||||
case 'PATCH': return await _client.patch(uri, headers: nullableHeaderParams, body: msgBody,);
|
||||
case 'HEAD': return await _client.head(uri, headers: nullableHeaderParams,);
|
||||
case 'GET': return await _client.get(uri, headers: nullableHeaderParams,);
|
||||
}
|
||||
} on SocketException catch (e, trace) {
|
||||
throw ApiException.withInner(HttpStatus.badRequest, 'Socket operation failed: $method $path', e, trace,);
|
||||
|
@ -16,4 +16,3 @@ targets:
|
||||
field_rename: none
|
||||
ignore_unannotated: false
|
||||
include_if_null: false
|
||||
nullable: true
|
||||
|
@ -9,15 +9,15 @@ authors:
|
||||
- '{{{pubAuthor}}} <{{{pubAuthorEmail}}}>'
|
||||
homepage: '{{{pubHomepage}}}'
|
||||
environment:
|
||||
sdk: '>=2.0.0 <3.0.0'
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
dependencies:
|
||||
http: '>=0.12.0 <0.13.0'
|
||||
intl: '^0.16.1'
|
||||
http: '>=0.13.0 <0.14.0'
|
||||
intl: '^0.17.0'
|
||||
meta: '^1.1.8'
|
||||
{{#json_serializable}}
|
||||
json_annotation: '^3.1.1'{{/json_serializable}}
|
||||
dev_dependencies:
|
||||
test: '>=1.3.0 <1.16.0'
|
||||
test: '>=1.16.0 <1.18.0'
|
||||
{{#json_serializable}}
|
||||
build_runner: '^1.0.0'
|
||||
build_runner: '^1.10.9'
|
||||
json_serializable: '^3.5.1'{{/json_serializable}}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{deps, [
|
||||
{cowboy, {git, "https://github.com/ninenines/cowboy.git", {tag, "2.5.0"}}},
|
||||
{cowboy, {git, "https://github.com/ninenines/cowboy.git", {tag, "2.8.0"}}},
|
||||
{rfc3339, {git, "https://github.com/talentdeficit/rfc3339.git", {tag, "master"}}},
|
||||
{jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.9.0"}}},
|
||||
{jesse, {git, "https://github.com/for-GET/jesse.git", {tag, "1.5.2"}}}
|
||||
{jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "v3.1.0"}}},
|
||||
{jesse, {git, "https://github.com/for-GET/jesse.git", {tag, "1.5.6"}}}
|
||||
]}.
|
||||
|
@ -25,7 +25,6 @@ import kotlinx.serialization.Contextual
|
||||
{{#parcelizeModels}}
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
{{/parcelizeModels}}
|
||||
{{/multiplatform}}
|
||||
{{#multiplatform}}
|
||||
|
@ -97,7 +97,7 @@ import {{packageName}}.infrastructure.toMultiValue
|
||||
{{/hasQueryParams}}
|
||||
val localVariableHeaders: MutableMap<String, String> = mutableMapOf({{#hasFormParams}}"Content-Type" to {{^consumes}}"multipart/form-data"{{/consumes}}{{#consumes.0}}"{{{mediaType}}}"{{/consumes.0}}{{/hasFormParams}})
|
||||
{{#headerParams}}
|
||||
{{{paramName}}}?.apply { localVariableHeaders["{{baseName}}"] = {{#isContainer}}this.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}this.toString(){{/isContainer}} }
|
||||
{{{paramName}}}{{^required}}?{{/required}}.apply { localVariableHeaders["{{baseName}}"] = {{#isContainer}}this.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}this.toString(){{/isContainer}} }
|
||||
{{/headerParams}}
|
||||
|
||||
val localVariableConfig = RequestConfig(
|
||||
|
84
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/README.mustache
vendored
Normal file
84
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/README.mustache
vendored
Normal 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}}
|
65
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/api_doc.mustache
vendored
Normal file
65
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/api_doc.mustache
vendored
Normal 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}}
|
15
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/class_doc.mustache
vendored
Normal file
15
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/class_doc.mustache
vendored
Normal 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}}
|
52
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/data_class.mustache
vendored
Normal file
52
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/data_class.mustache
vendored
Normal 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}}
|
@ -0,0 +1,4 @@
|
||||
{{#description}}
|
||||
/* {{{description}}} */
|
||||
{{/description}}
|
||||
{{>modelMutable}} {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}? = {{#defaultvalue}}{{defaultvalue}}{{/defaultvalue}}{{^defaultvalue}}null{{/defaultvalue}}
|
@ -0,0 +1,4 @@
|
||||
{{#description}}
|
||||
/* {{{description}}} */
|
||||
{{/description}}
|
||||
{{>modelMutable}} {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}
|
9
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/enum_class.mustache
vendored
Normal file
9
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/enum_class.mustache
vendored
Normal 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}}
|
||||
}
|
7
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/enum_doc.mustache
vendored
Normal file
7
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/enum_doc.mustache
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
# {{classname}}
|
||||
|
||||
## Enum
|
||||
|
||||
{{#allowableValues}}{{#enumVars}}
|
||||
* `{{name}}` (value: `{{{value}}}`)
|
||||
{{/enumVars}}{{/allowableValues}}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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)
|
@ -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"]
|
@ -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}}
|
101
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/libraries/ktor/README.mustache
vendored
Normal file
101
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/libraries/ktor/README.mustache
vendored
Normal 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}}
|
@ -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}}
|
@ -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}}
|
@ -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)
|
||||
}
|
@ -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}}
|
@ -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}}
|
||||
}
|
||||
}
|
@ -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'
|
||||
}
|
@ -0,0 +1 @@
|
||||
org.gradle.caching=true
|
@ -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.
|
||||
*/
|
@ -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>
|
11
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/licenseInfo.mustache
vendored
Normal file
11
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/licenseInfo.mustache
vendored
Normal 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.
|
||||
*/
|
11
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/model.mustache
vendored
Normal file
11
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/model.mustache
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
{{>licenseInfo}}
|
||||
package {{modelPackage}}
|
||||
|
||||
{{#imports}}import {{import}}
|
||||
{{/imports}}
|
||||
|
||||
{{#models}}
|
||||
{{#model}}
|
||||
{{#isEnum}}{{>enum_class}}{{/isEnum}}{{^isEnum}}{{>data_class}}{{/isEnum}}
|
||||
{{/model}}
|
||||
{{/models}}
|
1
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/modelMutable.mustache
vendored
Normal file
1
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/modelMutable.mustache
vendored
Normal file
@ -0,0 +1 @@
|
||||
{{#modelMutable}}var{{/modelMutable}}{{^modelMutable}}val{{/modelMutable}}
|
3
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/model_doc.mustache
vendored
Normal file
3
modules/openapi-generator/src/main/resources/kotlin-server-deprecated/model_doc.mustache
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{{#models}}{{#model}}
|
||||
{{#isEnum}}{{>enum_doc}}{{/isEnum}}{{^isEnum}}{{>class_doc}}{{/isEnum}}
|
||||
{{/model}}{{/models}}
|
@ -0,0 +1 @@
|
||||
rootProject.name = '{{artifactId}}'
|
@ -2,8 +2,8 @@
|
||||
|
||||
## Requires
|
||||
|
||||
* Kotlin 1.1.2
|
||||
* Gradle 3.3
|
||||
* Kotlin 1.4.31
|
||||
* Gradle 6.8.2
|
||||
|
||||
## Build
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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}}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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}}
|
||||
|
@ -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}}
|
||||
|
@ -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}}
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -22,3 +22,4 @@ typing >= 3.5.2.2
|
||||
pyyaml < 5.3; python_version<="2.7"
|
||||
{{/supportPython2}}
|
||||
setuptools >= 21.0.0
|
||||
Flask == 1.1.2
|
||||
|
@ -2,7 +2,10 @@
|
||||
|
||||
{{>partial_header}}
|
||||
|
||||
import inspect
|
||||
try:
|
||||
from inspect import getfullargspec
|
||||
except ImportError:
|
||||
from inspect import getargspec as getfullargspec
|
||||
import pprint
|
||||
import re # noqa: F401
|
||||
import six
|
||||
@ -203,7 +206,7 @@ class {{classname}}(object):
|
||||
|
||||
def convert(x):
|
||||
if hasattr(x, "to_dict"):
|
||||
args = inspect.getargspec(x.to_dict).args
|
||||
args = getfullargspec(x.to_dict).args
|
||||
if len(args) == 1:
|
||||
return x.to_dict()
|
||||
else:
|
||||
|
@ -17,6 +17,9 @@ from {{packageName}}.model_utils import ( # noqa: F401
|
||||
none_type,
|
||||
validate_get_composed_info,
|
||||
)
|
||||
from ..model_utils import OpenApiModel
|
||||
from {{packageName}}.exceptions import ApiAttributeError
|
||||
|
||||
{{#models}}
|
||||
{{#model}}
|
||||
{{#imports}}
|
||||
|
@ -0,0 +1,9 @@
|
||||
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__,),
|
||||
)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user