Merge remote-tracking branch 'origin/4.2.x' into 5.0.x

This commit is contained in:
William Cheng 2019-08-12 00:22:19 +08:00
commit 1c31dce4bd
2707 changed files with 105802 additions and 13266 deletions

4
.gitignore vendored
View File

@ -232,3 +232,7 @@ samples/client/petstore/elm/index.html
# C
samples/client/petstore/c/build
samples/client/petstore/c/*.so
# Ruby
samples/openapi3/client/petstore/ruby/Gemfile.lock
samples/openapi3/client/petstore/ruby-faraday/Gemfile.lock

View File

@ -94,6 +94,12 @@ before_install:
# - Rely on `kerl` for [pre-compiled versions available](https://docs.travis-ci.com/user/languages/erlang#Choosing-OTP-releases-to-test-against). Rely on installation path chosen by [`travis-erlang-builder`](https://github.com/travis-ci/travis-erlang-builder/blob/e6d016b1a91ca7ecac5a5a46395bde917ea13d36/bin/compile#L18).
# - . ~/otp/18.2.1/activate && erl -version
#- curl -f -L -o ./rebar3 https://s3.amazonaws.com/rebar3/rebar3 && chmod +x ./rebar3 && ./rebar3 version && export PATH="${TRAVIS_BUILD_DIR}:$PATH"
# install Qt 5.10
- sudo add-apt-repository --yes ppa:beineri/opt-qt-5.10.1-trusty
- sudo apt-get update -qq
- sudo apt-get install qt510-meta-minimal
- source /opt/qt510/bin/qt510-env.sh
- qmake -v
# show host table to confirm petstore.swagger.io is mapped to localhost
- cat /etc/hosts

View File

@ -2,8 +2,19 @@ kind: pipeline
name: default
steps:
- name: test
# test Java 11 HTTP client
- name: java11-test
image: hirokimatsumoto/alpine-openjdk-11
commands:
- ./mvnw clean install
- ./mvnw --quiet verify -Psamples.droneio
# test ocaml petstore client
- name: ocaml-test
image: ocaml/opam2:4.07
commands:
- sudo apt-get -y install m4
- cd samples/client/petstore/ocaml
- opam install ppx_deriving_yojson cohttp ppx_deriving cohttp-lwt-unix
- eval $(opam env)
- sudo chmod -R 777 .
- dune build --build-dir=./_build

View File

@ -10,6 +10,7 @@ import java.util.TimeZone;
import org.junit.*;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
public class ApiClientTest {
@ -329,24 +330,18 @@ public class ApiClientTest {
assertEquals("sun.gif", apiClient.sanitizeFilename(".\\sun.gif"));
}
@Test
public void testInterceptorCleanupWithNewClient() {
public void testNewHttpClient() {
OkHttpClient oldClient = apiClient.getHttpClient();
assertEquals(1, oldClient.networkInterceptors().size());
OkHttpClient newClient = new OkHttpClient();
apiClient.setHttpClient(newClient);
assertEquals(1, apiClient.getHttpClient().networkInterceptors().size());
apiClient.setHttpClient(newClient);
assertEquals(1, apiClient.getHttpClient().networkInterceptors().size());
apiClient.setHttpClient(oldClient.newBuilder().build());
assertThat(apiClient.getHttpClient(), is(not(oldClient)));
}
@Test
public void testInterceptorCleanupWithSameClient() {
OkHttpClient oldClient = apiClient.getHttpClient();
assertEquals(1, oldClient.networkInterceptors().size());
apiClient.setHttpClient(oldClient);
assertEquals(1, apiClient.getHttpClient().networkInterceptors().size());
/**
* Tests the invariant that the HttpClient for the ApiClient must never be null
*/
@Test(expected = NullPointerException.class)
public void testNullHttpClient() {
apiClient.setHttpClient(null);
}
}

View File

@ -17,7 +17,7 @@
<div align="center">
[![Join the chat at https://gitter.im/OpenAPITools/openapi-generator](https://badges.gitter.im/OpenAPITools/openapi-generator.svg)](https://gitter.im/OpenAPITools/openapi-generator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Jion the Slack chat room](https://img.shields.io/badge/Slack-Join%20the%20chat%20room-orange)](https://join.slack.com/t/openapi-generator/shared_invite/enQtNzAyNDMyOTU0OTE1LTY5ZDBiNDI5NzI5ZjQ1Y2E5OWVjMjZkYzY1ZGM2MWQ4YWFjMzcyNDY5MGI4NjQxNDBiMTlmZTc5NjY2ZTQ5MGM)
[![Stable releaases in the Maven store](https://img.shields.io/maven-metadata/v/http/central.maven.org/maven2/org/openapitools/openapi-generator/maven-metadata.xml.svg)](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.openapitools%22%20AND%20a%3A%22openapi-generator%22)
[![Follow OpenAPI Generator Twitter account to get the latest update](https://img.shields.io/twitter/follow/oas_generator.svg?style=social&label=Follow)](https://twitter.com/oas_generator)
@ -59,7 +59,7 @@ OpenAPI Generator allows generation of API client libraries (SDK generation), se
| | Languages/Frameworks |
|-|-|
**API clients** | **ActionScript**, **Ada**, **Apex**, **Bash**, **C**, **C#** (.net 2.0, 3.5 or later), **C++** (cpp-restsdk, Qt5, Tizen), **Clojure**, **Dart (1.x, 2.x)**, **Elixir**, **Elm**, **Eiffel**, **Erlang**, **Go**, **Groovy**, **Haskell** (http-client, Servant), **Java** (Jersey1.x, Jersey2.x, OkHttp, Retrofit1.x, Retrofit2.x, Feign, RestTemplate, RESTEasy, Vertx, Google API Client Library for Java, Rest-assured, Spring 5 Web Client), **Kotlin**, **Lua**, **Node.js/JavaScript** (ES5, ES6, AngularJS with Google Closure Compiler annotations, Flow types) **Objective-C**, **Perl**, **PHP**, **PowerShell**, **Python**, **R**, **Ruby**, **Rust** (rust, rust-server), **Scala** (akka, http4s, scalaz, swagger-async-httpclient), **Swift** (2.x, 3.x, 4.x), **Typescript** (AngularJS, Angular (2.x - 7.x), Aurelia, Axios, Fetch, Inversify, jQuery, Node, Rxjs)
**API clients** | **ActionScript**, **Ada**, **Apex**, **Bash**, **C**, **C#** (.net 2.0, 3.5 or later), **C++** (cpp-restsdk, Qt5, Tizen), **Clojure**, **Dart (1.x, 2.x)**, **Elixir**, **Elm**, **Eiffel**, **Erlang**, **Go**, **Groovy**, **Haskell** (http-client, Servant), **Java** (Jersey1.x, Jersey2.x, OkHttp, Retrofit1.x, Retrofit2.x, Feign, RestTemplate, RESTEasy, Vertx, Google API Client Library for Java, Rest-assured, Spring 5 Web Client), **Kotlin**, **Lua**, **Node.js/JavaScript** (ES5, ES6, AngularJS with Google Closure Compiler annotations, Flow types), **Objective-C**, **OCaml**, **Perl**, **PHP**, **PowerShell**, **Python**, **R**, **Ruby**, **Rust** (rust, rust-server), **Scala** (akka, http4s, scalaz, swagger-async-httpclient), **Swift** (2.x, 3.x, 4.x), **Typescript** (AngularJS, Angular (2.x - 7.x), Aurelia, Axios, Fetch, Inversify, jQuery, Node, Rxjs)
**Server stubs** | **Ada**, **C#** (ASP.NET Core, NancyFx), **C++** (Pistache, Restbed, Qt5 QHTTPEngine), **Erlang**, **F#** (Giraffe), **Go** (net/http, Gin), **Haskell** (Servant), **Java** (MSF4J, Spring, Undertow, JAX-RS: CDI, CXF, Inflector, RestEasy, Play Framework, [PKMST](https://github.com/ProKarma-Inc/pkmst-getting-started-examples)), **Kotlin** (Spring Boot, Ktor), **PHP** (Laravel, Lumen, Slim, Silex, [Symfony](https://symfony.com/), [Zend Expressive](https://github.com/zendframework/zend-expressive)), **Python** (Flask), **NodeJS**, **Ruby** (Sinatra, Rails5), **Rust** (rust-server), **Scala** ([Finch](https://github.com/finagle/finch), [Lagom](https://github.com/lagom/lagom), [Play](https://www.playframework.com/), Scalatra)
**API documentation generators** | **HTML**, **Confluence Wiki**
**Configuration files** | [**Apache2**](https://httpd.apache.org/)
@ -101,8 +101,9 @@ The OpenAPI Specification has undergone 3 revisions since initial creation in 20
OpenAPI Generator Version | Release Date | Notes
---------------------------- | ------------ | -----
5.0.0 (upcoming major release) [SNAPSHOT](https://oss.sonatype.org/content/repositories/snapshots/org/openapitools/openapi-generator-cli/5.0.0-SNAPSHOT/)| 13.05.2020 | Major release with breaking changes (no fallback)
4.1.0 (upcoming minor release) [SNAPSHOT](https://oss.sonatype.org/content/repositories/snapshots/org/openapitools/openapi-generator-cli/4.1.0-SNAPSHOT/)| 31.07.2019 | Minor release (breaking changes with fallbacks)
[4.0.3](https://github.com/OpenAPITools/openapi-generator/releases/tag/v4.0.3) (latest stable release) | 09.07.2019 | Patch release (bug fixes, minor enhancements, etc)
4.2.0 (upcoming minor release) [SNAPSHOT](https://oss.sonatype.org/content/repositories/snapshots/org/openapitools/openapi-generator-cli/4.2.0-SNAPSHOT/)| 09.10.2019 | Minor release (breaking changes with fallbacks)
4.1.1 (upcoming minor release) [SNAPSHOT](https://oss.sonatype.org/content/repositories/snapshots/org/openapitools/openapi-generator-cli/4.1.1-SNAPSHOT/)| 23.08.2019 | Patch release (bug fixes, enhancements)
[4.1.0](https://github.com/OpenAPITools/openapi-generator/releases/tag/v4.1.0) (latest stable release) | 09.08.2019 | Minor release (breaking changes with fallbacks)
OpenAPI Spec compatibility: 1.0, 1.1, 1.2, 2.0, 3.0
@ -158,16 +159,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: `http://central.maven.org/maven2/org/openapitools/openapi-generator-cli/4.0.3/openapi-generator-cli-4.0.3.jar`
JAR location: `http://central.maven.org/maven2/org/openapitools/openapi-generator-cli/4.1.0/openapi-generator-cli-4.1.0.jar`
For **Mac/Linux** users:
```sh
wget http://central.maven.org/maven2/org/openapitools/openapi-generator-cli/4.0.3/openapi-generator-cli-4.0.3.jar -O openapi-generator-cli.jar
wget http://central.maven.org/maven2/org/openapitools/openapi-generator-cli/4.1.0/openapi-generator-cli-4.1.0.jar -O openapi-generator-cli.jar
```
For **Windows** users, you will need to install [wget](http://gnuwin32.sourceforge.net/packages/wget.htm) or you can use Invoke-WebRequest in PowerShell (3.0+), e.g.
```
Invoke-WebRequest -OutFile openapi-generator-cli.jar http://central.maven.org/maven2/org/openapitools/openapi-generator-cli/4.0.3/openapi-generator-cli-4.0.3.jar
Invoke-WebRequest -OutFile openapi-generator-cli.jar http://central.maven.org/maven2/org/openapitools/openapi-generator-cli/4.1.0/openapi-generator-cli-4.1.0.jar
```
After downloading the JAR, run `java -jar openapi-generator-cli.jar help` to show the usage.
@ -382,10 +383,10 @@ openapi-generator version
```
<!-- RELEASE_VERSION -->
Or install a particular OpenAPI Generator version (e.g. v4.0.3):
Or install a particular OpenAPI Generator version (e.g. v4.1.0):
```sh
npm install @openapitools/openapi-generator-cli@cli-4.0.3 -g
npm install @openapitools/openapi-generator-cli@cli-4.1.0 -g
```
Or install it as dev-dependency:
@ -409,7 +410,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/2_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](http://central.maven.org/maven2/org/openapitools/openapi-generator-cli/4.0.3/openapi-generator-cli-4.0.3.jar)
You can also download the JAR (latest release) directly from [maven.org](http://central.maven.org/maven2/org/openapitools/openapi-generator-cli/4.1.0/openapi-generator-cli-4.1.0.jar)
<!-- /RELEASE_VERSION -->
To get a list of **general** options available, please run `java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar help generate`
@ -550,6 +551,7 @@ Here are some companies/projects (alphabetical order) using OpenAPI Generator in
- [CAM](https://www.cam-inc.co.jp/)
- [Camptocamp](https://www.camptocamp.com/en)
- [codecentric AG](https://www.codecentric.de/)
- [Commencis](https://www.commencis.com/)
- [Cupix](https://www.cupix.com/)
- [DB Systel](https://www.dbsystel.de)
- [FormAPI](https://formapi.io/)
@ -594,6 +596,7 @@ Here are some companies/projects (alphabetical order) using OpenAPI Generator in
- 2018/12/09 - [openapi-generator をカスタマイズする方法](https://qiita.com/watiko/items/0961287c02eac9211572) by [@watiko](https://qiita.com/watiko)
- 2019/01/03 - [Calling a Swagger service from Apex using openapi-generator](https://lekkimworld.com/2019/01/03/calling-a-swagger-service-from-apex-using-openapi-generator/) by [Mikkel Flindt Heisterberg](https://lekkimworld.com)
- 2019/01/13 - [OpenAPI GeneratorでRESTful APIの定義書から色々自動生成する](https://ky-yk-d.hatenablog.com/entry/2019/01/13/234108) by [@ky_yk_d](https://twitter.com/ky_yk_d)
- 2019/01/20 - [Contract-First API Development with OpenAPI Generator and Connexion](https://medium.com/commencis/contract-first-api-development-with-openapi-generator-and-connexion-b21bbf2f9244) by [Anil Can Aydin](https://github.com/anlcnydn)
- 2019/01/30 - [Rapid Application Development With API First Approach Using Open-API Generator](https://dzone.com/articles/rapid-api-development-using-open-api-generator) by [Milan Sonkar](https://dzone.com/users/828329/milan_sonkar.html)
- 2019/02/02 - [平静を保ち、コードを生成せよ 〜 OpenAPI Generator誕生の背景と軌跡 〜](https://speakerdeck.com/akihito_nakano/gunmaweb34) by [中野暁人](https://github.com/ackintosh) at [Gunma.web #34 スキーマ駆動開発](https://gunmaweb.connpass.com/event/113974/)
- 2019/02/20 - [An adventure in OpenAPI V3 code generation](https://mux.com/blog/an-adventure-in-openapi-v3-api-code-generation/) by [Phil Cluff](https://mux.com/blog/author/philc/)
@ -673,16 +676,19 @@ Here is a list of template creators:
* Java (Vertx): @lopesmcc
* Java (Google APIs Client Library): @charlescapps
* Java (Rest-assured): @viclovsky
* Java (Java 11 Native HTTP client): @bbdouglas
* Javascript/NodeJS: @jfiala
* Javascript (Closure-annotated Angular) @achew22
* Javascript (Flow types) @jaypea
* JMeter: @davidkiss
* Kotlin: @jimschubert [:heart:](https://www.patreon.com/jimschubert)
* Lua: @daurnimator
* OCaml: @cgensoul
* Perl: @wing328 [:heart:](https://www.patreon.com/wing328)
* PHP (Guzzle): @baartosz
* PowerShell: @beatcracker
* R: @ramnov
* Ruby (Faraday): @meganemura @dkliban
* Rust: @farcaller
* Rust (rust-server): @metaswitch
* Scala (scalaz & http4s): @tbrown1979
@ -725,6 +731,7 @@ Here is a list of template creators:
* JAX-RS RestEasy (JBoss EAP): @jfiala
* Kotlin: @jimschubert [:heart:](https://www.patreon.com/jimschubert)
* Kotlin (Spring Boot): @dr4ke616
* NodeJS Express: @YishTish
* PHP Laravel: @renepardon
* PHP Lumen: @abcsun
* PHP Slim: @jfastnacht
@ -802,13 +809,14 @@ If you want to join the committee, please kindly apply by sending an email to te
| Lua | @daurnimator (2017/08) |
| NodeJS/Javascript | @CodeNinjai (2017/07) @frol (2017/07) @cliffano (2017/07) |
| ObjC | |
| OCaml | @cgensoul (2019/08) |
| Perl | @wing328 (2017/07) [:heart:](https://www.patreon.com/wing328) @yue9944882 (2019/06) |
| PHP | @jebentier (2017/07), @dkarlovi (2017/07), @mandrean (2017/08), @jfastnacht (2017/09), @ackintosh (2017/09) [:heart:](https://www.patreon.com/ackintosh/overview), @ybelenko (2018/07), @renepardon (2018/12) |
| PowerShell | |
| Python | @taxpon (2017/07) @frol (2017/07) @mbohlool (2017/07) @cbornet (2017/09) @kenjones-cisco (2017/11) @tomplus (2018/10) @Jyhess (2019/01) |
| R | @Ramanth (2019/07) @saigiridhar21 (2019/07) |
| Ruby | @cliffano (2017/07) @zlx (2017/09) @autopp (2019/02) |
| Rust | @frol (2017/07) @farcaller (2017/08) @bjgill (2017/12) |
| Rust | @frol (2017/07) @farcaller (2017/08) @bjgill (2017/12) @richardwhiuk (2019/07) |
| Scala | @clasnake (2017/07), @jimschubert (2017/09) [:heart:](https://www.patreon.com/jimschubert), @shijinkui (2018/01), @ramzimaalej (2018/03) |
| Swift | @jgavris (2017/07) @ehyche (2017/08) @Edubits (2017/09) @jaz-ah (2017/09) @d-date (2018/03) |
| TypeScript | @TiFu (2017/07) @taxpon (2017/07) @sebastianhaas (2017/07) @kenisteward (2017/07) @Vrolijkx (2017/09) @macjohnny (2018/01) @nicokoenig (2018/09) @topce (2018/10) @akehir (2019/07) |

View File

@ -0,0 +1,32 @@
#!/bin/sh
SCRIPT="$0"
echo "# START SCRIPT: $SCRIPT"
while [ -h "$SCRIPT" ] ; do
ls=`ls -ld "$SCRIPT"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
SCRIPT="$link"
else
SCRIPT=`dirname "$SCRIPT"`/"$link"
fi
done
if [ ! -d "${APP_DIR}" ]; then
APP_DIR=`dirname "$SCRIPT"`/..
APP_DIR=`cd "${APP_DIR}"; pwd`
fi
executable="./modules/openapi-generator-cli/target/openapi-generator-cli.jar"
if [ ! -f "$executable" ]
then
mvn -B clean package
fi
# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties"
ags="generate -t modules/openapi-generator/src/main/resources/nodejs-express-server -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g nodejs-express-server -o samples/server/petstore/nodejs-express-server -Dservice $@"
java $JAVA_OPTS -jar $executable $ags

34
bin/ocaml-petstore.sh Executable file
View File

@ -0,0 +1,34 @@
#!/bin/sh
SCRIPT="$0"
echo "# START SCRIPT: $SCRIPT"
while [ -h "$SCRIPT" ] ; do
ls=`ls -ld "$SCRIPT"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
SCRIPT="$link"
else
SCRIPT=`dirname "$SCRIPT"`/"$link"
fi
done
if [ ! -d "${APP_DIR}" ]; then
APP_DIR=`dirname "$SCRIPT"`/..
APP_DIR=`cd "${APP_DIR}"; pwd`
fi
executable="./modules/openapi-generator-cli/target/openapi-generator-cli.jar"
if [ ! -f "$executable" ]
then
mvn -B clean package
fi
# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties"
args="generate -t modules/openapi-generator/src/main/resources/ocaml -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g ocaml -o samples/client/petstore/ocaml --additional-properties packageName=petstore_client $@"
echo "java ${JAVA_OPTS} -jar ${executable} ${args}"
java $JAVA_OPTS -jar $executable $args

View File

@ -0,0 +1,34 @@
#!/bin/sh
SCRIPT="$0"
echo "# START SCRIPT: $SCRIPT"
while [ -h "$SCRIPT" ] ; do
ls=`ls -ld "$SCRIPT"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
SCRIPT="$link"
else
SCRIPT=`dirname "$SCRIPT"`/"$link"
fi
done
if [ ! -d "${APP_DIR}" ]; then
APP_DIR=`dirname "$SCRIPT"`/..
APP_DIR=`cd "${APP_DIR}"; pwd`
fi
executable="./modules/openapi-generator-cli/target/openapi-generator-cli.jar"
if [ ! -f "$executable" ]
then
mvn -B clean package
fi
# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DdebugOperations -DloggerPath=conf/log4j.properties"
args="generate -t modules/openapi-generator/src/main/resources/ocaml -i modules/openapi-generator/src/test/resources/3_0/petstore-with-fake-endpoints-models-for-testing.yaml -g ocaml -o samples/openapi3/client/petstore/ocaml/ --additional-properties packageName=petstore_client $@"
echo "java ${JAVA_OPTS} -jar ${executable} ${args}"
java $JAVA_OPTS -jar $executable $args

View File

@ -23,7 +23,7 @@ if [ ! -d "${APP_DIR}" ]; then
fi
# Make sure that we are regenerating the sample by removing any existing target directory
TARGET_DIR="$SCRIPT_DIR/../../samples/server/petstore/php-symfony/SymfonyBundle-php"
TARGET_DIR="$SCRIPT_DIR/../../samples/openapi3/server/petstore/php-symfony/SymfonyBundle-php"
if [ -d "$TARGET_DIR" ]; then
rm -rf $TARGET_DIR
fi

View File

@ -0,0 +1,43 @@
#!/bin/sh
SCRIPT="$0"
echo "# START SCRIPT: $SCRIPT"
while [ -h "$SCRIPT" ] ; do
ls=`ls -ld "$SCRIPT"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
SCRIPT="$link"
else
SCRIPT=`dirname "$SCRIPT"`/"$link"
fi
done
if [ ! -d "${APP_DIR}" ]; then
APP_DIR=`dirname "$SCRIPT"`/..
APP_DIR=`cd "${APP_DIR}"; pwd`
fi
executable="./modules/openapi-generator-cli/target/openapi-generator-cli.jar"
if [ ! -f "$executable" ]
then
mvn clean package
fi
# purge lib/doc folder
echo "purge ruby petstore lib, docs folder"
rm -Rf samples/openapi3/client/petstore/ruby-faraday/lib
rm -Rf samples/openapi3/client/petstore/ruby-faraday/docs
# purge test files other than integration test
# NOTE: spec/custom/*.rb and spec/petstore_helper.rb are not generated files
echo "purge ruby petstore spec"
find samples/openapi3/client/petstore/ruby-faraday/spec -type d -not -name spec -not -name custom | xargs rm -Rf
find samples/openapi3/client/petstore/ruby-faraday/spec -type f -not -name petstore_helper.rb -not -iwholename '*/spec/custom/*' | xargs rm -Rf
# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties"
ags="generate -t modules/openapi-generator/src/main/resources/ruby-client -i modules/openapi-generator/src/test/resources/3_0/petstore-with-fake-endpoints-models-for-testing.yaml -g ruby -c bin/openapi3/ruby-petstore-faraday.json -o samples/openapi3/client/petstore/ruby-faraday --additional-properties skipFormModel=true $@"
java $JAVA_OPTS -jar $executable $ags

View File

@ -0,0 +1,6 @@
{
"gemName": "petstore",
"moduleName": "Petstore",
"library": "faraday",
"gemVersion": "1.0.0"
}

View File

@ -0,0 +1,32 @@
#!/bin/sh
SCRIPT="$0"
echo "# START SCRIPT: $SCRIPT"
while [ -h "$SCRIPT" ] ; do
ls=`ls -ld "$SCRIPT"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
SCRIPT="$link"
else
SCRIPT=`dirname "$SCRIPT"`/"$link"
fi
done
if [ ! -d "${APP_DIR}" ]; then
APP_DIR=`dirname "$SCRIPT"`/..
APP_DIR=`cd "${APP_DIR}"; pwd`
fi
executable="./modules/openapi-generator-cli/target/openapi-generator-cli.jar"
if [ ! -f "$executable" ]
then
mvn -B clean package
fi
# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
ags="generate -t modules/openapi-generator/src/main/resources/ruby-client -i modules/openapi-generator/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -g ruby -c bin/ruby-petstore-faraday.json -o samples/client/petstore/ruby-faraday $@"
java $JAVA_OPTS -jar $executable $ags

View File

@ -0,0 +1,6 @@
{
"gemName": "petstore",
"moduleName": "Petstore",
"library": "faraday",
"gemVersion": "1.0.0"
}

View File

@ -1,5 +1,6 @@
{
"gemName": "petstore",
"library": "typhoeus",
"moduleName": "Petstore",
"gemVersion": "1.0.0"
}

View File

@ -2,6 +2,7 @@
# Generate clients:
./bin/spring-cloud-feign-petstore.sh
./bin/spring-cloud-feign-async-petstore.sh
./bin/spring-stubs.sh
# Generate spring-mvc servers:

View File

@ -0,0 +1,35 @@
#!/bin/sh
SCRIPT="$0"
echo "# START SCRIPT: $SCRIPT"
while [ -h "$SCRIPT" ] ; do
ls=`ls -ld "$SCRIPT"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
SCRIPT="$link"
else
SCRIPT=`dirname "$SCRIPT"`/"$link"
fi
done
if [ ! -d "${APP_DIR}" ]; then
APP_DIR=`dirname "$SCRIPT"`/..
APP_DIR=`cd "${APP_DIR}"; pwd`
fi
executable="./modules/openapi-generator-cli/target/openapi-generator-cli.jar"
if [ ! -f "$executable" ]
then
mvn -B clean package
fi
# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties"
ags="generate -t modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-cloud -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g spring -c bin/spring-cloud-feign-petstore.json -o samples/client/petstore/spring-cloud-async --additional-properties hideGenerationTimestamp=true,java8=true,async=true $@"
echo "Removing files and folders under samples/client/petstore/spring-cloud-async/src/main"
rm -rf samples/client/petstore/spring-cloud-async/src/main
find samples/client/petstore/spring-cloud-async -maxdepth 1 -type f ! -name "README.md" -exec rm {} +
java $JAVA_OPTS -jar $executable $ags

View File

@ -1,6 +1,7 @@
{
"npmName": "@openapitools/typescript-angular-petstore",
"npmVersion": "1.0.0",
"npmRepository" : "https://skimdb.npmjs.com/registry",
"snapshot" : false
"stringEnums": true,
"npmRepository": "https://skimdb.npmjs.com/registry",
"snapshot": false
}

View File

@ -3,5 +3,6 @@
./bin/typescript-axios-petstore-target-es6.sh
./bin/typescript-axios-petstore-with-npm-version.sh
./bin/typescript-axios-petstore-with-npm-version-and-separate-models-and-api.sh
./bin/typescript-axios-petstore-with-complex-headers.sh
./bin/typescript-axios-petstore-interfaces.sh
./bin/typescript-axios-petstore.sh

View File

@ -0,0 +1,32 @@
#!/bin/sh
SCRIPT="$0"
echo "# START SCRIPT: $SCRIPT"
while [ -h "$SCRIPT" ] ; do
ls=`ls -ld "$SCRIPT"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
SCRIPT="$link"
else
SCRIPT=`dirname "$SCRIPT"`/"$link"
fi
done
if [ ! -d "${APP_DIR}" ]; then
APP_DIR=`dirname "$SCRIPT"`/..
APP_DIR=`cd "${APP_DIR}"; pwd`
fi
executable="./modules/openapi-generator-cli/target/openapi-generator-cli.jar"
if [ ! -f "$executable" ]
then
mvn -B clean package
fi
# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
ags="generate -i modules/openapi-generator/src/test/resources/3_0/petstore-with-complex-headers.yaml -g typescript-axios -o samples/client/petstore/typescript-axios/builds/with-complex-headers $@"
java $JAVA_OPTS -jar $executable $ags

View File

@ -13,7 +13,7 @@ sleep 5
declare -a scripts=(
# SAMPLES
"./bin/openapi3/ruby-client-petstore.sh"
"./bin/ruby-client-petstore.sh"
"./bin/openapi3/ruby-client-faraday-petstore.sh"
"./bin/java-petstore-all.sh"
"./bin/java-jaxrs-petstore-server-all.sh"
"./bin/java-msf4j-petstore-server.sh"

10
bin/windows/ocaml-petstore.bat Executable file
View File

@ -0,0 +1,10 @@
set executable=.\modules\openapi-generator-cli\target\openapi-generator-cli.jar
If Not Exist %executable% (
mvn clean package
)
REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -DloggerPath=conf/log4j.properties
set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore.yaml -g ocaml -o samples\client\petstore\ocaml
java %JAVA_OPTS% -jar %executable% %ags%

View File

@ -0,0 +1,10 @@
set executable=.\modules\openapi-generator-cli\target\openapi-generator-cli.jar
If Not Exist %executable% (
mvn clean package
)
REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M
set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -g python-experimental -o samples\client\petstore\python-experimental -DpackageName=petstore_api
java %JAVA_OPTS% -jar %executable% %ags%

View File

@ -2,6 +2,7 @@
call bin\windows\typescript-axios-petstore.bat
call bin\windows\typescript-axios-petstore-target-es6.bat
call bin\windows\typescript-axios-petstore-with-complex-headers.bat
call bin\windows\typescript-axios-petstore-with-npm-version.bat
call bin\windows\typescript-axios-petstore-interfaces.bat
call bin\windows\typescript-axios-petstore-with-npm-version-and-separate-models-and-api.bat

View File

@ -0,0 +1,12 @@
@ECHO OFF
set executable=.\modules\openapi-generator-cli\target\openapi-generator-cli.jar
If Not Exist %executable% (
mvn clean package
)
REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M
set ags=generate -i modules\openapi-generator\src\test\resources\3_0\petstore-with-complex-headers.yaml -g typescript-axios -o samples\client\petstore\typescript-axios\builds\with-complex-headers
java %JAVA_OPTS% -jar %executable% %ags%

View File

@ -39,6 +39,7 @@ The following generators are available:
- [kotlin](generators/kotlin.md)
- [lua](generators/lua.md)
- [objc](generators/objc.md)
- [ocaml](generators/ocaml.md)
- [perl](generators/perl.md)
- [php](generators/php.md)
- [powershell](generators/powershell.md)
@ -93,6 +94,7 @@ The following generators are available:
- [jaxrs-spec](generators/jaxrs-spec.md)
- [kotlin-server](generators/kotlin-server.md)
- [kotlin-spring](generators/kotlin-spring.md)
- [nodejs-express-server](generators/nodejs-express-server.md) (beta)
- [nodejs-server-deprecated](generators/nodejs-server-deprecated.md) (deprecated)
- [php-laravel](generators/php-laravel.md)
- [php-lumen](generators/php-lumen.md)

View File

@ -0,0 +1,14 @@
---
id: generator-opts-server-nodejs-express-server
title: Config Options for nodejs-express-server
sidebar_label: nodejs-express-server
---
| Option | Description | Values | Default |
| ------ | ----------- | ------ | ------- |
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true|
|allowUnicodeIdentifiers|boolean, toggles whether unicode identifiers are allowed in names or not, default is false| |false|
|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|
|serverPort|TCP port to listen on.| |null|

View File

@ -0,0 +1,13 @@
---
id: generator-opts-client-ocaml-client
title: Config Options for ocaml-client
sidebar_label: ocaml-client
---
| Option | Description | Values | Default |
| ------ | ----------- | ------ | ------- |
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true|
|allowUnicodeIdentifiers|boolean, toggles whether unicode identifiers are allowed in names or not, default is false| |false|
|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|

13
docs/generators/ocaml.md Normal file
View File

@ -0,0 +1,13 @@
---
id: generator-opts-client-ocaml
title: Config Options for ocaml
sidebar_label: ocaml
---
| Option | Description | Values | Default |
| ------ | ----------- | ------ | ------- |
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true|
|allowUnicodeIdentifiers|boolean, toggles whether unicode identifiers are allowed in names or not, default is false| |false|
|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|

View File

@ -11,4 +11,4 @@ sidebar_label: openapi-yaml
|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true|
|allowUnicodeIdentifiers|boolean, toggles whether unicode identifiers are allowed in names or not, default is false| |false|
|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|
|outputFile|output filename| |null|
|outputFile|Output filename| |openapi/openapi.yaml|

View File

@ -22,3 +22,4 @@ sidebar_label: ruby
|gemAuthor|gem author (only one is supported).| |null|
|gemAuthorEmail|gem author email (only one is supported).| |null|
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true|
|library|HTTP library template (sub-template) to use|<dl><dt>**faraday**</dt><dd>Faraday (https://github.com/lostisland/faraday) (Beta support)</dd><dt>**typhoeus**</dt><dd>Typhoeus &gt;= 1.0.1 (https://github.com/typhoeus/typhoeus)</dd><dl>|typhoeus|

View File

@ -26,3 +26,4 @@ sidebar_label: typescript-angular
|modelSuffix|The suffix of the generated model.| |null|
|modelFileSuffix|The suffix of the file of the generated model (model&lt;suffix&gt;.ts).| |null|
|fileNaming|Naming convention for the output files: 'camelCase', 'kebab-case'.| |camelCase|
|stringEnums|Generate string enums instead of objects for enum values.| |false|

View File

@ -16,7 +16,7 @@ The transform logic needs to implement [CodegenConfig.java](https://github.com/o
> OpenAPI Generator applies user-defined templates via options:
> * CLI: `-t/--template` CLI options
> * Maven Plugin: `templateDirectory`
> * Gradle Plugin: `templateDir`
> * Gradle Plugin: `templateDir`
Built-in templates are written in Mustache and processed by [jmustache](https://github.com/samskivert/jmustache). Beginning with version 4.0.0, we support experimental Handlebars and user-defined template engines via plugins.
@ -26,7 +26,7 @@ OpenAPI Generator supports user-defined templates. This approach is often the ea
### Custom Logic
For this example, let's modify a Java client to use AOP via [jcabi/jcabi-aspects](https://github.com/jcabi/jcabi-aspects). We'll log API method execution at the `INFO` level. The jcabi-aspects project could also be used to implement method retries on failures; this would be a great exercise to further play around with templating.
For this example, let's modify a Java client to use AOP via [jcabi/jcabi-aspects](https://github.com/jcabi/jcabi-aspects). We'll log API method execution at the `INFO` level. The jcabi-aspects project could also be used to implement method retries on failures; this would be a great exercise to further play around with templating.
The Java generator supports a `library` option. This option works by defining base templates, then applying library-specific template overrides. This allows for template reuse for libraries sharing the same programming language. Templates defined as a library need only modify or extend the templates concerning the library, and generation falls back to the root templates (the "defaults") when not extended by the library. Generators which support the `library` option will only support the libraries known by the generator at compile time, and will throw a runtime error if you try to provide a custom library name.
@ -77,7 +77,7 @@ index 3b40702..a6d12e0 100644
+++ b/libraries/resteasy/build.gradle.mustache
@@ -134,6 +134,7 @@ ext {
}
dependencies {
+ compile "com.jcabi:jcabi-aspects:0.22.6"
compile "io.swagger:swagger-annotations:$swagger_annotations_version"
@ -95,7 +95,7 @@ index a4d0f9f..49b17c7 100644
+++ b/libraries/resteasy/api.mustache
@@ -1,5 +1,6 @@
package {{package}};
+import com.jcabi.aspects.Loggable;
import {{invokerPackage}}.ApiException;
import {{invokerPackage}}.ApiClient;
@ -134,7 +134,7 @@ index 04a9d55..7a93c50 100644
apply plugin: 'idea'
apply plugin: 'eclipse'
+apply plugin: 'aspectj'
group = '{{groupId}}'
version = '{{artifactVersion}}'
@@ -12,6 +13,7 @@ buildscript {
@ -144,14 +144,14 @@ index 04a9d55..7a93c50 100644
+ classpath "net.uberfoo.gradle:gradle-aspectj:2.2"
}
}
@@ -140,9 +142,18 @@ ext {
jersey_version = "1.19.4"
jodatime_version = "2.9.9"
junit_version = "4.12"
+ aspectjVersion = '1.9.0'
}
+sourceCompatibility = '1.8'
+targetCompatibility = '1.8'
+
@ -199,7 +199,7 @@ Make sure your custom template compiles:
```bash
cd ~/.openapi-generator/example
gradle assemble
# or, regenerate the wrapper
# or, regenerate the wrapper
gradle wrapper --gradle-version 4.8 --distribution-type all
./gradlew assemble
```
@ -291,7 +291,7 @@ Modifications to the new project's `build.gradle` should be made in the `plugins
id 'org.jetbrains.kotlin.jvm' version '1.3.11'
id "com.github.johnrengelman.shadow" version "5.0.0"
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
compile "org.openapitools:openapi-generator-core:4.0.0-SNAPSHOT"
@ -301,7 +301,7 @@ Modifications to the new project's `build.gradle` should be made in the `plugins
The above configuration for the `shadow` plugin is strictly optional. It is not needed, for instance, if you plan to publish your adapter and consume it via the Maven or Gradle plugins.
Next, create a new class file called `PebbleTemplateEngineAdapter` under `src/kotlin`. We'll define the template adapter's name as `pebble` and we'll also list this as the only supported file extension. We'll implement the adapter by extending `AbstractTemplatingEngineAdapter`, which includes reusable logic, such as retrieving a list of all possible template names for our provided template extensions(s).
Next, create a new class file called `PebbleTemplateEngineAdapter` under `src/kotlin`. We'll define the template adapter's name as `pebble` and we'll also list this as the only supported file extension. We'll implement the adapter by extending `AbstractTemplatingEngineAdapter`, which includes reusable logic, such as retrieving a list of all possible template names for our provided template extensions(s).
The class in its simplest form looks like this (with inline comments):
@ -374,7 +374,7 @@ import (
{% endfor %}
)
type Generated{{classname}}Servicer
type Generated{{classname}}Servicer
// etc
```
@ -402,7 +402,7 @@ type {{item.classname}} struct {
> Find object structures passed to templates later in this document's **Structures** section.
Finally, we can compile some code by explicitly defining our classpath and jar entrypoint for CLI (be sure to modify `/your/path` below)
Finally, we can compile some code by explicitly defining our classpath and jar entrypoint for CLI (be sure to modify `/your/path` below)
```bash
java $JAVA_OPTS -cp /your/path/build/libs/pebble-template-adapter-1.0-SNAPSHOT-all.jar:modules/openapi-generator-cli/target/openapi-generator-cli.jar \
@ -428,49 +428,49 @@ Examples for the following structures will be presented using the following spec
```yaml
swagger: "2.0"
info:
info:
version: "1.0.0"
title: "Swagger Petstore"
description: "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification"
termsOfService: "http://swagger.io/terms/"
contact:
contact:
name: "Swagger API Team"
license:
license:
name: "MIT"
host: "petstore.swagger.io"
basePath: "/api"
schemes:
schemes:
- "http"
consumes:
consumes:
- "application/json"
produces:
produces:
- "application/json"
paths:
/pets:
get:
paths:
/pets:
get:
description: "Returns all pets from the system that the user has access to"
produces:
produces:
- "application/json"
responses:
responses:
"200":
description: "A list of pets."
schema:
schema:
type: "array"
items:
items:
$ref: "#/definitions/Pet"
definitions:
Pet:
definitions:
Pet:
type: "object"
required:
required:
- "id"
- "name"
properties:
id:
properties:
id:
type: "integer"
format: "int64"
name:
name:
type: "string"
tag:
tag:
type: "string"
```
@ -478,9 +478,9 @@ Examples for the following structures will be presented using the following spec
### Operations
> Inspect operation structures passed to templates with system property `-DdebugOpenAPI`
>
>
> Example:
>
>
> ```bash
> openapi-generator generate -g go \
> -o out \
@ -510,9 +510,9 @@ Here, an Operation with tag `Pet` will generate two files: `SWGPetApi.h` and `SW
### Models
> Inspect models passed to templates with system property `-DdebugModels`
>
>
> Execute:
>
>
> ```bash
> openapi-generator generate -g go \
> -o out \
@ -730,14 +730,14 @@ Templates are passed redundant properties, depending on the semantics of the arr
* `readOnlyVars` lists all model properties marked with `readonly` in the spec document
* `allVars` lists all model properties. This may include the same set as `vars`, but may also include generator-defined properties
We expose the same properties in multiple sets because this allows us to conditionally iterate over properties based on some condition ("is it required" or "is it readonly"). This is driven by the use of the logic-less Mustache templates. It is possible that models passed to the templating engine may be cleaned up as we support more template engines, but such an effort will go through a deprecation phase and would be communicated at runtime through log messages.
We expose the same properties in multiple sets because this allows us to conditionally iterate over properties based on some condition ("is it required" or "is it readonly"). This is driven by the use of the logic-less Mustache templates. It is possible that models passed to the templating engine may be cleaned up as we support more template engines, but such an effort will go through a deprecation phase and would be communicated at runtime through log messages.
### supportingFiles
> Inspect supportingFiles passed to templates with system property `-DdebugSupportingFiles`
>
>
> Execute:
>
>
> ```bash
> openapi-generator generate -g go \
> -o out \
@ -755,10 +755,25 @@ Supporting files can either be processed through the templating engine or copied
> This is a very limited list of variable name explanations. Feel free to [open a pull request](https://github.com/OpenAPITools/openapi-generator/pull/new/master) to add to this documentation!
- **complexType**: stores the name of the model (e.g. Pet)
- **complexType**: stores the name of the model (e.g. Pet)
- **isContainer**: true if the parameter or property is an array or a map.
- **isPrimitiveType**: true if the parameter or property type is a primitive type (e.g. string, integer, etc) as defined in the spec.
## Mustache Lambdas
Many generators (*those extending DefaultCodegen*) come with a small set of lambda functions available under the key `lambda`:
- `lowercase` - Converts all of the characters in this fragment to lower case using the rules of the `ROOT` locale.
- `uppercase` - Converts all of the characters in this fragment to upper case using the rules of the `ROOT` locale.
- `titlecase` - Converts text in a fragment to title case. For example `once upon a time` to `Once Upon A Time`.
- `camelcase` - Converts text in a fragment to camelCase. For example `Input-text` to `inputText`.
- `indented` - Prepends 4 spaces indention from second line of a fragment on. First line will be indented by Mustache.
- `indented_8` - Prepends 8 spaces indention from second line of a fragment on. First line will be indented by Mustache.
- `indented_12` - Prepends 12 spaces indention from second line of a fragment on. First line will be indented by Mustache.
- `indented_16` -Prepends 16 spaces indention from second line of a fragment on. First line will be indented by Mustache.
Lambda is invoked by `lambda.[lambda name]` expression. For example: `{{#lambda.lowercase}}FRAGMENT TO LOWERCASE{{/lambda.lowercase}}` to lower case text between `lambda.lowercase`.
## Extensions
OpenAPI supports a concept called "Extensions". These are called "Specification Extensions" [in 3.x](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#specificationExtensions) and "Vendor Extensions" [in 2.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#vendorExtensions).
@ -861,7 +876,7 @@ paths:
#### x-mysqlSchema
MySQL schema generator creates vendor extensions based on openapi `dataType` and `dataFormat`. When user defined extensions with same key already exists codegen accepts those as is. It means it won't validate properties or correct it for you. Every model in `definitions` can contain table related and column related extensions like in example below:
MySQL schema generator creates vendor extensions based on openapi `dataType` and `dataFormat`. When user defined extensions with same key already exists codegen accepts those as is. It means it won't validate properties or correct it for you. Every model in `definitions` can contain table related and column related extensions like in example below:
```yaml
definitions:

View File

@ -45,7 +45,7 @@ compileJava.dependsOn tasks.openApiGenerate
[source,group]
----
plugins {
id "org.openapi.generator" version "4.0.3"
id "org.openapi.generator" version "4.1.0"
}
----
@ -61,7 +61,7 @@ buildscript {
// url "https://plugins.gradle.org/m2/"
}
dependencies {
classpath "org.openapitools:openapi-generator-gradle-plugin:4.0.3"
classpath "org.openapitools:openapi-generator-gradle-plugin:4.1.0"
}
}
@ -609,7 +609,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath('org.openapitools:openapi-generator-gradle-plugin:4.0.3') {
classpath('org.openapitools:openapi-generator-gradle-plugin:4.1.0') {
exclude group: 'com.google.guava'
}
}

View File

@ -17,5 +17,5 @@ gradle generateGoWithInvalidSpec
The samples can be tested against other versions of the plugin using the `openApiGeneratorVersion` property. For example:
```bash
gradle -PopenApiGeneratorVersion=4.0.3 openApiValidate
gradle -PopenApiGeneratorVersion=4.1.0 openApiValidate
```

View File

@ -1,3 +1,3 @@
# RELEASE_VERSION
openApiGeneratorVersion=4.0.3
openApiGeneratorVersion=4.1.0
# /RELEASE_VERSION

View File

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

View File

@ -13,7 +13,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<!-- RELEASE_VERSION -->
<version>4.0.3</version>
<version>4.1.1-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<executions>
<execution>
@ -88,6 +88,13 @@
<version>${jersey-version}</version>
</dependency>
<!-- @Nullable annotation -->
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
</dependency>
<!-- JSON processing: jackson -->
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>

View File

@ -19,7 +19,7 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<!-- RELEASE_VERSION -->
<version>4.0.3</version>
<version>5.0.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<dependencies>
<dependency>
@ -101,6 +101,13 @@
<version>${jersey-version}</version>
</dependency>
<!-- @Nullable annotation -->
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
</dependency>
<!-- JSON processing: jackson -->
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>

View File

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

View File

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

View File

@ -240,13 +240,10 @@ public class DefaultCodegen implements CodegenConfig {
}
if (additionalProperties.containsKey("lambda")) {
LOGGER.warn("A property named 'lambda' already exists. Mustache lambdas renamed from 'lambda' to '_lambda'. " +
"You'll likely need to use a custom template, " +
"see https://github.com/OpenAPITools/openapi-generator/blob/master/docs/templating.md. ");
additionalProperties.put("_lambda", lambdas);
} else {
additionalProperties.put("lambda", lambdas);
LOGGER.error("A property called 'lambda' already exists in additionalProperties");
throw new RuntimeException("A property called 'lambda' already exists in additionalProperties");
}
additionalProperties.put("lambda", lambdas);
}
// override with any special post-processing for all models
@ -1870,6 +1867,9 @@ public class DefaultCodegen implements CodegenConfig {
}
}
if(composed.getRequired() != null) {
required.addAll(composed.getRequired());
}
addVars(m, unaliasPropertySchema(properties), required, unaliasPropertySchema(allProperties), allRequired);
// end of code block for composed schema
@ -1975,6 +1975,10 @@ public class DefaultCodegen implements CodegenConfig {
addProperties(properties, required, component);
}
if(schema.getRequired() != null) {
required.addAll(schema.getRequired());
}
if (composedSchema.getOneOf() != null) {
throw new RuntimeException("Please report the issue: Cannot process oneOf (Composed Scheme) in addProperties: " + schema);
}
@ -4017,6 +4021,19 @@ public class DefaultCodegen implements CodegenConfig {
* @return sanitized string
*/
public String sanitizeName(String name, String removeCharRegEx) {
return sanitizeName(name, removeCharRegEx, new ArrayList<String>());
}
/**
* Sanitize name (parameter, property, method, etc)
*
* @param name string to be sanitize
* @param removeCharRegEx a regex containing all char that will be removed
* @param exceptionList a list of matches which should not be sanitized (i.e expections)
* @return sanitized string
*/
@SuppressWarnings("static-method")
public String sanitizeName(String name, String removeCharRegEx, ArrayList<String> exceptionList) {
// NOTE: performance wise, we should have written with 2 replaceAll to replace desired
// character with _ or empty character. Below aims to spell out different cases we've
// encountered so far and hopefully make it easier for others to add more special
@ -4034,27 +4051,27 @@ public class DefaultCodegen implements CodegenConfig {
}
// input[] => input
name = name.replaceAll("\\[\\]", ""); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
name = this.sanitizeValue(name, "\\[\\]", "", exceptionList);
// input[a][b] => input_a_b
name = name.replaceAll("\\[", "_");
name = name.replaceAll("\\]", "");
name = this.sanitizeValue(name, "\\[", "_", exceptionList);
name = this.sanitizeValue(name, "\\]", "", exceptionList);
// input(a)(b) => input_a_b
name = name.replaceAll("\\(", "_");
name = name.replaceAll("\\)", "");
name = this.sanitizeValue(name, "\\(", "_", exceptionList);
name = this.sanitizeValue(name, "\\)", "", exceptionList);
// input.name => input_name
name = name.replaceAll("\\.", "_");
name = this.sanitizeValue(name, "\\.", "_", exceptionList);
// input-name => input_name
name = name.replaceAll("-", "_");
name = this.sanitizeValue(name, "-", "_", exceptionList);
// a|b => a_b
name = name.replace("|", "_");
name = this.sanitizeValue(name, "\\|", "_", exceptionList);
// input name and age => input_name_and_age
name = name.replaceAll(" ", "_");
name = this.sanitizeValue(name, " ", "_", exceptionList);
// /api/films/get => _api_films_get
// \api\films\get => _api_films_get
@ -4072,6 +4089,13 @@ public class DefaultCodegen implements CodegenConfig {
return name;
}
private String sanitizeValue(String value, String replaceMatch, String replaceValue, ArrayList<String> exceptionList) {
if (exceptionList.size() == 0 || !exceptionList.contains(replaceMatch)) {
return value.replaceAll(replaceMatch, replaceValue);
}
return value;
}
/**
* Sanitize tag
*
@ -4908,6 +4932,15 @@ public class DefaultCodegen implements CodegenConfig {
codegenParameter.dataType = codegenProperty.dataType;
codegenParameter.description = codegenProperty.description;
codegenParameter.paramName = toParamName(codegenParameter.baseName);
codegenParameter.minimum = codegenProperty.minimum;
codegenParameter.maximum = codegenProperty.maximum;
codegenParameter.exclusiveMinimum = codegenProperty.exclusiveMinimum;
codegenParameter.exclusiveMaximum = codegenProperty.exclusiveMaximum;
codegenParameter.minLength = codegenProperty.minLength;
codegenParameter.maxLength = codegenProperty.maxLength;
codegenParameter.pattern = codegenProperty.pattern;
if (codegenProperty.complexType != null) {
imports.add(codegenProperty.complexType);

View File

@ -407,9 +407,19 @@ public class InlineModelResolver {
}
}
/**
* Generates a unique model name. Non-alphanumeric characters will be replaced
* with underscores
*
* @param title String title field in the schema if present
* @param key String model name
*/
private String resolveModelName(String title, String key) {
if (title == null) {
return uniqueName(key);
// for auto-generated schema name, replace non-alphanumeric characters with underscore
// to avoid bugs with schema look up with inline schema created on the fly
// e.g. io.schema.User_name => io_schema_User_name
return uniqueName(key).replaceAll("[^A-Za-z0-9]", "_");
} else {
return uniqueName(title);
}
@ -589,4 +599,4 @@ public class InlineModelResolver {
target.addExtension(extName, vendorExtensions.get(extName));
}
}
}
}

View File

@ -153,13 +153,14 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
"DateTime?",
"DateTime",
"DateTimeOffset?",
"DataTimeOffset",
"DateTimeOffset",
"Boolean",
"Double",
"Int32",
"Int64",
"Float",
"Guid?",
"Guid",
"System.IO.Stream", // not really a primitive, we include it to avoid model import
"Object")
);
@ -192,7 +193,7 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
// nullable type
nullableType = new HashSet<String>(
Arrays.asList("decimal", "bool", "int", "float", "long", "double", "DateTime", "Guid")
Arrays.asList("decimal", "bool", "int", "float", "long", "double", "DateTime", "DateTimeOffset", "Guid")
);
// value Types
valueTypes = new HashSet<String>(
@ -226,9 +227,9 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
public void useDateTimeOffset(boolean flag) {
this.useDateTimeOffsetFlag = flag;
if (flag) {
typeMapping.put("DateTime", "DateTimeOffset?");
typeMapping.put("DateTime", "DateTimeOffset");
} else {
typeMapping.put("DateTime", "DateTime?");
typeMapping.put("DateTime", "DateTime");
}
}

View File

@ -19,6 +19,7 @@ package org.openapitools.codegen.languages;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.ComposedSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import org.apache.commons.io.FilenameUtils;
@ -54,6 +55,9 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp
protected String npmName = null;
protected String npmVersion = "1.0.0";
protected String enumSuffix = "Enum";
protected String classEnumSeparator = ".";
public AbstractTypeScriptClientCodegen() {
super();
@ -94,7 +98,7 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp
"object"
));
languageGenericTypes = new HashSet<>(Arrays.asList(
languageGenericTypes = new HashSet<>(Collections.singletonList(
"Array"
));
@ -144,8 +148,7 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp
if (StringUtils.isEmpty(System.getenv("TS_POST_PROCESS_FILE"))) {
LOGGER.info("Hint: Environment variable 'TS_POST_PROCESS_FILE' (optional) not defined. E.g. to format the source code, please try 'export TS_POST_PROCESS_FILE=\"/usr/local/bin/prettier --write\"' (Linux/Mac)");
LOGGER.info("Note: To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI).");
}
else if (!this.isEnablePostProcessFile()) {
} else if (!this.isEnablePostProcessFile()) {
LOGGER.info("Warning: Environment variable 'TS_POST_PROCESS_FILE' is set but file post-processing is not enabled. To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI).");
}
@ -177,7 +180,7 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp
this.setNpmVersion(openAPI.getInfo().getVersion());
}
if (additionalProperties.containsKey(SNAPSHOT) && Boolean.valueOf(additionalProperties.get(SNAPSHOT).toString())) {
if (additionalProperties.containsKey(SNAPSHOT) && Boolean.parseBoolean(additionalProperties.get(SNAPSHOT).toString())) {
if (npmVersion.toUpperCase(Locale.ROOT).matches("^.*-SNAPSHOT$")) {
this.setNpmVersion(npmVersion + "." + SNAPSHOT_SUFFIX_FORMAT.format(new Date()));
} else {
@ -260,45 +263,45 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp
final String regex = "^.*[+*:;,.()-]+.*$";
final Pattern pattern = Pattern.compile(regex);
final Matcher matcher = pattern.matcher(str);
boolean matches = matcher.matches();
return matches;
return matcher.matches();
}
@Override
public String toModelName(String name) {
name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
public String toModelName(final String name) {
ArrayList<String> exceptions = new ArrayList<String>(Arrays.asList("\\|", " "));
String sanName = sanitizeName(name, "(?![| ])\\W", exceptions);
if (!StringUtils.isEmpty(modelNamePrefix)) {
name = modelNamePrefix + "_" + name;
sanName = modelNamePrefix + "_" + sanName;
}
if (!StringUtils.isEmpty(modelNameSuffix)) {
name = name + "_" + modelNameSuffix;
sanName = sanName + "_" + modelNameSuffix;
}
// model name cannot use reserved keyword, e.g. return
if (isReservedWord(name)) {
String modelName = camelize("model_" + name);
LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to " + modelName);
if (isReservedWord(sanName)) {
String modelName = camelize("model_" + sanName);
LOGGER.warn(sanName + " (reserved word) cannot be used as model name. Renamed to " + modelName);
return modelName;
}
// model name starts with number
if (name.matches("^\\d.*")) {
String modelName = camelize("model_" + name); // e.g. 200Response => Model200Response (after camelize)
LOGGER.warn(name + " (model name starts with number) cannot be used as model name. Renamed to " + modelName);
if (sanName.matches("^\\d.*")) {
String modelName = camelize("model_" + sanName); // e.g. 200Response => Model200Response (after camelize)
LOGGER.warn(sanName + " (model name starts with number) cannot be used as model name. Renamed to " + modelName);
return modelName;
}
if (languageSpecificPrimitives.contains(name)) {
String modelName = camelize("model_" + name);
LOGGER.warn(name + " (model name matches existing language type) cannot be used as a model name. Renamed to " + modelName);
if (languageSpecificPrimitives.contains(sanName)) {
String modelName = camelize("model_" + sanName);
LOGGER.warn(sanName + " (model name matches existing language type) cannot be used as a model name. Renamed to " + modelName);
return modelName;
}
// camelize the model name
// phone_number => PhoneNumber
return camelize(name);
return camelize(sanName);
}
@Override
@ -549,8 +552,7 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp
@Override
public String toEnumName(CodegenProperty property) {
String enumName = toModelName(property.name) + "Enum";
String enumName = toModelName(property.name) + enumSuffix;
if (enumName.matches("\\d.*")) { // starts with number
return "_" + enumName;
} else {
@ -569,14 +571,14 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp
// name enum with model name, e.g. StatusEnum => Pet.StatusEnum
for (CodegenProperty var : cm.vars) {
if (Boolean.TRUE.equals(var.isEnum)) {
var.datatypeWithEnum = var.datatypeWithEnum.replace(var.enumName, cm.classname + "." + var.enumName);
var.datatypeWithEnum = var.datatypeWithEnum.replace(var.enumName, cm.classname + classEnumSeparator + var.enumName);
}
}
if (cm.parent != null) {
for (CodegenProperty var : cm.allVars) {
if (Boolean.TRUE.equals(var.isEnum)) {
var.datatypeWithEnum = var.datatypeWithEnum
.replace(var.enumName, cm.classname + "." + var.enumName);
.replace(var.enumName, cm.classname + classEnumSeparator + var.enumName);
}
}
}
@ -696,4 +698,14 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp
}
}
}
@Override
public String toAnyOfName(List<String> names, ComposedSchema composedSchema) {
return String.join(" | ", names);
}
@Override
public String toOneOfName(List<String> names, ComposedSchema composedSchema) {
return String.join(" | ", names);
}
}

View File

@ -22,6 +22,7 @@ import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.parser.util.SchemaTypeUtil;
import org.openapitools.codegen.*;
import org.openapitools.codegen.utils.ModelUtils;
import org.openapitools.codegen.utils.URLPathUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -326,6 +327,7 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
supportingFiles.add(new SupportingFile("Solution.mustache", "", packageName + ".sln"));
supportingFiles.add(new SupportingFile("gitignore", packageFolder, ".gitignore"));
supportingFiles.add(new SupportingFile("validateModel.mustache", packageFolder + File.separator + "Attributes", "ValidateModelStateAttribute.cs"));
supportingFiles.add(new SupportingFile("typeConverter.mustache", packageFolder + File.separator + "Converters", "CustomEnumConverter.cs"));
supportingFiles.add(new SupportingFile("Project.csproj.mustache", packageFolder, packageName + ".csproj"));
if (!isLibrary) {
supportingFiles.add(new SupportingFile("Dockerfile.mustache", packageFolder, "Dockerfile"));
@ -353,7 +355,7 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
supportingFiles.add(new SupportingFile("Filters" + File.separator + "GeneratePathParamsValidationFilter.mustache",
packageFolder + File.separator + "Filters", "GeneratePathParamsValidationFilter.cs"));
}
supportingFiles.add(new SupportingFile("Authentication" + File.separator + "ApiAuthentication.mustache",packageFolder + File.separator + "Authentication", "ApiAuthentication.cs"));
}
@ -407,12 +409,12 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
@Override
public String getNullableType(Schema p, String type) {
boolean isNullableExpected = p.getNullable() == null || (p.getNullable() != null && p.getNullable());
if (isNullableExpected && languageSpecificPrimitives.contains(type + "?")) {
return type + "?";
} else if (languageSpecificPrimitives.contains(type)) {
return type;
if (languageSpecificPrimitives.contains(type)) {
if (isSupportNullable() && ModelUtils.isNullable(p) && nullableType.contains(type)) {
return type + "?";
} else {
return type;
}
} else {
return null;
}

View File

@ -879,15 +879,15 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen {
return null;
}
}
@Override
public String getNullableType(Schema p, String type) {
boolean isNullableExpected = p.getNullable() == null || (p.getNullable() != null && p.getNullable());
if (isNullableExpected && languageSpecificPrimitives.contains(type + "?")) {
return type + "?";
} else if (languageSpecificPrimitives.contains(type)) {
return type;
if (languageSpecificPrimitives.contains(type)) {
if (isSupportNullable() && ModelUtils.isNullable(p) && nullableType.contains(type)) {
return type + "?";
} else {
return type;
}
} else {
return null;
}

View File

@ -359,7 +359,10 @@ public class CppRestSdkClientCodegen extends AbstractCppCodegen {
return "new " + toModelName(ModelUtils.getSimpleRef(p.get$ref())) + "()";
} else if (ModelUtils.isStringSchema(p)) {
return "utility::conversions::to_string_t(\"\")";
} else if (ModelUtils.isFreeFormObject(p)) {
return "new Object()";
}
return "nullptr";
}

View File

@ -0,0 +1,390 @@
/*
* Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
*
* 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
*
* http://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.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.PathItem.HttpMethod;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.info.Info;
import org.openapitools.codegen.*;
import org.openapitools.codegen.meta.GeneratorMetadata;
import org.openapitools.codegen.meta.Stability;
import org.openapitools.codegen.utils.URLPathUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.net.URL;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import static org.openapitools.codegen.utils.StringUtils.*;
public class NodeJSExpressServerCodegen extends DefaultCodegen implements CodegenConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(NodeJSExpressServerCodegen.class);
public static final String EXPORTED_NAME = "exportedName";
public static final String SERVER_PORT = "serverPort";
protected String apiVersion = "1.0.0";
protected String defaultServerPort = "8080";
protected String implFolder = "services";
protected String projectName = "openapi-server";
protected String exportedName;
public NodeJSExpressServerCodegen() {
super();
generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata)
.stability(Stability.BETA)
.build();
outputFolder = "generated-code/nodejs-express-server";
embeddedTemplateDir = templateDir = "nodejs-express-server";
setReservedWordsLowerCase(
Arrays.asList(
"break", "case", "class", "catch", "const", "continue", "debugger",
"default", "delete", "do", "else", "export", "extends", "finally",
"for", "function", "if", "import", "in", "instanceof", "let", "new",
"return", "super", "switch", "this", "throw", "try", "typeof", "var",
"void", "while", "with", "yield")
);
additionalProperties.put("apiVersion", apiVersion);
additionalProperties.put("implFolder", implFolder);
// no model file
modelTemplateFiles.clear();
apiTemplateFiles.put("controller.mustache", ".js");
apiTemplateFiles.put("service.mustache", ".js");
supportingFiles.add(new SupportingFile("openapi.mustache", "api", "openapi.yaml"));
supportingFiles.add(new SupportingFile("config.mustache", "", "config.js"));
supportingFiles.add(new SupportingFile("expressServer.mustache", "", "expressServer.js"));
supportingFiles.add(new SupportingFile("index.mustache", "", "index.js"));
supportingFiles.add(new SupportingFile("logger.mustache", "", "logger.js"));
supportingFiles.add(new SupportingFile("eslintrc.mustache", "", ".eslintrc.json"));
// utils folder
supportingFiles.add(new SupportingFile("utils" + File.separator + "openapiRouter.mustache", "utils", "openapiRouter.js"));
// controllers folder
supportingFiles.add(new SupportingFile("controllers" + File.separator + "index.mustache", "controllers", "index.js"));
supportingFiles.add(new SupportingFile("controllers" + File.separator + "Controller.mustache", "controllers", "Controller.js"));
// service folder
supportingFiles.add(new SupportingFile("services" + File.separator + "index.mustache", "services", "index.js"));
supportingFiles.add(new SupportingFile("services" + File.separator + "Service.mustache", "services", "Service.js"));
// do not overwrite if the file is already present
writeOptional(outputFolder, new SupportingFile("package.mustache", "", "package.json"));
writeOptional(outputFolder, new SupportingFile("README.mustache", "", "README.md"));
cliOptions.add(new CliOption(SERVER_PORT,
"TCP port to listen on."));
}
@Override
public String apiPackage() {
return "controllers";
}
/**
* Configures the type of generator.
*
* @return the CodegenType for this generator
* @see org.openapitools.codegen.CodegenType
*/
@Override
public CodegenType getTag() {
return CodegenType.SERVER;
}
/**
* Configures a friendly name for the generator. This will be used by the generator
* to select the library with the -g flag.
*
* @return the friendly name for the generator
*/
@Override
public String getName() {
return "nodejs-express-server";
}
/**
* Returns human-friendly help for the generator. Provide the consumer with help
* tips, parameters here
*
* @return A string value for the help message
*/
@Override
public String getHelp() {
return "Generates a NodeJS Express server (alpha). IMPORTANT: this generator may subject to breaking changes without further notice).";
}
@Override
public String toApiName(String name) {
if (name.length() == 0) {
return "Default";
}
return camelize(name);
}
@Override
public String toApiFilename(String name) {
return toApiName(name) + "Controller";
}
@Override
public String apiFilename(String templateName, String tag) {
String result = super.apiFilename(templateName, tag);
if (templateName.equals("service.mustache")) {
String stringToMatch = File.separator + "controllers" + File.separator;
String replacement = File.separator + implFolder + File.separator;
result = result.replaceAll(Pattern.quote(stringToMatch), replacement);
stringToMatch = "Controller.js";
replacement = "Service.js";
result = result.replaceAll(Pattern.quote(stringToMatch), replacement);
}
return result;
}
/*
@Override
protected String implFileFolder(String output) {
return outputFolder + File.separator + output + File.separator + apiPackage().replace('.', File.separatorChar);
}
*/
/**
* Escapes a reserved word as defined in the `reservedWords` array. Handle escaping
* those terms here. This logic is only called if a variable matches the reserved words
*
* @return the escaped term
*/
@Override
public String escapeReservedWord(String name) {
if (this.reservedWordsMappings().containsKey(name)) {
return this.reservedWordsMappings().get(name);
}
return "_" + name;
}
/**
* Location to write api files. You can use the apiPackage() as defined when the class is
* instantiated
*/
@Override
public String apiFileFolder() {
return outputFolder + File.separator + apiPackage().replace('.', File.separatorChar);
}
public String getExportedName() {
return exportedName;
}
public void setExportedName(String name) {
exportedName = name;
}
@Override
public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> objs, List<Object> allModels) {
@SuppressWarnings("unchecked")
Map<String, Object> objectMap = (Map<String, Object>) objs.get("operations");
@SuppressWarnings("unchecked")
List<CodegenOperation> operations = (List<CodegenOperation>) objectMap.get("operation");
for (CodegenOperation operation : operations) {
operation.httpMethod = operation.httpMethod.toLowerCase(Locale.ROOT);
List<CodegenParameter> params = operation.allParams;
if (params != null && params.size() == 0) {
operation.allParams = null;
}
List<CodegenResponse> responses = operation.responses;
if (responses != null) {
for (CodegenResponse resp : responses) {
if ("0".equals(resp.code)) {
resp.code = "default";
}
}
}
if (operation.examples != null && !operation.examples.isEmpty()) {
// Leave application/json* items only
for (Iterator<Map<String, String>> it = operation.examples.iterator(); it.hasNext(); ) {
final Map<String, String> example = it.next();
final String contentType = example.get("contentType");
if (contentType == null || !contentType.startsWith("application/json")) {
it.remove();
}
}
}
}
return objs;
}
@SuppressWarnings("unchecked")
private static List<Map<String, Object>> getOperations(Map<String, Object> objs) {
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
Map<String, Object> apiInfo = (Map<String, Object>) objs.get("apiInfo");
List<Map<String, Object>> apis = (List<Map<String, Object>>) apiInfo.get("apis");
for (Map<String, Object> api : apis) {
result.add((Map<String, Object>) api.get("operations"));
}
return result;
}
private static List<Map<String, Object>> sortOperationsByPath(List<CodegenOperation> ops) {
Multimap<String, CodegenOperation> opsByPath = ArrayListMultimap.create();
for (CodegenOperation op : ops) {
opsByPath.put(op.path, op);
}
List<Map<String, Object>> opsByPathList = new ArrayList<Map<String, Object>>();
for (Entry<String, Collection<CodegenOperation>> entry : opsByPath.asMap().entrySet()) {
Map<String, Object> opsByPathEntry = new HashMap<String, Object>();
opsByPathList.add(opsByPathEntry);
opsByPathEntry.put("path", entry.getKey());
opsByPathEntry.put("operation", entry.getValue());
List<CodegenOperation> operationsForThisPath = Lists.newArrayList(entry.getValue());
operationsForThisPath.get(operationsForThisPath.size() - 1).hasMore = false;
if (opsByPathList.size() < opsByPath.asMap().size()) {
opsByPathEntry.put("hasMore", "true");
}
}
return opsByPathList;
}
@Override
public void processOpts() {
super.processOpts();
if (additionalProperties.containsKey(EXPORTED_NAME)) {
setExportedName((String) additionalProperties.get(EXPORTED_NAME));
}
/*
* Supporting Files. You can write single files for the generator with the
* entire object tree available. If the input file has a suffix of `.mustache
* it will be processed by the template engine. Otherwise, it will be copied
*/
// supportingFiles.add(new SupportingFile("controller.mustache",
// "controllers",
// "controller.js")
// );
}
@Override
public void preprocessOpenAPI(OpenAPI openAPI) {
URL url = URLPathUtils.getServerURL(openAPI);
String host = URLPathUtils.getProtocolAndHost(url);
String port = URLPathUtils.getPort(url, defaultServerPort) ;
String basePath = url.getPath();
if (additionalProperties.containsKey(SERVER_PORT)) {
port = additionalProperties.get(SERVER_PORT).toString();
}
this.additionalProperties.put(SERVER_PORT, port);
if (openAPI.getInfo() != null) {
Info info = openAPI.getInfo();
if (info.getTitle() != null) {
// when info.title is defined, use it for projectName
// used in package.json
projectName = info.getTitle()
.replaceAll("[^a-zA-Z0-9]", "-")
.replaceAll("^[-]*", "")
.replaceAll("[-]*$", "")
.replaceAll("[-]{2,}", "-")
.toLowerCase(Locale.ROOT);
this.additionalProperties.put("projectName", projectName);
}
}
// need vendor extensions
Paths paths = openAPI.getPaths();
if (paths != null) {
for (String pathname : paths.keySet()) {
PathItem path = paths.get(pathname);
Map<HttpMethod, Operation> operationMap = path.readOperationsMap();
if (operationMap != null) {
for (HttpMethod method : operationMap.keySet()) {
Operation operation = operationMap.get(method);
String tag = "default";
if (operation.getTags() != null && operation.getTags().size() > 0) {
tag = toApiName(operation.getTags().get(0));
}
if (operation.getOperationId() == null) {
operation.setOperationId(getOrGenerateOperationId(operation, pathname, method.toString()));
}
// add x-openapi-router-controller
if (operation.getExtensions() == null ||
operation.getExtensions().get("x-openapi-router-controller") == null) {
operation.addExtension("x-openapi-router-controller", sanitizeTag(tag) + "Controller");
}
// add x-openapi-router-service
if (operation.getExtensions() == null ||
operation.getExtensions().get("x-openapi-router-service") == null) {
operation.addExtension("x-openapi-router-service", sanitizeTag(tag) + "Service");
}
}
}
}
}
}
@Override
public Map<String, Object> postProcessSupportingFileData(Map<String, Object> objs) {
generateYAMLSpecFile(objs);
for (Map<String, Object> operations : getOperations(objs)) {
@SuppressWarnings("unchecked")
List<CodegenOperation> ops = (List<CodegenOperation>) operations.get("operation");
List<Map<String, Object>> opsByPathList = sortOperationsByPath(ops);
operations.put("operationsByPath", opsByPathList);
}
return super.postProcessSupportingFileData(objs);
}
@Override
public String removeNonNameElementToCamelCase(String name) {
return removeNonNameElementToCamelCase(name, "[-:;#]");
}
@Override
public String escapeUnsafeCharacters(String input) {
return input.replace("*/", "*_/").replace("/*", "/_*");
}
@Override
public String escapeQuotationMark(String input) {
// remove " to avoid code injection
return input.replace("\"", "");
}
}

View File

@ -0,0 +1,794 @@
/*
* Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
*
* 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
*
* http://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.base.Strings;
import io.swagger.v3.oas.models.*;
import io.swagger.v3.oas.models.headers.Header;
import io.swagger.v3.oas.models.media.*;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.responses.ApiResponse;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.*;
import org.openapitools.codegen.utils.ModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.*;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.capitalize;
import static org.openapitools.codegen.utils.StringUtils.camelize;
import static org.openapitools.codegen.utils.StringUtils.escape;
import static org.openapitools.codegen.utils.StringUtils.underscore;
public class OCamlClientCodegen extends DefaultCodegen implements CodegenConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(OCamlClientCodegen.class);
public static final String PACKAGE_NAME = "packageName";
public static final String PACKAGE_VERSION = "packageVersion";
static final String X_MODEL_MODULE = "x-modelModule";
public static final String CO_HTTP = "cohttp";
protected String packageName = "openapi";
protected String packageVersion = "1.0.0";
protected String apiDocPath = "docs/";
protected String modelDocPath = "docs/";
protected String apiFolder = "src/apis";
protected String modelFolder = "src/models";
private Map<String, List<String>> enumNames = new HashMap<>();
private Map<String, Schema> enumHash = new HashMap<>();
private Map<String, String> enumUniqNames;
public CodegenType getTag() {
return CodegenType.CLIENT;
}
public String getName() {
return "ocaml";
}
public String getHelp() {
return "Generates an OCaml client library (beta).";
}
public OCamlClientCodegen() {
super();
outputFolder = "generated-code/ocaml";
modelTemplateFiles.put("model.mustache", ".ml");
// default HIDE_GENERATION_TIMESTAMP to true
hideGenerationTimestamp = Boolean.TRUE;
embeddedTemplateDir = templateDir = "ocaml";
setReservedWordsLowerCase(
Arrays.asList(
"and", "as", "assert", "asr", "begin", "class",
"constraint", "do", "done", "downto", "else", "end",
"exception", "external", "false", "for ", "fun", "function",
"functor", "if", "in", "include", "inherit", "initializer",
"land", "lazy", "let", "lor", "lsl", "lsr",
"lxor", "match", "method", "mod", "module", "mutable",
"new", "nonrec", "object", "of", "open", "or",
"private", "rec", "sig", "struct", "then", "to",
"true", "try", "type", "val", "virtual", "when",
"while", "with",
"result"
)
);
importMapping.remove("File");
supportingFiles.add(new SupportingFile("dune.mustache", "", "dune"));
supportingFiles.add(new SupportingFile("dune-project.mustache", "", "dune-project"));
supportingFiles.add(new SupportingFile("readme.mustache", "", "README.md"));
defaultIncludes = new HashSet<>(
Arrays.asList(
"int",
"int32",
"int64",
"float",
"bool",
"char",
"string",
"list"
)
);
languageSpecificPrimitives = new HashSet<>(
Arrays.asList(
"int",
"int32",
"int64",
"float",
"bool",
"char",
"string",
"bytes",
"list",
"Yojson.Safe.t"
)
);
instantiationTypes.clear();
typeMapping.clear();
typeMapping.put("boolean", "bool");
typeMapping.put("int", "int32");
typeMapping.put("long", "int64");
typeMapping.put("short", "int");
typeMapping.put("char", "char");
typeMapping.put("float", "float");
typeMapping.put("double", "float");
typeMapping.put("integer", "int32");
typeMapping.put("number", "float");
typeMapping.put("date", "string");
typeMapping.put("object", "Yojson.Safe.t");
typeMapping.put("any", "Yojson.Safe.t");
typeMapping.put("file", "string");
typeMapping.put("ByteArray", "string");
// lib
typeMapping.put("string", "string");
typeMapping.put("UUID", "string");
typeMapping.put("URI", "string");
typeMapping.put("set", "`Set");
typeMapping.put("passsword", "string");
typeMapping.put("DateTime", "string");
// supportedLibraries.put(CO_HTTP, "HTTP client: CoHttp.");
//
// CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "library template (sub-template) to use.");
// libraryOption.setEnum(supportedLibraries);
// // set hyper as the default
// libraryOption.setDefault(CO_HTTP);
// cliOptions.add(libraryOption);
// setLibrary(CO_HTTP);
}
@Override
public Map<String, Object> postProcessAllModels(Map<String, Object> superobjs) {
List<String> toRemove = new ArrayList<>();
for (Map.Entry<String, Object> modelEntry : superobjs.entrySet()) {
Map<String, Object> objs = (Map<String, Object>) modelEntry.getValue();
// process enum in models
List<Object> models = (List<Object>) objs.get("models");
for (Object _mo : models) {
Map<String, Object> mo = (Map<String, Object>) _mo;
CodegenModel cm = (CodegenModel) mo.get("model");
// for enum model
if (Boolean.TRUE.equals(cm.isEnum) && cm.allowableValues != null) {
toRemove.add(modelEntry.getKey());
} else {
enrichPropertiesWithEnumDefaultValues(cm.getAllVars());
enrichPropertiesWithEnumDefaultValues(cm.getReadOnlyVars());
enrichPropertiesWithEnumDefaultValues(cm.getReadWriteVars());
enrichPropertiesWithEnumDefaultValues(cm.getRequiredVars());
enrichPropertiesWithEnumDefaultValues(cm.getOptionalVars());
enrichPropertiesWithEnumDefaultValues(cm.getVars());
enrichPropertiesWithEnumDefaultValues(cm.getParentVars());
}
}
}
for (String keyToRemove : toRemove) {
superobjs.remove(keyToRemove);
}
return superobjs;
}
private void enrichPropertiesWithEnumDefaultValues(List<CodegenProperty> properties) {
for (CodegenProperty property : properties) {
if (property.get_enum() != null && property.get_enum().size() == 1) {
String value = property.get_enum().get(0);
property.defaultValue = ocamlizeEnumValue(value);
}
}
}
@Override
protected void updateDataTypeWithEnumForMap(CodegenProperty property) {
CodegenProperty baseItem = property.items;
while (baseItem != null && (Boolean.TRUE.equals(baseItem.isMapContainer)
|| Boolean.TRUE.equals(baseItem.isListContainer))) {
baseItem = baseItem.items;
}
if (baseItem != null) {
// set default value for variable with inner enum
if (property.defaultValue != null) {
property.defaultValue = property.defaultValue.replace(", " + property.items.baseType, ", " + toEnumName(property.items));
}
updateCodegenPropertyEnum(property);
}
}
@Override
protected void updateDataTypeWithEnumForArray(CodegenProperty property) {
CodegenProperty baseItem = property.items;
while (baseItem != null && (Boolean.TRUE.equals(baseItem.isMapContainer)
|| Boolean.TRUE.equals(baseItem.isListContainer))) {
baseItem = baseItem.items;
}
if (baseItem != null) {
// set default value for variable with inner enum
if (property.defaultValue != null) {
property.defaultValue = property.defaultValue.replace(baseItem.baseType, toEnumName(baseItem));
}
updateCodegenPropertyEnum(property);
}
}
@SuppressWarnings("unchecked")
private String hashEnum(Schema schema) {
return ((List<Object>) schema.getEnum()).stream().map(String::valueOf).collect(Collectors.joining(","));
}
private boolean isEnumSchema(Schema schema) {
return schema != null && schema.getEnum() != null && !schema.getEnum().isEmpty();
}
private void collectEnumSchemas(String parentName, String sName, Schema schema) {
if (schema instanceof ArraySchema) {
collectEnumSchemas(parentName, sName, ((ArraySchema) schema).getItems());
} else if (schema instanceof MapSchema && schema.getAdditionalProperties() instanceof Schema) {
collectEnumSchemas(parentName, sName, (Schema) schema.getAdditionalProperties());
} else if (isEnumSchema(schema)) {
String h = hashEnum(schema);
if (!enumHash.containsKey(h)) {
enumHash.put(h, schema);
enumNames.computeIfAbsent(h, k -> new ArrayList<>()).add(sName.toLowerCase(Locale.ROOT));
if (parentName != null) {
enumNames.get(h).add((parentName + "_" + sName).toLowerCase(Locale.ROOT));
}
}
}
}
private void collectEnumSchemas(String sName, Schema schema) {
collectEnumSchemas(null, sName, schema);
}
@SuppressWarnings("unchecked")
private void collectEnumSchemas(String parentName, Map<String, Schema> schemas) {
for (String sName : schemas.keySet()) {
Schema schema = schemas.get(sName);
collectEnumSchemas(parentName, sName, schema);
if (schema.getProperties() != null) {
String pName = parentName != null ? parentName + "_" + sName : sName;
collectEnumSchemas(pName, schema.getProperties());
}
if (schema.getAdditionalProperties() != null && schema.getAdditionalProperties() instanceof Schema) {
String pName = parentName != null ? parentName + "_" + sName : sName;
collectEnumSchemas(pName, (Schema) schema.getAdditionalProperties());
}
if (schema instanceof ArraySchema) {
ArraySchema s = (ArraySchema) schema;
if (s.getItems() != null) {
String pName = parentName != null ? parentName + "_" + sName : sName;
collectEnumSchemas(pName, s.getItems());
}
}
}
}
private void collectEnumSchemas(Operation operation) {
if (operation != null) {
if (operation.getParameters() != null) {
for (Parameter parameter : operation.getParameters()) {
collectEnumSchemas(parameter.getName(), parameter.getSchema());
}
}
if (operation.getRequestBody() != null && operation.getRequestBody().getContent() != null) {
Content content = operation.getRequestBody().getContent();
for (String p : content.keySet()) {
collectEnumSchemas(p, content.get(p).getSchema());
}
}
if (operation.getResponses() != null) {
for (String s : operation.getResponses().keySet()) {
ApiResponse apiResponse = operation.getResponses().get(s);
if (apiResponse.getContent() != null) {
Content content = apiResponse.getContent();
for (String p : content.keySet()) {
collectEnumSchemas(p, content.get(p).getSchema());
}
}
if (apiResponse.getHeaders() != null) {
Map<String, Header> headers = apiResponse.getHeaders();
for (String h : headers.keySet()) {
Header header = headers.get(h);
collectEnumSchemas(h, header.getSchema());
}
}
}
}
}
}
private String sanitizeOCamlTypeName(String name) {
String typeName = name.replace("-", "_").replace(" ", "_").trim();
int i = 0;
char c;
while (i < typeName.length() && (Character.isDigit(c = typeName.charAt(i)) || c == '_')) {
i++;
}
return typeName.substring(i);
}
private void computeEnumUniqNames() {
Map<String, String> definitiveNames = new HashMap<>();
for (String h : enumNames.keySet()) {
boolean hasDefName = false;
List<String> nameCandidates = enumNames.get(h);
for (String name : nameCandidates) {
String candidate = sanitizeOCamlTypeName(name);
if (!definitiveNames.containsKey(candidate) && !reservedWords.contains(candidate)) {
definitiveNames.put(candidate, h);
hasDefName = true;
break;
}
}
if (!hasDefName) {
int i = 0;
String candidate;
while (definitiveNames.containsKey(candidate = sanitizeOCamlTypeName(nameCandidates.get(0) + "_" + i))) {
i++;
}
definitiveNames.put(candidate, h);
}
}
enumUniqNames = definitiveNames.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
}
private void collectEnumSchemas(OpenAPI openAPI) {
Components components = openAPI.getComponents();
if (components != null && components.getSchemas() != null && !components.getSchemas().isEmpty()) {
collectEnumSchemas(null, components.getSchemas());
}
Paths paths = openAPI.getPaths();
if (paths != null && !paths.isEmpty()) {
for (String path : paths.keySet()) {
PathItem item = paths.get(path);
collectEnumSchemas(item.getGet());
collectEnumSchemas(item.getPost());
collectEnumSchemas(item.getPut());
collectEnumSchemas(item.getDelete());
collectEnumSchemas(item.getPatch());
collectEnumSchemas(item.getOptions());
collectEnumSchemas(item.getHead());
collectEnumSchemas(item.getTrace());
}
}
computeEnumUniqNames();
}
@Override
public void preprocessOpenAPI(OpenAPI openAPI) {
collectEnumSchemas(openAPI);
supportingFiles.add(new SupportingFile("lib.mustache", "", packageName + ".opam"));
supportingFiles.add(new SupportingFile("support.mustache", "src/support", "request.ml"));
supportingFiles.add(new SupportingFile("json.mustache", "src/support", "jsonSupport.ml"));
supportingFiles.add(new SupportingFile("enums.mustache", "src/support", "enums.ml"));
}
@Override
public void processOpts() {
super.processOpts();
if (StringUtils.isEmpty(System.getenv("OCAML_POST_PROCESS_FILE"))) {
LOGGER.info("Hint: Environment variable 'OCAML_POST_PROCESS_FILE' (optional) not defined. E.g. to format the source code, please try 'export OCAML_POST_PROCESS_FILE=\"ocamlformat -i --enable-outside-detected-project\"' (Linux/Mac)");
LOGGER.info("Note: To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI).");
} else if (!this.isEnablePostProcessFile()) {
LOGGER.info("Warning: Environment variable 'OCAML_POST_PROCESS_FILE' is set but file post-processing is not enabled. To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI).");
}
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) {
setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME));
} else {
setPackageName("openapi");
}
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_VERSION)) {
setPackageVersion((String) additionalProperties.get(CodegenConstants.PACKAGE_VERSION));
} else {
setPackageVersion("1.0.0");
}
additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName);
additionalProperties.put(CodegenConstants.PACKAGE_VERSION, packageVersion);
additionalProperties.put("apiDocPath", apiDocPath);
additionalProperties.put("modelDocPath", modelDocPath);
apiTemplateFiles.put("api-impl.mustache", ".ml");
apiTemplateFiles.put("api-intf.mustache", ".mli");
modelPackage = packageName;
apiPackage = packageName;
}
@Override
public String escapeReservedWord(String name) {
if (this.reservedWordsMappings().containsKey(name)) {
return this.reservedWordsMappings().get(name);
}
return '_' + name;
}
@Override
public String apiFileFolder() {
return (outputFolder + File.separator + apiFolder).replace("/", File.separator);
}
@Override
public String modelFileFolder() {
return (outputFolder + File.separator + modelFolder).replace("/", File.separator);
}
@Override
public String toVarName(String name) {
// replace - with _ e.g. created-at => created_at
name = sanitizeName(name.replaceAll("-", "_"));
// snake_case, e.g. PetId => pet_id
name = underscore(name);
// for reserved word or word starting with number, append _
if (isReservedWord(name))
name = escapeReservedWord(name);
// for reserved word or word starting with number, append _
if (name.matches("^\\d.*"))
name = "var_" + name;
return name;
}
@Override
public String toParamName(String name) {
return toVarName(name);
}
@Override
public String toModelName(String name) {
// camelize the model name
// phone_number => PhoneNumber
return capitalize(toModelFilename(name)) + ".t";
}
@Override
public String toModelFilename(String name) {
if (!Strings.isNullOrEmpty(modelNamePrefix)) {
name = modelNamePrefix + "_" + name;
}
if (!Strings.isNullOrEmpty(modelNameSuffix)) {
name = name + "_" + modelNameSuffix;
}
name = sanitizeName(name);
// model name cannot use reserved keyword, e.g. return
if (isReservedWord(name)) {
LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to " + ("model_" + name));
name = "model_" + name; // e.g. return => ModelReturn (after camelize)
}
// model name starts with number or _
if (name.matches("^\\d.*|^_.*")) {
LOGGER.warn(name + " (model name starts with number) cannot be used as model name. Renamed to " + ("model_" + name));
name = "model_" + name; // e.g. 200Response => Model200Response (after camelize)
}
return underscore(name);
}
@Override
public String toApiFilename(final String name) {
// replace - with _ e.g. created-at => created_at
final String _name = name.replaceAll("-", "_");
// e.g. PetApi.ml => pet_api.ml
return underscore(_name) + "_api";
}
@Override
public String apiDocFileFolder() {
return (outputFolder + "/" + apiDocPath).replace('/', File.separatorChar);
}
@Override
public String modelDocFileFolder() {
return (outputFolder + "/" + modelDocPath).replace('/', File.separatorChar);
}
@Override
public String toModelDocFilename(String name) {
return toModelName(name);
}
@Override
public String toApiDocFilename(String name) {
return toApiName(name);
}
@Override
public String getTypeDeclaration(Schema p) {
if (ModelUtils.isArraySchema(p)) {
ArraySchema ap = (ArraySchema) p;
Schema inner = ap.getItems();
if (inner == null) {
LOGGER.warn(ap.getName() + "(array property) does not have a proper inner type defined.Default to string");
inner = new StringSchema().description("TODO default missing array inner type to string");
}
return getTypeDeclaration(inner) + " list";
} else if (ModelUtils.isMapSchema(p)) {
Schema inner = ModelUtils.getAdditionalProperties(p);
if (inner == null) {
LOGGER.warn(p.getName() + "(map property) does not have a proper inner type defined. Default to string");
inner = new StringSchema().description("TODO default missing map inner type to string");
}
String prefix = inner.getEnum() != null ? "Enums." : "";
return "(string * " + prefix + getTypeDeclaration(inner) + ") list";
} else if (p.getEnum() != null) {
String h = hashEnum(p);
return enumUniqNames.get(h);
}
Schema referencedSchema = ModelUtils.getReferencedSchema(openAPI, p);
if (referencedSchema != null && referencedSchema.getEnum() != null) {
String h = hashEnum(referencedSchema);
return "Enums." + enumUniqNames.get(h);
}
// Not using the supertype invocation, because we want to UpperCamelize
// the type.
String schemaType = getSchemaType(p);
if (typeMapping.containsKey(schemaType)) {
return typeMapping.get(schemaType);
}
if (typeMapping.containsValue(schemaType)) {
return schemaType;
}
if (languageSpecificPrimitives.contains(schemaType)) {
return schemaType;
}
return toModelName(schemaType);
}
@Override
public String getSchemaType(Schema p) {
String schemaType = super.getSchemaType(p);
if (typeMapping.containsKey(schemaType)) {
String type = typeMapping.get(schemaType);
if (languageSpecificPrimitives.contains(type)) {
return type;
}
}
return capitalize(toModelFilename(schemaType));
}
@Override
public String toOperationId(String operationId) {
String sanitizedOperationId = sanitizeName(operationId);
// method name cannot use reserved keyword, e.g. return
if (isReservedWord(sanitizedOperationId) || sanitizedOperationId.matches("^[0-9].*")) {
LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + underscore("call_" + operationId));
sanitizedOperationId = "call_" + sanitizedOperationId;
}
return underscore(sanitizedOperationId);
}
private Map<String, Object> allowableValues(String valueString) {
Map<String, Object> result = new HashMap<>();
result.put("values", buildEnumValues(valueString));
return result;
}
private List<Map<String, Object>> buildEnumValues(String valueString) {
List<Map<String, Object>> result = new ArrayList<>();
for (String v : valueString.split(",")) {
Map<String, Object> m = new HashMap<>();
String value = v.isEmpty() ? "empty" : v;
m.put("name", value);
m.put("camlEnumValueName", ocamlizeEnumValue(value));
result.add(m);
}
return result;
}
public String toEnumValueName(String name) {
if (reservedWords.contains(name)) {
return escapeReservedWord(name);
} else if (((CharSequence) name).chars().anyMatch(character -> specialCharReplacements.keySet().contains("" + ((char) character)))) {
return escape(name, specialCharReplacements, Collections.singletonList("_"), null);
} else {
return name;
}
}
private String ocamlizeEnumValue(String value) {
String sanitizedValue =
toEnumValueName(value.isEmpty() ? "empty" : value)
.replace(" ", "_");
if (!sanitizedValue.matches("^[a-zA-Z_].*")) {
sanitizedValue = "_" + sanitizedValue;
}
return "`" + capitalize(sanitizedValue);
}
private CodegenModel buildEnumModel(String enumName, String values) {
CodegenModel m = new CodegenModel();
m.setAllowableValues(allowableValues(values));
m.setName(enumName);
m.setClassname(enumName);
m.setDataType(enumName);
String[] vals = values.split(",");
if (vals.length == 1) {
m.setDefaultValue(ocamlizeEnumValue(vals[0]));
}
m.isEnum = true;
return m;
}
private Map<String, Object> buildEnumModelWrapper(String enumName, String values) {
Map<String, Object> m = new HashMap<>();
m.put("importPath", packageName + "." + enumName);
m.put("model", buildEnumModel(enumName, values));
return m;
}
@Override
public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> objs, List<Object> allModels) {
@SuppressWarnings("unchecked")
Map<String, Object> objectMap = (Map<String, Object>) objs.get("operations");
@SuppressWarnings("unchecked")
List<CodegenOperation> operations = (List<CodegenOperation>) objectMap.get("operation");
for (CodegenOperation operation : operations) {
// http method verb conversion, depending on client library (e.g. Hyper: PUT => Put, Reqwest: PUT => put)
//if (CO_HTTP.equals(getLibrary())) {
for (CodegenParameter param : operation.bodyParams) {
if (param.isModel && param.dataType.endsWith(".t")) {
param.vendorExtensions.put(X_MODEL_MODULE, param.dataType.substring(0, param.dataType.lastIndexOf('.')));
}
}
if ("Yojson.Safe.t".equals(operation.returnBaseType)) {
operation.vendorExtensions.put("x-returnFreeFormObject", true);
}
}
for (Map.Entry<String, String> e : enumUniqNames.entrySet()) {
allModels.add(buildEnumModelWrapper(e.getValue(), e.getKey()));
}
enumUniqNames.clear();
return objs;
}
@Override
protected boolean needToImport(String type) {
return !defaultIncludes.contains(type)
&& !languageSpecificPrimitives.contains(type);
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public void setPackageVersion(String packageVersion) {
this.packageVersion = packageVersion;
}
@Override
public String escapeQuotationMark(String input) {
// remove " to avoid code injection
return input.replace("\"", "");
}
@Override
public String escapeUnsafeCharacters(String input) {
return input
.replace("*)", "*_)")
.replace("(*", "(_*")
.replace("\"", "''");
}
@Override
public String toEnumName(CodegenProperty property) {
String hash = String.join(",", property.get_enum());
if (enumUniqNames.containsKey(hash)) {
return enumUniqNames.get(hash);
}
throw new IllegalArgumentException("Unreferenced enum " + hash);
}
@Override
public String toDefaultValue(Schema p) {
if (p.getDefault() != null) {
if (p.getEnum() != null) {
return ocamlizeEnumValue(p.getDefault().toString());
}
return p.getDefault().toString();
} else {
return null;
}
}
@Override
public void postProcessFile(File file, String fileType) {
super.postProcessFile(file, fileType);
if (file == null) {
return;
}
String ocamlPostProcessFile = System.getenv("OCAML_POST_PROCESS_FILE");
if (StringUtils.isEmpty(ocamlPostProcessFile)) {
return; // skip if OCAML_POST_PROCESS_FILE env variable is not defined
}
// only process files with ml or mli extension
if ("ml".equals(FilenameUtils.getExtension(file.toString())) || "mli".equals(FilenameUtils.getExtension(file.toString()))) {
String command = ocamlPostProcessFile + " " + file.toString();
try {
Process p = Runtime.getRuntime().exec(command);
int exitValue = p.waitFor();
if (exitValue != 0) {
LOGGER.error("Error running the command ({}). Exit value: {}", command, exitValue);
} else {
LOGGER.info("Successfully executed: " + command);
}
} catch (Exception e) {
LOGGER.error("Error running the command ({}). Exception: {}", command, e.getMessage());
}
}
}
}

View File

@ -42,7 +42,7 @@ public class OpenAPIYamlGenerator extends DefaultCodegen implements CodegenConfi
super();
embeddedTemplateDir = templateDir = "openapi-yaml";
outputFolder = "generated-code/openapi-yaml";
cliOptions.add(new CliOption(OUTPUT_NAME, "output filename"));
cliOptions.add(CliOption.newString(OUTPUT_NAME, "Output filename").defaultValue(outputFile));
supportingFiles.add(new SupportingFile("README.md", "", "README.md"));
}

View File

@ -406,7 +406,7 @@ public class PhpSymfonyServerCodegen extends AbstractPhpCodegen implements Codeg
// Create a variable to display the correct return type in comments for interfaces
if (op.returnType != null) {
op.vendorExtensions.put("x-commentType", op.returnType);
if (!op.returnTypeIsPrimitive) {
if (op.returnContainer != null && op.returnContainer.equals("array")) {
op.vendorExtensions.put("x-commentType", op.returnType + "[]");
}
} else {

View File

@ -16,14 +16,29 @@
package org.openapitools.codegen.languages;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.models.media.Schema;
import org.openapitools.codegen.*;
import org.openapitools.codegen.utils.ModelUtils;
import java.util.*;
import java.util.regex.Pattern;
public class PythonClientExperimentalCodegen extends PythonClientCodegen {
private static final Logger LOGGER = LoggerFactory.getLogger(PythonClientExperimentalCodegen.class);
public PythonClientExperimentalCodegen() {
super();
supportingFiles.add(new SupportingFile("python-experimental/api_client.mustache", packagePath(), "api_client.py"));
apiDocTemplateFiles.put("python-experimental/api_doc.mustache", ".md");
apiTemplateFiles.put("python-experimental/api.mustache", ".py");
modelDocTemplateFiles.put("python-experimental/model_doc.mustache", ".md");
modelTemplateFiles.put("python-experimental/model.mustache", ".py");
}
/**
@ -36,4 +51,108 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
public String getName() {
return "python-experimental";
}
public String dateToString(Schema p, Date date, DateFormat dateFormatter, DateFormat dateTimeFormatter) {
// converts a date into a date or date-time python string
if (!(ModelUtils.isDateSchema(p) || ModelUtils.isDateTimeSchema(p))) {
throw new RuntimeException("passed schema must be of type Date or DateTime");
}
if (ModelUtils.isDateSchema(p)) {
return "dateutil_parser('" + dateFormatter.format(date) + "').date()";
}
return "dateutil_parser('" + dateTimeFormatter.format(date) + "')";
}
/**
* Return the default value of the property
* @param p OpenAPI property object
* @return string presentation of the default value of the property
*/
@Override
public String toDefaultValue(Schema p) {
// if a variable has no default set and only has one allowed value
// using enum of length == 1 we use that value. Server/client usage:
// python servers: should only use default values for optional params
// python clients: should only use default values for required params
Object defaultObject = null;
Boolean enumLengthOne = (p.getEnum() != null && p.getEnum().size() == 1);
if (p.getDefault() != null) {
defaultObject = p.getDefault();
} else if (enumLengthOne) {
defaultObject = p.getEnum().get(0);
}
// convert datetime and date enums if they exist
DateFormat iso8601Date = new SimpleDateFormat("yyyy-MM-dd", Locale.ROOT);
DateFormat iso8601DateTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ROOT);
TimeZone utc = TimeZone.getTimeZone("UTC");
iso8601Date.setTimeZone(utc);
iso8601DateTime.setTimeZone(utc);
if (ModelUtils.isDateSchema(p) || ModelUtils.isDateTimeSchema(p)) {
List<Object> currentEnum = p.getEnum();
List<String> fixedEnum = new ArrayList<String>();
String fixedValue = null;
Date date = null;
if (currentEnum != null && !currentEnum.isEmpty()) {
for (Object enumItem : currentEnum) {
date = (Date) enumItem;
fixedValue = dateToString(p, date, iso8601Date, iso8601DateTime);
fixedEnum.add(fixedValue);
}
p.setEnum(fixedEnum);
}
// convert the example if it exists
Object currentExample = p.getExample();
if (currentExample != null) {
date = (Date) currentExample;
fixedValue = dateToString(p, date, iso8601Date, iso8601DateTime);
fixedEnum.add(fixedValue);
p.setExample(fixedValue);
}
// fix defaultObject
if (defaultObject != null) {
date = (Date) defaultObject;
fixedValue = dateToString(p, date, iso8601Date, iso8601DateTime);
p.setDefault(fixedValue);
defaultObject = fixedValue;
}
}
if (defaultObject == null) {
return null;
}
String defaultValue = null;
if (ModelUtils.isStringSchema(p)) {
defaultValue = defaultObject.toString();
if (ModelUtils.isDateSchema(p) || ModelUtils.isDateTimeSchema(p)) {
return defaultValue;
}
if (!ModelUtils.isByteArraySchema(p) && !ModelUtils.isBinarySchema(p) && !ModelUtils.isFileSchema(p) && !ModelUtils.isUUIDSchema(p) && !ModelUtils.isEmailSchema(p) && !ModelUtils.isDateTimeSchema(p) && !ModelUtils.isDateSchema(p)) {
if (Pattern.compile("\r\n|\r|\n").matcher((String) defaultValue).find()) {
defaultValue = "'''" + defaultValue + "'''";
} else {
defaultValue = "'" + defaultValue + "'";
}
}
return defaultValue;
} else if (ModelUtils.isIntegerSchema(p) || ModelUtils.isNumberSchema(p) || ModelUtils.isBooleanSchema(p)) {
defaultValue = String.valueOf(defaultObject);
if (ModelUtils.isBooleanSchema(p)) {
if (Boolean.valueOf(defaultValue) == false) {
return "False";
} else {
return "True";
}
}
return defaultValue;
} else {
return defaultObject.toString();
}
}
}

View File

@ -46,6 +46,8 @@ public class RubyClientCodegen extends AbstractRubyCodegen {
public static final String GEM_DESCRIPTION = "gemDescription";
public static final String GEM_AUTHOR = "gemAuthor";
public static final String GEM_AUTHOR_EMAIL = "gemAuthorEmail";
public static final String FARADAY = "faraday";
public static final String TYPHOEUS = "typhoeus";
protected String gemName;
protected String moduleName;
@ -54,8 +56,8 @@ public class RubyClientCodegen extends AbstractRubyCodegen {
protected String libFolder = "lib";
protected String gemLicense = "unlicense";
protected String gemRequiredRubyVersion = ">= 1.9";
protected String gemHomepage = "http://org.openapitools";
protected String gemSummary = "A ruby wrapper for the REST APIs";
protected String gemHomepage = "https://openapitools.org";
protected String gemSummary = "A Ruby SDK for the REST API";
protected String gemDescription = "This gem maps to a REST API";
protected String gemAuthor = "";
protected String gemAuthorEmail = "";
@ -141,6 +143,15 @@ public class RubyClientCodegen extends AbstractRubyCodegen {
cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, CodegenConstants.HIDE_GENERATION_TIMESTAMP_DESC).
defaultValue(Boolean.TRUE.toString()));
supportedLibraries.put(FARADAY, "Faraday (https://github.com/lostisland/faraday) (Beta support)");
supportedLibraries.put(TYPHOEUS, "Typhoeus >= 1.0.1 (https://github.com/typhoeus/typhoeus)");
CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "HTTP library template (sub-template) to use");
libraryOption.setEnum(supportedLibraries);
// set TYPHOEUS as the default
libraryOption.setDefault(TYPHOEUS);
cliOptions.add(libraryOption);
setLibrary(TYPHOEUS);
}
@Override
@ -209,10 +220,8 @@ public class RubyClientCodegen extends AbstractRubyCodegen {
setModelPackage("models");
setApiPackage("api");
supportingFiles.add(new SupportingFile("gemspec.mustache", "", gemName + ".gemspec"));
supportingFiles.add(new SupportingFile("gem.mustache", libFolder, gemName + ".rb"));
String gemFolder = libFolder + File.separator + gemName;
supportingFiles.add(new SupportingFile("api_client.mustache", gemFolder, "api_client.rb"));
supportingFiles.add(new SupportingFile("api_error.mustache", gemFolder, "api_error.rb"));
supportingFiles.add(new SupportingFile("configuration.mustache", gemFolder, "configuration.rb"));
supportingFiles.add(new SupportingFile("version.mustache", gemFolder, "version.rb"));
@ -221,9 +230,20 @@ public class RubyClientCodegen extends AbstractRubyCodegen {
supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore"));
supportingFiles.add(new SupportingFile("Rakefile.mustache", "", "Rakefile"));
supportingFiles.add(new SupportingFile("Gemfile.mustache", "", "Gemfile"));
supportingFiles.add(new SupportingFile("Gemfile.lock.mustache", "", "Gemfile.lock"));
supportingFiles.add(new SupportingFile("rubocop.mustache", "", ".rubocop.yml"));
supportingFiles.add(new SupportingFile("travis.mustache", "", ".travis.yml"));
supportingFiles.add(new SupportingFile("gemspec.mustache", "", gemName + ".gemspec"));
supportingFiles.add(new SupportingFile("configuration.mustache", gemFolder, "configuration.rb"));
supportingFiles.add(new SupportingFile("api_client.mustache", gemFolder, "api_client.rb"));
if (TYPHOEUS.equals(getLibrary())) {
// for Typhoeus
} else if (FARADAY.equals(getLibrary())) {
// for Faraday
additionalProperties.put("isFaraday", Boolean.TRUE);
} else {
throw new RuntimeException("Invalid HTTP library " + getLibrary() + ". Only faraday, typhoeus are supported.");
}
// test files should not be overwritten
writeOptional(outputFolder, new SupportingFile("rspec.mustache", "", ".rspec"));

View File

@ -709,6 +709,9 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig {
consumesPlainText = true;
} else if (isMimetypeWwwFormUrlEncoded(mediaType)) {
additionalProperties.put("usesUrlEncodedForm", true);
} else if (isMimetypeMultipartFormData(mediaType)) {
op.vendorExtensions.put("consumesMultipart", true);
additionalProperties.put("apiUsesMultipart", true);
}
}
}
@ -724,8 +727,8 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig {
} else {
op.bodyParam.vendorExtensions.put("consumesJson", true);
}
}
for (CodegenParameter param : op.bodyParams) {
processParam(param, op);
@ -789,6 +792,8 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig {
codegenParameter.isPrimitiveType = false;
codegenParameter.isListContainer = false;
codegenParameter.isString = false;
codegenParameter.isByteArray = ModelUtils.isByteArraySchema(original_schema);
// This is a model, so should only have an example if explicitly
// defined.

View File

@ -69,6 +69,9 @@ public class SpringCodegen extends AbstractJavaCodegen
public static final String RETURN_SUCCESS_CODE = "returnSuccessCode";
public static final String UNHANDLED_EXCEPTION_HANDLING = "unhandledException";
public static final String OPEN_BRACE = "{";
public static final String CLOSE_BRACE = "}";
protected String title = "OpenAPI Spring";
protected String configPackage = "org.openapitools.configuration";
protected String basePackage = "org.openapitools";
@ -103,7 +106,7 @@ public class SpringCodegen extends AbstractJavaCodegen
invokerPackage = "org.openapitools.api";
artifactId = "openapi-spring";
// clioOptions default redifinition need to be updated
// clioOptions default redefinition need to be updated
updateOption(CodegenConstants.INVOKER_PACKAGE, this.getInvokerPackage());
updateOption(CodegenConstants.ARTIFACT_ID, this.getArtifactId());
updateOption(CodegenConstants.API_PACKAGE, apiPackage);
@ -113,6 +116,8 @@ public class SpringCodegen extends AbstractJavaCodegen
// spring uses the jackson lib
additionalProperties.put("jackson", "true");
additionalProperties.put("openbrace", OPEN_BRACE);
additionalProperties.put("closebrace", CLOSE_BRACE);
cliOptions.add(new CliOption(TITLE, "server title name or client service name").defaultValue(title));
cliOptions.add(new CliOption(CONFIG_PACKAGE, "configuration package for generated code").defaultValue(this.getConfigPackage()));
@ -132,7 +137,7 @@ public class SpringCodegen extends AbstractJavaCodegen
cliOptions.add(CliOption.newBoolean(IMPLICIT_HEADERS, "Skip header parameters in the generated API methods using @ApiImplicitParams annotation.", implicitHeaders));
cliOptions.add(CliOption.newBoolean(OPENAPI_DOCKET_CONFIG, "Generate Spring OpenAPI Docket configuration class.", openapiDocketConfig));
cliOptions.add(CliOption.newBoolean(API_FIRST, "Generate the API from the OAI spec at server compile time (API first approach)", apiFirst));
cliOptions.add(CliOption.newBoolean(USE_OPTIONAL,"Use Optional container for optional parameters", useOptional));
cliOptions.add(CliOption.newBoolean(USE_OPTIONAL, "Use Optional container for optional parameters", useOptional));
cliOptions.add(CliOption.newBoolean(HATEOAS, "Use Spring HATEOAS library to allow adding HATEOAS links", hateoas));
cliOptions.add(CliOption.newBoolean(RETURN_SUCCESS_CODE, "Generated server returns 2xx code", returnSuccessCode));
cliOptions.add(CliOption.newBoolean(UNHANDLED_EXCEPTION_HANDLING, "Declare operation methods to throw a generic exception and allow unhandled exceptions (useful for Spring `@ControllerAdvice` directives).", unhandledException));
@ -172,7 +177,7 @@ public class SpringCodegen extends AbstractJavaCodegen
@Override
public void processOpts() {
List<Pair<String,String>> configOptions = additionalProperties.entrySet().stream()
List<Pair<String, String>> configOptions = additionalProperties.entrySet().stream()
.filter(e -> !Arrays.asList(API_FIRST, "hideGenerationTimestamp").contains(e.getKey()))
.filter(e -> cliOptions.stream().map(CliOption::getOpt).anyMatch(opt -> opt.equals(e.getKey())))
.map(e -> Pair.of(e.getKey(), e.getValue().toString()))
@ -220,7 +225,7 @@ public class SpringCodegen extends AbstractJavaCodegen
} else {
additionalProperties.put(BASE_PACKAGE, basePackage);
}
if (additionalProperties.containsKey(VIRTUAL_SERVICE)) {
this.setVirtualService(Boolean.valueOf(additionalProperties.get(VIRTUAL_SERVICE).toString()));
}
@ -248,7 +253,7 @@ public class SpringCodegen extends AbstractJavaCodegen
}
if (additionalProperties.containsKey(REACTIVE)) {
if (!library.equals(SPRING_BOOT)) {
if (!SPRING_BOOT.equals(library)) {
throw new IllegalArgumentException("Currently, reactive option is only supported with Spring-boot");
}
this.setReactive(Boolean.valueOf(additionalProperties.get(REACTIVE).toString()));
@ -287,7 +292,7 @@ public class SpringCodegen extends AbstractJavaCodegen
if (additionalProperties.containsKey(API_FIRST)) {
this.setApiFirst(Boolean.valueOf(additionalProperties.get(API_FIRST).toString()));
}
if (additionalProperties.containsKey(HATEOAS)) {
this.setHateoas(Boolean.valueOf(additionalProperties.get(HATEOAS).toString()));
}
@ -398,12 +403,15 @@ public class SpringCodegen extends AbstractJavaCodegen
apiTemplateFiles.put("apiDelegate.mustache", "Delegate.java");
}
if (this.java8) {
additionalProperties.put("javaVersion", "1.8");
additionalProperties.put("jdk8-default-interface", !this.skipDefaultInterface);
if (!SPRING_CLOUD_LIBRARY.equals(library)) {
additionalProperties.put("jdk8", true);
if (SPRING_CLOUD_LIBRARY.equals(library)) {
additionalProperties.put("jdk8-default-interface", false);
} else {
additionalProperties.put("jdk8-default-interface", !this.skipDefaultInterface);
}
additionalProperties.put("jdk8", true);
if (this.async) {
additionalProperties.put(RESPONSE_WRAPPER, "CompletableFuture");
}
@ -414,7 +422,8 @@ public class SpringCodegen extends AbstractJavaCodegen
additionalProperties.put(RESPONSE_WRAPPER, "Callable");
}
if(!this.apiFirst && !this.reactive) {
if (!this.apiFirst && !this.reactive) {
additionalProperties.put("useSpringfox", true);
}
@ -458,7 +467,7 @@ public class SpringCodegen extends AbstractJavaCodegen
@Override
public void addOperationToGroup(String tag, String resourcePath, Operation operation, CodegenOperation co, Map<String, List<CodegenOperation>> operations) {
if((library.equals(SPRING_BOOT) || library.equals(SPRING_MVC_LIBRARY)) && !useTags) {
if ((library.equals(SPRING_BOOT) || library.equals(SPRING_MVC_LIBRARY)) && !useTags) {
String basePath = resourcePath;
if (basePath.startsWith("/")) {
basePath = basePath.substring(1);
@ -490,7 +499,7 @@ public class SpringCodegen extends AbstractJavaCodegen
}
*/
if(!additionalProperties.containsKey(TITLE)) {
if (!additionalProperties.containsKey(TITLE)) {
// From the title, compute a reasonable name for the package and the API
String title = openAPI.getInfo().getTitle();
@ -506,7 +515,7 @@ public class SpringCodegen extends AbstractJavaCodegen
additionalProperties.put(TITLE, this.title);
}
if(!additionalProperties.containsKey(SERVER_PORT)) {
if (!additionalProperties.containsKey(SERVER_PORT)) {
URL url = URLPathUtils.getServerURL(openAPI);
this.additionalProperties.put(SERVER_PORT, URLPathUtils.getPort(url, 8080));
}
@ -578,7 +587,7 @@ public class SpringCodegen extends AbstractJavaCodegen
}
});
if(implicitHeaders){
if (implicitHeaders) {
removeHeadersFromAllParams(operation.allParams);
}
}
@ -589,12 +598,12 @@ public class SpringCodegen extends AbstractJavaCodegen
private interface DataTypeAssigner {
void setReturnType(String returnType);
void setReturnContainer(String returnContainer);
}
/**
*
* @param returnType The return type that needs to be converted
* @param returnType The return type that needs to be converted
* @param dataTypeAssigner An object that will assign the data to the respective fields in the model.
*/
private void doDataTypeAssignment(String returnType, DataTypeAssigner dataTypeAssigner) {
@ -625,29 +634,30 @@ public class SpringCodegen extends AbstractJavaCodegen
/**
* This method removes header parameters from the list of parameters and also
* corrects last allParams hasMore state.
*
* @param allParams list of all parameters
*/
private void removeHeadersFromAllParams(List<CodegenParameter> allParams) {
if(allParams.isEmpty()){
if (allParams.isEmpty()) {
return;
}
final ArrayList<CodegenParameter> copy = new ArrayList<>(allParams);
allParams.clear();
for(CodegenParameter p : copy){
if(!p.isHeaderParam){
for (CodegenParameter p : copy) {
if (!p.isHeaderParam) {
allParams.add(p);
}
}
if (!allParams.isEmpty()) {
allParams.get(allParams.size()-1).hasMore =false;
allParams.get(allParams.size() - 1).hasMore = false;
}
}
@Override
public Map<String, Object> postProcessSupportingFileData(Map<String, Object> objs) {
generateYAMLSpecFile(objs);
if(library.equals(SPRING_CLOUD_LIBRARY)) {
if (library.equals(SPRING_CLOUD_LIBRARY)) {
List<CodegenSecurity> authMethods = (List<CodegenSecurity>) objs.get("authMethods");
if (authMethods != null) {
for (CodegenSecurity authMethod : authMethods) {
@ -717,9 +727,13 @@ public class SpringCodegen extends AbstractJavaCodegen
return this.basePackage;
}
public void setInterfaceOnly(boolean interfaceOnly) { this.interfaceOnly = interfaceOnly; }
public void setInterfaceOnly(boolean interfaceOnly) {
this.interfaceOnly = interfaceOnly;
}
public void setDelegatePattern(boolean delegatePattern) { this.delegatePattern = delegatePattern; }
public void setDelegatePattern(boolean delegatePattern) {
this.delegatePattern = delegatePattern;
}
public void setSingleContentTypes(boolean singleContentTypes) {
this.singleContentTypes = singleContentTypes;
@ -729,13 +743,21 @@ public class SpringCodegen extends AbstractJavaCodegen
public void setJava8(boolean java8) { this.java8 = java8; }
public void setVirtualService(boolean virtualService) { this.virtualService = virtualService; }
public void setVirtualService(boolean virtualService) {
this.virtualService = virtualService;
}
public void setAsync(boolean async) { this.async = async; }
public void setAsync(boolean async) {
this.async = async;
}
public void setReactive(boolean reactive) { this.reactive = reactive; }
public void setReactive(boolean reactive) {
this.reactive = reactive;
}
public void setResponseWrapper(String responseWrapper) { this.responseWrapper = responseWrapper; }
public void setResponseWrapper(String responseWrapper) {
this.responseWrapper = responseWrapper;
}
public void setUseTags(boolean useTags) {
this.useTags = useTags;
@ -752,7 +774,7 @@ public class SpringCodegen extends AbstractJavaCodegen
public void setApiFirst(boolean apiFirst) {
this.apiFirst = apiFirst;
}
public void setHateoas(boolean hateoas) {
this.hateoas = hateoas;
}
@ -793,7 +815,7 @@ public class SpringCodegen extends AbstractJavaCodegen
objs = super.postProcessModelsEnum(objs);
//Add imports for Jackson
List<Map<String, String>> imports = (List<Map<String, String>>)objs.get("imports");
List<Map<String, String>> imports = (List<Map<String, String>>) objs.get("imports");
List<Object> models = (List<Object>) objs.get("models");
for (Object _mo : models) {
Map<String, Object> mo = (Map<String, Object>) _mo;

View File

@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.*;
import java.util.regex.Pattern;
import static org.apache.commons.lang3.StringUtils.capitalize;
import static org.openapitools.codegen.utils.StringUtils.*;
@ -48,6 +49,8 @@ public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCode
public static final String MODEL_SUFFIX = "modelSuffix";
public static final String MODEL_FILE_SUFFIX = "modelFileSuffix";
public static final String FILE_NAMING = "fileNaming";
public static final String STRING_ENUMS = "stringEnums";
public static final String STRING_ENUMS_DESC = "Generate string enums instead of objects for enum values.";
protected String ngVersion = "7.0.0";
protected String npmRepository = null;
@ -56,6 +59,7 @@ public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCode
protected String modelSuffix = "";
protected String modelFileSuffix = "";
protected String fileNaming = "camelCase";
protected Boolean stringEnums = false;
private boolean taggedUnions = false;
@ -88,6 +92,7 @@ public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCode
this.cliOptions.add(new CliOption(MODEL_SUFFIX, "The suffix of the generated model."));
this.cliOptions.add(new CliOption(MODEL_FILE_SUFFIX, "The suffix of the file of the generated model (model<suffix>.ts)."));
this.cliOptions.add(new CliOption(FILE_NAMING, "Naming convention for the output files: 'camelCase', 'kebab-case'.").defaultValue(this.fileNaming));
this.cliOptions.add(new CliOption(STRING_ENUMS, STRING_ENUMS_DESC).defaultValue(String.valueOf(this.stringEnums)));
}
@Override
@ -136,6 +141,15 @@ public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCode
addNpmPackageGeneration(ngVersion);
}
if (additionalProperties.containsKey(STRING_ENUMS)) {
setStringEnums(Boolean.valueOf(additionalProperties.get(STRING_ENUMS).toString()));
additionalProperties.put("stringEnums", getStringEnums());
if (getStringEnums()) {
enumSuffix = "";
classEnumSeparator = "";
}
}
if (additionalProperties.containsKey(WITH_INTERFACES)) {
boolean withInterfaces = Boolean.parseBoolean(additionalProperties.get(WITH_INTERFACES).toString());
if (withInterfaces) {
@ -151,8 +165,9 @@ public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCode
if (!additionalProperties.containsKey(PROVIDED_IN_ROOT)) {
additionalProperties.put(PROVIDED_IN_ROOT, true);
} else {
additionalProperties.put(PROVIDED_IN_ROOT, Boolean.valueOf(
(String) additionalProperties.get(PROVIDED_IN_ROOT)));
additionalProperties.put(PROVIDED_IN_ROOT, Boolean.parseBoolean(
additionalProperties.get(PROVIDED_IN_ROOT).toString()
));
}
} else {
additionalProperties.put(PROVIDED_IN_ROOT, false);
@ -271,6 +286,14 @@ public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCode
return indexPackage.replace('.', File.separatorChar);
}
public void setStringEnums(boolean value) {
stringEnums = value;
}
public Boolean getStringEnums() {
return stringEnums;
}
@Override
public boolean isDataTypeFile(final String dataType) {
return dataType != null && dataType.equals("Blob");
@ -330,7 +353,11 @@ public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCode
objs.put("apiFilename", getApiFilenameFromClassname(objs.get("classname").toString()));
List<CodegenOperation> ops = (List<CodegenOperation>) objs.get("operation");
boolean hasSomeFormParams = false;
for (CodegenOperation op : ops) {
if (op.getHasFormParams()) {
hasSomeFormParams = true;
}
if ((boolean) additionalProperties.get("useHttpClient")) {
op.httpMethod = op.httpMethod.toLowerCase(Locale.ENGLISH);
} else {
@ -406,6 +433,8 @@ public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCode
op.path = pathBuffer.toString();
}
operations.put("hasSomeFormParams", hasSomeFormParams);
// Add additional filename information for model imports in the services
List<Map<String, Object>> imports = (List<Map<String, Object>>) operations.get("imports");
for (Map<String, Object> im : imports) {
@ -458,12 +487,33 @@ public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCode
}
}
// Add additional filename information for imports
mo.put("tsImports", toTsImports(cm, cm.imports));
Set<String> parsedImports = parseImports(cm);
mo.put("tsImports", toTsImports(cm, parsedImports));
}
}
return result;
}
/**
* Parse imports
*/
private Set<String> parseImports(CodegenModel cm) {
Set<String> newImports = new HashSet<String>();
if (cm.imports.size() > 0) {
for (String name : cm.imports) {
if (name.indexOf(" | ") >= 0) {
String[] parts = name.split(" \\| ");
for (String s : parts) {
newImports.add(s);
}
} else {
newImports.add(name);
}
}
}
return newImports;
}
private List<Map<String, String>> toTsImports(CodegenModel cm, Set<String> imports) {
List<Map<String, String>> tsImports = new ArrayList<>();
for (String im : imports) {

View File

@ -50,9 +50,9 @@ public class TypeScriptFetchClientCodegen extends AbstractTypeScriptClientCodege
outputFolder = "generated-code/typescript-fetch";
embeddedTemplateDir = templateDir = "typescript-fetch";
this.apiPackage = "apis";
this.apiPackage = "src" + File.separator +"apis";
this.modelPackage = "src" + File.separator + "models";
this.apiTemplateFiles.put("apis.mustache", ".ts");
this.modelPackage = "models";
this.modelTemplateFiles.put("models.mustache", ".ts");
this.addExtraReservedWords();
@ -84,8 +84,8 @@ public class TypeScriptFetchClientCodegen extends AbstractTypeScriptClientCodege
super.processOpts();
additionalProperties.put("isOriginalModelPropertyNaming", getModelPropertyNaming().equals("original"));
additionalProperties.put("modelPropertyNaming", getModelPropertyNaming());
supportingFiles.add(new SupportingFile("index.mustache", "", "index.ts"));
supportingFiles.add(new SupportingFile("runtime.mustache", "", "runtime.ts"));
supportingFiles.add(new SupportingFile("index.mustache", "src", "index.ts"));
supportingFiles.add(new SupportingFile("runtime.mustache", "src", "runtime.ts"));
supportingFiles.add(new SupportingFile("tsconfig.mustache", "", "tsconfig.json"));
supportingFiles.add(new SupportingFile("gitignore", "", ".gitignore"));
@ -188,6 +188,7 @@ public class TypeScriptFetchClientCodegen extends AbstractTypeScriptClientCodege
//Files for building our lib
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
supportingFiles.add(new SupportingFile("package.mustache", "", "package.json"));
supportingFiles.add(new SupportingFile("npmignore.mustache", "", ".npmignore"));
}
@Override
@ -216,7 +217,7 @@ public class TypeScriptFetchClientCodegen extends AbstractTypeScriptClientCodege
// models for a given operation.
List<Map<String, Object>> imports = (List<Map<String, Object>>) operations.get("imports");
for (Map<String, Object> im : imports) {
im.put("className", im.get("import").toString().replace("models.", ""));
im.put("className", im.get("import").toString().replace(modelPackage() + ".", ""));
}
}

View File

@ -18,7 +18,6 @@
package org.openapitools.codegen.languages;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.parser.util.SchemaTypeUtil;
import org.openapitools.codegen.*;
import org.openapitools.codegen.utils.ModelUtils;
import org.slf4j.Logger;
@ -33,6 +32,7 @@ public class TypeScriptNodeClientCodegen extends AbstractTypeScriptClientCodegen
private static final Logger LOGGER = LoggerFactory.getLogger(TypeScriptNodeClientCodegen.class);
public static final String NPM_REPOSITORY = "npmRepository";
private static final String DEFAULT_IMPORT_PREFIX = "./";
protected String npmRepository = null;
protected String apiSuffix = "Api";
@ -96,22 +96,37 @@ public class TypeScriptNodeClientCodegen extends AbstractTypeScriptClientCodegen
if (name.length() == 0) {
return "default" + apiSuffix;
}
if (importMapping.containsKey(name)) {
return importMapping.get(name);
}
return camelize(name, true) + apiSuffix;
}
@Override
public String toApiImport(String name) {
if (importMapping.containsKey(name)) {
return importMapping.get(name);
}
return apiPackage() + "/" + toApiFilename(name);
}
@Override
public String toModelFilename(String name) {
return camelize(toModelName(name), true);
if (importMapping.containsKey(name)) {
return importMapping.get(name);
}
return DEFAULT_IMPORT_PREFIX + camelize(toModelName(name), true);
}
@Override
public String toModelImport(String name) {
return modelPackage() + "/" + toModelFilename(name);
if (importMapping.containsKey(name)) {
return importMapping.get(name);
}
return modelPackage() + "/" + camelize(toModelName(name), true);
}
@Override

View File

@ -187,24 +187,14 @@ public class ApiClient {
}
/**
* Set HTTP client
* Set HTTP client, which must never be null.
*
* @param newHttpClient An instance of OkHttpClient
* @return Api Client
* @throws NullPointerException when newHttpClient is null
*/
public ApiClient setHttpClient(OkHttpClient newHttpClient) {
if(!httpClient.equals(newHttpClient)) {
OkHttpClient.Builder builder = newHttpClient.newBuilder();
Iterator<Interceptor> networkInterceptorIterator = httpClient.networkInterceptors().iterator();
while(networkInterceptorIterator.hasNext()) {
builder.addNetworkInterceptor(networkInterceptorIterator.next());
}
Iterator<Interceptor> interceptorIterator = httpClient.interceptors().iterator();
while(interceptorIterator.hasNext()) {
builder.addInterceptor(interceptorIterator.next());
}
this.httpClient = builder.build();
}
this.httpClient = Objects.requireNonNull(newHttpClient, "HttpClient must not be null!");
return this;
}

View File

@ -555,6 +555,16 @@ public class ApiClient {
return isForm ? formParams : obj;
}
/**
* Expand path template with variables
* @param pathTemplate path template with placeholders
* @param variables variables to replace
* @return path with placeholders replaced by variables
*/
public String expandPath(String pathTemplate, Map<String, Object> variables) {
return restTemplate.getUriTemplateHandler().expand(pathTemplate, variables).toString();
}
/**
* Invoke API by sending HTTP request with the given options.
*
@ -619,9 +629,6 @@ public class ApiClient {
if (responseEntity.getStatusCode() == HttpStatus.NO_CONTENT) {
return null;
} else if (responseEntity.getStatusCode().is2xxSuccessful()) {
if (returnType == null) {
return null;
}
return responseEntity.getBody();
} else {
// The error handler built into the RestTemplate should handle 400 and 500 series errors.

View File

@ -6,6 +6,7 @@ import {{invokerPackage}}.ApiClient;
{{/imports}}
{{^fullJavaUtil}}import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@ -72,7 +73,7 @@ public class {{classname}} {
// create path and map variables
final Map<String, Object> uriVariables = new HashMap<String, Object>();{{#pathParams}}
uriVariables.put("{{baseName}}", {{#collectionFormat}}apiClient.collectionPathParameterToString(ApiClient.CollectionFormat.valueOf("{{{collectionFormat}}}".toUpperCase()), {{{paramName}}}){{/collectionFormat}}{{^collectionFormat}}{{{paramName}}}{{/collectionFormat}});{{/pathParams}}{{/hasPathParams}}
String path = UriComponentsBuilder.fromPath("{{{path}}}"){{#hasPathParams}}.buildAndExpand(uriVariables){{/hasPathParams}}{{^hasPathParams}}.build(){{/hasPathParams}}.toUriString();
String path = apiClient.expandPath("{{{path}}}", {{#hasPathParams}}uriVariables{{/hasPathParams}}{{^hasPathParams}}Collections.<String, Object>emptyMap(){{/hasPathParams}});
final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<String, String>();
final HttpHeaders headerParams = new HttpHeaders();

View File

@ -20,7 +20,8 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#parcela
{{/isContainer}}
{{/isEnum}}
{{#jackson}}
@JsonProperty("{{baseName}}")
public static final String JSON_PROPERTY_{{nameInSnakeCase}} = "{{baseName}}";
@JsonProperty(JSON_PROPERTY_{{nameInSnakeCase}})
{{#withXml}}
{{^isContainer}}
@JacksonXmlProperty({{#isXmlAttribute}}isAttribute = true, {{/isXmlAttribute}}{{#xmlNamespace}}namespace="{{xmlNamespace}}", {{/xmlNamespace}}localName = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}")

View File

@ -16,10 +16,12 @@ public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#seriali
{{/isContainer}}
{{/isEnum}}
{{#jackson}}
@JsonProperty("{{baseName}}")
public static final String JSON_PROPERTY_{{nameInSnakeCase}} = "{{baseName}}";
@JsonProperty(JSON_PROPERTY_{{nameInSnakeCase}})
{{/jackson}}
{{#gson}}
@SerializedName("{{baseName}}")
public static final String SERIALIZED_NAME_{{nameInSnakeCase}} = "{{baseName}}";
@SerializedName(SERIALIZED_NAME_{{nameInSnakeCase}})
{{/gson}}
{{#isContainer}}
private {{{datatypeWithEnum}}} {{name}}{{#required}} = {{{defaultValue}}}{{/required}}{{^required}} = null{{/required}};

View File

@ -24,25 +24,25 @@ public class ClientConfiguration {
{{#authMethods}}
{{#isBasic}}
{{=<% %>=}}@Value("${<%title%>.security.<%name%>.username:}")<%={{ }}=%>
@Value("${{openbrace}}{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.username:{{closebrace}}")
private String {{{name}}}Username;
{{=<% %>=}}@Value("${<%title%>.security.<%name%>.password:}")<%={{ }}=%>
@Value("${{openbrace}}{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.password:{{closebrace}}")
private String {{{name}}}Password;
@Bean
@ConditionalOnProperty(name = "{{{title}}}.security.{{{name}}}.username")
@ConditionalOnProperty(name = "{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.username")
public BasicAuthRequestInterceptor {{{name}}}RequestInterceptor() {
return new BasicAuthRequestInterceptor(this.{{{name}}}Username, this.{{{name}}}Password);
}
{{/isBasic}}
{{#isApiKey}}
{{=<% %>=}}@Value("${<%title%>.security.<%name%>.key:}")<%={{ }}=%>
@Value("${{openbrace}}{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.key:{{closebrace}}")
private String {{{name}}}Key;
@Bean
@ConditionalOnProperty(name = "{{{title}}}.security.{{{name}}}.key")
@ConditionalOnProperty(name = "{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.key")
public ApiKeyRequestInterceptor {{{name}}}RequestInterceptor() {
return new ApiKeyRequestInterceptor({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{{keyParamName}}}", this.{{{name}}}Key);
}
@ -50,15 +50,15 @@ public class ClientConfiguration {
{{/isApiKey}}
{{#isOAuth}}
@Bean
@ConditionalOnProperty("{{{title}}}.security.{{{name}}}.client-id")
@ConditionalOnProperty("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.client-id")
public OAuth2FeignRequestInterceptor {{{name}}}RequestInterceptor() {
return new OAuth2FeignRequestInterceptor(new DefaultOAuth2ClientContext(), {{{name}}}ResourceDetails());
}
{{#isCode}}
@Bean
@ConditionalOnProperty("{{{title}}}.security.{{{name}}}.client-id")
@ConfigurationProperties("{{{title}}}.security.{{{name}}}")
@ConditionalOnProperty("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.client-id")
@ConfigurationProperties("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}")
public AuthorizationCodeResourceDetails {{{name}}}ResourceDetails() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setAccessTokenUri("{{{tokenUrl}}}");
@ -69,8 +69,8 @@ public class ClientConfiguration {
{{/isCode}}
{{#isPassword}}
@Bean
@ConditionalOnProperty("{{{title}}}.security.{{{name}}}.client-id")
@ConfigurationProperties("{{{title}}}.security.{{{name}}}")
@ConditionalOnProperty("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.client-id")
@ConfigurationProperties("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}")
public ResourceOwnerPasswordResourceDetails {{{name}}}ResourceDetails() {
ResourceOwnerPasswordResourceDetails details = new ResourceOwnerPasswordResourceDetails();
details.setAccessTokenUri("{{{tokenUrl}}}");
@ -80,8 +80,8 @@ public class ClientConfiguration {
{{/isPassword}}
{{#isApplication}}
@Bean
@ConditionalOnProperty("{{{title}}}.security.{{{name}}}.client-id")
@ConfigurationProperties("{{{title}}}.security.{{{name}}}")
@ConditionalOnProperty("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.client-id")
@ConfigurationProperties("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}")
public ClientCredentialsResourceDetails {{{name}}}ResourceDetails() {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
details.setAccessTokenUri("{{{tokenUrl}}}");
@ -91,8 +91,8 @@ public class ClientConfiguration {
{{/isApplication}}
{{#isImplicit}}
@Bean
@ConditionalOnProperty("{{{title}}}.security.{{{name}}}.client-id")
@ConfigurationProperties("{{{title}}}.security.{{{name}}}")
@ConditionalOnProperty("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.client-id")
@ConfigurationProperties("{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}")
public ImplicitResourceDetails {{{name}}}ResourceDetails() {
ImplicitResourceDetails details = new ImplicitResourceDetails();
details.setUserAuthorizationUri("{{{authorizationUrl}}}");

View File

@ -29,6 +29,7 @@
<sourceDirectory>src/main/java</sourceDirectory>
</build>
{{^parentOverridden}}
<dependencyManagement>
<dependencies>
<dependency>
@ -41,11 +42,14 @@
</dependencies>
</dependencyManagement>
{{/parentOverridden}}
<dependencies>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
{{^parentOverridden}}
<version>${swagger-core-version}</version>
{{/parentOverridden}}
</dependency>
<!-- @Nullable annotation -->
<dependency>
@ -84,13 +88,17 @@
<dependency>
<groupId>com.github.joschi.jackson</groupId>
<artifactId>jackson-datatype-threetenbp</artifactId>
{{^parentOverridden}}
<version>2.6.4</version>
{{/parentOverridden}}
</dependency>
{{/threetenbp}}
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>jackson-databind-nullable</artifactId>
{{^parentOverridden}}
<version>0.1.0</version>
{{/parentOverridden}}
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@ -104,5 +112,14 @@
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
{{/hateoas}}
{{#useBeanValidation}}
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
{{^parentOverridden}}
<version>6.0.16.Final</version>
{{/parentOverridden}}
</dependency>
{{/useBeanValidation}}
</dependencies>
</project>

View File

@ -61,7 +61,9 @@ org.openapitools.codegen.languages.JMeterClientCodegen
org.openapitools.codegen.languages.LuaClientCodegen
org.openapitools.codegen.languages.MysqlSchemaCodegen
org.openapitools.codegen.languages.NodeJSServerCodegen
org.openapitools.codegen.languages.NodeJSExpressServerCodegen
org.openapitools.codegen.languages.ObjcClientCodegen
org.openapitools.codegen.languages.OCamlClientCodegen
org.openapitools.codegen.languages.OpenAPIGenerator
org.openapitools.codegen.languages.OpenAPIYamlGenerator
org.openapitools.codegen.languages.PerlClientCodegen

View File

@ -60,7 +60,7 @@ namespace {{packageName}}
// Add framework services.
services
.AddMvc({{^useDefaultRoutng}}opts => opts.EnableEndpointRouting = false{{/useDefaultRoutng}})
.AddMvc({{^useDefaultRouting}}opts => opts.EnableEndpointRouting = false{{/useDefaultRouting}})
{{#compatibilityVersion}}
.SetCompatibilityVersion(CompatibilityVersion.{{compatibilityVersion}})
{{/compatibilityVersion}}
@ -127,12 +127,12 @@ namespace {{packageName}}
//TODO: Or alternatively use the original Swagger contract that's included in the static files
// c.SwaggerEndpoint("/openapi-original.json", "{{#appName}}{{{appName}}}{{/appName}}{{^appName}}{{packageName}}{{/appName}} Original");
}){{/useSwashbuckle}};{{^useDefaultRoutng}}
}){{/useSwashbuckle}};{{^useDefaultRouting}}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});{{/useDefaultRoutng}}
});{{/useDefaultRouting}}
if (env.IsDevelopment())
{

View File

@ -40,9 +40,14 @@ namespace {{apiPackage}}
[SwaggerOperation("{{operationId}}")]{{#responses}}{{#dataType}}
[SwaggerResponse(statusCode: {{code}}, type: typeof({{&dataType}}), description: "{{message}}")]{{/dataType}}{{^dataType}}{{/dataType}}{{/responses}}{{/useSwashbuckle}}{{^useSwashbuckle}}{{#responses}}{{#dataType}}
[ProducesResponseType(statusCode: {{code}}, type: typeof({{&dataType}}))]{{/dataType}}{{^dataType}}{{/dataType}}{{/responses}}{{/useSwashbuckle}}
public {{operationModifier}} {{#operationResultTask}}{{#operationIsAsync}}async {{/operationIsAsync}}Task<{{/operationResultTask}}IActionResult{{#operationResultTask}}>{{/operationResultTask}} {{operationId}}({{#allParams}}{{>pathParam}}{{>queryParam}}{{>bodyParam}}{{>formParam}}{{>headerParam}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{^generateBody}};{{/generateBody}}
public {{operationModifier}} {{#operationResultTask}}{{#operationIsAsync}}async {{/operationIsAsync}}Task<{{/operationResultTask}}IActionResult{{#operationResultTask}}>{{/operationResultTask}} {{operationId}}({{#allParams}}{{>pathParam}}{{>queryParam}}{{>bodyParam}}{{>formParam}}{{>headerParam}}{{#hasMore}}{{^isCookieParam}}, {{/isCookieParam}}{{/hasMore}}{{/allParams}}){{^generateBody}};{{/generateBody}}
{{#generateBody}}
{ {{#responses}}
{
{{#cookieParams}}
var {{paramName}} = Request.Cookies["{{paramName}}"];
{{/cookieParams}}
{{#responses}}
{{#dataType}}
//TODO: Uncomment the next line to return response {{code}} or use other options such as return this.NotFound(), return this.BadRequest(..), ...
// return StatusCode({{code}}, default({{&dataType}}));
@ -50,7 +55,8 @@ namespace {{apiPackage}}
{{^dataType}}
//TODO: Uncomment the next line to return response {{code}} or use other options such as return this.NotFound(), return this.BadRequest(..), ...
// return StatusCode({{code}});
{{/dataType}}{{/responses}}
{{/dataType}}
{{/responses}}
{{#returnType}}
string exampleJson = null;
{{#examples}}

View File

@ -5,7 +5,8 @@
{{#description}}
/// <value>{{{description}}}</value>
{{/description}}
{{#allowableValues}}{{#enumVars}}{{#-first}}{{#isString}}[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]{{/isString}}{{/-first}}{{/enumVars}}{{/allowableValues}}
{{#allowableValues}}{{#enumVars}}{{#-first}}{{#isString}}[TypeConverter(typeof(CustomEnumConverter<{{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}>))]
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]{{/isString}}{{/-first}}{{/enumVars}}{{/allowableValues}}
public enum {{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}
{
{{#allowableValues}}{{#enumVars}}

View File

@ -3,9 +3,11 @@ using System;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using {{packageName}}.Converters;
{{#models}}
{{#model}}

View File

@ -0,0 +1,33 @@
using System;
using System.ComponentModel;
using System.Globalization;
using Newtonsoft.Json;
namespace {{packageName}}.Converters
{
/// <summary>
/// Custom string to enum converter
/// </summary>
public class CustomEnumConverter<T> : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var s = value as string;
if (string.IsNullOrEmpty(s))
{
return null;
}
return JsonConvert.DeserializeObject<T>(@"""" + value.ToString() + @"""");
}
}
}

View File

@ -416,18 +416,16 @@ build_request_path() {
local query_request_part=""
local count=0
for qparam in "${query_params[@]}"; do
if [[ "${operation_parameters[$qparam]}" == "" ]]; then
continue
fi
# Get the array of parameter values
local parameter_value=""
local parameter_values
mapfile -t parameter_values < <(sed -e 's/'":::"'/\n/g' <<<"${operation_parameters[$qparam]}")
if [[ -n "${parameter_values[*]}" ]]; then
if [[ $((count++)) -gt 0 ]]; then
query_request_part+="&"
fi
fi
{{#hasAuthMethods}}{{#authMethods}}{{#isApiKey}}{{#isKeyInQuery}}
if [[ ${qparam} == "{{keyParamName}}" ]]; then
if [[ -n "${parameter_values[*]}" ]]; then
@ -508,6 +506,9 @@ build_request_path() {
fi
if [[ -n "${parameter_value}" ]]; then
if [[ -n "${query_request_part}" ]]; then
query_request_part+="&"
fi
query_request_part+="${parameter_value}"
fi

View File

@ -70,7 +70,12 @@ void {{classname}}::{{operationIdSnakeCase}}_handler(const Pistache::Rest::Reque
try {
{{#hasBodyParam}}
{{#bodyParam}}
{{^isPrimitiveType}}
nlohmann::json::parse(request.body()).get_to({{paramName}});
{{/isPrimitiveType}}
{{#isPrimitiveType}}
{{paramName}} = request.body();
{{/isPrimitiveType}}
{{/bodyParam}}
{{/hasBodyParam}}
this->{{operationIdSnakeCase}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}{{#hasParams}}, {{/hasParams}}response);

View File

@ -265,16 +265,19 @@ std::shared_ptr<std::istream> ModelBase::fromBase64( const utility::string_t& en
int64_t ModelBase::int64_tFromJson(const web::json::value& val)
{
return val.as_number().to_int64();
return val.is_null() ? std::numeric_limits<int64_t>::quiet_NaN() : val.as_number().to_int64();
}
int32_t ModelBase::int32_tFromJson(const web::json::value& val)
{
return val.as_integer();
return val.is_null() ? std::numeric_limits<int32_t>::quiet_NaN() : val.as_integer();
}
float ModelBase::floatFromJson(const web::json::value& val)
{
return static_cast<float>(val.as_double());
return val.is_null() ? std::numeric_limits<float>::quiet_NaN() : static_cast<float>(val.as_double());
}
utility::string_t ModelBase::stringFromJson(const web::json::value& val)
{
return val.is_string() ? val.as_string() : utility::conversions::to_string_t("");
@ -282,15 +285,15 @@ utility::string_t ModelBase::stringFromJson(const web::json::value& val)
utility::datetime ModelBase::dateFromJson(const web::json::value& val)
{
return utility::datetime::from_string(val.as_string(), utility::datetime::ISO_8601);
return val.is_null() ? utility::datetime::from_string(L"NULL", utility::datetime::ISO_8601) : utility::datetime::from_string(val.as_string(), utility::datetime::ISO_8601);
}
bool ModelBase::boolFromJson(const web::json::value& val)
{
return val.as_bool();
return val.is_null() ? false : val.as_bool();
}
double ModelBase::doubleFromJson(const web::json::value& val)
{
return val.as_double();
return val.is_null() ? std::numeric_limits<double>::quiet_NaN(): val.as_double();
}
int64_t ModelBase::int64_tFromHttpContent(std::shared_ptr<HttpContent> val)

View File

@ -270,6 +270,14 @@ namespace {{packageName}}.Client
}
}
if (configuration.DefaultHeaders != null)
{
foreach (var headerParam in configuration.DefaultHeaders)
{
request.AddHeader(headerParam.Key, headerParam.Value);
}
}
if (options.HeaderParameters != null)
{
foreach (var headerParam in options.HeaderParameters)

View File

@ -35,7 +35,7 @@ namespace {{packageName}}.Client
#endregion Constants
#region Static Members
/// <summary>
/// Default creation of exceptions for a given method name and response object
/// </summary>
@ -65,7 +65,7 @@ namespace {{packageName}}.Client
/// Example: http://localhost:3000/v1/
/// </summary>
private String _basePath;
/// <summary>
/// Gets or sets the API key based on the authentication name.
/// This is the key and value comprising the "secret" for acessing an API.
@ -94,7 +94,7 @@ namespace {{packageName}}.Client
{
UserAgent = "{{#httpUserAgent}}{{.}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/{{packageVersion}}/csharp{{/httpUserAgent}}";
BasePath = "{{{basePath}}}";
DefaultHeader = new {{^net35}}Concurrent{{/net35}}Dictionary<string, string>();
DefaultHeaders = new {{^net35}}Concurrent{{/net35}}Dictionary<string, string>();
ApiKey = new {{^net35}}Concurrent{{/net35}}Dictionary<string, string>();
ApiKeyPrefix = new {{^net35}}Concurrent{{/net35}}Dictionary<string, string>();
@ -107,15 +107,15 @@ namespace {{packageName}}.Client
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")]
public Configuration(
IDictionary<string, string> defaultHeader,
IDictionary<string, string> defaultHeaders,
IDictionary<string, string> apiKey,
IDictionary<string, string> apiKeyPrefix,
string basePath = "{{{basePath}}}") : this()
{
if (string.{{^net35}}IsNullOrWhiteSpace{{/net35}}{{#net35}}IsNullOrEmpty{{/net35}}(basePath))
throw new ArgumentException("The provided basePath is invalid.", "basePath");
if (defaultHeader == null)
throw new ArgumentNullException("defaultHeader");
if (defaultHeaders == null)
throw new ArgumentNullException("defaultHeaders");
if (apiKey == null)
throw new ArgumentNullException("apiKey");
if (apiKeyPrefix == null)
@ -123,9 +123,9 @@ namespace {{packageName}}.Client
BasePath = basePath;
foreach (var keyValuePair in defaultHeader)
foreach (var keyValuePair in defaultHeaders)
{
DefaultHeader.Add(keyValuePair);
DefaultHeaders.Add(keyValuePair);
}
foreach (var keyValuePair in apiKey)
@ -156,7 +156,23 @@ namespace {{packageName}}.Client
/// <summary>
/// Gets or sets the default header.
/// </summary>
public virtual IDictionary<string, string> DefaultHeader { get; set; }
[Obsolete("Use DefaultHeaders instead.")]
public virtual IDictionary<string, string> DefaultHeader
{
get
{
return DefaultHeaders;
}
set
{
DefaultHeaders = value;
}
}
/// <summary>
/// Gets or sets the default headers.
/// </summary>
public virtual IDictionary<string, string> DefaultHeaders { get; set; }
/// <summary>
/// Gets or sets the HTTP timeout (milliseconds) of ApiClient. Default to 100000 milliseconds.
@ -374,17 +390,17 @@ namespace {{packageName}}.Client
Dictionary<string, string> apiKey = first.ApiKey.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Dictionary<string, string> apiKeyPrefix = first.ApiKeyPrefix.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Dictionary<string, string> defaultHeader = first.DefaultHeader.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Dictionary<string, string> defaultHeaders = first.DefaultHeaders.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
foreach (var kvp in second.ApiKey) apiKey[kvp.Key] = kvp.Value;
foreach (var kvp in second.ApiKeyPrefix) apiKeyPrefix[kvp.Key] = kvp.Value;
foreach (var kvp in second.DefaultHeader) defaultHeader[kvp.Key] = kvp.Value;
foreach (var kvp in second.DefaultHeaders) defaultHeaders[kvp.Key] = kvp.Value;
var config = new Configuration
{
ApiKey = apiKey,
ApiKeyPrefix = apiKeyPrefix,
DefaultHeader = defaultHeader,
DefaultHeader = defaultHeaders,
BasePath = second.BasePath ?? first.BasePath,
Timeout = second.Timeout,
UserAgent = second.UserAgent ?? first.UserAgent,

View File

@ -1,5 +1,6 @@
{{>partial_header}}
using System;
using System.Collections.Generic;
namespace {{packageName}}.Client
@ -43,8 +44,15 @@ namespace {{packageName}}.Client
/// Gets the default header.
/// </summary>
/// <value>Default header.</value>
[Obsolete("Use DefaultHeaders instead.")]
IDictionary<string, string> DefaultHeader { get; }
/// <summary>
/// Gets the default headers.
/// </summary>
/// <value>Default headers.</value>
IDictionary<string, string> DefaultHeaders { get; }
/// <summary>
/// Gets the temp folder path.
/// </summary>

View File

@ -32,7 +32,7 @@ namespace {{packageName}}.{{apiPackage}}
/// <exception cref="{{packageName}}.Client.ApiException">Thrown when fails to make API call</exception>
{{#allParams}}/// <param name="{{paramName}}">{{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}</param>
{{/allParams}}/// <returns>{{#returnType}}{{returnType}}{{/returnType}}</returns>
{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}} ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}} ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default({{{dataType}}}){{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
/// <summary>
/// {{summary}}
@ -43,7 +43,7 @@ namespace {{packageName}}.{{apiPackage}}
/// <exception cref="{{packageName}}.Client.ApiException">Thrown when fails to make API call</exception>
{{#allParams}}/// <param name="{{paramName}}">{{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}</param>
{{/allParams}}/// <returns>ApiResponse of {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Object(void){{/returnType}}</returns>
ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Object{{/returnType}}> {{operationId}}WithHttpInfo ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Object{{/returnType}}> {{operationId}}WithHttpInfo ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default({{{dataType}}}){{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
{{/operation}}
#endregion Synchronous Operations
{{#supportsAsync}}
@ -58,7 +58,7 @@ namespace {{packageName}}.{{apiPackage}}
/// <exception cref="{{packageName}}.Client.ApiException">Thrown when fails to make API call</exception>
{{#allParams}}/// <param name="{{paramName}}">{{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}</param>
{{/allParams}}/// <returns>Task of {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}void{{/returnType}}</returns>
{{#returnType}}System.Threading.Tasks.Task<{{{returnType}}}>{{/returnType}}{{^returnType}}System.Threading.Tasks.Task{{/returnType}} {{operationId}}Async ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
{{#returnType}}System.Threading.Tasks.Task<{{{returnType}}}>{{/returnType}}{{^returnType}}System.Threading.Tasks.Task{{/returnType}} {{operationId}}Async ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default({{{dataType}}}){{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
/// <summary>
/// {{summary}}
@ -69,7 +69,7 @@ namespace {{packageName}}.{{apiPackage}}
/// <exception cref="{{packageName}}.Client.ApiException">Thrown when fails to make API call</exception>
{{#allParams}}/// <param name="{{paramName}}">{{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}</param>
{{/allParams}}/// <returns>Task of ApiResponse{{#returnType}} ({{returnType}}){{/returnType}}</returns>
System.Threading.Tasks.Task<ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Object{{/returnType}}>> {{operationId}}AsyncWithHttpInfo ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
System.Threading.Tasks.Task<ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Object{{/returnType}}>> {{operationId}}AsyncWithHttpInfo ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default({{{dataType}}}){{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
{{/operation}}
#endregion Asynchronous Operations
{{/supportsAsync}}
@ -190,7 +190,7 @@ namespace {{packageName}}.{{apiPackage}}
/// <exception cref="{{packageName}}.Client.ApiException">Thrown when fails to make API call</exception>
{{#allParams}}/// <param name="{{paramName}}">{{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}</param>
{{/allParams}}/// <returns>{{#returnType}}{{returnType}}{{/returnType}}</returns>
public {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}} ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}})
public {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}} ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default({{{dataType}}}){{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}})
{
{{#returnType}}ApiResponse<{{{returnType}}}> localVarResponse = {{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
return localVarResponse.Data;{{/returnType}}{{^returnType}}{{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{/returnType}}
@ -202,7 +202,7 @@ namespace {{packageName}}.{{apiPackage}}
/// <exception cref="{{packageName}}.Client.ApiException">Thrown when fails to make API call</exception>
{{#allParams}}/// <param name="{{paramName}}">{{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}</param>
{{/allParams}}/// <returns>ApiResponse of {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Object(void){{/returnType}}</returns>
public ApiResponse<{{#returnType}} {{{returnType}}} {{/returnType}}{{^returnType}}Object{{/returnType}}> {{operationId}}WithHttpInfo ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}})
public ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Object{{/returnType}}> {{operationId}}WithHttpInfo ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default({{{dataType}}}){{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}})
{
{{#allParams}}
{{#required}}
@ -325,7 +325,7 @@ namespace {{packageName}}.{{apiPackage}}
/// <exception cref="{{packageName}}.Client.ApiException">Thrown when fails to make API call</exception>
{{#allParams}}/// <param name="{{paramName}}">{{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}</param>
{{/allParams}}/// <returns>Task of {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}void{{/returnType}}</returns>
{{#returnType}}public async System.Threading.Tasks.Task<{{{returnType}}}>{{/returnType}}{{^returnType}}public async System.Threading.Tasks.Task{{/returnType}} {{operationId}}Async ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}})
{{#returnType}}public async System.Threading.Tasks.Task<{{{returnType}}}>{{/returnType}}{{^returnType}}public async System.Threading.Tasks.Task{{/returnType}} {{operationId}}Async ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default({{{dataType}}}){{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}})
{
{{#returnType}}ApiResponse<{{{returnType}}}> localVarResponse = await {{operationId}}AsyncWithHttpInfo({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
return localVarResponse.Data;{{/returnType}}{{^returnType}}await {{operationId}}AsyncWithHttpInfo({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{/returnType}}
@ -338,7 +338,7 @@ namespace {{packageName}}.{{apiPackage}}
/// <exception cref="{{packageName}}.Client.ApiException">Thrown when fails to make API call</exception>
{{#allParams}}/// <param name="{{paramName}}">{{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}</param>
{{/allParams}}/// <returns>Task of ApiResponse{{#returnType}} ({{returnType}}){{/returnType}}</returns>
public async System.Threading.Tasks.Task<ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Object{{/returnType}}>> {{operationId}}AsyncWithHttpInfo ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}})
public async System.Threading.Tasks.Task<ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Object{{/returnType}}>> {{operationId}}AsyncWithHttpInfo ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = default({{{dataType}}}){{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}})
{
{{#allParams}}
{{#required}}

View File

@ -74,9 +74,9 @@ import 'package:{{pubName}}/api.dart';
{{/authMethods}}
{{/hasAuthMethods}}
var api_instance = new {{classname}}();
var api_instance = {{classname}}();
{{#allParams}}
var {{paramName}} = {{#isListContainer}}[{{/isListContainer}}{{#isBodyParam}}new {{dataType}}(){{/isBodyParam}}{{^isBodyParam}}{{{example}}}{{/isBodyParam}}{{#isListContainer}}]{{/isListContainer}}; // {{{dataType}}} | {{{description}}}
var {{paramName}} = {{#isListContainer}}[{{/isListContainer}}{{#isBodyParam}}{{dataType}}(){{/isBodyParam}}{{^isBodyParam}}{{{example}}}{{/isBodyParam}}{{#isListContainer}}]{{/isListContainer}}; // {{{dataType}}} | {{{description}}}
{{/allParams}}
try {

View File

@ -19,7 +19,7 @@ class {{classname}} {
{{#allParams}}
{{#required}}
if({{paramName}} == null) {
throw new ApiException(400, "Missing required param: {{paramName}}");
throw ApiException(400, "Missing required param: {{paramName}}");
}
{{/required}}
{{/allParams}}
@ -51,7 +51,7 @@ class {{classname}} {
if(contentType.startsWith("multipart/form-data")) {
bool hasFields = false;
MultipartRequest mp = new MultipartRequest(null, null);
MultipartRequest mp = MultipartRequest(null, null);
{{#formParams}}
{{^isFile}}
if ({{paramName}} != null) {
@ -89,7 +89,7 @@ class {{classname}} {
authNames);
if(response.statusCode >= 400) {
throw new ApiException(response.statusCode, _decodeBodyBytes(response));
throw ApiException(response.statusCode, _decodeBodyBytes(response));
} else if(response.body != null) {
{{#isListContainer}}
{{#returnType}}
@ -99,7 +99,7 @@ class {{classname}} {
{{^isListContainer}}
{{#isMapContainer}}
{{#returnType}}
return new {{{returnType}}}.from(apiClient.deserialize(_decodeBodyBytes(response), '{{{returnType}}}'));
return {{{returnType}}}.from(apiClient.deserialize(_decodeBodyBytes(response), '{{{returnType}}}'));
{{/returnType}};
{{/isMapContainer}}
{{^isMapContainer}}

View File

@ -45,9 +45,9 @@ import 'package:{{pubName}}/api.dart';
{{/authMethods}}
{{/hasAuthMethods}}
var api_instance = new {{classname}}();
var api_instance = {{classname}}();
{{#allParams}}
var {{paramName}} = {{#isListContainer}}[{{/isListContainer}}{{#isBodyParam}}new {{dataType}}(){{/isBodyParam}}{{^isBodyParam}}{{{example}}}{{/isBodyParam}}{{#isListContainer}}]{{/isListContainer}}; // {{{dataType}}} | {{{description}}}
var {{paramName}} = {{#isListContainer}}[{{/isListContainer}}{{#isBodyParam}}{{dataType}}(){{/isBodyParam}}{{^isBodyParam}}{{{example}}}{{/isBodyParam}}{{#isListContainer}}]{{/isListContainer}}; // {{{dataType}}} | {{{description}}}
{{/allParams}}
try {

View File

@ -5,7 +5,7 @@ import 'package:test/test.dart';
/// tests for {{classname}}
void main() {
var instance = new {{classname}}();
var instance = {{classname}}();
group('tests for {{classname}}', () {
{{#operation}}

View File

@ -36,7 +36,7 @@ class {{classname}} {
{{name}} = {{complexType}}.mapFromJson(json['{{baseName}}']);
{{/isMapContainer}}
{{^isMapContainer}}
{{name}} = new {{complexType}}.fromJson(json['{{baseName}}']);
{{name}} = {{complexType}}.fromJson(json['{{baseName}}']);
{{/isMapContainer}}
{{/isListContainer}}
{{/complexType}}
@ -49,7 +49,12 @@ class {{classname}} {
{{name}} = (json['{{baseName}}'] as Map).cast<String, {{items.datatype}}>();
{{/isMapContainer}}
{{^isMapContainer}}
{{#isDouble}}
{{name}} = json['{{baseName}}'].toDouble();
{{/isDouble}}
{{^isDouble}}
{{name}} = json['{{baseName}}'];
{{/isDouble}}
{{/isMapContainer}}
{{/isListContainer}}
{{/complexType}}
@ -81,13 +86,13 @@ class {{classname}} {
}
static List<{{classname}}> listFromJson(List<dynamic> json) {
return json == null ? new List<{{classname}}>() : json.map((value) => new {{classname}}.fromJson(value)).toList();
return json == null ? List<{{classname}}>() : json.map((value) => {{classname}}.fromJson(value)).toList();
}
static Map<String, {{classname}}> mapFromJson(Map<String, dynamic> json) {
var map = new Map<String, {{classname}}>();
var map = Map<String, {{classname}}>();
if (json != null && json.isNotEmpty) {
json.forEach((String key, dynamic value) => map[key] = new {{classname}}.fromJson(value));
json.forEach((String key, dynamic value) => map[key] = {{classname}}.fromJson(value));
}
return map;
}

View File

@ -5,7 +5,7 @@ import 'package:test/test.dart';
// tests for {{classname}}
void main() {
var instance = new Pet();
var instance = {{classname}}();
group('test {{classname}}', () {
{{#vars}}

View File

@ -0,0 +1,72 @@
{{=<% %>=}}
# OpenAPI Generated JavaScript/Express Server
## Overview
This server was generated using the [OpenAPI Generator](https://openapi-generator.tech) project. The code generator, and it's generated code allows you to develop your system with an API-First attitude, where the API contract is the anchor and definer of your project, and your code and business-logic aims to complete and comply to the terms in the API contract.
### prerequisites
- NodeJS >= 10.4
- NPM >= 6.10.0
The code was written on a mac, so assuming all should work smoothly on Linux-based computers. However, there is no reason not to run this library on Windows-based machines. If you find an OS-related problem, please open an issue and it will be resolved.
### Running the server
To run the server, run:
```
npm start
```
### View and test the API
You can see the API documentation, and check the available endpoints by going to http://localhost:3000/api-docs/. Endpoints that require security need to have security handlers configured before they can return a successful response. At this point they will return [ a response code of 401](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401).
##### At this stage the server does not support document body sent in xml format. Forms will be supported in the near future.
### Node version and guidelines
The code was written using Node version 10.6, and complies to the [Airbnb .eslint guiding rules](https://github.com/airbnb/javascript).
### Project Files
#### Root Directory:
In the root directory we have (besides package.json, config.js, and log files):
- **logger.js** - where we define the logger for the project. The project uses winston, but the purpose of this file is to enable users to change and modify their own logger behavior.
- **index.js** - This is the project's 'main' file, and from here we launch the application. This is a very short and concise file, and the idea behind launching from this short file is to allow use-cases of launching the server with different parameters (changing config and/or logger) without affecting the rest of the code.
- **expressServer.js** - The core of the Express.js server. This is where the express server is initialized, together with the OpenAPI validator, OpenAPI UI, and other libraries needed to start our server. If we want to add external links, that's where they would go. Our project uses the [express-openapi-validator](https://www.npmjs.com/package/express-openapi-validator) library that acts as a first step in the routing process - requests that are directed to paths defined in the `openapi.yaml` file are caught by this process, and it's parameters and bodyContent are validated against the schema. A successful result of this validation will be a new 'openapi' object added to the request. If the path requested is not part of the openapi.yaml file, the validator ignores the request and passes it on, as is, down the flow of the Express server.
#### api/
- **openapi.yaml** - This is the OpenAPI contract to which this server will comply. The file was generated using the codegen, and should contain everything needed to run the API Gateway - no references to external models/schemas.
#### utils/
Currently a single file:
- **openapiRouter.js** - This is where the routing to our back-end code happens. If the request object includes an ```openapi``` object, it picks up the following values (that are part of the ```openapi.yaml``` file): 'x-openapi-router-controller', and 'x-openapi-router-service'. These variables are names of files/classes in the controllers and services directories respectively. The operationId of the request is also extracted. The operationId is a method in the controller and the service that was generated as part of the codegen process. The routing process sends the request and response objects to the controller, which will extract the expected variables from the request, and send it to be processed by the service, returning the response from the service to the caller.
#### controllers/
After validating the request, and ensuring this belongs to our API gateway, we send the request to a `controller`, where the variables and parameters are extracted from the request and sent to the relevant `service` for processing. The `controller` handles the response from the `service` and builds the appropriate HTTP response to be sent back to the user.
- **index.js** - load all the controllers that were generated for this project, and export them to be used dynamically by the `openapiRouter.js`. If you would like to customize your controller, it is advised that you link to your controller here, and ensure that the codegen does not rewrite this file.
- **Controller.js** - The core processor of the generated controllers. The generated controllers are designed to be as slim and generic as possible, referencing to the `Controller.js` for the business logic of parsing the needed variables and arguments from the request, and for building the HTTP response which will be sent back. The `Controller.js` is a class with static methods.
- **{{x-openapi-router-controller}}.js** - auto-generated code, processing all the operations. The Controller is a class that is constructed with the service class it will be sending the request to. Every request defined by the `openapi.yaml` has an operationId. The operationId is the name of the method that will be called. Every method receives the request and response, and calls the `Controller.js` to process the request and response, adding the service method that should be called for the actual business-logic processing.
#### services/
This is where the API Gateway ends, and the unique business-logic of your application kicks in. Every endpoint in the `openapi.yaml` has a variable 'x-openapi-router-service', which is the name of the service class that is generated. The operationID of the endpoint is the name of the method that will be called. The generated code provides a simple promise with a try/catch clause. A successful operation ends with a call to the generic `Service.js` to build a successful response (payload and response code), and a failure will call the generic `Service.js` to build a response with an error object and the relevant response code. It is recommended to have the services be generated automatically once, and after the initial build add methods manually.
- **index.js** - load all the services that were generated for this project, and export them to be used dynamically by the `openapiRouter.js`. If you would like to customize your service, it is advised that you link to your controller here, and ensure that the codegen does not rewrite this file.
- **Service.js** - A utility class, very simple and thin at this point, with two static methods for building a response object for successful and failed results in the service operation. The default response code is 200 for success and 500 for failure. It is recommended to send more accurate response codes and override these defaults when relevant.
- **{{x-openapi-router-service}}.js** - auto-generated code, providing a stub Promise for each operationId defined in the `openapi.yaml`. Each method receives the variables that were defined in the `openapi.yaml` file, and wraps a Promise in a try/catch clause. The Promise resolves both success and failure in a call to the `Service.js` utility class for building the appropriate response that will be sent back to the Controller and then to the caller of this endpoint.
#### tests/
- **serverTests.js** - basic server validation tests, checking that the server is up, that a call to an endpoint within the scope of the `openapi.yaml` file returns 200, that a call to a path outside that scope returns 200 if it exists and a 404 if not.
- **routingTests.js** - Runs through all the endpoints defined in the `openapi.yaml`, and constructs a dummy request to send to the server. Confirms that the response code is 200. At this point requests containing xml or formData fail - currently they are not supported in the router.
- **additionalEndpointsTests.js** - A test file for all the endpoints that are defined outside the openapi.yaml scope. Confirms that these endpoints return a successful 200 response.
Future tests should be written to ensure that the response of every request sent should conform to the structure defined in the `openapi.yaml`. This test will fail 100% initially, and the job of the development team will be to clear these tests.
#### models/
Currently a concept awaiting feedback. The idea is to have the objects defined in the openapi.yaml act as models which are passed between the different modules. This will conform the programmers to interact using defined objects, rather than loosley-defined JSON objects. Given the nature of JavaScript progrmmers, who want to work with their own bootstrapped parameters, this concept might not work. Keeping this here for future discussion and feedback.

View File

@ -0,0 +1,30 @@
const ExpressServer = require('./expressServer');
const logger = require('./logger');
// const swaggerRouter = require('./utils/swaggerRouter');
class App {
constructor(config) {
this.config = config;
}
async launch() {
try {
this.expressServer = new ExpressServer(this.config.URL_PORT, this.config.OPENAPI_YAML);
// this.expressServer.app.use(swaggerRouter());
await this.expressServer.launch();
logger.info('Express server running');
} catch (error) {
logger.error(error);
await this.close();
}
}
async close() {
if (this.expressServer !== undefined) {
await this.expressServer.close();
logger.info(`Server shut down on port ${this.config.URL_PORT}`);
}
}
}
module.exports = App;

View File

@ -0,0 +1,12 @@
const path = require('path');
const config = {
ROOT_DIR: __dirname,
URL_PORT: 3000,
URL_PATH: 'http://localhost',
BASE_VERSION: 'v2',
CONTROLLER_DIRECTORY: path.join(__dirname, 'controllers'),
};
config.OPENAPI_YAML = path.join(config.ROOT_DIR, 'api', 'openapi.yaml');
config.FULL_PATH = `${config.URL_PATH}:${config.URL_PORT}/${config.BASE_VERSION}`;
module.exports = config;

View File

@ -0,0 +1,18 @@
const Controller = require('./Controller');
class {{{classname}}}Controller {
constructor(Service) {
this.service = Service;
}
{{#operations}}
{{#operation}}
async {{operationId}}(request, response) {
await Controller.handleRequest(request, response, this.service.{{operationId}});
}
{{/operation}}
}
module.exports = {{classname}}Controller;
{{/operations}}

View File

@ -0,0 +1,72 @@
const logger = require('../logger');
class Controller {
static sendResponse(response, payload) {
/**
* The default response-code is 200. We want to allow to change that. in That case,
* payload will be an object consisting of a code and a payload. If not customized
* send 200 and the payload as received in this method.
*/
response.status(payload.code || 200);
const responsePayload = payload.payload !== undefined ? payload.payload : payload;
if (responsePayload instanceof Object) {
response.json(responsePayload);
} else {
response.end(responsePayload);
}
}
static sendError(response, error) {
response.status(error.code || 500);
if (error.error instanceof Object) {
response.json(error.error);
} else {
response.end(error.error || error.message);
}
}
static collectFiles(request) {
logger.info('Checking if files are expected in schema');
if (request.openapi.schema.requestBody !== undefined) {
const [contentType] = request.headers['content-type'].split(';');
if (contentType === 'multipart/form-data') {
const contentSchema = request.openapi.schema.requestBody.content[contentType].schema;
Object.entries(contentSchema.properties).forEach(([name, property]) => {
if (property.type === 'string' && ['binary', 'base64'].indexOf(property.format) > -1) {
request.body[name] = request.files.find(file => file.fieldname === name);
}
});
} else if (request.openapi.schema.requestBody.content[contentType] !== undefined
&& request.files !== undefined) {
[request.body] = request.files;
}
}
}
static collectRequestParams(request) {
this.collectFiles(request);
const requestParams = {};
if (request.openapi.schema.requestBody !== undefined) {
requestParams.body = request.body;
}
request.openapi.schema.parameters.forEach((param) => {
if (param.in === 'path') {
requestParams[param.name] = request.openapi.pathParams[param.name];
} else if (param.in === 'query') {
requestParams[param.name] = request.query[param.name];
}
});
return requestParams;
}
static async handleRequest(request, response, serviceOperation) {
try {
const serviceResponse = await serviceOperation(this.collectRequestParams(request));
Controller.sendResponse(response, serviceResponse);
} catch (error) {
Controller.sendError(response, error);
}
}
}
module.exports = Controller;

View File

@ -0,0 +1,25 @@
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
{{#-first}}
const {{classname}}Controller = require('./{{classname}}Controller');
{{/-first}}
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
module.exports = {
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
{{#-first}}
{{classname}}Controller,
{{/-first}}
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
};

View File

@ -0,0 +1,72 @@
const Service = require('../services/Service');
const testItems = require('../tests/testFiles/testItems.json');
class TestService {
static testGetController() {
return new Promise(
async (resolve, reject) => {
try {
resolve(Service.successResponse(
testItems,
200,
));
} catch (e) {
const message = e.getMessage() || 'Could not get items. Server error';
reject(Service.rejectResponse(message, 500));
}
},
);
sendResponse(request, response) {
response.status(200);
const objectToReturn = {};
Object.keys(request.swagger.paramValues).forEach((key) => {
const val = request.swagger.paramValues[key];
if (val instanceof Object) {
objectToReturn[key] = val.originalname || val.name || val;
} else {
objectToReturn[key] = request.swagger.paramValues[key];
}
});
response.json(objectToReturn);
}
confirmRouteGetSingle(request, response) {
this.sendResponse(request, response);
}
confirmRouteGetMany(request, response) {
this.sendResponse(request, response);
}
confirmRoutePost(request, response) {
this.sendResponse(request, response);
}
confirmRoutePut(request, response) {
this.sendResponse(request, response);
}
async testGetController(request, response) {
await Controller.handleRequest(request, response, this.service.testGetController);
}
async testPostController(request, response) {
await Controller.handleRequest(request, response, this.service.testPostController);
}
async testPutController(request, response) {
await Controller.handleRequest(request, response, this.service.testPutController);
}
async testDeleteController(request, response) {
await Controller.handleRequest(request, response, this.service.testDeleteController);
}
async testFindByIdController(request, response) {
await Controller.handleRequest(request, response, this.service.testFindByIdController);
}
}
module.exports = TestController;

View File

@ -0,0 +1,8 @@
// Use this file as a starting point for your project's .eslintrc.
// Copy this file, and add rule overrides as needed.
{
"extends": "airbnb",
"rules": {
"no-console": "off"
}
}

View File

@ -0,0 +1,93 @@
// const { Middleware } = require('swagger-express-middleware');
const path = require('path');
const swaggerUI = require('swagger-ui-express');
const yamljs = require('yamljs');
const express = require('express');
const cors = require('cors');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const { OpenApiValidator } = require('express-openapi-validator');
const openapiRouter = require('./utils/openapiRouter');
const logger = require('./logger');
class ExpressServer {
constructor(port, openApiYaml) {
this.port = port;
this.app = express();
this.openApiPath = openApiYaml;
this.schema = yamljs.load(openApiYaml);
this.setupMiddleware();
}
setupMiddleware() {
// this.setupAllowedMedia();
this.app.use(cors());
this.app.use(bodyParser.json());
this.app.use(express.json());
this.app.use(express.urlencoded({ extended: false }));
this.app.use(cookieParser());
this.app.use('/spec', express.static(path.join(__dirname, 'api')));
this.app.get('/hello', (req, res) => res.send('Hello World. path: '+this.openApiPath));
// this.app.get('/spec', express.static(this.openApiPath));
this.app.use('/api-docs', swaggerUI.serve, swaggerUI.setup(this.schema));
this.app.get('/login-redirect', (req, res) => {
res.status(200);
res.json(req.query);
});
this.app.get('/oauth2-redirect.html', (req, res) => {
res.status(200);
res.json(req.query);
});
new OpenApiValidator({
apiSpecPath: this.openApiPath,
}).install(this.app);
this.app.use(openapiRouter());
this.app.get('/', (req, res) => {
res.status(200);
res.end('Hello World');
});
}
addErrorHandler() {
this.app.use('*', (req, res) => {
res.status(404);
res.send(JSON.stringify({ error: `path ${req.baseUrl} doesn't exist` }));
});
/**
* suppressed eslint rule: The next variable is required here, even though it's not used.
*
** */
// eslint-disable-next-line no-unused-vars
this.app.use((error, req, res, next) => {
const errorResponse = error.error || error.errors || error.message || 'Unknown error';
res.status(error.status || 500);
res.type('json');
res.json({ error: errorResponse });
});
}
async launch() {
return new Promise(
async (resolve, reject) => {
try {
this.addErrorHandler();
this.server = await this.app.listen(this.port, () => {
console.log(`server running on port ${this.port}`);
resolve(this.server);
});
} catch (error) {
reject(error);
}
},
);
}
async close() {
if (this.server !== undefined) {
await this.server.close();
console.log(`Server on port ${this.port} shut down`);
}
}
}
module.exports = ExpressServer;

View File

@ -0,0 +1,28 @@
const config = require('./config');
const logger = require('./logger');
const ExpressServer = require('./expressServer');
// const App = require('./app');
// const app = new App(config);
// app.launch()
// .then(() => {
// logger.info('Server launched');
// })
// .catch((error) => {
// logger.error('found error, shutting down server');
// app.close()
// .catch(closeError => logger.error(closeError))
// .finally(() => logger.error(error));
// });
const launchServer = async () => {
try {
this.expressServer = new ExpressServer(config.URL_PORT, config.OPENAPI_YAML);
await this.expressServer.launch();
logger.info('Express server running');
} catch (error) {
logger.error(error);
await this.close();
}
};
launchServer().catch(e => logger.error(e));

View File

@ -0,0 +1,17 @@
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
defaultMeta: { service: 'user-service' },
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' }),
],
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({ format: winston.format.simple() }));
}
module.exports = logger;

View File

@ -0,0 +1 @@
{{{openapi-yaml}}}

View File

@ -0,0 +1,46 @@
{
"name": "openapi-petstore",
"version": "1.0.0",
"description": "This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.",
"main": "index.js",
"scripts": {
"prestart": "npm install",
"start": "node index.js"
},
"keywords": [
"openapi-generator",
"openapi"
],
"license": "Unlicense",
"private": true,
"dependencies": {
"body-parser": "^1.19.0",
"connect": "^3.2.0",
"cookie-parser": "^1.4.4",
"cors": "^2.8.5",
"express": "^4.16.4",
"express-openapi-validator": "^1.0.0",
"js-yaml": "^3.3.0",
"jstoxml": "^1.5.0",
"ono": "^5.0.1",
"openapi-sampler": "^1.0.0-beta.15",
"swagger-express-middleware": "^2.0.2",
"swagger-tools": "^0.10.4",
"swagger-ui-express": "^4.0.2",
"winston": "^3.2.1",
"yamljs": "^0.3.0",
"mocha": "^6.1.4",
"axios": "^0.19.0",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"eslint": "^5.16.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-plugin-import": "^2.17.2",
"form-data": "^2.3.3"
},
"eslintConfig": {
"env": {
"node": true
}
}
}

View File

@ -0,0 +1,45 @@
/* eslint-disable no-unused-vars */
const Service = require('./Service');
class {{{classname}}}Service {
{{#operations}}
{{#operation}}
/**
{{#summary}}
* {{{summary}}}
{{/summary}}
{{#notes}}
* {{{notes}}}
{{/notes}}
*
{{#allParams}}
* {{paramName}} {{{dataType}}} {{{description}}}{{^required}} (optional){{/required}}
{{/allParams}}
{{^returnType}}
* no response value expected for this operation
{{/returnType}}
{{#returnType}}
* returns {{{returnType}}}
{{/returnType}}
**/
static {{{operationId}}}({{#allParams}}{{#-first}}{ {{/-first}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{#-last}} }{{/-last}}{{/allParams}}) {
return new Promise(
async (resolve) => {
try {
resolve(Service.successResponse(''));
} catch (e) {
resolve(Service.rejectResponse(
e.message || 'Invalid input',
e.status || 405,
));
}
},
);
}
{{/operation}}
}
module.exports = {{{classname}}}Service;
{{/operations}}

View File

@ -0,0 +1,11 @@
class Service {
static rejectResponse(error, code = 500) {
return { error, code };
}
static successResponse(payload, code = 200) {
return { payload, code };
}
}
module.exports = Service;

View File

@ -0,0 +1,25 @@
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
{{#-first}}
const {{classname}}Service = require('./{{classname}}Service');
{{/-first}}
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
module.exports = {
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
{{#-first}}
{{classname}}Service,
{{/-first}}
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
};

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