diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b0ba09e394f..e84227bd6b5 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,7 +2,8 @@ - [ ] Read the [contribution guidelines](https://github.com/swagger-api/swagger-codegen/blob/master/CONTRIBUTING.md). - [ ] Ran the shell script under `./bin/` to update Petstore sample so that CIs can verify the change. (For instance, only need to run `./bin/{LANG}-petstore.sh` and `./bin/security/{LANG}-petstore.sh` if updating the {LANG} (e.g. php, ruby, python, etc) code generator or {LANG} client's mustache templates). Windows batch files can be found in `.\bin\windows\`. -- [ ] Filed the PR against the correct branch: master for non-breaking changes and `2.3.0` branch for breaking (non-backward compatible) changes. +- [ ] Filed the PR against the correct branch: `3.0.0` branch for changes related to OpenAPI spec 3.0. Default: `master`. +- [ ] Copied the [technical committee](https://github.com/swagger-api/swagger-codegen/#swagger-codegen-technical-committee) to review the pull request if your PR is targeting a particular programming language. ### Description of the PR diff --git a/.gitignore b/.gitignore index 0d24b8c983a..c31b8389c19 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.vscode *.iml out/ *.ipr @@ -73,6 +74,7 @@ samples/client/petstore/java/okhttp-gson/build/ samples/client/petstore/java/feign/build/ samples/client/petstore/java/retrofit/build/ samples/client/petstore/java/retrofit2/build/ +samples/client/petstore/java/retrofit2/hello.txt samples/client/petstore/java/retrofit2rx/build/ samples/client/petstore/java/default/build/ samples/client/petstore/scala/build/ @@ -88,6 +90,11 @@ samples/client/petstore/silex/SwaggerServer/venodr/ **/vendor/ **/composer.lock +#PHP-Symfony +samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/cache/ +samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/logs/ + + # Perl samples/client/petstore/perl/deep_module_test/ @@ -158,7 +165,25 @@ samples/client/petstore/typescript-fetch/**/dist/ samples/client/petstore/typescript-fetch/**/typings samples/client/petstore/typescript-angular2/npm/npm-debug.log samples/client/petstore/typescript-node/npm/npm-debug.log +samples/client/petstore/typescript-aurelia/**/dist/ +samples/client/petstore/typescript-angular/tsd-debug.log # aspnetcore samples/server/petstore/aspnetcore/.vs/ effective.pom +# kotlin +samples/client/petstore/kotlin/src/main/kotlin/test/ +\? + +# haskell +.stack-work +.cabal-sandbox +cabal.project.local + +# R +.Rproj.user + +# elixir +samples/client/petstore/elixir/_build/ +samples/client/petstore/elixir/deps/ +samples/client/petstore/elixir/mix.lock diff --git a/.java-version b/.java-version new file mode 100644 index 00000000000..010303b83ba --- /dev/null +++ b/.java-version @@ -0,0 +1 @@ +oracle64-1.8.0.152 diff --git a/.travis.yml b/.travis.yml index 0657f524586..f396f23ccd4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ sudo: required -language: objective-c -osx_image: xcode8.2 +language: java +jdk: + - oraclejdk8 + cache: directories: - $HOME/.m2 @@ -22,10 +24,9 @@ cache: - $HOME/samples/client/petstore/typescript-fetch/npm/with-npm-version/typings - $HOME/samples/client/petstore/typescript-angular/node_modules - $HOME/samples/client/petstore/typescript-angular/typings - - $HOME/.cocoapods/repos/master -# note: docker is not yet supported in iOS build -#services: -# - docker + +services: + - docker # comment out the host table change to use the public petstore server addons: @@ -33,55 +34,38 @@ addons: - petstore.swagger.io before_install: - - export SW=`pwd` - - rvm list - - rvm use 2.2.5 - - gem environment - - gem install bundler -N --no-ri --no-rdoc - - gem install cocoapods -v 1.2.1 -N --no-ri --no-rdoc - - gem install xcpretty -N --no-ri --no-rdoc - - pod --version - # comment out below to avoid errors - #- pod repo update - - pod setup --silent > /dev/null + # required when sudo: required for the Ruby petstore tests + - gem install bundler - npm install -g typescript + - npm install -g npm - npm config set registry http://registry.npmjs.org/ - - brew install sbt - - brew install leiningen - - brew install bats - - brew install curl - - brew install python3 - - pip install virtualenv - # start local petstore server - - git clone -b docker --single-branch https://github.com/wing328/swagger-samples - - cd swagger-samples/java/java-jersey-jaxrs - - sudo mvn jetty:run & - - cd $SW - # NOTE: iOS build not support docker at the moment + - sudo pip install virtualenv # to run petstore server locally via docker - #- docker pull swaggerapi/petstore - #- docker run -d -e SWAGGER_HOST=http://petstore.swagger.io -e SWAGGER_BASE_PATH=/v2 -p 80:8080 swaggerapi/petstore - #- docker ps -a + - docker pull swaggerapi/petstore + - docker run -d -e SWAGGER_HOST=http://petstore.swagger.io -e SWAGGER_BASE_PATH=/v2 -p 80:8080 swaggerapi/petstore + - docker ps -a + # Add bats test framework and cURL for Bash script integration tests + - sudo add-apt-repository ppa:duggan/bats --yes + - sudo apt-get update -qq + - sudo apt-get install -qq bats + - sudo apt-get install -qq curl + # comment out below as installation failed in travis # Add rebar3 build tool and recent Erlang/OTP for Erlang petstore server tests. # - Travis CI does not support rebar3 [yet](https://github.com/travis-ci/travis-ci/issues/6506#issuecomment-275189490). # - 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" # show host table to confirm petstore.swagger.io is mapped to localhost - cat /etc/hosts # show java version - java -version - # show brew version - - brew --version - # show xcpretty version - - xcpretty -v - # show go version - - go version install: # Add Godeps dependencies to GOPATH and PATH - - eval "$(curl -sL https://raw.githubusercontent.com/travis-ci/gimme/master/gimme | GIMME_GO_VERSION=1.4 bash)" - - export GOPATH="${TRAVIS_BUILD_DIR}/Godeps/_workspace" - - export PATH="${TRAVIS_BUILD_DIR}/Godeps/_workspace/bin:$PATH" + - eval "$(curl -sL https://raw.githubusercontent.com/travis-ci/gimme/master/gimme | GIMME_GO_VERSION=1.4 bash)" + - export GOPATH="${TRAVIS_BUILD_DIR}/Godeps/_workspace" + - export PATH="${TRAVIS_BUILD_DIR}/Godeps/_workspace/bin:$PATH" script: # fail fast @@ -94,17 +78,18 @@ script: - /bin/bash ./bin/utils/detect_tab_in_java_class.sh # run integration tests defined in maven pom.xml - mvn -q --batch-mode verify -Psamples -### docker-related tasks have been moved to CircleCI + # Below has been moved to CircleCI # docker: build generator image and push to Docker Hub #- if [ $DOCKER_HUB_USERNAME ]; then docker login --email=$DOCKER_HUB_EMAIL --username=$DOCKER_HUB_USERNAME --password=$DOCKER_HUB_PASSWORD && docker build -t $DOCKER_GENERATOR_IMAGE_NAME ./modules/swagger-generator && if [ ! -z "$TRAVIS_TAG" ]; then docker tag $DOCKER_GENERATOR_IMAGE_NAME:latest $DOCKER_GENERATOR_IMAGE_NAME:$TRAVIS_TAG; fi && if [ ! -z "$TRAVIS_TAG" ] || [ "$TRAVIS_BRANCH" = "master" ]; then docker push $DOCKER_GENERATOR_IMAGE_NAME; fi; fi ## docker: build cli image and push to Docker Hub #- if [ $DOCKER_HUB_USERNAME ]; then docker login --email=$DOCKER_HUB_EMAIL --username=$DOCKER_HUB_USERNAME --password=$DOCKER_HUB_PASSWORD && docker build -t $DOCKER_CODEGEN_CLI_IMAGE_NAME ./modules/swagger-codegen-cli && if [ ! -z "$TRAVIS_TAG" ]; then docker tag $DOCKER_CODEGEN_CLI_IMAGE_NAME:latest $DOCKER_CODEGEN_CLI_IMAGE_NAME:$TRAVIS_TAG; fi && if [ ! -z "$TRAVIS_TAG" ] || [ "$TRAVIS_BRANCH" = "master" ]; then docker push $DOCKER_CODEGEN_CLI_IMAGE_NAME; fi; fi -#env: -# - DOCKER_GENERATOR_IMAGE_NAME=swaggerapi/swagger-generator DOCKER_CODEGEN_CLI_IMAGE_NAME=swaggerapi/swagger-codegen-cli after_success: # push a snapshot version to maven repo - if [ $SONATYPE_USERNAME ] && [ -z $TRAVIS_TAG ] && [ "$TRAVIS_BRANCH" = "master" ]; then mvn clean deploy --settings .travis/settings.xml; - echo "Finished mvn clean deploy"; + echo "Finished mvn clean deploy for $TRAVIS_BRANCH"; fi; + +env: + - DOCKER_GENERATOR_IMAGE_NAME=swaggerapi/swagger-generator DOCKER_CODEGEN_CLI_IMAGE_NAME=swaggerapi/swagger-codegen-cli diff --git a/.travis.yml.bash b/.travis.yml.bash new file mode 100644 index 00000000000..f224cac3958 --- /dev/null +++ b/.travis.yml.bash @@ -0,0 +1,37 @@ +sudo: required +language: java +jdk: + - openjdk8 + +cache: + directories: + - $HOME/.m2 + - $HOME/.ivy2 + +services: + - docker + +addons: + hosts: + - petstore.swagger.io + +before_install: + # to run petstore server locally via docker + - docker pull swaggerapi/petstore + - docker run -d -e SWAGGER_HOST=http://petstore.swagger.io -e SWAGGER_BASE_PATH=/v2 -p 80:8080 swaggerapi/petstore + - docker ps -a + # Add bats test framework and cURL for Bash script integration tests + - sudo add-apt-repository ppa:duggan/bats --yes + - sudo apt-get update -qq + - sudo apt-get install -qq bats + - sudo apt-get install -qq curl + + # show host table to confirm petstore.swagger.io is mapped to localhost + - cat /etc/hosts + +script: + # fail fast + - set -e + # run integration tests defined in maven pom.xml + - cp pom.xml.bash pom.xml + - mvn --batch-mode verify -Psamples diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 970257aba6a..ab603bb2316 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,6 +35,7 @@ For a list of variables available in the template, please refer to this [page](h ### Style guide Code change should conform to the programming style guide of the respective languages: +- Ada: https://en.wikibooks.org/wiki/Ada_Style_Guide/Source_Code_Presentation - Android: https://source.android.com/source/code-style.html - Bash: https://github.com/bahamas10/bash-style-guide - C#: https://msdn.microsoft.com/en-us/library/vstudio/ff926074.aspx @@ -42,19 +43,23 @@ Code change should conform to the programming style guide of the respective lang - C++ (Tizen): https://wiki.tizen.org/Native_Platform_Coding_Idiom_and_Style_Guide#C.2B.2B_Coding_Style - Clojure: https://github.com/bbatsov/clojure-style-guide - Elixir: https://github.com/christopheradams/elixir_style_guide +- Eiffel: https://www.eiffel.org/doc/eiffel/Coding%20Standards - Erlang: https://github.com/inaka/erlang_guidelines - Haskell: https://github.com/tibbe/haskell-style-guide/blob/master/haskell-style.md - Java: https://google.github.io/styleguide/javaguide.html - JavaScript: https://github.com/airbnb/javascript/ +- Kotlin: https://kotlinlang.org/docs/reference/coding-conventions.html - Groovy: http://groovy-lang.org/style-guide.html - Go: https://github.com/golang/go/wiki/CodeReviewComments - ObjC: https://github.com/NYTimes/objective-c-style-guide - Perl: http://perldoc.perl.org/perlstyle.html - PHP: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md +- PowerShell: https://msdn.microsoft.com/en-us/library/dd878270(v=vs.85).aspx - Python: https://www.python.org/dev/peps/pep-0008/ +- R: https://google.github.io/styleguide/Rguide.xml - Ruby: https://github.com/bbatsov/ruby-style-guide - Scala: http://docs.scala-lang.org/style/ -- Swift: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html +- Swift: [Apple Developer](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html) - TypeScript: https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines For other languages, feel free to suggest. @@ -87,3 +92,4 @@ To start the CI tests, you can run `mvn verify -Psamples` (assuming you've all t - File a PR with meaningful title, description and commit messages. A good example is [PR-3306](https://github.com/swagger-api/swagger-codegen/pull/3306) - Recommended git settings - `git config --global core.autocrlf input` to tell Git convert CRLF to LF on commit but not the other way around +- To close an issue (e.g. issue 1542) automatically after a PR is merged, use keywords "fix", "close", "resolve" in the PR description, e.g. `fix #1542`. (Ref: [closing issues using keywords](https://help.github.com/articles/closing-issues-using-keywords/)) diff --git a/Dockerfile b/Dockerfile index ec291fef4af..0e32bf8ae55 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,22 +1,31 @@ FROM jimschubert/8-jdk-alpine-mvn:1.0 -ENV GEN_DIR /opt/swagger-codegen - RUN set -x && \ apk add --no-cache bash -RUN mkdir /opt +ENV GEN_DIR /opt/swagger-codegen +WORKDIR ${GEN_DIR} +VOLUME ${MAVEN_HOME}/.m2/repository -ADD . ${GEN_DIR} +# Required from a licensing standpoint +COPY ./LICENSE ${GEN_DIR} -VOLUME ${MAVEN_HOME}/.m2/repository +# Required to compile swagger-codegen +COPY ./google_checkstyle.xml ${GEN_DIR} -WORKDIR ${GEN_DIR} +# Modules are copied individually here to allow for caching of docker layers between major.minor versions +# NOTE: swagger-generator is not included here, it is available as swaggerapi/swagger-generator +COPY ./modules/swagger-codegen-maven-plugin ${GEN_DIR}/modules/swagger-codegen-maven-plugin +COPY ./modules/swagger-codegen-cli ${GEN_DIR}/modules/swagger-codegen-cli +COPY ./modules/swagger-codegen ${GEN_DIR}/modules/swagger-codegen +COPY ./pom.xml ${GEN_DIR} +# Pre-compile swagger-codegen-cli RUN mvn -am -pl "modules/swagger-codegen-cli" package +# This exists at the end of the file to benefit from cached layers when modifying docker-entrypoint.sh. COPY docker-entrypoint.sh /usr/local/bin/ ENTRYPOINT ["docker-entrypoint.sh"] -CMD ["build"] +CMD ["help"] diff --git a/README.md b/README.md index 74249ca6a21..90d496b155a 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,19 @@ # Swagger Code Generator -- Master: [![Build Status](https://img.shields.io/travis/swagger-api/swagger-codegen/master.svg?label=Petstore%20Integration%20Test)](https://travis-ci.org/swagger-api/swagger-codegen) +- Master (2.3.0): [![Build Status](https://img.shields.io/travis/swagger-api/swagger-codegen/master.svg?label=Petstore%20Integration%20Test)](https://travis-ci.org/swagger-api/swagger-codegen) [![Run Status](https://img.shields.io/shippable/5782588a3be4f4faa56c5bea.svg?label=Mustache%20Template%20Test)](https://app.shippable.com/projects/5782588a3be4f4faa56c5bea) [![Windows Test](https://ci.appveyor.com/api/projects/status/github/swagger-api/swagger-codegen?branch=master&svg=true&passingText=Windows%20Test%20-%20OK&failingText=Windows%20Test%20-%20Fails)](https://ci.appveyor.com/project/WilliamCheng/swagger-codegen-wh2wu) [![Java Test](https://circleci.com/gh/swagger-api/swagger-codegen.svg?style=shield)](https://circleci.com/gh/swagger-api/swagger-codegen) -- 2.3.0: [![Build Status](https://img.shields.io/travis/swagger-api/swagger-codegen/2.3.0.svg?label=Petstore%20Integration%20Test)](https://travis-ci.org/swagger-api/swagger-codegen) -[![Run Status](https://img.shields.io/shippable/5782588a3be4f4faa56c5bea/2.3.0.svg?label=Mustache%20Template%20Test)](https://app.shippable.com/projects/5782588a3be4f4faa56c5bea) -[![Windows Test](https://ci.appveyor.com/api/projects/status/github/swagger-api/swagger-codegen?branch=2.3.0&svg=true&passingText=Windows%20Test%20-%20OK&failingText=Windows%20Test%20-%20Fails)](https://ci.appveyor.com/project/WilliamCheng/swagger-codegen-wh2wu) -[![Java Test](https://circleci.com/gh/swagger-api/swagger-codegen/tree/2.3.0.svg?style=shield)](https://circleci.com/gh/swagger-api/swagger-codegen) +- 3.0.0: [![Build Status](https://img.shields.io/travis/swagger-api/swagger-codegen/3.0.0.svg?label=Petstore%20Integration%20Test)](https://travis-ci.org/swagger-api/swagger-codegen) +[![Run Status](https://img.shields.io/shippable/5782588a3be4f4faa56c5bea/3.0.0.svg?label=Mustache%20Template%20Test)](https://app.shippable.com/projects/5782588a3be4f4faa56c5bea) +[![Windows Test](https://ci.appveyor.com/api/projects/status/github/swagger-api/swagger-codegen?branch=3.0.0&svg=true&passingText=Windows%20Test%20-%20OK&failingText=Windows%20Test%20-%20Fails)](https://ci.appveyor.com/project/WilliamCheng/swagger-codegen-wh2wu) +[![Java Test](https://circleci.com/gh/swagger-api/swagger-codegen/tree/3.0.0.svg?style=shield)](https://circleci.com/gh/swagger-api/swagger-codegen) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.swagger/swagger-codegen-project/badge.svg?style=plastic)](https://maven-badges.herokuapp.com/maven-central/io.swagger/swagger-codegen-project) [![PR Stats](http://issuestats.com/github/swagger-api/swagger-codegen/badge/pr)](http://issuestats.com/github/swagger-api/swagger-codegen) [![Issue Stats](http://issuestats.com/github/swagger-api/swagger-codegen/badge/issue)](http://issuestats.com/github/swagger-api/swagger-codegen) -:star::star::star: If you would like to contribute, please refer to [guidelines](https://github.com/swagger-api/swagger-codegen/blob/master/CONTRIBUTING.md) and a list of [open tasks](https://github.com/swagger-api/swagger-codegen/issues?q=is%3Aopen+is%3Aissue+label%3A%22Need+community+contribution%22).:star::star::star: +:star::star::star: If you would like to contribute, please refer to [guidelines](https://github.com/swagger-api/swagger-codegen/blob/master/CONTRIBUTING.md) and a list of [open tasks](https://github.com/swagger-api/swagger-codegen/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22).:star::star::star: :notebook_with_decorative_cover: For more information, please refer to the [Wiki page](https://github.com/swagger-api/swagger-codegen/wiki) and [FAQ](https://github.com/swagger-api/swagger-codegen/wiki/FAQ) :notebook_with_decorative_cover: @@ -24,8 +24,8 @@ ## Overview This is the swagger codegen project, which allows generation of API client libraries (SDK generation), server stubs and documentation automatically given an [OpenAPI Spec](https://github.com/OAI/OpenAPI-Specification). Currently, the following languages/frameworks are supported: -- **API clients**: **ActionScript**, **Apex**, **Bash**, **C#** (.net 2.0, 4.0 or later), **C++** (cpprest, Qt5, Tizen), **Clojure**, **Dart**, **Elixir**, **Eiffel**, **Go**, **Groovy**, **Haskell**, **Java** (Jersey1.x, Jersey2.x, OkHttp, Retrofit1.x, Retrofit2.x, Feign, RestTemplate, RESTEasy), **Kotlin**, **Node.js** (ES5, ES6, AngularJS with Google Closure Compiler annotations) **Objective-C**, **Perl**, **PHP**, **PowerShell**, **Python**, **Ruby**, **Scala**, **Swift** (2.x, 3.x, 4.x), **Typescript** (Angular1.x, Angular2.x, Fetch, jQuery, Node) -- **Server stubs**: **C#** (ASP.NET Core, NancyFx), **C++** (Pistache, Restbed), **Erlang**, **Go**, **Haskell**, **Java** (MSF4J, Spring, Undertow, JAX-RS: CDI, CXF, Inflector, RestEasy, Play Framework), **PHP** (Lumen, Slim, Silex, [Symfony](https://symfony.com/), [Zend Expressive](https://github.com/zendframework/zend-expressive)), **Python** (Flask), **NodeJS**, **Ruby** (Sinatra, Rails5), **Scala** ([Finch](https://github.com/finagle/finch), Scalatra) +- **API clients**: **ActionScript**, **Ada**, **Apex**, **Bash**, **C#** (.net 2.0, 3.5 or later), **C++** (cpprest, Qt5, Tizen), **Clojure**, **Dart**, **Elixir**, **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), **Kotlin**, **Lua**, **Node.js** (ES5, ES6, AngularJS with Google Closure Compiler annotations) **Objective-C**, **Perl**, **PHP**, **PowerShell**, **Python**, **R**, **Ruby**, **Rust**, **Scala** (akka, http4s, swagger-async-httpclient), **Swift** (2.x, 3.x, 4.x), **Typescript** (Angular1.x, Angular2.x, Fetch, jQuery, Node) +- **Server stubs**: **C#** (ASP.NET Core, NancyFx), **C++** (Pistache, Restbed), **Erlang**, **Go**, **Haskell** (Servant), **Java** (MSF4J, Spring, Undertow, JAX-RS: CDI, CXF, Inflector, RestEasy, Play Framework), **PHP** (Lumen, Slim, Silex, [Symfony](https://symfony.com/), [Zend Expressive](https://github.com/zendframework/zend-expressive)), **Python** (Flask), **NodeJS**, **Ruby** (Sinatra, Rails5), **Rust**, **Scala** ([Finch](https://github.com/finagle/finch), [Lagom](https://github.com/lagom/lagom), Scalatra) - **API documentation generators**: **HTML**, **Confluence Wiki** - **Configuration files**: [**Apache2**](https://httpd.apache.org/) - **Others**: **JMeter** @@ -43,7 +43,7 @@ Check out [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) for addit - [OS X Users](#os-x-users) - [Building](#building) - [Docker](#docker) - - [Build and run](#build-and-run-using-docker) + - [Development in Docker](#development-in-docker) - [Run docker in Vagrant](#run-docker-in-vagrant) - [Public Docker image](#public-docker-image) - [Homebrew](#homebrew) @@ -62,13 +62,15 @@ Check out [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) for addit - [To build a server stub](#to-build-a-server-stub) - [To build the codegen library](#to-build-the-codegen-library) - [Workflow Integration](#workflow-integration) + - [Maven Integration](#maven-integration) + - [Gradle Integration](#gradle-integration) - [Github Integration](#github-integration) - [Online Generators](#online-generators) - [Guidelines for Contribution](https://github.com/swagger-api/swagger-codegen/wiki/Guidelines-for-Contribution) - [Companies/Projects using Swagger Codegen](#companiesprojects-using-swagger-codegen) - [Presentations/Videos/Tutorials/Books](#presentationsvideostutorialsbooks) - [Swagger Codegen Core Team](#swagger-codegen-core-team) - - [Swagger Codegen Evangelist](#swagger-codegen-evangelist) + - [Swagger Codegen Technical Committee](#swagger-codegen-technical-committee) - [License](#license) @@ -77,26 +79,29 @@ The OpenAPI Specification has undergone 3 revisions since initial creation in 20 Swagger Codegen Version | Release Date | OpenAPI Spec compatibility | Notes -------------------------- | ------------ | -------------------------- | ----- -2.3.0 (upcoming minor release) [SNAPSHOT](https://oss.sonatype.org/content/repositories/snapshots/io/swagger/swagger-codegen-cli/2.3.0-SNAPSHOT/)| Jul/Aug 2017 | 1.0, 1.1, 1.2, 2.0 | Minor release with breaking changes -[2.2.3](https://github.com/swagger-api/swagger-codegen/releases/tag/v2.2.2) (**current stable**) | 2017-07-15 | 1.0, 1.1, 1.2, 2.0 | [tag v2.2.2](https://github.com/swagger-api/swagger-codegen/tree/v2.2.3) +3.0.0 (upcoming major release) [SNAPSHOT](https://oss.sonatype.org/content/repositories/snapshots/io/swagger/swagger-codegen-cli/3.0.0-SNAPSHOT/)| TBD | 1.0, 1.1, 1.2, 2.0, 3.0 | Major release with breaking changes +2.3.0 (current master, upcoming minor release) [SNAPSHOT](https://oss.sonatype.org/content/repositories/snapshots/io/swagger/swagger-codegen-cli/2.3.0-SNAPSHOT/)| Jul/Aug 2017 | 1.0, 1.1, 1.2, 2.0 | Minor release with breaking changes +[2.2.3](https://github.com/swagger-api/swagger-codegen/releases/tag/v2.2.3) (**current stable**) | 2017-07-15 | 1.0, 1.1, 1.2, 2.0 | [tag v2.2.3](https://github.com/swagger-api/swagger-codegen/tree/v2.2.3) [2.2.2](https://github.com/swagger-api/swagger-codegen/releases/tag/v2.2.2) | 2017-03-01 | 1.0, 1.1, 1.2, 2.0 | [tag v2.2.2](https://github.com/swagger-api/swagger-codegen/tree/v2.2.2) [2.2.1](https://github.com/swagger-api/swagger-codegen/releases/tag/v2.2.1) | 2016-08-07 | 1.0, 1.1, 1.2, 2.0 | [tag v2.2.1](https://github.com/swagger-api/swagger-codegen/tree/v2.2.1) [2.1.6](https://github.com/swagger-api/swagger-codegen/releases/tag/v2.1.6) | 2016-04-06 | 1.0, 1.1, 1.2, 2.0 | [tag v2.1.6](https://github.com/swagger-api/swagger-codegen/tree/v2.1.6) -2.0.17 | 2014-08-22 | 1.1, 1.2 | [tag v2.0.17](https://github.com/swagger-api/swagger-codegen/tree/v2.0.17) +2.0.17 | 2014-08-22 | 1.1, 1.2 | [tag v2.0.17](https://github.com/swagger-api/swagger-codegen/tree/2.0.17) 1.0.4 | 2012-04-12 | 1.0, 1.1 | [tag v1.0.4](https://github.com/swagger-api/swagger-codegen/tree/swagger-codegen_2.9.1-1.1) ### Prerequisites If you're looking for the latest stable version, you can grab it directly from Maven.org (Java 7 runtime at a minimum): -``` -wget http://central.maven.org/maven2/io/swagger/swagger-codegen-cli/2.2.2/swagger-codegen-cli-2.2.2.jar -O swagger-codegen-cli.jar +```sh +wget http://central.maven.org/maven2/io/swagger/swagger-codegen-cli/2.2.3/swagger-codegen-cli-2.2.3.jar -O swagger-codegen-cli.jar java -jar swagger-codegen-cli.jar help ``` +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 swagger-codegen-cli.jar http://central.maven.org/maven2/io/swagger/swagger-codegen-cli/2.2.3/swagger-codegen-cli-2.2.3.jar` + On a mac, it's even easier with `brew`: -``` +```sh brew install swagger-codegen ``` @@ -110,7 +115,7 @@ To build from source, you need the following installed and available in your $PA Don't forget to install Java 7 or 8. You probably have 1.6. Export JAVA_HOME in order to use the supported Java version: -``` +```sh export JAVA_HOME=`/usr/libexec/java_home -v 1.8` export PATH=${JAVA_HOME}/bin:$PATH ``` @@ -118,7 +123,7 @@ export PATH=${JAVA_HOME}/bin:$PATH ### Building After cloning the project, you can build it from source with this command: -``` +```sh mvn clean package ``` @@ -127,7 +132,7 @@ mvn clean package To install, run `brew install swagger-codegen` Here is an example usage: -``` +```sh swagger-codegen generate -i http://petstore.swagger.io/v2/swagger.json -l ruby -o /tmp/test/ ``` @@ -140,7 +145,7 @@ in the docker container. It also maps `~/.m2/repository` to the appropriate cont To execute `mvn package`: -``` +```sh git clone https://github.com/swagger-api/swagger-codegen cd swagger-codegen ./run-in-docker.sh mvn package @@ -150,7 +155,7 @@ Build artifacts are now accessible in your working directory. Once built, `run-in-docker.sh` will act as an executable for swagger-codegen-cli. To generate code, you'll need to output to a directory under `/gen` (e.g. `/gen/out`). For example: -``` +```sh ./run-in-docker.sh help # Executes 'help' command for swagger-codegen-cli ./run-in-docker.sh langs # Executes 'langs' command for swagger-codegen-cli ./run-in-docker.sh /gen/bin/go-petstore.sh # Builds the Go client @@ -160,7 +165,7 @@ Once built, `run-in-docker.sh` will act as an executable for swagger-codegen-cli #### Run Docker in Vagrant Prerequisite: install [Vagrant](https://www.vagrantup.com/downloads.html) and [VirtualBox](https://www.virtualbox.org/wiki/Downloads). - ``` + ```sh git clone http://github.com/swagger-api/swagger-codegen.git cd swagger-codegen vagrant up @@ -181,7 +186,7 @@ The Swagger Generator image can act as a self-hosted web application and API for Example usage (note this assumes `jq` is installed for command line processing of JSON): -``` +```sh # Start container and save the container id CID=$(docker run -d swaggerapi/swagger-generator) # allow for startup @@ -208,7 +213,7 @@ To generate code with this image, you'll need to mount a local location as a vol Example: -``` +```sh docker run --rm -v ${PWD}:/local swaggerapi/swagger-codegen-cli generate \ -i http://petstore.swagger.io/v2/swagger.json \ -l go \ @@ -231,7 +236,7 @@ java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jar generate \ ``` (if you're on Windows, replace the last command with `java -jar modules\swagger-codegen-cli\target\swagger-codegen-cli.jar generate -i http://petstore.swagger.io/v2/swagger.json -l php -o c:\temp\php_api_client`) -You can also download the JAR (latest release) directly from [maven.org](http://central.maven.org/maven2/io/swagger/swagger-codegen-cli/2.2.2/swagger-codegen-cli-2.2.2.jar) +You can also download the JAR (latest release) directly from [maven.org](http://central.maven.org/maven2/io/swagger/swagger-codegen-cli/2.2.3/swagger-codegen-cli-2.2.3.jar) To get a list of **general** options available, please run `java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jar help generate` @@ -242,7 +247,7 @@ To get a list of PHP specified options (which can be passed to the generator wit ### To generate a sample client library You can build a client against the swagger sample [petstore](http://petstore.swagger.io) API as follows: -``` +```sh ./bin/java-petstore.sh ``` @@ -250,7 +255,7 @@ You can build a client against the swagger sample [petstore](http://petstore.swa This will run the generator with this command: -``` +```sh java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jar generate \ -i http://petstore.swagger.io/v2/swagger.json \ -l java \ @@ -305,13 +310,13 @@ OPTIONS You can then compile and run the client, as well as unit tests against it: -``` +```sh cd samples/client/petstore/java mvn package ``` Other languages have petstore samples, too: -``` +```sh ./bin/android-petstore.sh ./bin/java-petstore.sh ./bin/objc-petstore.sh @@ -328,7 +333,7 @@ You can look at `modules/swagger-codegen/src/main/resources/${your-language}` fo ### Making your own codegen modules If you're starting a project with a new language and don't see what you need, swagger-codegen can help you create a project to generate your own libraries: -``` +```sh java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jar meta \ -o output/myLibrary -n myClientCodegen -p com.my.company.codegen ``` @@ -337,7 +342,7 @@ This will write, in the folder `output/myLibrary`, all the files you need to get You would then compile your library in the `output/myLibrary` folder with `mvn package` and execute the codegen like such: -``` +```sh java -cp output/myLibrary/target/myClientCodegen-swagger-codegen-1.0.0.jar:modules/swagger-codegen-cli/target/swagger-codegen-cli.jar io.swagger.codegen.SwaggerCodegen ``` For Windows users, you will need to use `;` instead of `:` in the classpath, e.g. @@ -347,7 +352,7 @@ java -cp output/myLibrary/target/myClientCodegen-swagger-codegen-1.0.0.jar;modul Note the `myClientCodegen` is an option now, and you can use the usual arguments for generating your library: -``` +```sh java -cp output/myLibrary/target/myClientCodegen-swagger-codegen-1.0.0.jar:modules/swagger-codegen-cli/target/swagger-codegen-cli.jar \ io.swagger.codegen.SwaggerCodegen generate -l myClientCodegen\ -i http://petstore.swagger.io/v2/swagger.json \ @@ -376,7 +381,7 @@ You may not want to generate *all* models in your project. Likewise you may wan The default is generate *everything* supported by the specific library. Once you enable a feature, it will restrict the contents generated: -``` +```sh # generate only models java -Dmodels {opts} @@ -391,7 +396,7 @@ java -Dmodels -DsupportingFiles ``` To control the specific files being generated, you can pass a CSV list of what you want: -``` +```sh # generate the User and Pet models only -Dmodels=User,Pet @@ -402,7 +407,7 @@ To control the specific files being generated, you can pass a CSV list of what y To control generation of docs and tests for api and models, pass false to the option. For api, these options are `-DapiTests=false` and `-DapiDocs=false`. For models, `-DmodelTests=false` and `-DmodelDocs=false`. These options default to true and don't limit the generation of the feature options listed above (like `-Dapi`): -``` +```sh # generate only models (with tests and documentation) java -Dmodels {opts} @@ -429,7 +434,7 @@ The ignore file allows for better control over overwriting existing files than t Examples: -``` +```sh # Swagger Codegen Ignore # Lines beginning with a # are comments @@ -459,11 +464,15 @@ src/IO.Swagger.Test/Model/AnimalFarmTests.cs The `.swagger-codegen-ignore` file must exist in the root of the output directory. +Upon first code generation, you may also pass the CLI option `--ignore-file-override=/path/to/ignore_file` for greater control over generated outputs. Note that this is a complete override, and will override the `.swagger-codegen-ignore` file in an output directory when regenerating code. + +Editor support for `.swagger-codegen-ignore` files is available in IntelliJ via the [.ignore plugin](https://plugins.jetbrains.com/plugin/7495--ignore). + ### Customizing the generator There are different aspects of customizing the code generator beyond just creating or modifying templates. Each language has a supporting configuration file to handle different type mappings, etc: -``` +```sh $ ls -1 modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ AbstractJavaJAXRSServerCodegen.java AbstractTypeScriptClientCodegen.java @@ -474,7 +483,7 @@ TypeScriptNodeClientCodegen.java Each of these files creates reasonable defaults so you can get running quickly. But if you want to configure package names, prefixes, model folders, etc. you can use a json config file to pass the values. -``` +```sh java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jar generate \ -i http://petstore.swagger.io/v2/swagger.json \ -l java \ @@ -482,7 +491,7 @@ java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jar generate \ -c path/to/config.json ``` and `config.json` contains the following as an example: -``` +```json { "apiPackage" : "petstore" } @@ -491,7 +500,7 @@ and `config.json` contains the following as an example: Supported config options can be different per language. Running `config-help -l {lang}` will show available options. **These options are applied via configuration file (e.g. config.json) or by passing them with `-D{optionName}={optionValue}`**. (If `-D{optionName}` does not work, please open a [ticket](https://github.com/swagger-api/swagger-codegen/issues/new) and we'll look into it) -``` +```sh java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jar config-help -l java ``` @@ -513,6 +522,7 @@ CONFIG OPTIONS okhttp-gson (default) - HTTP client: OkHttp 2.4.0. JSON processing: Gson 2.3.1 retrofit - HTTP client: OkHttp 2.4.0. JSON processing: Gson 2.3.1 (Retrofit 1.9.0) retrofit2 - HTTP client: OkHttp 2.5.0. JSON processing: Gson 2.4 (Retrofit 2.0.0-beta2) + google-api-client - HTTP client: google-api-client 1.23.0. JSON processing: Jackson 2.8.9 ``` Your config file for Java can look like @@ -584,7 +594,7 @@ http://online.swagger.io/validator/debug?url=http://petstore.swagger.io/v2/swagg To do so, just use the `-l dynamic-html` flag when reading a spec file. This creates HTML documentation that is available as a single-page application with AJAX. To view the documentation: -``` +```sh cd samples/dynamic-html/ npm install node . @@ -597,7 +607,7 @@ Which launches a node.js server so the AJAX calls have a place to go. To do so, just use the `-l html` flag when reading a spec file. This creates a single, simple HTML file with embedded css so you can ship it as an email attachment, or load it from your filesystem: -``` +```sh cd samples/html/ open index.html ``` @@ -611,16 +621,22 @@ Please refer to https://github.com/swagger-api/swagger-codegen/wiki/Server-stub- This will create the swagger-codegen library from source. -``` +```sh mvn package ``` Note! The templates are included in the library generated. If you want to modify the templates, you'll need to either repackage the library OR specify a path to your scripts -## Workflow integration +## Workflow Integration + +### Maven Integration You can use the [swagger-codegen-maven-plugin](modules/swagger-codegen-maven-plugin/README.md) for integrating with your workflow, and generating any codegen target. +### Gradle Integration + +[Gradle Swagger Generator Plugin](https://github.com/int128/gradle-swagger-generator-plugin) is available for generating source code and API document. + ## GitHub Integration To push the auto-generated SDK to GitHub, we provide `git_push.sh` to streamline the process. For example: @@ -628,7 +644,7 @@ To push the auto-generated SDK to GitHub, we provide `git_push.sh` to streamline 1) Create a new repository in GitHub (Ref: https://help.github.com/articles/creating-a-new-repository/) 2) Generate the SDK -``` +```sh java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jar generate \ -i modules/swagger-codegen/src/test/resources/2_0/petstore.json -l perl \ --git-user-id "wing328" \ @@ -637,7 +653,7 @@ To push the auto-generated SDK to GitHub, we provide `git_push.sh` to streamline -o /var/tmp/perl/petstore ``` 3) Push the SDK to GitHub -``` +```sh cd /var/tmp/perl/petstore /bin/sh ./git_push.sh ``` @@ -647,13 +663,13 @@ cd /var/tmp/perl/petstore One can also generate API client or server using the online generators (https://generator.swagger.io) For example, to generate Ruby API client, simply send the following HTTP request using curl: -``` +```sh curl -X POST -H "content-type:application/json" -d '{"swaggerUrl":"http://petstore.swagger.io/v2/swagger.json"}' https://generator.swagger.io/api/gen/clients/ruby ``` Then you will receieve a JSON response with the URL to download the zipped code. To customize the SDK, you can `POST` to `https://generator.swagger.io/gen/clients/{language}` with the following HTTP body: -``` +```json { "options": {}, "swaggerUrl": "http://petstore.swagger.io/v2/swagger.json" @@ -662,7 +678,7 @@ To customize the SDK, you can `POST` to `https://generator.swagger.io/gen/client in which the `options` for a language can be obtained by submitting a `GET` request to `https://generator.swagger.io/api/gen/clients/{language}`: For example, `curl https://generator.swagger.io/api/gen/clients/python` returns -``` +```json { "packageName":{ "opt":"packageName", @@ -685,7 +701,7 @@ For example, `curl https://generator.swagger.io/api/gen/clients/python` returns } ``` To set package name to `pet_store`, the HTTP body of the request is as follows: -``` +```json { "options": { "packageName": "pet_store" @@ -694,12 +710,12 @@ To set package name to `pet_store`, the HTTP body of the request is as follows: } ``` and here is the curl command: -``` +```sh curl -H "Content-type: application/json" -X POST -d '{"options": {"packageName": "pet_store"},"swaggerUrl": "http://petstore.swagger.io/v2/swagger.json"}' https://generator.swagger.io/api/gen/clients/python ``` Instead of using `swaggerUrl` with an URL to the OpenAPI/Swagger spec, one can include the spec in the JSON payload with `spec`, e.g. -``` +```json { "options": {}, "spec": { @@ -721,6 +737,7 @@ Please refer to this [page](https://github.com/swagger-api/swagger-codegen/blob/ Companies/Projects using Swagger Codegen ---------------------------------------- Here are some companies/projects using Swagger Codegen in production. To add your company/project to the list, please visit [README.md](https://github.com/swagger-api/swagger-codegen/blob/master/README.md) and click on the icon to edit the page. +- [Accengage](https://www.accengage.com/) - [Activehours](https://www.activehours.com/) - [Acunetix](https://www.acunetix.com/) - [Atlassian](https://www.atlassian.com/) @@ -735,8 +752,9 @@ Here are some companies/projects using Swagger Codegen in production. To add you - [Bufferfly Network](https://www.butterflynetinc.com/) - [Cachet Financial](http://www.cachetfinancial.com/) - [carpolo](http://www.carpolo.co/) -- [CloudBoost](https://www.CloudBoost.io/) - [Cisco](http://www.cisco.com/) +- [CloudBoost](https://www.CloudBoost.io/) +- [Cloudsmith](https://cloudsmith.io/) - [Conplement](http://www.conplement.de/) - [Cummins](http://www.cummins.com/) - [Cupix](http://www.cupix.com) @@ -744,20 +762,25 @@ Here are some companies/projects using Swagger Codegen in production. To add you - [DecentFoX](http://decentfox.com/) - [DocRaptor](https://docraptor.com) - [DocuSign](https://www.docusign.com) +- [Elastic](https://www.elastic.co/) - [Ergon](http://www.ergon.ch/) - [Dell EMC](https://www.emc.com/) - [eureka](http://eure.jp/) - [everystory.us](http://everystory.us) - [Expected Behavior](http://www.expectedbehavior.com/) - [Fastly](https://www.fastly.com/) +- [FINRA](https://github.com/FINRAOS/herd/) - [Flat](https://flat.io) - [Finder](http://en.finder.pl/) +- [Fitwell](https://fitwell.co/) - [FH Münster - University of Applied Sciences](http://www.fh-muenster.de) - [Fotition](https://www.fotition.com/) - [Gear Zero Network](https://www.gearzero.ca) - [General Electric](https://www.ge.com/) +- [Genesys - PureCloud](http://developer.mypurecloud.com/) - [Germin8](http://www.germin8.com) - [GigaSpaces](http://www.gigaspaces.com) +- [GMO Pepabo](https://pepabo.com/en/) - [goTransverse](http://www.gotransverse.com/api) - [GraphHopper](https://graphhopper.com/) - [Gravitate Solutions](http://gravitatesolutions.com/) @@ -768,7 +791,6 @@ Here are some companies/projects using Swagger Codegen in production. To add you - [IMS Health](http://www.imshealth.com/en/solution-areas/technology-and-applications) - [Individual Standard IVS](http://www.individual-standard.com) - [Intent HQ](http://www.intenthq.com) -- [Interactive Intelligence](http://developer.mypurecloud.com/) - [Kabuku](http://www.kabuku.co.jp/en) - [Kurio](https://kurio.co.id) - [Kuroi](http://kuroiwebdesign.com/) @@ -782,12 +804,15 @@ Here are some companies/projects using Swagger Codegen in production. To add you - [LXL Tech](http://lxltech.com) - [Lyft](https://www.lyft.com/developers) - [MailMojo](https://mailmojo.no/) +- [Metaswitch](https://www.metaswitch.com/) - [Mindera](http://mindera.com/) - [Mporium](http://mporium.com/) - [Neverfail](https://neverfail.com/) +- [NTT DATA](http://www.nttdata.com/) - [nViso](http://www.nviso.ch/) - [Okiok](https://www.okiok.com) - [Onedata](http://onedata.org) +- [Open International Systems](https://openintl.com/) - [OrderCloud.io](http://ordercloud.io) - [OSDN](https://osdn.jp) - [PagerDuty](https://www.pagerduty.com) @@ -802,6 +827,7 @@ Here are some companies/projects using Swagger Codegen in production. To add you - [QuantiModo](https://quantimo.do/) - [QuickBlox](https://quickblox.com/) - [Rapid7](https://rapid7.com/) +- [Red Hat](https://www.redhat.com/) - [Reload! A/S](https://reload.dk/) - [REstore](https://www.restore.eu) - [Revault Sàrl](http://revault.ch) @@ -825,6 +851,7 @@ Here are some companies/projects using Swagger Codegen in production. To add you - [Svenska Spel AB](https://www.svenskaspel.se/) - [Switch Database](https://www.switchdatabase.com/) - [TaskData](http://www.taskdata.com/) +- [ThirdWatch.ai](https://www.thirdwatch.ai/) - [ThoughtWorks](https://www.thoughtworks.com) - [Trexle](https://trexle.com/) - [Upwork](http://upwork.com/) @@ -843,6 +870,9 @@ Here are some companies/projects using Swagger Codegen in production. To add you Presentations/Videos/Tutorials/Books ---------------------------------------- +- 2011/08/31 - [Introducing Swagger](https://www.slideshare.net/fehguy/introducing-swagger) by [Tony Tam](https://twitter.com/fehguy) +- 2014/05/22 - [Swagger APIs for Humans and Robots](https://www.slideshare.net/fehguy/swagger-apis-for-humans-and-robots-gluecon) by [Tony Tam](https://twitter.com/fehguy) at [Gluecon](http://gluecon.com/) +- 2014/11/11 - [Genie 2.0: Second Wish Granted!](https://medium.com/netflix-techblog/genie-2-0-second-wish-granted-d888d79455c6) by [Tom Gianos](http://www.linkedin.com/in/thomasgianos/) and [Amit Sharma](https://www.linkedin.com/pub/amit-sharma/5/163/a83) @ [Netflix](https://www.netflix.com/) Big Data Platform Team - 2015/07/28 - [Enriching RESTful Services with Swagger](https://blog.philipphauer.de/enriching-restful-services-swagger/) by [Philipp Hauer](https://blog.philipphauer.de/) - 2015/11/11 - [Generate client stubs & document your REST-API using Swagger & Spring](https://www.youtube.com/watch?v=43GhBbP--oI) by [Johannes Fiala](https://github.com/jfiala) @ Devoxx Belgium 2015 - 2015/12/03 - [こんなに簡単! Swagger Codegenのカスタマイズ](http://qiita.com/Quramy/items/c583f3213f0b77ff1bac) by [Quramy](http://qiita.com/Quramy) @@ -850,16 +880,45 @@ Presentations/Videos/Tutorials/Books - 2016/01/15 - [How to end manual REST-API client coding](https://www.youtube.com/watch?v=RzZRdqZp6Oo) by [Johannes Fiala](https://github.com/jfiala) @ dotJS 2015 - 2016/04/27 - [Automated REST API Development](https://yos.io/2016/04/27/automated-api-development/) by [Yos Riady](https://www.linkedin.com/in/yosriady) - 2016/05/29 - [Generating Java Spring-MVC code from Swagger Spec](https://www.clianz.com/2016/05/29/java-mvc-swagger-gen/) by [@icha024](https://github.com/icha024) +- 2016/08/23 - [Generating Dreamfactory Client SDKs based on Swagger API Definitions](http://blog.dreamfactory.com/generating-dreamfactory-client-sdks-based-on-swagger-api-definitions) by [Phil Schuler](https://github.com/philicious/) +- 2016/09/28 - [1 UNBELIEVABLE TRICK TO CREATE AN EASY TO CONSUME API IN .NET](https://stapp.space/1-simple-trick-to-create-a-good-api-in-net/) by [Piotr Stapp](https://stapp.space/author/piotr-stapp/) +- 2016/10/10 - [Using swagger-codegen with Marketo](http://developers.marketo.com/blog/using-swagger-codegen-with-marketo/) by [Kenny Elkington](http://developers.marketo.com/blog/using-swagger-codegen-with-marketo/) +- 2016/10/12 - [Designing a Swagger API](https://sookocheff.com/post/api/swagger/) by [Kevin Sookocheff](https://sookocheff.com/) - 2016/11/05 - [How to generate a REST Application](https://www.youtube.com/watch?v=iyC9BWMe75Q) by [Johannes Fiala](https://github.com/jfiala) @ DevFest Vienna 2016 -- 2016/11/10 - [Building an AEM API clients ecosystem](http://blog.cliffano.com/2016/11/10/adobe-marketing-cloud-community-expo/) by Cliffano Subagio, Michael Diender, Stephen Shim from Shine Solutions @ [Adobe Marketing Cloud Community Expo (AMCCE)](https://www.meetup.com/Melbourne-AEM-CQ-Meetup/events/233363101/) +- 2016/11/10 - [Building an AEM API clients ecosystem](http://blog.cliffano.com/2016/11/10/adobe-marketing-cloud-community-expo/) by Cliffano Subagio, Michael Diender, Stephen Shim from [Shine Solutions](https://shinesolutions.com/) @ [Adobe Marketing Cloud Community Expo (AMCCE)](https://www.meetup.com/Melbourne-AEM-CQ-Meetup/events/233363101/) - 2016/11/18 - [How to generate a REST CXF3 application from Swagger-Contract](https://www.slideshare.net/johannes_fiala/how-to-generate-a-rest-cxf3-application-from-swagger-apacheconeu-2016) by [Johannes Fiala](https://github.com/jfiala) @ ApacheConEU 2016 - 2016/11/25 - [Swagger Codegen for Swift3 and NodeJS](https://normand1.github.io/blog/swift/swagger/codegen/2016/11/25/Swagger-Codegen-for-Swift3-and-NodeJS.html) by [David Norman](https://github.com/normand1) +- 2016/12/08 - [Generate client side code using Swagger Codegen](https://carra-lucia-ltd.co.uk/2016/12/08/generate-client-side-code-using-swagger-codegen/) by [theFerkel](https://carra-lucia-ltd.co.uk/author/theferkel/) +- 2017/01/16 - [Zero to API in 4 minutes](https://cidrblock.github.io/zero-to-api-in-4-minutes.html) by [Bradley A. Thornton](https://github.com/cidrblock) +- 2017/02/09 - [「Swaggerを利用した新規サービス開発」というタイトルで登壇して来ました](https://techblog.recochoku.jp/1055) by [recotech](https://www.slideshare.net/recotech) - 2017/03/03 - [Swagger Codegen の使い方の簡単な説明です](https://speakerdeck.com/wagyu298/swagger-codegen) by [wagyu298](https://github.com/wagyu298) - 2017/03/24 - [Using Open API Specification To Put Lyft SDK Support in the Fast Lane](https://medium.com/lyft-developer-platform/using-open-api-specification-to-put-lyft-sdk-support-in-the-fast-lane-7b623218e4ee) by [Val Polouchkine](https://github.com/vpolouchkine) +- 2017/04/13 - [Automatically Generating your API Client with Swagger and Swagger Codegen](https://www.youtube.com/watch?v=EzKwi-u9jQo) by [Jesse Collis](https://github.com/jessedc) @ Melbourne Cocoaheads - 2017/04/27 - [Swagger Codegen のPHP実装があまりにアレだったので、ライブラリ自作して公開してみた](http://qiita.com/imunew/items/2e9c472e0097e329f2cd) by [imunew](http://qiita.com/imunew) - 2017/05/17 - [Diseño de APIs con OpenAPI](https://www.slideshare.net/pjmolina/diseo-de-apis-con-openapi) by [Pedro J. Molina](https://github.com/pjmolina) @ [JSDayES 2017](http://2017.jsday.es/) - 2017/05/22 - [Presentation of the Vert.x-Swagger project](http://vertx.io/blog/presentation-of-the-vert-x-swagger-project/) by [@phiz71](http://github.com/phiz71) +- 2017/05/22 - [Automatically generating your API from a swagger file using gradle](https://www.jcore.com/2017/05/22/automatically-generating-api-using-swagger-and-gradle/) by [Deniz Turan](https://www.jcore.com/author/deniz/) - 2017/06/21 - [Swagger Presentation - Warsaw Ruby Users Group](https://www.youtube.com/watch?v=uCnnDMFQB8U) by [@rafalpetryka](http://github.com/rafalpetryka) +- 2017/06/29 - [Making SDKs: the bespoke, the hopeful and the generated](https://devrel.net/developer-experience/making-sdks-bespoke-hopeful-generated) by [Tristan Sokol](https://github.com/tristansokol) ([Square](https://github.com/square)) at DevXcon 2017 +- 2017/07/11 - [OpenAPI development with Python](https://www.slideshare.net/TakuroWada/20170711-euro-python2017) by [和田拓朗](https://github.com/taxpon) at [EuroPython 2017](https://ep2017.europython.eu/en/) +- 2017/07/29 - [How Square makes its SDKs](https://medium.com/square-corner-blog/how-square-makes-its-sdks-6a0fd7ea4b2d) by [Tristan Sokol](https://github.com/tristansokol) ([Square](https://github.com/square)) +- 2017/07/31 - [How to Generate a Deployable REST CXF3 Application from a Swagger-Contract](https://www.youtube.com/watch?v=gM63rJlUHZQ) by [Johannes Fiala](https://github.com/jfiala) @ Voxxed Days Vienna +- 2017/08/11 - [Swagger Codegen 自动生成Retrofit 代码](https://juejin.im/entry/598d8eb86fb9a03c52459e2a) by [徐磊](http://www.jianshu.com/u/792c738b33fc) +- 2017/08/24 - [APIs First](https://engineering.squarespace.com/blog/2017/apis-first) by [roykachouh](https://github.com/roykachouh) ([Square](https://github.com/square)) +- 2017/08/31 - [Bringing Jenkins Remote Access API To The Masses](http://blog.cliffano.com/2017/09/01/jenkins-world-2017/) by [Cliffano Subagio](http://cliffano.com) from [Shine Solutions](https://shinesolutions.com/) @ [Jenkins World 2017](https://jenkinsworld20162017.sched.com/) +- 2017/09/08 - [Swagger Codegen で自動生成したクライアントSDKを使う(iOS編)](http://blog.techium.jp/entry/2017/09/08/071650) by [kfurue](http://profile.hatena.ne.jp/kfurue/) +- 2017/09/09 - [Swagger Codegen で自動生成したクライアントSDKを使う(RxSwift 編)](http://blog.techium.jp/entry/2017/09/09/113003) by [kfurue](http://profile.hatena.ne.jp/kfurue/) +- 2017/09/09 - [OpenAPIを利用したPythonWebアプリケーション開発](https://www.slideshare.net/TakuroWada/openapipythonweb) by [和田拓朗](https://github.com/taxpon) at [PyCon JP 2017](https://pycon.jp/2017/ja/) +- 2017/09/21 - [Generating an Elastic Cloud Enterprise Client](https://www.elastic.co/blog/generating-an-elastic-cloud-enterprise-client) by [Greg Marzouka](https://github.com/gmarz) ([Elastic](https://www.elastic.co/)) +- 2017/09/26 - [How to Use IoT Application Enablement Api Hub JSON Descriptor in Postman and How to Generate Client Code](https://www.linkedin.com/in/radu-simen/) by [Radu Simen](https://www.linkedin.com/in/radu-simen/) ([SAP](https://www.sap.com/)) +- 2017/09/28 - [Swagger Codegenで APIクライアントgem 自動生成 #m3kt](https://speakerdeck.com/juntaki/swagger-codegende-apikuraiantogem-zi-dong-sheng-cheng-number-m3kt) by [Jumpei Takiyasu](https://github.com/juntaki) +- 2017/09/30 - [Swaggerのテンプレートを魔改造した話 #渋谷java](https://www.slideshare.net/int128/swagger-80309224) by [Hidetake Iwata](https://github.com/int128) ([NTT DATA Corporation](http://www.nttdata.com/global/en/)) +- 2017/10/04 - [Enterprise-friendly Java client for Microsoft Machine Learning Server](https://blogs.msdn.microsoft.com/mlserver/2017/10/04/enterprise-friendly-java-client-for-microsoft-machine-learning-server/) by [Pratik Palnitkar](https://www.linkedin.com/in/pratikpalnitkar/) ([Microsoft](https://www.microsoft.com/)) +- 2017/10/08 - [Generating a REST Ada client with OpenAPI and Swagger Codegen](https://blog.vacs.fr/vacs/blogs/post.html?post=2017/10/08/Generating-a-REST-Ada-client-with-OpenAPI-and-Swagger-Codegen) by [Stephane Carrez](https://github.com/stcarrez) +- 2017/11/08 - [A Beginner's Guide to Code Generation for REST APIs](https://gum.co/swagger_codegen_beginner)(eBook) by [William Cheng](https://twitter.com/wing328) +- 2017/11/18 - [10年前のレガシーシステムをサーバーサイドKotlinでフルリニューアルしている話 #jjug_ccc #ccc_g2](https://speakerdeck.com/maeharin/10nian-qian-falseregasisisutemuwosabasaidokotlindehururiniyuarusiteiruhua-number-jjug-ccc-number-ccc-g2) by [Hidenori Maehara](https://github.com/maeharin) +- 2017/11/21 - [swagger-codegen から眺める Swift4](https://speakerdeck.com/d_date/swagger-codegen-karatiao-meru-swift4) by [Daiki Matsudate](https://github.com/d-date) +- 2017/12/07 - [API-driven development with OpenAPI and Swagger, Part 2](https://www.itworld.com/article/3199190/apis/api-driven-development-with-openapi-and-swagger-part-2.html) by Matthew Tyson # Swagger Codegen Core Team @@ -902,7 +961,7 @@ Swagger Codegen core team members are contributors who have been making signific | Java Play Framework | | | NancyFX | | | NodeJS | @kolyjjj (2016/05/01) | -| PHP Lumen | @abcsum (2016/05/01) | +| PHP Lumen | @abcsun (2016/05/01) | | PHP Silex | | | PHP Slim | | | Python Flask | | @@ -914,34 +973,44 @@ Swagger Codegen core team members are contributors who have been making signific ## Template Creator Here is a list of template creators: * API Clients: + * Ada: @stcarrez * Akka-Scala: @cchafer * Apex: @asnelling * Bash: @bkryza * C++ REST: @Danielku15 * C# (.NET 2.0): @who * C# (.NET Standard 1.3 ): @Gronsak - * C# (.NET 4.5 refactored): @jim + * C# (.NET 4.5 refactored): @jimschubert * Clojure: @xhh * Dart: @yissachar * Elixir: @niku - * Eiffel: @jvelilla + * Eiffel: @jvelilla + * Erlang: @tsloughter * Groovy: @victorgit * Go: @wing328 * Go (rewritten in 2.3.0): @antihax + * Haskell (http-client): @jonschoning * Java (Feign): @davidkiss * Java (Retrofit): @0legg - * Java (Retrofi2): @emilianobonassi + * Java (Retrofit2): @emilianobonassi * Java (Jersey2): @xhh * Java (okhttp-gson): @xhh * Java (RestTemplate): @nbruno * Java (RESTEasy): @gayathrigs + * Java (Vertx): @lopesmcc + * Java (Google APIs Client Library): @charlescapps * Javascript/NodeJS: @jfiala * Javascript (Closure-annotated Angular) @achew22 - * JMeter @davidkiss - * Kotlin @jimschubert + * JMeter: @davidkiss + * Kotlin: @jimschubert + * Lua: @daurnimator * Perl: @wing328 * PHP (Guzzle): @baartosz * PowerShell: @beatcracker + * R: @ramnov + * Rust: @farcaller + * Rust (rust-server): @metaswitch + * Scala (scalaz & http4s): @tbrown1979 * Swift: @tkqubo * Swift 3: @hexelon * Swift 4: @ehyche @@ -966,12 +1035,14 @@ Here is a list of template creators: * JAX-RS CXF: @hiveship * JAX-RS CXF (CDI): @nickcmaynard * JAX-RS RestEasy (JBoss EAP): @jfiala - * PHP Lumen: @abcsum + * PHP Lumen: @abcsun * PHP Slim: @jfastnacht * PHP Symfony: @ksm2 * PHP Zend Expressive (with Path Handler): @Articus * Ruby on Rails 5: @zlx + * Rust (rust-server): @metaswitch * Scala Finch: @jimschubert + * Scala Lagom: @gmkumar2005 * Documentation * HTML Doc 2: @jhitchcock * Confluence Wiki: @jhitchcock @@ -982,7 +1053,7 @@ Here is a list of template creators: Here are the requirements to become a core team member: - rank within top 50 in https://github.com/swagger-api/swagger-codegen/graphs/contributors - - to contribute, here are some good [starting points](https://github.com/swagger-api/swagger-codegen/issues?q=is%3Aopen+is%3Aissue+label%3A%22Need+community+contribution%22) + - to contribute, here are some good [starting points](https://github.com/swagger-api/swagger-codegen/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) - regular contributions to the project - about 3 hours per week - for contribution, it can be addressing issues, reviewing PRs submitted by others, submitting PR to fix bugs or make enhancements, etc @@ -991,25 +1062,52 @@ Here are the requirements to become a core team member: To become a Template Creator, simply submit a PR for new API client (e.g. Rust, Elixir) or server stub (e.g. Ruby Grape) generator. -# Swagger Codegen Evangelist +# Swagger Codegen Technical Committee + +Members of the Swagger Codegen technical committee shoulder the following responsibilities: -Swagger Codegen Evangelist shoulders one or more of the following responsibilities: +- Provides guidance and direction to other users +- Reviews pull requests and issues +- Improves the generator by making enhancements, fixing bugs or updating documentations +- Sets the technical direction of the generator -- publishes articles on the benefit of Swagger Codegen -- organizes local Meetups -- presents the benefits of Swagger Codegen in local Meetups or conferences -- actively answers questions from others in [Github](https://github.com/swagger-api/swagger-codegen/issues), [StackOverflow](stackoverflow.com/search?q=%5Bswagger%5D) -- submits PRs to improve Swagger Codegen -- reviews PRs submitted by the others -- ranks within top 100 in the [contributor list](https://github.com/swagger-api/swagger-codegen/graphs/contributors) +Who is eligible? Those who want to join must have at least 3 PRs merged into a generator. (Exceptions can be granted to template creators or contributors who have made a lot of code changes with less than 3 merged PRs) -If you want to be a Swagger Codegen Evangelist, please kindly apply by sending an email to wing328hk@gmail.com (@wing328) +If you want to join the committee, please kindly apply by sending an email to wing328hk@gmail.com ([@wing328](https://github.com/wing328)) with your Github ID. -### List of Swagger Codegen Evangelists +## Members of Technical Committee + +| Languages | Member (join date) | +|:-------------|:-------------| +| ActionScript | | +| Android | @jaz-ah (2017/09) | +| Apex | | +| Bash | @frol (2017/07) @bkryza (2017/08) @kenjones-cisco (2017/09) | +| C++ | @ravinikam (2017/07) @stkrwork (2017/07) @fvarose (2017/11) | +| C# | @mandrean (2017/08) @jimschubert (2017/09) | +| Clojure | | +| Dart | @ircecho (2017/07) | +| Eiffel | @jvelilla (2017/09) | +| Elixir | | +| Erlang | @tsloughter (2017/11) | +| Groovy | | +| Go | @antihax (2017/11) | +| Haskell | | +| Java | @bbdouglas (2017/07) @JFCote (2017/08) @sreeshas (2017/08) @jfiala (2017/08) @lukoyanov (2017/09) @cbornet (2017/09) | +| Kotlin | @jimschubert (2017/09) | +| Lua | @daurnimator (2017/08) | +| NodeJS/Javascript | @CodeNinjai (2017/07) @frol (2017/07) @cliffano (2017/07) | +| ObjC | | +| Perl | @wing328 (2017/07) | +| PHP | @jebentier (2017/07) @dkarlovi (2017/07) @mandrean (2017/08) @jfastnacht (2017/09) @ackintosh (2017/09) | +| Python | @taxpon (2017/07) @frol (2017/07) @mbohlool (2017/07) @cbornet (2017/09) @kenjones-cisco (2017/11)| +| R | | +| Ruby | @cliffano (2017/07) @zlx (2017/09) | +| Rust | @frol (2017/07) @farcaller (2017/08) | +| Scala | @clasnake (2017/07) @jimschubert (2017/09) | +| Swift | @jgavris (2017/07) @ehyche (2017/08) @Edubits (2017/09) @jaz-ah (2017/09) | +| TypeScript | @TiFu (2017/07) @taxpon (2017/07) @sebastianhaas (2017/07) @kenisteward (2017/07) @Vrolijkx (2017/09) | -- Cliffano Subagio (@cliffano from Australia joined on Dec 9, 2016) - - [Building An AEM API Clients Ecosystem](http://www.slideshare.net/cliffano/building-an-aem-api-clients-ecosystem) - - [Adobe Marketing Cloud Community Expo](http://blog.cliffano.com/2016/11/10/adobe-marketing-cloud-community-expo/) # License information on Generated Code diff --git a/bin/ada-petstore.sh b/bin/ada-petstore.sh new file mode 100755 index 00000000000..d46e5013378 --- /dev/null +++ b/bin/ada-petstore.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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" +model="modules/swagger-codegen/src/test/resources/2_0/petstore.yaml" +ags="$@ generate --template-dir modules/swagger-codegen/src/main/resources/Ada -l ada" +ags="$ags -i $model -t modules/swagger-codegen/src/main/resources/Ada -o samples/client/petstore/ada" +ags="$ags -DprojectName=Petstore --model-package Samples.Petstore" + +java $JAVA_OPTS -jar $executable $ags +rm -rf samples/client/petstore/ada/src/server diff --git a/bin/apache2-petstore-config.sh b/bin/apache2-petstore-config.sh old mode 100644 new mode 100755 diff --git a/bin/csharp-dotnet2-petstore.sh b/bin/csharp-dotnet2-petstore.sh index eb4a95c76e1..9199f1ae278 100755 --- a/bin/csharp-dotnet2-petstore.sh +++ b/bin/csharp-dotnet2-petstore.sh @@ -26,6 +26,6 @@ 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/swagger-codegen/src/test/resources/2_0/petstore.yaml -l CsharpDotNet2 -o samples/client/petstore/csharp-dotnet2/SwaggerClientTest/Lib/SwaggerClient --additional-properties hideGenerationTimestamp=true" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l csharp-dotnet2 -o samples/client/petstore/csharp-dotnet2/SwaggerClientTest/Lib/SwaggerClient --additional-properties hideGenerationTimestamp=true" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/csharp-petstore-all.sh b/bin/csharp-petstore-all.sh index 81817c3670f..70e5f09e0b1 100755 --- a/bin/csharp-petstore-all.sh +++ b/bin/csharp-petstore-all.sh @@ -1,6 +1,6 @@ #!/bin/sh -# C# Petstore API client +# C# Petstore API client (.NET 3.5) ./bin/csharp-petstore.sh # C# Petstore API client with PropertyChanged @@ -8,3 +8,7 @@ # C# Petstore API client (v5.0 for .net standarnd 1.3+) ./bin/csharp-petstore-net-standard.sh + +# C# Petstore API client (.NET 4.0) +./bin/csharp-petstore-net-40.sh + diff --git a/bin/csharp-petstore-net-40.json b/bin/csharp-petstore-net-40.json new file mode 100644 index 00000000000..884f7ea9bc9 --- /dev/null +++ b/bin/csharp-petstore-net-40.json @@ -0,0 +1,3 @@ +{ + "targetFramework": "v4.0" +} diff --git a/bin/csharp-petstore-net-40.sh b/bin/csharp-petstore-net-40.sh new file mode 100755 index 00000000000..fd5d0876d04 --- /dev/null +++ b/bin/csharp-petstore-net-40.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l csharp -o samples/client/petstore/csharp/SwaggerClientNet40 --additional-properties packageGuid={321C8C3F-0156-40C1-AE42-D59761FB9B6C} -c ./bin/csharp-petstore-net-40.json" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/eiffel-petstore.sh b/bin/eiffel-petstore.sh new file mode 100755 index 00000000000..f306cccfe70 --- /dev/null +++ b/bin/eiffel-petstore.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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" +args="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l eiffel -o samples/client/petstore/eiffel/" + +java $JAVA_OPTS -jar $executable $args diff --git a/bin/elixir-petstore.sh b/bin/elixir-petstore.sh index 915a02cfc05..5145f5b4dae 100755 --- a/bin/elixir-petstore.sh +++ b/bin/elixir-petstore.sh @@ -24,6 +24,11 @@ then mvn clean package fi +# remove existing lib and model file +echo "removing existing lib, model files" +rm -Rf "samples/client/petstore/elixir/lib/swagger_petstore/model/" +rm -Rf "samples/client/petstore/elixir/lib/swagger_petstore/lib/" + # 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" args="$@ generate -t modules/swagger-codegen/src/main/resources/elixir -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l elixir -o samples/client/petstore/elixir" diff --git a/bin/erlang-petstore-client.sh b/bin/erlang-petstore-client.sh new file mode 100755 index 00000000000..686ecd25629 --- /dev/null +++ b/bin/erlang-petstore-client.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/erlang-client -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l erlang-client -o samples/client/petstore/erlang-client" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/go-petstore-server.sh b/bin/go-petstore-server.sh index 7822b954adc..287027a714e 100755 --- a/bin/go-petstore-server.sh +++ b/bin/go-petstore-server.sh @@ -26,6 +26,7 @@ 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/swagger-codegen/src/test/resources/2_0/petstore.yaml -l go-server -o samples/server/petstore/go-api-server -DpackageName=petstoreserver --additional-properties hideGenerationTimestamp=true -Dservice" + +ags="$@ generate -t modules/swagger-codegen/src/main/resources/go-server -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l go-server -o samples/server/petstore/go-api-server -DpackageName=petstoreserver --additional-properties hideGenerationTimestamp=true -Dservice" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/haskell-http-client-petstore.sh b/bin/haskell-http-client-petstore.sh new file mode 100755 index 00000000000..62b655666d7 --- /dev/null +++ b/bin/haskell-http-client-petstore.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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" + +args="$@ generate -t modules/swagger-codegen/src/main/resources/haskell-http-client -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l haskell-http-client -o samples/client/petstore/haskell-http-client" + +echo "java ${JAVA_OPTS} -jar ${executable} ${args}" +java $JAVA_OPTS -jar $executable $args diff --git a/bin/java-petstore-all.sh b/bin/java-petstore-all.sh index cf9bb61db49..4abe8e6c303 100755 --- a/bin/java-petstore-all.sh +++ b/bin/java-petstore-all.sh @@ -16,3 +16,4 @@ ./bin/java-petstore-resttemplate.sh ./bin/java-petstore-resttemplate-withxml.sh ./bin/java-petstore-resteasy.sh +./bin/java-petstore-google-api-client.sh diff --git a/bin/java-petstore-google-api-client.json b/bin/java-petstore-google-api-client.json new file mode 100644 index 00000000000..66df1f8c601 --- /dev/null +++ b/bin/java-petstore-google-api-client.json @@ -0,0 +1,4 @@ +{ + "library": "google-api-client", + "artifactId": "swagger-petstore-google-api-client" +} diff --git a/bin/java-petstore-google-api-client.sh b/bin/java-petstore-google-api-client.sh new file mode 100755 index 00000000000..09c9e14139a --- /dev/null +++ b/bin/java-petstore-google-api-client.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/Java/libraries/google-api-client -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l java -c bin/java-petstore-google-api-client.json -o samples/client/petstore/java/google-api-client -DhideGenerationTimestamp=true" + +echo "Removing files and folders under samples/client/petstore/java/google-api-client/src/main" +rm -rf samples/client/petstore/java/google-api-client/src/main +find samples/client/petstore/java/google-api-client -maxdepth 1 -type f ! -name "README.md" -exec rm {} + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/java-petstore-resteasy-all.sh b/bin/java-petstore-resteasy-all.sh new file mode 100755 index 00000000000..c1a346943f7 --- /dev/null +++ b/bin/java-petstore-resteasy-all.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +./bin/java-petstore-resteasy.sh +./bin/jaxrs-resteasy-eap-petstore-server.sh +./bin/jaxrs-resteasy-eap-java8-petstore-server.sh +./bin/jaxrs-resteasy-joda-petstore-server.sh +./bin/jaxrs-resteasy-eap-joda-petstore-server.sh +./bin/jaxrs-resteasy-petstore-server.sh* diff --git a/bin/java-petstore-retrofit.sh b/bin/java-petstore-retrofit.sh index 3b370fde7dd..c40494cbb9d 100755 --- a/bin/java-petstore-retrofit.sh +++ b/bin/java-petstore-retrofit.sh @@ -26,7 +26,7 @@ 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/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l java -c bin/java-petstore-retrofit.json -o samples/client/petstore/java/retrofit -DhideGenerationTimestamp=true" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l java -c bin/java-petstore-retrofit.json -o samples/client/petstore/java/retrofit -DhideGenerationTimestamp=true,dateLibrary=joda" echo "Removing files and folders under samples/client/petstore/java/retrofit/src/main" rm -rf samples/client/petstore/java/retrofit/src/main diff --git a/bin/java-petstore-retrofit2-all.sh b/bin/java-petstore-retrofit2-all.sh index 607a1116aae..e8f28de689d 100755 --- a/bin/java-petstore-retrofit2-all.sh +++ b/bin/java-petstore-retrofit2-all.sh @@ -1,6 +1,7 @@ #!/bin/sh ./bin/java-petstore-retrofit2-play24.sh +./bin/java-petstore-retrofit2-play25.sh ./bin/java-petstore-retrofit2.sh ./bin/java-petstore-retrofit2rx.sh ./bin/java-petstore-retrofit2rx2.sh diff --git a/bin/java-petstore-retrofit2-play24.json b/bin/java-petstore-retrofit2-play24.json index 45b46996d1c..f5398065c20 100644 --- a/bin/java-petstore-retrofit2-play24.json +++ b/bin/java-petstore-retrofit2-play24.json @@ -1 +1 @@ -{"useBeanValidation":"true","enableBuilderSupport":"true","library":"retrofit2", "usePlay24WS":"true"} +{"useBeanValidation":"true","enableBuilderSupport":"true","library":"retrofit2","usePlayWS":"true","playVersion":"play24","dateLibrary":"java8"} diff --git a/bin/java-petstore-retrofit2-play25.json b/bin/java-petstore-retrofit2-play25.json new file mode 100644 index 00000000000..49eda94f83a --- /dev/null +++ b/bin/java-petstore-retrofit2-play25.json @@ -0,0 +1 @@ +{"useBeanValidation":"true","enableBuilderSupport":"true","library":"retrofit2","usePlayWS":"true","playVersion":"play25"} diff --git a/bin/java-petstore-retrofit2-play25.sh b/bin/java-petstore-retrofit2-play25.sh new file mode 100755 index 00000000000..0a459b47a66 --- /dev/null +++ b/bin/java-petstore-retrofit2-play25.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l java -c bin/java-petstore-retrofit2-play25.json -o samples/client/petstore/java/retrofit2-play25 -DhideGenerationTimestamp=true" + +echo "Removing files and folders under samples/client/petstore/java/retrofit2-play25/src/main" +rm -rf samples/client/petstore/java/retrofit2-play25/src/main +find samples/client/petstore/java/retrofit2-play25 -maxdepth 1 -type f ! -name "README.md" -exec rm {} + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/java-petstore-vertx.json b/bin/java-petstore-vertx.json new file mode 100644 index 00000000000..9f979f78605 --- /dev/null +++ b/bin/java-petstore-vertx.json @@ -0,0 +1,4 @@ +{ + "library": "vertx", + "artifactId": "swagger-petstore-vertx" +} diff --git a/bin/java-petstore-vertx.sh b/bin/java-petstore-vertx.sh new file mode 100755 index 00000000000..a2e9d9baa9a --- /dev/null +++ b/bin/java-petstore-vertx.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/Java/libraries/vertx -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l java -c bin/java-petstore-vertx.json -o samples/client/petstore/java/vertx -DhideGenerationTimestamp=true" + +echo "Removing files and folders under samples/client/petstore/java/vertx/src/main" +rm -rf samples/client/petstore/java/vertx/src/main +find samples/client/petstore/java/vertx -maxdepth 1 -type f ! -name "README.md" -exec rm {} + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/java-play-framework-petstore-server-all.sh b/bin/java-play-framework-petstore-server-all.sh new file mode 100755 index 00000000000..b8dfe47694f --- /dev/null +++ b/bin/java-play-framework-petstore-server-all.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +./bin/java-play-framework-petstore-server.sh +./bin/java-play-framework-petstore-server-controller-only.sh +./bin/java-play-framework-petstore-server-no-bean-validation.sh +./bin/java-play-framework-petstore-server-no-exception-handling.sh +./bin/java-play-framework-petstore-server-no-interface.sh +./bin/java-play-framework-petstore-server-no-swagger-ui.sh +./bin/java-play-framework-petstore-server-no-wrap-calls.sh +./bin/java-play-framework-petstore-server-fake-endpoints.sh \ No newline at end of file diff --git a/bin/java-play-framework-petstore-server-controller-only.sh b/bin/java-play-framework-petstore-server-controller-only.sh new file mode 100755 index 00000000000..b20da2d534d --- /dev/null +++ b/bin/java-play-framework-petstore-server-controller-only.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/JavaPlayFramework -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l java-play-framework -o samples/server/petstore/java-play-framework-controller-only -DhideGenerationTimestamp=true,controllerOnly=true" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/java-play-framework-petstore-server-fake-endpoints.sh b/bin/java-play-framework-petstore-server-fake-endpoints.sh new file mode 100755 index 00000000000..e6caabb4465 --- /dev/null +++ b/bin/java-play-framework-petstore-server-fake-endpoints.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/JavaPlayFramework -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l java-play-framework -o samples/server/petstore/java-play-framework-fake-endpoints -DhideGenerationTimestamp=true" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/java-play-framework-petstore-server-no-bean-validation.sh b/bin/java-play-framework-petstore-server-no-bean-validation.sh new file mode 100755 index 00000000000..27f08046e45 --- /dev/null +++ b/bin/java-play-framework-petstore-server-no-bean-validation.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/JavaPlayFramework -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l java-play-framework -o samples/server/petstore/java-play-framework-no-bean-validation -DhideGenerationTimestamp=true,useBeanValidation=false" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/java-play-framework-petstore-server-no-exception-handling.sh b/bin/java-play-framework-petstore-server-no-exception-handling.sh new file mode 100755 index 00000000000..6d9ad0d037c --- /dev/null +++ b/bin/java-play-framework-petstore-server-no-exception-handling.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/JavaPlayFramework -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l java-play-framework -o samples/server/petstore/java-play-framework-no-exception-handling -DhideGenerationTimestamp=true,handleExceptions=false" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/java-play-framework-petstore-server-no-interface.sh b/bin/java-play-framework-petstore-server-no-interface.sh new file mode 100755 index 00000000000..a3057b61676 --- /dev/null +++ b/bin/java-play-framework-petstore-server-no-interface.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/JavaPlayFramework -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l java-play-framework -o samples/server/petstore/java-play-framework-no-interface -DhideGenerationTimestamp=true,useInterfaces=false" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/java-play-framework-petstore-server-no-swagger-ui.sh b/bin/java-play-framework-petstore-server-no-swagger-ui.sh new file mode 100755 index 00000000000..7a3b461b87a --- /dev/null +++ b/bin/java-play-framework-petstore-server-no-swagger-ui.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/JavaPlayFramework -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l java-play-framework -o samples/server/petstore/java-play-framework-no-swagger-ui -DhideGenerationTimestamp=true,useSwaggerUI=false" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/java-play-framework-petstore-server-no-wrap-calls.sh b/bin/java-play-framework-petstore-server-no-wrap-calls.sh new file mode 100755 index 00000000000..d6e01a4be7f --- /dev/null +++ b/bin/java-play-framework-petstore-server-no-wrap-calls.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/JavaPlayFramework -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l java-play-framework -o samples/server/petstore/java-play-framework-no-wrap-calls -DhideGenerationTimestamp=true,wrapCalls=false" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/jaxrs-cxf-petstore-server-annotated-base-path.sh b/bin/jaxrs-cxf-petstore-server-annotated-base-path.sh index eb79a73b25a..837af96bdaa 100755 --- a/bin/jaxrs-cxf-petstore-server-annotated-base-path.sh +++ b/bin/jaxrs-cxf-petstore-server-annotated-base-path.sh @@ -26,6 +26,6 @@ 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/swagger-codegen/src/main/resources/JavaJaxRS/cxf -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l jaxrs-cxf -o samples/server/petstore/jaxrs-cxf-annotated-base-path -DhideGenerationTimestamp=true,useAnnotatedBasePath=true" +ags="$@ generate --artifact-id swagger-cxf-annotated-basepath -t modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l jaxrs-cxf -o samples/server/petstore/jaxrs-cxf-annotated-base-path -DhideGenerationTimestamp=true,useAnnotatedBasePath=true" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/jaxrs-cxf-petstore-server-non-spring-application.sh b/bin/jaxrs-cxf-petstore-server-non-spring-application.sh index e22a18dea6e..ac85e109deb 100755 --- a/bin/jaxrs-cxf-petstore-server-non-spring-application.sh +++ b/bin/jaxrs-cxf-petstore-server-non-spring-application.sh @@ -26,6 +26,6 @@ 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/swagger-codegen/src/main/resources/JavaJaxRS/cxf -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l jaxrs-cxf -o samples/server/petstore/jaxrs-cxf-non-spring-app -DhideGenerationTimestamp=true,generateNonSpringApplication=true" +ags="$@ generate --artifact-id swagger-cxf-server-non-spring -t modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l jaxrs-cxf -o samples/server/petstore/jaxrs-cxf-non-spring-app -DhideGenerationTimestamp=true,generateNonSpringApplication=true" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/jaxrs-jersey1-petstore-server.sh b/bin/jaxrs-jersey1-petstore-server.sh index 13938c8d1eb..f04b6b9f3d1 100755 --- a/bin/jaxrs-jersey1-petstore-server.sh +++ b/bin/jaxrs-jersey1-petstore-server.sh @@ -26,7 +26,7 @@ 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/swagger-codegen/src/main/resources/JavaJaxRS/libraries/jersey1 -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l jaxrs -o samples/server/petstore/jaxrs/jersey1 -DhideGenerationTimestamp=true,serverPort=8080 --library=jersey1 --artifact-id=swagger-jaxrs-jersey1-server" +ags="$@ generate -t modules/swagger-codegen/src/main/resources/JavaJaxRS -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l jaxrs -o samples/server/petstore/jaxrs/jersey1 -DhideGenerationTimestamp=true,serverPort=8080 --library=jersey1 --artifact-id=swagger-jaxrs-jersey1-server" echo "Removing files and folders under samples/server/petstore/jaxrs/jersey1/src/main" rm -rf samples/server/petstore/jaxrs/jersey1/src/main diff --git a/bin/jaxrs-jersey1-usetags-petstore-server.sh b/bin/jaxrs-jersey1-usetags-petstore-server.sh new file mode 100755 index 00000000000..b115995f6f2 --- /dev/null +++ b/bin/jaxrs-jersey1-usetags-petstore-server.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/JavaJaxRS -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l jaxrs -o samples/server/petstore/jaxrs/jersey1-useTags -DhideGenerationTimestamp=true,serverPort=8080 --library=jersey1 --artifact-id=swagger-jaxrs-jersey1-useTags --additional-properties useTags=true" + +echo "Removing files and folders under samples/server/petstore/jaxrs/jersey1-useTags/src/main" +rm -rf samples/server/petstore/jaxrs/jersey1-useTags/src/main +find samples/server/petstore/jaxrs/jersey1-useTags -maxdepth 1 -type f ! -name "README.md" -exec rm {} + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/jaxrs-resteasy-eap-java8-petstore-server.json b/bin/jaxrs-resteasy-eap-java8-petstore-server.json new file mode 100644 index 00000000000..3d80aca58ce --- /dev/null +++ b/bin/jaxrs-resteasy-eap-java8-petstore-server.json @@ -0,0 +1,3 @@ +{ + "dateLibrary": "java8" +} diff --git a/bin/jaxrs-resteasy-eap-java8-petstore-server.sh b/bin/jaxrs-resteasy-eap-java8-petstore-server.sh new file mode 100755 index 00000000000..b5897c06382 --- /dev/null +++ b/bin/jaxrs-resteasy-eap-java8-petstore-server.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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 --artifact-id swagger-jaxrs-resteasy-eap-java8-server -t modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l jaxrs-resteasy-eap -o samples/server/petstore/jaxrs-resteasy/eap-java8 -DhideGenerationTimestamp=true -c ./bin/jaxrs-resteasy-eap-java8-petstore-server.json" + +echo "Removing files and folders under samples/server/petstore/jaxrs-resteasy/eap-java8/src/main" +rm -rf samples/server/petstore/jaxrs-resteasy/eap-java8/src/main +find samples/server/petstore/jaxrs-resteasy/eap-java8 -maxdepth 1 -type f ! -name "README.md" -exec rm {} + + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/jaxrs-spec-petstore-server-interface.sh b/bin/jaxrs-spec-petstore-server-interface.sh new file mode 100755 index 00000000000..d47718f2788 --- /dev/null +++ b/bin/jaxrs-spec-petstore-server-interface.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l jaxrs-spec -o samples/server/petstore/jaxrs-spec-interface +-DhideGenerationTimestamp=true +-DserializableModel=true +-DinterfaceOnly=true" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/jaxrs-spec-petstore-server.sh b/bin/jaxrs-spec-petstore-server.sh index 9482e1a8972..5350bab881c 100755 --- a/bin/jaxrs-spec-petstore-server.sh +++ b/bin/jaxrs-spec-petstore-server.sh @@ -27,6 +27,7 @@ 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/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l jaxrs-spec -o samples/server/petstore/jaxrs-spec --DhideGenerationTimestamp=true" +-DhideGenerationTimestamp=true +-DserializableModel=true" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/jaxrs-usetags-petstore-server.sh b/bin/jaxrs-usetags-petstore-server.sh new file mode 100755 index 00000000000..8d2595609f6 --- /dev/null +++ b/bin/jaxrs-usetags-petstore-server.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/JavaJaxRS -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l jaxrs -o samples/server/petstore/jaxrs/jersey2-useTags -DhideGenerationTimestamp=true,serverPort=8080 --artifact-id=swagger-jaxrs-jersey2-useTags --additional-properties useTags=true" + +echo "Removing files and folders under samples/server/petstore/jaxrs/jersey2-useTags/src/main" +rm -rf samples/server/petstore/jaxrs/jersey2-useTags/src/main +find samples/server/petstore/jaxrs/jersey2-useTags -maxdepth 1 -type f ! -name "README.md" -exec rm {} + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/lua-petstore.sh b/bin/lua-petstore.sh new file mode 100755 index 00000000000..2b8f732d14b --- /dev/null +++ b/bin/lua-petstore.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/lua -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l lua -o samples/client/petstore/lua -DpackageName=petstore $@" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/nancyfx-petstore-server-async.sh b/bin/nancyfx-petstore-server-async.sh new file mode 100755 index 00000000000..b07c010731f --- /dev/null +++ b/bin/nancyfx-petstore-server-async.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/nancyfx -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l nancyfx -o samples/server/petstore/nancyfx-async --additional-properties packageGuid={768B8DC6-54EE-4D40-9B20-7857E1D742A4},asyncServer=true" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/nancyfx-petstore-server.sh b/bin/nancyfx-petstore-server.sh index 38db6514ded..13516ee67e9 100755 --- a/bin/nancyfx-petstore-server.sh +++ b/bin/nancyfx-petstore-server.sh @@ -26,6 +26,6 @@ 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/swagger-codegen/src/main/resources/nancyfx -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l nancyfx -o samples/server/petstore/nancyfx --additional-properties packageGuid={768B8DC6-54EE-4D40-9B20-7857E1D742A4}" +ags="generate $@ -t modules/swagger-codegen/src/main/resources/nancyfx -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l nancyfx -o samples/server/petstore/nancyfx --additional-properties packageGuid={768B8DC6-54EE-4D40-9B20-7857E1D742A4},asyncServer=false" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/nodejs-petstore-server.sh b/bin/nodejs-petstore-server.sh index a23f108b605..4abd112408a 100755 --- a/bin/nodejs-petstore-server.sh +++ b/bin/nodejs-petstore-server.sh @@ -26,6 +26,6 @@ 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/swagger-codegen/src/test/resources/2_0/petstore.yaml -l nodejs-server -o samples/server/petstore/nodejs -Dservice" +ags="$@ generate -t modules/swagger-codegen/src/main/resources/nodejs -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l nodejs-server -o samples/server/petstore/nodejs -Dservice" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/php-silex-petstore-server.sh b/bin/php-silex-petstore-server.sh new file mode 100755 index 00000000000..85b412517ee --- /dev/null +++ b/bin/php-silex-petstore-server.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/php-silex -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l php-silex -o samples/server/petstore/php-silex" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/php-symfony-petstore.sh b/bin/php-symfony-petstore.sh new file mode 100755 index 00000000000..17d9418b386 --- /dev/null +++ b/bin/php-symfony-petstore.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +SCRIPT="$0" + +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 + +# Make sure that the working directory is the root dir +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd "${SCRIPT_DIR}/../" + +if [ ! -d "${APP_DIR}" ]; then + APP_DIR=`dirname "$SCRIPT"`/.. + APP_DIR=`cd "${APP_DIR}"; pwd` +fi + +# Make sure that we are regenerating the sample by removing any existing target directory +TARGET_DIR="$SCRIPT_DIR/../samples/server/petstore/php-symfony" +if [ -d "$TARGET_DIR" ]; then + rm -rf $TARGET_DIR +fi + +executable="$SCRIPT_DIR/../modules/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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 $SCRIPT_DIR/../modules/swagger-codegen/src/main/resources/php-symfony -i $SCRIPT_DIR/../modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l php-symfony -o $TARGET_DIR $@" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/powershell-petstore.sh b/bin/powershell-petstore.sh index 6e4c9b533aa..9d2803414a0 100755 --- a/bin/powershell-petstore.sh +++ b/bin/powershell-petstore.sh @@ -26,6 +26,6 @@ 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/swagger-codegen/src/main/resources/powershell -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l powershell -o samples/client/petstore/powershell_test --additional-properties packageGuid=a27b908d-2a20-467f-bc32-af6f3a654ac5,csharpClientPath=\$ScriptDir\..\..\petstore\csharp\SwaggerClient $@" +ags="generate -t modules/swagger-codegen/src/main/resources/powershell -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l powershell -o samples/client/petstore/powershell --additional-properties packageGuid=a27b908d-2a20-467f-bc32-af6f3a654ac5,csharpClientPath=\$ScriptDir\..\..\petstore\csharp\SwaggerClient $@" java ${JAVA_OPTS} -jar ${executable} ${ags} diff --git a/bin/python-asyncio-petstore.sh b/bin/python-asyncio-petstore.sh new file mode 100755 index 00000000000..83f23b0ee9b --- /dev/null +++ b/bin/python-asyncio-petstore.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/python -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l python -o samples/client/petstore/python-asyncio -DpackageName=petstore_api --library asyncio $@" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/python-flask-all.sh b/bin/python-flask-all.sh new file mode 100755 index 00000000000..ed4ca88d286 --- /dev/null +++ b/bin/python-flask-all.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +./bin/python-flask-petstore.sh +./bin/python-flask-petstore-python2.sh diff --git a/bin/flaskConnexion-python2.sh b/bin/python-flask-petstore-python2.sh similarity index 100% rename from bin/flaskConnexion-python2.sh rename to bin/python-flask-petstore-python2.sh diff --git a/bin/flaskConnexion.sh b/bin/python-flask-petstore.sh similarity index 100% rename from bin/flaskConnexion.sh rename to bin/python-flask-petstore.sh diff --git a/bin/python-tornado-petstore.sh b/bin/python-tornado-petstore.sh new file mode 100755 index 00000000000..993623cbfb2 --- /dev/null +++ b/bin/python-tornado-petstore.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/python -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l python -o samples/client/petstore/python-tornado -DpackageName=petstore_api --library tornado $@" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/r-petstore.sh b/bin/r-petstore.sh new file mode 100755 index 00000000000..6e00e6010e2 --- /dev/null +++ b/bin/r-petstore.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/r -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l r -o samples/client/petstore/r_test -DpackageName=petstore $@" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/run-all-petstore b/bin/run-all-petstore index be5901e0697..755ab2f749d 100755 --- a/bin/run-all-petstore +++ b/bin/run-all-petstore @@ -8,7 +8,7 @@ echo "Please press CTRL+C to stop or the script will continue in 10 seconds." sleep 10 -for SCRIPT in ./bin/*.sh +for SCRIPT in `ls -l ./bin/*.sh | grep -v all` do if [ -f $SCRIPT -a -x $SCRIPT ] then diff --git a/bin/rust-petstore.sh b/bin/rust-petstore.sh new file mode 100755 index 00000000000..6ccfa708269 --- /dev/null +++ b/bin/rust-petstore.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/rust -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l rust -o samples/client/petstore/rust -DpackageName=petstore_client $@" + +java ${JAVA_OPTS} -jar ${executable} ${ags} diff --git a/bin/rust-server-petstore.sh b/bin/rust-server-petstore.sh new file mode 100755 index 00000000000..e7e655d4812 --- /dev/null +++ b/bin/rust-server-petstore.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/rust-server -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l rust-server -o samples/server/petstore/rust-server -DpackageName=petstore_api" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/scala-async-petstore.sh b/bin/scala-async-petstore.sh deleted file mode 100755 index 5456c98b27a..00000000000 --- a/bin/scala-async-petstore.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh - -SCRIPT="$0" - -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/swagger-codegen-cli/target/swagger-codegen-cli.jar" - -if [ ! -f "$executable" ] -then - mvn 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/swagger-codegen/src/main/resources/asyncscala -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l async-scala -o samples/client/petstore/async-scala" - -java $JAVA_OPTS -jar $executable $ags diff --git a/bin/scala-lagom-server-petstore.sh b/bin/scala-lagom-server-petstore.sh new file mode 100755 index 00000000000..12abeb043d4 --- /dev/null +++ b/bin/scala-lagom-server-petstore.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/scala-lagom-server -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l scala-lagom-server -o samples/server/petstore/scala-lagom-server" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/scalaz-petstore.sh b/bin/scalaz-petstore.sh new file mode 100755 index 00000000000..266e8cbf9e7 --- /dev/null +++ b/bin/scalaz-petstore.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/scalaz -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l scalaz -o samples/client/petstore/scalaz" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/security/silex-petstore-server.sh b/bin/security/silex-petstore-server.sh index a939c2da9a8..229eac1391c 100755 --- a/bin/security/silex-petstore-server.sh +++ b/bin/security/silex-petstore-server.sh @@ -26,6 +26,6 @@ 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/swagger-codegen/src/main/resources/silex -i modules/swagger-codegen/src/test/resources/2_0/petstore-security-test.yaml -l silex-PHP -o samples/server/petstore-security-test/silex" +ags="$@ generate -t modules/swagger-codegen/src/main/resources/php-silex -i modules/swagger-codegen/src/test/resources/2_0/petstore-security-test.yaml -l php-silex -o samples/server/petstore-security-test/silex" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/security/typescript-angular2.sh b/bin/security/typescript-angular2.sh index cdf5b44b695..192932f1b3b 100755 --- a/bin/security/typescript-angular2.sh +++ b/bin/security/typescript-angular2.sh @@ -26,6 +26,6 @@ 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/swagger-codegen/src/main/resources/typescript-angular2 -i modules/swagger-codegen/src/test/resources/2_0/petstore-security-test.yaml -l typescript-angular2 -o samples/client/petstore-security-test/typescript-angular2" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore-security-test.yaml -l typescript-angular -o samples/client/petstore-security-test/typescript-angular2" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/security/typescript-fetch-petstore.sh b/bin/security/typescript-fetch-petstore.sh new file mode 100755 index 00000000000..17c61cf26ed --- /dev/null +++ b/bin/security/typescript-fetch-petstore.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/typescript-fetch -i modules/swagger-codegen/src/test/resources/2_0/petstore-security-test.yaml -l typescript-fetch -o samples/client/petstore-security-test/typescript-fetch" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/security/typescript-fetch.sh b/bin/security/typescript-fetch.sh deleted file mode 100755 index 6b8757a5fb9..00000000000 --- a/bin/security/typescript-fetch.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh - -SCRIPT="$0" - -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/swagger-codegen-cli/target/swagger-codegen-cli.jar" - -if [ ! -f "$executable" ] -then - mvn 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/swagger-codegen/src/main/resources/TypeScript-Fetch -i modules/swagger-codegen/src/test/resources/2_0/petstore-security-test.yaml -l typescript-fetch -o samples/client/petstore-security-test/typescript-fetch" - -java $JAVA_OPTS -jar $executable $ags diff --git a/bin/security/windows/csharp-petstore.bat b/bin/security/windows/csharp-petstore.bat new file mode 100644 index 00000000000..2bb9ba99d00 --- /dev/null +++ b/bin/security/windows/csharp-petstore.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules/swagger-codegen/src/test/resources/2_0/petstore-security-test.yaml -l csharp -o samples/client/petstore-security-test/csharp/SwaggerClient --additional-properties packageGuid={8CE139DF-64BC-4591-85F8-8506C2B67514} + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/silex-petstore-server.sh b/bin/silex-petstore-server.sh deleted file mode 100755 index c273102828b..00000000000 --- a/bin/silex-petstore-server.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh - -SCRIPT="$0" - -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/swagger-codegen-cli/target/swagger-codegen-cli.jar" - -if [ ! -f "$executable" ] -then - mvn 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/swagger-codegen/src/main/resources/silex -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l silex-PHP -o samples/server/petstore/silex" - -java $JAVA_OPTS -jar $executable $ags diff --git a/bin/swift3-petstore-all.sh b/bin/swift3-petstore-all.sh index 4f38026418d..a27fe261859 100755 --- a/bin/swift3-petstore-all.sh +++ b/bin/swift3-petstore-all.sh @@ -38,3 +38,11 @@ java $JAVA_OPTS -jar $executable $ags ags="$@ generate -t modules/swagger-codegen/src/main/resources/swift3 -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l swift3 -c ./bin/swift3-petstore-rxswift.json -o samples/client/petstore/swift3/rxswift" echo "#### Petstore Swift API client (rxswift) ####" java $JAVA_OPTS -jar $executable $ags + +ags="$@ generate -t modules/swagger-codegen/src/main/resources/swift3 -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l swift3 -c ./bin/swift3-petstore-unwraprequired.json -o samples/client/petstore/swift3/unwraprequired" +echo "#### Petstore Swift API client (unwraprequired) ####" +java $JAVA_OPTS -jar $executable $ags + +ags="$@ generate -t modules/swagger-codegen/src/main/resources/swift3 -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l swift3 -c ./bin/swift3-petstore-objcCompatible.json -o samples/client/petstore/swift3/objcCompatible" +echo "#### Petstore Swift API client (objcCompatible) ####" +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/swift3-petstore-objcCompatible.json b/bin/swift3-petstore-objcCompatible.json new file mode 100644 index 00000000000..e56770806b9 --- /dev/null +++ b/bin/swift3-petstore-objcCompatible.json @@ -0,0 +1,7 @@ +{ + "podSummary": "PetstoreClient", + "podHomepage": "https://github.com/swagger-api/swagger-codegen", + "podAuthors": "", + "projectName": "PetstoreClient", + "objcCompatible": true +} \ No newline at end of file diff --git a/bin/swift3-petstore-objcCompatible.sh b/bin/swift3-petstore-objcCompatible.sh new file mode 100755 index 00000000000..d774688fdab --- /dev/null +++ b/bin/swift3-petstore-objcCompatible.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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 -v -t modules/swagger-codegen/src/main/resources/swift3 -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l swift3 -c ./bin/swift3-petstore-objcCompatible.json -o samples/client/petstore/swift3/objcCompatible $@" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/swift3-petstore-unwraprequired.json b/bin/swift3-petstore-unwraprequired.json new file mode 100644 index 00000000000..f077c429ee8 --- /dev/null +++ b/bin/swift3-petstore-unwraprequired.json @@ -0,0 +1,7 @@ +{ + "podSummary": "PetstoreClient", + "podHomepage": "https://github.com/swagger-api/swagger-codegen", + "podAuthors": "", + "projectName": "PetstoreClient", + "unwrapRequired": true +} \ No newline at end of file diff --git a/bin/swift3-petstore-unwraprequired.sh b/bin/swift3-petstore-unwraprequired.sh new file mode 100755 index 00000000000..65355a18ecf --- /dev/null +++ b/bin/swift3-petstore-unwraprequired.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/swift3 -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l swift3 -c ./bin/swift3-petstore-unwraprequired.json -o samples/client/petstore/swift3/unwraprequired" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/swift4-all.sh b/bin/swift4-all.sh new file mode 100755 index 00000000000..bb9110f635e --- /dev/null +++ b/bin/swift4-all.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/swift4 -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l swift4 -c ./bin/swift4-petstore.json -o samples/client/petstore/swift4/default" + +echo "#### Petstore Swift API client (default) ####" +java $JAVA_OPTS -jar $executable $ags + +ags="$@ generate -t modules/swagger-codegen/src/main/resources/swift4 -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l swift4 -c ./bin/swift4-petstore-promisekit.json -o samples/client/petstore/swift4/promisekit" +echo "#### Petstore Swift API client (promisekit) ####" +java $JAVA_OPTS -jar $executable $ags + +ags="$@ generate -t modules/swagger-codegen/src/main/resources/swift4 -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l swift4 -c ./bin/swift4-petstore-rxswift.json -o samples/client/petstore/swift4/rxswift" +echo "#### Petstore Swift API client (rxswift) ####" +java $JAVA_OPTS -jar $executable $ags + +ags="$@ generate -t modules/swagger-codegen/src/main/resources/swift4 -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l swift4 -c ./bin/swift4-petstore-objcCompatible.json -o samples/client/petstore/swift4/objcCompatible" +echo "#### Petstore Swift API client (objcCompatible) ####" +java $JAVA_OPTS -jar $executable $ags + +ags="$@ generate -t modules/swagger-codegen/src/main/resources/swift4 -i modules/swagger-codegen/src/test/resources/2_0/swift4Test.json -l swift4 -c ./bin/swift4-test.json -o samples/client/test/swift4/default" +echo "#### Swift4Test Swift API client (default) ####" +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/swift4-petstore-all.sh b/bin/swift4-petstore-all.sh index 88d870bb582..28bfc0416c1 100755 --- a/bin/swift4-petstore-all.sh +++ b/bin/swift4-petstore-all.sh @@ -38,3 +38,7 @@ java $JAVA_OPTS -jar $executable $ags ags="$@ generate -t modules/swagger-codegen/src/main/resources/swift4 -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l swift4 -c ./bin/swift4-petstore-rxswift.json -o samples/client/petstore/swift4/rxswift" echo "#### Petstore Swift API client (rxswift) ####" java $JAVA_OPTS -jar $executable $ags + +ags="$@ generate -t modules/swagger-codegen/src/main/resources/swift4 -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l swift4 -c ./bin/swift4-petstore-objcCompatible.json -o samples/client/petstore/swift4/objcCompatible" +echo "#### Petstore Swift API client (objcCompatible) ####" +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/swift4-petstore-objcCompatible.json b/bin/swift4-petstore-objcCompatible.json new file mode 100644 index 00000000000..e56770806b9 --- /dev/null +++ b/bin/swift4-petstore-objcCompatible.json @@ -0,0 +1,7 @@ +{ + "podSummary": "PetstoreClient", + "podHomepage": "https://github.com/swagger-api/swagger-codegen", + "podAuthors": "", + "projectName": "PetstoreClient", + "objcCompatible": true +} \ No newline at end of file diff --git a/bin/swift4-petstore-objcCompatible.sh b/bin/swift4-petstore-objcCompatible.sh new file mode 100755 index 00000000000..45c212368f2 --- /dev/null +++ b/bin/swift4-petstore-objcCompatible.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/swift4 -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l swift4 -c ./bin/swift4-petstore-objcCompatible.json -o samples/client/petstore/swift4/objcCompatible" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/swift4-test.json b/bin/swift4-test.json new file mode 100644 index 00000000000..1734cf8d998 --- /dev/null +++ b/bin/swift4-test.json @@ -0,0 +1,6 @@ +{ + "podSummary": "TestClient", + "podHomepage": "https://github.com/swagger-api/swagger-codegen", + "podAuthors": "", + "projectName": "TestClient" +} \ No newline at end of file diff --git a/bin/swift4-test.sh b/bin/swift4-test.sh new file mode 100755 index 00000000000..4f9e8f0248c --- /dev/null +++ b/bin/swift4-test.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/main/resources/swift4 -i modules/swagger-codegen/src/test/resources/2_0/swift4Test.json -l swift4 -c ./bin/swift4-test.json -o samples/client/test/swift4/default" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-angular-petstore-all.sh b/bin/typescript-angular-petstore-all.sh new file mode 100755 index 00000000000..c3cdf8ba34f --- /dev/null +++ b/bin/typescript-angular-petstore-all.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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" + +echo "Typescript Petstore API client (default)" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular -o samples/client/petstore/typescript-angular-v2/default --additional-properties ngVersion=2" +java $JAVA_OPTS -jar $executable $ags + +echo "Typescript Petstore API client (npm setting)" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular -c bin/typescript-petstore-npm.json -o samples/client/petstore/typescript-angular-v2/npm --additional-properties ngVersion=2" +java $JAVA_OPTS -jar $executable $ags + +echo "Typescript Petstore API client (with interfaces generated)" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular -o samples/client/petstore/typescript-angular-v2/with-interfaces -D withInterfaces=true --additional-properties ngVersion=2" +java $JAVA_OPTS -jar $executable $ags + +echo "Typescript Petstore API client (v4 { Adding InjectionToken Over OpaqueToken })" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular -c bin/typescript-petstore-npm.json -o samples/client/petstore/typescript-angular-v4/npm --additional-properties ngVersion=4" +java $JAVA_OPTS -jar $executable $ags + +echo "Typescript Petstore API client (v4.3 { Adding HttpClientModule over HttpModule })" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular -c bin/typescript-petstore-npm.json -o samples/client/petstore/typescript-angular-v4.3/npm --additional-properties ngVersion=4.3" +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-angular-petstore.sh b/bin/typescript-angular-petstore.sh deleted file mode 100755 index 60c10790c90..00000000000 --- a/bin/typescript-angular-petstore.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh - -SCRIPT="$0" - -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/swagger-codegen-cli/target/swagger-codegen-cli.jar" - -if [ ! -f "$executable" ] -then - mvn 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/swagger-codegen/src/test/resources/2_0/petstore.json -l typescript-angular -o samples/client/petstore/typescript-angular" - -java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-angular-v2-petstore-interfaces.sh b/bin/typescript-angular-v2-petstore-interfaces.sh new file mode 100755 index 00000000000..bbc8861fce3 --- /dev/null +++ b/bin/typescript-angular-v2-petstore-interfaces.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular -o samples/client/petstore/typescript-angular-v2/with-interfaces -D withInterfaces=true --additional-properties ngVersion=2" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-angular-v2-petstore-with-npm.sh b/bin/typescript-angular-v2-petstore-with-npm.sh new file mode 100755 index 00000000000..c19bead1557 --- /dev/null +++ b/bin/typescript-angular-v2-petstore-with-npm.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular -c bin/typescript-petstore-npm.json -o samples/client/petstore/typescript-angular-v2/npm --additional-properties ngVersion=2" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-angular-v2-petstore.sh b/bin/typescript-angular-v2-petstore.sh new file mode 100755 index 00000000000..bdac72ae304 --- /dev/null +++ b/bin/typescript-angular-v2-petstore.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular -o samples/client/petstore/typescript-angular-v2/default --additional-properties ngVersion=2" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-angular-v4-petstore-with-npm.sh b/bin/typescript-angular-v4-petstore-with-npm.sh new file mode 100755 index 00000000000..f0fcba26874 --- /dev/null +++ b/bin/typescript-angular-v4-petstore-with-npm.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular -c bin/typescript-petstore-npm.json -o samples/client/petstore/typescript-angular-v4/npm --additional-properties ngVersion=4" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-angular-v4.3-petstore-with-npm.sh b/bin/typescript-angular-v4.3-petstore-with-npm.sh new file mode 100755 index 00000000000..35f8447c5de --- /dev/null +++ b/bin/typescript-angular-v4.3-petstore-with-npm.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular -c bin/typescript-petstore-npm.json -o samples/client/petstore/typescript-angular-v4.3/npm --additional-properties ngVersion=4.3" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-angular2-petstore-all.sh b/bin/typescript-angular2-petstore-all.sh deleted file mode 100755 index 1c805738b52..00000000000 --- a/bin/typescript-angular2-petstore-all.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh - -SCRIPT="$0" - -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/swagger-codegen-cli/target/swagger-codegen-cli.jar" - -if [ ! -f "$executable" ] -then - mvn 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" - -echo "Typescript Petstore API client (default)" -ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular2 -o samples/client/petstore/typescript-angular2/default" -java $JAVA_OPTS -jar $executable $ags - -echo "Typescript Petstore API client (npm setting)" -ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular2 -c bin/typescript-petstore-npm.json -o samples/client/petstore/typescript-angular2/npm" -java $JAVA_OPTS -jar $executable $ags - -echo "Typescript Petstore API client (with interfaces generated)" -ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular2 -o samples/client/petstore/typescript-angular2/with-interfaces -D withInterfaces=true" -java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-angular2-petstore-interfaces.sh b/bin/typescript-angular2-petstore-interfaces.sh deleted file mode 100755 index 8fc81f13d68..00000000000 --- a/bin/typescript-angular2-petstore-interfaces.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh - -SCRIPT="$0" - -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/swagger-codegen-cli/target/swagger-codegen-cli.jar" - -if [ ! -f "$executable" ] -then - mvn 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/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular2 -o samples/client/petstore/typescript-angular2/with-interfaces -D withInterfaces=true" - -java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-angular2-petstore-with-npm.sh b/bin/typescript-angular2-petstore-with-npm.sh deleted file mode 100755 index 0b96d7bd732..00000000000 --- a/bin/typescript-angular2-petstore-with-npm.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh - -SCRIPT="$0" - -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/swagger-codegen-cli/target/swagger-codegen-cli.jar" - -if [ ! -f "$executable" ] -then - mvn 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/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular2 -c bin/typescript-petstore-npm.json -o samples/client/petstore/typescript-angular2/npm" - -java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-angular2-petstore.sh b/bin/typescript-angular2-petstore.sh deleted file mode 100755 index dbb00a91344..00000000000 --- a/bin/typescript-angular2-petstore.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh - -SCRIPT="$0" - -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/swagger-codegen-cli/target/swagger-codegen-cli.jar" - -if [ ! -f "$executable" ] -then - mvn 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/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular2 -o samples/client/petstore/typescript-angular2/default" - -java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-angularjs-petstore.sh b/bin/typescript-angularjs-petstore.sh new file mode 100755 index 00000000000..107e6291149 --- /dev/null +++ b/bin/typescript-angularjs-petstore.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angularjs -o samples/client/petstore/typescript-angularjs" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-aurelia-petstore.sh b/bin/typescript-aurelia-petstore.sh new file mode 100755 index 00000000000..ff96244a24c --- /dev/null +++ b/bin/typescript-aurelia-petstore.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-aurelia -o samples/client/petstore/typescript-aurelia/default" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-fetch-petstore-target-es6.json b/bin/typescript-fetch-petstore-target-es6.json index 83914bd569c..b87d9e8521d 100644 --- a/bin/typescript-fetch-petstore-target-es6.json +++ b/bin/typescript-fetch-petstore-target-es6.json @@ -1,3 +1,7 @@ { + "npmName": "@swagger/typescript-fetch-petstore", + "npmVersion": "1.0.0", + "npmRepository" : "https://skimdb.npmjs.com/registry", + "snapshot" : false, "supportsES6": true } diff --git a/bin/typescript-fetch-petstore-target-es6.sh b/bin/typescript-fetch-petstore-target-es6.sh index 84a6562eeb6..391859c4d4b 100755 --- a/bin/typescript-fetch-petstore-target-es6.sh +++ b/bin/typescript-fetch-petstore-target-es6.sh @@ -26,6 +26,6 @@ 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/swagger-codegen/src/test/resources/2_0/petstore.json -l typescript-fetch -c bin/typescript-fetch-petstore-target-es6.json -o samples/client/petstore/typescript-fetch/builds/es6-target" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-fetch -c bin/typescript-fetch-petstore-target-es6.json -o samples/client/petstore/typescript-fetch/builds/es6-target" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-fetch-petstore-with-npm-version.json b/bin/typescript-fetch-petstore-with-npm-version.json index b8193c8fa74..40b04115a3f 100644 --- a/bin/typescript-fetch-petstore-with-npm-version.json +++ b/bin/typescript-fetch-petstore-with-npm-version.json @@ -1,4 +1,6 @@ { "npmName": "@swagger/typescript-fetch-petstore", - "npmVersion": "0.0.1" + "npmVersion": "1.0.0", + "npmRepository" : "https://skimdb.npmjs.com/registry", + "snapshot" : false } diff --git a/bin/typescript-fetch-petstore-with-npm-version.sh b/bin/typescript-fetch-petstore-with-npm-version.sh index fd9225f0e72..7d5706f657e 100755 --- a/bin/typescript-fetch-petstore-with-npm-version.sh +++ b/bin/typescript-fetch-petstore-with-npm-version.sh @@ -26,6 +26,6 @@ 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/swagger-codegen/src/test/resources/2_0/petstore.json -l typescript-fetch -c bin/typescript-fetch-petstore-with-npm-version.json -o samples/client/petstore/typescript-fetch/builds/with-npm-version" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-fetch -c bin/typescript-fetch-petstore-with-npm-version.json -o samples/client/petstore/typescript-fetch/builds/with-npm-version" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-fetch-petstore.sh b/bin/typescript-fetch-petstore.sh index 50d56f34609..241258c4071 100755 --- a/bin/typescript-fetch-petstore.sh +++ b/bin/typescript-fetch-petstore.sh @@ -26,6 +26,6 @@ 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/swagger-codegen/src/test/resources/2_0/petstore.json -l typescript-fetch -o samples/client/petstore/typescript-fetch/builds/default" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-fetch -o samples/client/petstore/typescript-fetch/builds/default" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-jquery-all.sh b/bin/typescript-jquery-all.sh deleted file mode 100755 index 7203fa099a5..00000000000 --- a/bin/typescript-jquery-all.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/sh - -SCRIPT="$0" - -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/swagger-codegen-cli/target/swagger-codegen-cli.jar" - -if [ ! -f "$executable" ] -then - mvn 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" - -echo "Typescript jquery Petstore API client (default setting)" -ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.json -l typescript-jquery -o samples/client/petstore/typescript-jquery/default" -java $JAVA_OPTS -jar $executable $ags - -echo "Typescript jquery Petstore API client with npm setting" -ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.json -l typescript-jquery -c bin/typescript-petstore-npm.json -o samples/client/petstore/typescript-jquery/npm" -java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-jquery-petstore-all.sh b/bin/typescript-jquery-petstore-all.sh new file mode 100755 index 00000000000..1a00eb1ce1c --- /dev/null +++ b/bin/typescript-jquery-petstore-all.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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" + +echo "Typescript Petstore API client (default)" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-jquery -o samples/client/petstore/typescript-jquery/default" +java $JAVA_OPTS -jar $executable $ags + +echo "Typescript Petstore API client (npm setting)" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-jquery -c bin/typescript-jquery-petstore-npm.json -o samples/client/petstore/typescript-jquery/npm" +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-jquery-petstore-npm.json b/bin/typescript-jquery-petstore-npm.json new file mode 100644 index 00000000000..c1ec66df6ea --- /dev/null +++ b/bin/typescript-jquery-petstore-npm.json @@ -0,0 +1,6 @@ +{ + "npmName": "@swagger/jquery-typescript-petstore", + "npmVersion": "0.0.1", + "npmRepository" : "https://skimdb.npmjs.com/registry", + "snapshot" : false +} diff --git a/bin/typescript-jquery-petstore-with-npm.sh b/bin/typescript-jquery-petstore-with-npm.sh new file mode 100755 index 00000000000..576d2ff5c09 --- /dev/null +++ b/bin/typescript-jquery-petstore-with-npm.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-jquery -c bin/typescript-jquery-petstore-npm.json -o samples/client/petstore/typescript-jquery/npm" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-jquery-petstore.sh b/bin/typescript-jquery-petstore.sh new file mode 100755 index 00000000000..fcce07152dd --- /dev/null +++ b/bin/typescript-jquery-petstore.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +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/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn 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/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-jquery -o samples/client/petstore/typescript-jquery/default" + +java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-node-petstore-all.sh b/bin/typescript-node-petstore-all.sh index 65f25060725..df9dcb034c4 100755 --- a/bin/typescript-node-petstore-all.sh +++ b/bin/typescript-node-petstore-all.sh @@ -28,9 +28,9 @@ fi export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties" echo "Typescript node Petstore API client (default setting)" -ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.json -l typescript-node -o samples/client/petstore/typescript-node/default" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-node -o samples/client/petstore/typescript-node/default" java $JAVA_OPTS -jar $executable $ags echo "Typescript node Petstore API client with npm setting" -ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.json -l typescript-node -c bin/typescript-petstore-npm.json -o samples/client/petstore/typescript-node/npm" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-node -c bin/typescript-petstore-npm.json -o samples/client/petstore/typescript-node/npm" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-node-petstore-with-npm.sh b/bin/typescript-node-petstore-with-npm.sh index e369be758e7..00972be059d 100755 --- a/bin/typescript-node-petstore-with-npm.sh +++ b/bin/typescript-node-petstore-with-npm.sh @@ -26,6 +26,6 @@ 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/swagger-codegen/src/test/resources/2_0/petstore.json -l typescript-node -c bin/typescript-petstore-npm.json -o samples/client/petstore/typescript-node/npm" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-node -c bin/typescript-petstore-npm.json -o samples/client/petstore/typescript-node/npm" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-node-petstore.sh b/bin/typescript-node-petstore.sh index c9d16d96113..f97d4a31657 100755 --- a/bin/typescript-node-petstore.sh +++ b/bin/typescript-node-petstore.sh @@ -26,6 +26,6 @@ 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/swagger-codegen/src/test/resources/2_0/petstore.json -l typescript-node -o samples/client/petstore/typescript-node/default" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-node -o samples/client/petstore/typescript-node/default" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/windows/async-scala-petstore.bat b/bin/windows/async-scala-petstore.bat deleted file mode 100755 index be7caec9f6e..00000000000 --- a/bin/windows/async-scala-petstore.bat +++ /dev/null @@ -1,10 +0,0 @@ -set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-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\swagger-codegen\src\test\resources\2_0\petstore.yaml -l async-scala -o samples\client\petstore\async-scala - -java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/csharp-dotnet2-petstore.bat b/bin/windows/csharp-dotnet2-petstore.bat new file mode 100755 index 00000000000..90f5ddf7910 --- /dev/null +++ b/bin/windows/csharp-dotnet2-petstore.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l csharp-dotnet2 -o samples/client/petstore/csharp-dotnet2/SwaggerClientTest/Lib/SwaggerClient --additional-properties hideGenerationTimestamp=true + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/csharp-petstore-all.bat b/bin/windows/csharp-petstore-all.bat index ecef022653a..04e79076ba3 100755 --- a/bin/windows/csharp-petstore-all.bat +++ b/bin/windows/csharp-petstore-all.bat @@ -1,8 +1,16 @@ REM C# Petstore API client -.\bin\windows\csharp-petstore.bat +call .\bin\windows\csharp-petstore.bat REM C# Petstore API client with PropertyChanged -.\bin\windows\csharp-property-changed-petstore.bat +call .\bin\windows\csharp-property-changed-petstore.bat REM C# Petstore API client (v5.0 for .net standarnd 1.3+) -.\bin\windows\csharp-petstore-netstandard.bat +call .\bin\windows\csharp-petstore-netstandard.bat + +call .\bin\windows\csharp-dotnet2-petstore.bat + +call .\bin\windows\csharp-petstore-netcore-project.bat + +call .\bin\windows\csharp-property-changed-petstore.bat + +call .\bin\windows\csharp-petstore-net-40.bat \ No newline at end of file diff --git a/bin/windows/csharp-petstore-net-40.bat b/bin/windows/csharp-petstore-net-40.bat new file mode 100644 index 00000000000..113b2efd987 --- /dev/null +++ b/bin/windows/csharp-petstore-net-40.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l csharp -o samples/client/petstore/csharp/SwaggerClientNet40 --additional-properties packageGuid={321C8C3F-0156-40C1-AE42-D59761FB9B6C} -c ./bin/csharp-petstore-net-40.json + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/csharp-property-changed-petstore.bat b/bin/windows/csharp-property-changed-petstore.bat index 4e92f348a49..a09f557def3 100644 --- a/bin/windows/csharp-property-changed-petstore.bat +++ b/bin/windows/csharp-property-changed-petstore.bat @@ -5,6 +5,6 @@ If Not Exist %executable% ( ) REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -set ags=generate -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l csharp -o samples\client\petstore\csharp\SwaggerClientNetStandard --additional-properties targetFramework=v5.0,packageGuid={3AB1F259-1769-484B-9411-84505FCCBD55} +set ags=generate -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l csharp -o samples\client\petstore\csharp\SwaggerClientWithPropertyChanged --additional-properties=generatePropertyChanged=true,packageGuid={5CD900DE-8266-412F-A758-28E1F9C623D5} java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/eiffel-petstore.bat b/bin/windows/eiffel-petstore.bat new file mode 100644 index 00000000000..9028359e8c3 --- /dev/null +++ b/bin/windows/eiffel-petstore.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-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\swagger-codegen\src\test\resources\2_0\petstore.yaml -l eiffel -o samples\client\petstore\eiffel + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/erlang-petstore-client.bat b/bin/windows/erlang-petstore-client.bat new file mode 100755 index 00000000000..228c0c7ca46 --- /dev/null +++ b/bin/windows/erlang-petstore-client.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -t modules\swagger-codegen\src\main\resources\erlang-client -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l erlang-client -o samples\client\petstore\erlang-client + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/haskell-http-client-petstore.bat b/bin/windows/haskell-http-client-petstore.bat new file mode 100755 index 00000000000..95e16a1238c --- /dev/null +++ b/bin/windows/haskell-http-client-petstore.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-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\swagger-codegen\src\test\resources\2_0\petstore.yaml -l haskell-http-client -o samples\client\petstore\haskell-http-client + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/java-petstore-okhttp-gson.bat b/bin/windows/java-petstore-okhttp-gson.bat index 58b3edef39e..2b61d1decbd 100755 --- a/bin/windows/java-petstore-okhttp-gson.bat +++ b/bin/windows/java-petstore-okhttp-gson.bat @@ -5,6 +5,6 @@ If Not Exist %executable% ( ) REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l java -o samples\client\petstore\java --library=okhttp-gson -DdateLibrary=joda,hideGenerationTimestamp=true +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -l java -o samples\client\petstore\java --library=okhttp-gson -DdateLibrary=joda -DhideGenerationTimestamp=true java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/java-petstore.bat b/bin/windows/java-petstore.bat index e68f7219914..dfcb8393335 100755 --- a/bin/windows/java-petstore.bat +++ b/bin/windows/java-petstore.bat @@ -5,6 +5,6 @@ If Not Exist %executable% ( ) REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l java -o samples\client\petstore\java -DdateLibrary=joda,hideGenerationTimestamp=true +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -l java -o samples\client\petstore\java -DdateLibrary=joda -DhideGenerationTimestamp=true java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/java-play-framework-petstore-server.bat b/bin/windows/java-play-framework-petstore-server.bat index 058e54e5e79..f0011360a52 100644 --- a/bin/windows/java-play-framework-petstore-server.bat +++ b/bin/windows/java-play-framework-petstore-server.bat @@ -5,6 +5,6 @@ If Not Exist %executable% ( ) REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l java-play-framework -o samples\server\petstore\java-play-framework +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l java-play-framework -o samples\server\petstore\java-play-framework -DhideGenerationTimestamp=true java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/java-vertx-async-petstore-server.bat b/bin/windows/java-vertx-async-petstore-server.bat index 105eaa23687..8195b0664a4 100644 --- a/bin/windows/java-vertx-async-petstore-server.bat +++ b/bin/windows/java-vertx-async-petstore-server.bat @@ -5,6 +5,6 @@ If Not Exist %executable% ( ) REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l java-vertx -o samples\server\petstore\java-vertx\async -DvertxSwaggerRouterVersion=1.2.0 +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l java-vertx -o samples\server\petstore\java-vertx\async -DvertxSwaggerRouterVersion=1.2.0 -DhideGenerationTimestamp=true java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/java-vertx-rx-petstore-server.bat b/bin/windows/java-vertx-rx-petstore-server.bat index fc7b2397547..ee45c09eeae 100644 --- a/bin/windows/java-vertx-rx-petstore-server.bat +++ b/bin/windows/java-vertx-rx-petstore-server.bat @@ -5,6 +5,6 @@ If Not Exist %executable% ( ) REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l java-vertx -o samples\server\petstore\java-vertx\rx -DvertxSwaggerRouterVersion=1.2.0,rxInterface=true +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l java-vertx -o samples\server\petstore\java-vertx\rx -DvertxSwaggerRouterVersion=1.2.0 -DrxInterface=true java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/jaxrs-spec-petstore-server-interface.bat b/bin/windows/jaxrs-spec-petstore-server-interface.bat new file mode 100644 index 00000000000..5ca4f38101e --- /dev/null +++ b/bin/windows/jaxrs-spec-petstore-server-interface.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -DloggerPath=conf/log4j.properties +set ags=generate --artifact-id "jaxrs-cxf-client-petstore-client" -i modules\swagger-codegen\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -l jaxrs-spec -o samples\server\petstore\jaxrs-spec-interface -DhideGenerationTimestamp=true,serializableModel=true,interfaceOnly=true + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/jaxrs-spec-petstore-server.bat b/bin/windows/jaxrs-spec-petstore-server.bat new file mode 100644 index 00000000000..30c19a50891 --- /dev/null +++ b/bin/windows/jaxrs-spec-petstore-server.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -DloggerPath=conf/log4j.properties +set ags=generate --artifact-id "jaxrs-cxf-client-petstore-client" -i modules\swagger-codegen\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -l jaxrs-spec -o samples\server\petstore\jaxrs-spec -DhideGenerationTimestamp=true,serializableModel=true + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/lua-petstore.bat b/bin/windows/lua-petstore.bat new file mode 100755 index 00000000000..39f9c81fa32 --- /dev/null +++ b/bin/windows/lua-petstore.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-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\swagger-codegen\src\test\resources\2_0\petstore.yaml -l lua -o samples\client\petstore\lua + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/php-silex-petstore.bat b/bin/windows/php-silex-petstore.bat new file mode 100644 index 00000000000..7e5ae1d8de1 --- /dev/null +++ b/bin/windows/php-silex-petstore.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l php-silex -o samples\server\petstore\php-silex + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/python-petstore.bat b/bin/windows/python-petstore.bat index 1c5441f216f..f4170d9d91c 100755 --- a/bin/windows/python-petstore.bat +++ b/bin/windows/python-petstore.bat @@ -5,6 +5,6 @@ If Not Exist %executable% ( ) REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l python -o samples\client\petstore\python +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -l python -o samples\client\petstore\python -DpackageName=petstore_api java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/python2-flask-petstore.bat b/bin/windows/python2-flask-petstore.bat new file mode 100755 index 00000000000..ae0b14f21f5 --- /dev/null +++ b/bin/windows/python2-flask-petstore.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l python-flask -o samples\server\petstore\flaskConnexion-python2 -c bin\supportPython2.json -D service + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/python3-flask-petstore.bat b/bin/windows/python3-flask-petstore.bat new file mode 100755 index 00000000000..97d95f4f8de --- /dev/null +++ b/bin/windows/python3-flask-petstore.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l python-flask -o samples\server\petstore\flaskConnexion -D service + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/r-petstore.bat b/bin/windows/r-petstore.bat new file mode 100755 index 00000000000..07b8194d76f --- /dev/null +++ b/bin/windows/r-petstore.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-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\swagger-codegen\src\test\resources\2_0\petstore.yaml -l r -o samples\client\petstore\r + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/rust-petstore.bat b/bin/windows/rust-petstore.bat new file mode 100644 index 00000000000..70645379abb --- /dev/null +++ b/bin/windows/rust-petstore.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-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\swagger-codegen\src\test\resources\2_0\petstore.yaml -l rust -o samples\client\petstore\rust + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/rust-server-petstore.bat b/bin/windows/rust-server-petstore.bat new file mode 100755 index 00000000000..ff565aa1e04 --- /dev/null +++ b/bin/windows/rust-server-petstore.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-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\swagger-codegen\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -l rust-server -o samples\server\petstore\rust-server + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/scala-lagom-petstore.bat b/bin/windows/scala-lagom-petstore.bat new file mode 100644 index 00000000000..c2dfed7ae1f --- /dev/null +++ b/bin/windows/scala-lagom-petstore.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l scala-lagom-server -o samples\server\petstore\scala-lagom-server + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/spring-mvc-petstore-j8-async-server.bat b/bin/windows/spring-mvc-petstore-j8-async-server.bat index b9c9745e8d9..9fc1c4e20d1 100644 --- a/bin/windows/spring-mvc-petstore-j8-async-server.bat +++ b/bin/windows/spring-mvc-petstore-j8-async-server.bat @@ -5,6 +5,6 @@ If Not Exist %executable% ( ) REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l spring --library=spring-mvc -o samples\server\petstore\spring-mvc-j8-async -c bin\spring-mvc-petstore-j8-async.json +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -l spring --library=spring-mvc -o samples\server\petstore\spring-mvc-j8-async -c bin\spring-mvc-petstore-j8-async.json -DhideGenerationTimestamp=true java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/spring-mvc-petstore-server.bat b/bin/windows/spring-mvc-petstore-server.bat index 18975dead96..ebac66c3c15 100644 --- a/bin/windows/spring-mvc-petstore-server.bat +++ b/bin/windows/spring-mvc-petstore-server.bat @@ -5,6 +5,6 @@ If Not Exist %executable% ( ) REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l spring --library=spring-mvc -o samples\server\petstore\spring-mvc +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -c bin\spring-mvc-petstore-server.json -l spring --library=spring-mvc -o samples\server\petstore\spring-mvc -DhideGenerationTimestamp=true java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/springboot-petstore-server.bat b/bin/windows/springboot-petstore-server.bat index 4a9ca037c3b..886c6204371 100644 --- a/bin/windows/springboot-petstore-server.bat +++ b/bin/windows/springboot-petstore-server.bat @@ -5,6 +5,6 @@ If Not Exist %executable% ( ) REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l spring -o samples\server\petstore\springboot +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -l spring -o samples\server\petstore\springboot -DhideGenerationTimestamp=true java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-angular-petstore-all.bat b/bin/windows/typescript-angular-petstore-all.bat new file mode 100644 index 00000000000..dff81b05a62 --- /dev/null +++ b/bin/windows/typescript-angular-petstore-all.bat @@ -0,0 +1,7 @@ +call .\bin\windows\typescript-angular-v2-with-npm.bat +call .\bin\windows\typescript-angular-v2-interfaces.bat +call .\bin\windows\typescript-angular-v2.bat +call .\bin\windows\typescript-angular-v4-with-npm.bat +call .\bin\windows\typescript-angular-v4.3-with-npm.bat + + diff --git a/bin/windows/typescript-angular-v2-interfaces.bat b/bin/windows/typescript-angular-v2-interfaces.bat new file mode 100644 index 00000000000..c25d9c61e75 --- /dev/null +++ b/bin/windows/typescript-angular-v2-interfaces.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -c bin\typescript-petstore-npm.json -l typescript-angular -o samples\client\petstore\typescript-angular-v2\with-interfaces -D withInterfaces=true --additional-properties ngVersion=2 + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-angular-v2-with-npm.bat b/bin/windows/typescript-angular-v2-with-npm.bat new file mode 100644 index 00000000000..a1d5b2330a0 --- /dev/null +++ b/bin/windows/typescript-angular-v2-with-npm.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -c bin\typescript-petstore-npm.json -l typescript-angular -o samples\client\petstore\typescript-angular-v2\npm --additional-properties ngVersion=2 + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-angular-v2.bat b/bin/windows/typescript-angular-v2.bat new file mode 100755 index 00000000000..ec9da16154f --- /dev/null +++ b/bin/windows/typescript-angular-v2.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l typescript-angular -o samples\client\petstore\typescript-angular-v2\default --additional-properties ngVersion=2 + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-angular-v4-with-npm.bat b/bin/windows/typescript-angular-v4-with-npm.bat new file mode 100644 index 00000000000..f89de17373d --- /dev/null +++ b/bin/windows/typescript-angular-v4-with-npm.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -c bin\typescript-petstore-npm.json -l typescript-angular -o samples\client\petstore\typescript-angular-v4\npm --additional-properties ngVersion=4 + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-angular-v4.3-with-npm.bat b/bin/windows/typescript-angular-v4.3-with-npm.bat new file mode 100644 index 00000000000..5ad1cbd9b2d --- /dev/null +++ b/bin/windows/typescript-angular-v4.3-with-npm.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -c bin\typescript-petstore-npm.json -l typescript-angular -o samples\client\petstore\typescript-angular-v4.3\npm --additional-properties ngVersion=4.3 + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-angular.bat b/bin/windows/typescript-angular.bat deleted file mode 100755 index 47e575494f0..00000000000 --- a/bin/windows/typescript-angular.bat +++ /dev/null @@ -1,10 +0,0 @@ -set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar - -If Not Exist %executable% ( - mvn clean package -) - -REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l typescript-angular -o samples\client\petstore\typescript-angular - -java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-angular2-all.bat b/bin/windows/typescript-angular2-all.bat deleted file mode 100644 index 119de7e7bcc..00000000000 --- a/bin/windows/typescript-angular2-all.bat +++ /dev/null @@ -1,18 +0,0 @@ -set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar - -If Not Exist %executable% ( - mvn clean package -) - -REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -echo "Typescript Petstore API client (default)" -set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l typescript-angular2 -o samples\client\petstore\typescript-angular2\default -java %JAVA_OPTS% -jar %executable% %ags% - -echo "Typescript Petstore API client (with interfaces generated)" -set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l typescript-angular2 -o samples\client\petstore\typescript-angular2\with-interfaces -D withInterfaces=true -java %JAVA_OPTS% -jar %executable% %ags% - -echo "Typescript Petstore API client (npm setting)" -set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l typescript-angular2 -c bin\typescript-petstore-npm.json -o samples\client\petstore\typescript-angular2\npm -java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-angular2-with-npm.bat b/bin/windows/typescript-angular2-with-npm.bat deleted file mode 100644 index 0bf7a88ad11..00000000000 --- a/bin/windows/typescript-angular2-with-npm.bat +++ /dev/null @@ -1,10 +0,0 @@ -set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar - -If Not Exist %executable% ( - mvn clean package -) - -REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -c bin\typescript-petstore-npm.json -l typescript-angular2 -o samples\client\petstore\typescript-angular2\npm - -java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-angular2.bat b/bin/windows/typescript-angular2.bat deleted file mode 100755 index 81be77b6bff..00000000000 --- a/bin/windows/typescript-angular2.bat +++ /dev/null @@ -1,10 +0,0 @@ -set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar - -If Not Exist %executable% ( - mvn clean package -) - -REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l typescript-angular2 -o samples\client\petstore\typescript-angular2\default - -java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-angularjs.bat b/bin/windows/typescript-angularjs.bat new file mode 100755 index 00000000000..b33a07f367a --- /dev/null +++ b/bin/windows/typescript-angularjs.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l typescript-angularjs -o samples\client\petstore\typescript-angularjs + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-aurelia-petstore.bat b/bin/windows/typescript-aurelia-petstore.bat new file mode 100644 index 00000000000..06fe370bef1 --- /dev/null +++ b/bin/windows/typescript-aurelia-petstore.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l typescript-aurelia -o samples\client\petstore\typescript-aurelia\default + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-fetch-petstore-all.bat b/bin/windows/typescript-fetch-petstore-all.bat new file mode 100644 index 00000000000..ed41df84bb5 --- /dev/null +++ b/bin/windows/typescript-fetch-petstore-all.bat @@ -0,0 +1,5 @@ +@ECHO OFF + +call bin\windows\typescript-fetch-petstore.bat +call bin\windows\typescript-fetch-petstore-target-es6.bat +call bin\windows\typescript-fetch-petstore-with-npm-version.bat diff --git a/bin/windows/typescript-fetch-petstore-target-es6.bat b/bin/windows/typescript-fetch-petstore-target-es6.bat new file mode 100644 index 00000000000..99a1f26429e --- /dev/null +++ b/bin/windows/typescript-fetch-petstore-target-es6.bat @@ -0,0 +1,12 @@ +@ECHO OFF + +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l typescript-fetch -c bin\typescript-fetch-petstore-target-es6.json -o samples\client\petstore\typescript-fetch\builds\es6-target + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-fetch-petstore-with-npm-version.bat b/bin/windows/typescript-fetch-petstore-with-npm-version.bat new file mode 100644 index 00000000000..30cdf1a9d44 --- /dev/null +++ b/bin/windows/typescript-fetch-petstore-with-npm-version.bat @@ -0,0 +1,12 @@ +@ECHO OFF + +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l typescript-fetch -c bin\typescript-fetch-petstore-with-npm-version.json -o samples\client\petstore\typescript-fetch\builds\with-npm-version + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-fetch-petstore.bat b/bin/windows/typescript-fetch-petstore.bat new file mode 100644 index 00000000000..a2732b88748 --- /dev/null +++ b/bin/windows/typescript-fetch-petstore.bat @@ -0,0 +1,14 @@ +@ECHO OFF + +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M + +echo +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l typescript-fetch -o samples\client\petstore\typescript-fetch\builds\default + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-fetch.bat b/bin/windows/typescript-fetch.bat deleted file mode 100755 index a2b45b39901..00000000000 --- a/bin/windows/typescript-fetch.bat +++ /dev/null @@ -1,10 +0,0 @@ -set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar - -If Not Exist %executable% ( - mvn clean package -) - -REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l typescript-fetch -o samples\client\petstore\typescript-fetch - -java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-jquery-with-npm.bat b/bin/windows/typescript-jquery-with-npm.bat new file mode 100644 index 00000000000..630042e35db --- /dev/null +++ b/bin/windows/typescript-jquery-with-npm.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -c bin\typescript-petstore-npm.json -l typescript-jquery -o samples\client\petstore\typescript-jquery\npm + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-jquery.bat b/bin/windows/typescript-jquery.bat new file mode 100644 index 00000000000..24d9378ec62 --- /dev/null +++ b/bin/windows/typescript-jquery.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l typescript-jquery -o samples\client\petstore\typescript-jquery\default + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 556b6ff2f63..ca6b8d4e361 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -11,7 +11,7 @@ codegen="${cli}/target/swagger-codegen-cli.jar" cmdsrc="${cli}/src/main/java/io/swagger/codegen/cmd" pattern="@Command(name = \"$1\"" -if expr "x$1" : 'x[a-z][a-z-]*$' > /dev/null && fgrep -qe "$pattern" "$cmdsrc"/*.java; then +if expr "x$1" : 'x[a-z][a-z-]*$' > /dev/null && fgrep -qe "$pattern" "$cmdsrc"/*.java || expr "$1" = 'help' > /dev/null; then # If ${GEN_DIR} has been mapped elsewhere from default, and that location has not been built if [[ ! -f "${codegen}" ]]; then (cd "${GEN_DIR}" && exec mvn -am -pl "modules/swagger-codegen-cli" -Duser.home=$(dirname MAVEN_CONFIG) package) diff --git a/modules/swagger-codegen-cli/pom.xml b/modules/swagger-codegen-cli/pom.xml index 22075128a7f..87780115964 100644 --- a/modules/swagger-codegen-cli/pom.xml +++ b/modules/swagger-codegen-cli/pom.xml @@ -3,7 +3,7 @@ io.swagger swagger-codegen-project - 2.2.3 + 2.3.0-SNAPSHOT ../.. 4.0.0 diff --git a/modules/swagger-codegen-maven-plugin/README.md b/modules/swagger-codegen-maven-plugin/README.md index 5592cfe2384..34abd375b15 100644 --- a/modules/swagger-codegen-maven-plugin/README.md +++ b/modules/swagger-codegen-maven-plugin/README.md @@ -11,7 +11,7 @@ Add to your `build->plugins` section (default phase is `generate-sources` phase) io.swagger swagger-codegen-maven-plugin - 2.2.2 + 2.2.3 @@ -46,7 +46,7 @@ mvn clean compile - `apiPackage` - the package to use for generated api objects/classes - `invokerPackage` - the package to use for the generated invoker objects - `modelNamePrefix` and `modelNameSuffix` - Sets the pre- or suffix for model classes and enums -- `useJaxbAnnotations` - enable Jaxb annotations inside the generated models +- `withXml` - enable XML annotations inside the generated models and API (only works with Java `language` and libraries that provide support for JSON and XML) - `configOptions` - a map of language-specific parameters (see below) - `configHelp` - dumps the configuration help for the specified library (generates no sources) - `ignoreFileOverride` - specifies the full path to a `.swagger-codegen-ignore` used for pattern based overrides of generated outputs diff --git a/modules/swagger-codegen-maven-plugin/pom.xml b/modules/swagger-codegen-maven-plugin/pom.xml index f78b68cb572..96424dcc063 100644 --- a/modules/swagger-codegen-maven-plugin/pom.xml +++ b/modules/swagger-codegen-maven-plugin/pom.xml @@ -6,7 +6,7 @@ io.swagger swagger-codegen-project - 2.2.3 + 2.3.0-SNAPSHOT ../.. swagger-codegen-maven-plugin diff --git a/modules/swagger-codegen/.gitignore b/modules/swagger-codegen/.gitignore index f4ccae271fa..3e9b4fcd792 100644 --- a/modules/swagger-codegen/.gitignore +++ b/modules/swagger-codegen/.gitignore @@ -1,2 +1,3 @@ /.settings/ /test-output/ +/bin/ diff --git a/modules/swagger-codegen/pom.xml b/modules/swagger-codegen/pom.xml index 698bbd233ec..f745c673b8c 100644 --- a/modules/swagger-codegen/pom.xml +++ b/modules/swagger-codegen/pom.xml @@ -3,7 +3,7 @@ io.swagger swagger-codegen-project - 2.2.3 + 2.3.0-SNAPSHOT ../.. 4.0.0 @@ -278,6 +278,12 @@ commonmark 0.9.0 + + org.mockito + mockito-core + 2.8.47 + test + diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenConfig.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenConfig.java index 682388b766f..bd44d7a3979 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenConfig.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenConfig.java @@ -1,26 +1,26 @@ package io.swagger.codegen; -import io.swagger.models.Model; -import io.swagger.models.Operation; -import io.swagger.models.Swagger; -import io.swagger.models.auth.SecuritySchemeDefinition; -import io.swagger.models.properties.Property; - import java.util.List; import java.util.Map; import java.util.Set; import com.samskivert.mustache.Mustache.Compiler; +import io.swagger.models.Model; +import io.swagger.models.Operation; +import io.swagger.models.Swagger; +import io.swagger.models.auth.SecuritySchemeDefinition; +import io.swagger.models.properties.Property; + public interface CodegenConfig { CodegenType getTag(); - + String getName(); String getHelp(); Map additionalProperties(); - + Map vendorExtensions(); String testPackage(); @@ -92,7 +92,7 @@ public interface CodegenConfig { CodegenModel fromModel(String name, Model model, Map allDefinitions); CodegenOperation fromOperation(String resourcePath, String httpMethod, Operation operation, Map definitions, Swagger swagger); - + CodegenOperation fromOperation(String resourcePath, String httpMethod, Operation operation, Map definitions); List fromSecurity(Map schemes); @@ -118,7 +118,7 @@ public interface CodegenConfig { Map modelDocTemplateFiles(); Set languageSpecificPrimitives(); - + Map reservedWordsMappings(); void preprocessSwagger(Swagger swagger); @@ -136,11 +136,11 @@ public interface CodegenConfig { String toApiTestFilename(String name); String toModelTestFilename(String name); - + String toApiDocFilename(String name); String toModelDocFilename(String name); - + String toModelImport(String name); String toApiImport(String name); @@ -148,11 +148,13 @@ public interface CodegenConfig { void addOperationToGroup(String tag, String resourcePath, Operation operation, CodegenOperation co, Map> operations); Map postProcessAllModels(Map objs); - + Map postProcessModels(Map objs); Map postProcessOperations(Map objs); + Map postProcessOperationsWithModels(Map objs, List allModels); + Map postProcessSupportingFileData(Map objs); void postProcessModelProperty(CodegenModel model, CodegenProperty property); @@ -207,4 +209,11 @@ public interface CodegenConfig { void setIgnoreFilePathOverride(String ignoreFileOverride); String getIgnoreFilePathOverride(); + + String toBooleanGetter(String name); + + String toSetter(String name); + + String toGetter(String name); + } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenConstants.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenConstants.java index 4dea75e8208..379102f417a 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenConstants.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenConstants.java @@ -4,6 +4,14 @@ * A class for storing constants that are used throughout the project. */ public class CodegenConstants { + public static final String APIS = "apis"; + public static final String MODELS = "models"; + public static final String SUPPORTING_FILES = "supportingFiles"; + public static final String MODEL_TESTS = "modelTests"; + public static final String MODEL_DOCS = "modelDocs"; + public static final String API_TESTS = "apiTests"; + public static final String API_DOCS = "apiDocs"; + public static final String API_PACKAGE = "apiPackage"; public static final String API_PACKAGE_DESC = "package for generated api classes"; @@ -146,6 +154,10 @@ public class CodegenConstants { public static final String DOTNET_FRAMEWORK_DESC = "The target .NET framework version."; public static enum MODEL_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case, original} + public static enum ENUM_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case, original, UPPERCASE} + + public static final String ENUM_PROPERTY_NAMING = "enumPropertyNaming"; + public static final String ENUM_PROPERTY_NAMING_DESC = "Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', and 'original'"; public static final String MODEL_NAME_PREFIX = "modelNamePrefix"; public static final String MODEL_NAME_PREFIX_DESC = "Prefix that will be prepended to all model names. Default is the empty string."; @@ -171,6 +183,9 @@ public static enum MODEL_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case public static final String SUPPORTS_ES6 = "supportsES6"; public static final String SUPPORTS_ES6_DESC = "Generate code that conforms to ES6."; + public static final String SUPPORTS_ASYNC = "supportsAsync"; + public static final String SUPPORTS_ASYNC_DESC = "Generate code that supports async operations."; + public static final String EXCLUDE_TESTS = "excludeTests"; public static final String EXCLUDE_TESTS_DESC = "Specifies that no tests are to be generated."; @@ -195,6 +210,9 @@ public static enum MODEL_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case public static final String NON_PUBLIC_API = "nonPublicApi"; public static final String NON_PUBLIC_API_DESC = "Generates code with reduced access modifiers; allows embedding elsewhere without exposing non-public API calls to consumers."; + public static final String VALIDATABLE = "validatable"; + public static final String VALIDATABLE_DESC = "Generates self-validatable models."; + public static final String IGNORE_FILE_OVERRIDE = "ignoreFileOverride"; public static final String IGNORE_FILE_OVERRIDE_DESC = "Specifies an override location for the .swagger-codegen-ignore file. Most useful on initial generation."; diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenModel.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenModel.java index 42c525ec814..7b8cf07c471 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenModel.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenModel.java @@ -9,7 +9,6 @@ import io.swagger.models.ExternalDocs; - public class CodegenModel { public String parent, parentSchema; public List interfaces; @@ -40,7 +39,7 @@ public class CodegenModel { public Set allMandatory; public Set imports = new TreeSet(); - public boolean hasVars, emptyVars, hasMoreModels, hasEnums, isEnum, hasRequired, isArrayModel, hasChildren; + public boolean hasVars, emptyVars, hasMoreModels, hasEnums, isEnum, hasRequired, hasOptional, isArrayModel, hasChildren; public boolean hasOnlyReadOnly = true; // true if all properties are read-only public ExternalDocs externalDocs; diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenOperation.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenOperation.java index 8270a8fc4a8..71597b6fc8f 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenOperation.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenOperation.java @@ -1,6 +1,7 @@ package io.swagger.codegen; import io.swagger.models.ExternalDocs; +import io.swagger.models.Tag; import java.util.ArrayList; import java.util.HashSet; @@ -11,12 +12,12 @@ public class CodegenOperation { public final List responseHeaders = new ArrayList(); - public boolean hasAuthMethods, hasConsumes, hasProduces, hasParams, hasOptionalParams, + public boolean hasAuthMethods, hasConsumes, hasProduces, hasParams, hasOptionalParams, hasRequiredParams, returnTypeIsPrimitive, returnSimpleType, subresourceOperation, isMapContainer, isListContainer, isMultipart, hasMore = true, isResponseBinary = false, isResponseFile = false, hasReference = false, isRestfulIndex, isRestfulShow, isRestfulCreate, isRestfulUpdate, isRestfulDestroy, - isRestful; + isRestful, isDeprecated; public String path, operationId, returnType, httpMethod, returnBaseType, returnContainer, summary, unescapedNotes, notes, baseName, defaultResponse, discriminator; public List> consumes, produces, prioritizedContentTypes; @@ -27,15 +28,17 @@ public class CodegenOperation { public List queryParams = new ArrayList(); public List headerParams = new ArrayList(); public List formParams = new ArrayList(); + public List requiredParams = new ArrayList(); public List authMethods; - public List tags; + public List tags; public List responses = new ArrayList(); public Set imports = new HashSet(); public List> examples; + public List> requestBodyExamples; public ExternalDocs externalDocs; public Map vendorExtensions; public String nickname; // legacy support - public String operationIdLowerCase; // for mardown documentation + public String operationIdLowerCase; // for markdown documentation public String operationIdCamelCase; // for class names public String operationIdSnakeCase; @@ -138,6 +141,15 @@ public boolean isRestfulUpdate() { return Arrays.asList("PUT", "PATCH").contains(httpMethod.toUpperCase()) && isMemberPath(); } + /** + * Check if body param is allowed for the request method + * + * @return true request method is PUT, PATCH or POST; false otherwise + */ + public boolean isBodyAllowed() { + return Arrays.asList("PUT", "PATCH", "POST").contains(httpMethod.toUpperCase()); + } + /** * Check if act as Restful destroy method * @@ -220,6 +232,8 @@ public boolean equals(Object o) { return false; if (isResponseFile != that.isResponseFile) return false; + if (isDeprecated != that.isDeprecated) + return false; if (path != null ? !path.equals(that.path) : that.path != null) return false; if (operationId != null ? !operationId.equals(that.operationId) : that.operationId != null) @@ -304,6 +318,7 @@ public int hashCode() { result = 31 * result + (isResponseBinary ? 13:31); result = 31 * result + (isResponseFile ? 13:31); result = 31 * result + (hasReference ? 13:31); + result = 31 * result + (isDeprecated ? 13:31); result = 31 * result + (path != null ? path.hashCode() : 0); result = 31 * result + (operationId != null ? operationId.hashCode() : 0); result = 31 * result + (returnType != null ? returnType.hashCode() : 0); diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenParameter.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenParameter.java index e151d4b1ee7..296122899b8 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenParameter.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenParameter.java @@ -14,7 +14,7 @@ public class CodegenParameter { public String example; // example value (x-example) public String jsonSchema; - public boolean isString, isInteger, isLong, isFloat, isDouble, isByteArray, isBinary, isBoolean, isDate, isDateTime; + public boolean isString, isNumeric, isInteger, isLong, isNumber, isFloat, isDouble, isByteArray, isBinary, isBoolean, isDate, isDateTime, isUuid; public boolean isListContainer, isMapContainer; public boolean isFile, notFile; public boolean isEnum; @@ -133,13 +133,16 @@ public CodegenParameter copy() { output.isBinary = this.isBinary; output.isByteArray = this.isByteArray; output.isString = this.isString; + output.isNumeric = this.isNumeric; output.isInteger = this.isInteger; output.isLong = this.isLong; output.isDouble = this.isDouble; output.isFloat = this.isFloat; + output.isNumber = this.isNumber; output.isBoolean = this.isBoolean; output.isDate = this.isDate; output.isDateTime = this.isDateTime; + output.isUuid = this.isUuid; output.isListContainer = this.isListContainer; output.isMapContainer = this.isMapContainer; @@ -209,10 +212,14 @@ public boolean equals(Object o) { return false; if (isString != that.isString) return false; + if (isNumeric != that.isNumeric) + return false; if (isInteger != that.isInteger) return false; if (isLong != that.isLong) return false; + if (isNumber != that.isNumber) + return false; if (isFloat != that.isFloat) return false; if (isDouble != that.isDouble) @@ -227,6 +234,8 @@ public boolean equals(Object o) { return false; if (isDateTime != that.isDateTime) return false; + if (isUuid != that.isUuid) + return false; if (isListContainer != that.isListContainer) return false; if (isMapContainer != that.isMapContainer) @@ -298,15 +307,18 @@ public int hashCode() { result = 31 * result + (example != null ? example.hashCode() : 0); result = 31 * result + (jsonSchema != null ? jsonSchema.hashCode() : 0); result = 31 * result + (isString ? 13:31); + result = 31 * result + (isNumeric ? 13:31); result = 31 * result + (isInteger ? 13:31); result = 31 * result + (isLong ? 13:31); result = 31 * result + (isFloat ? 13:31); + result = 31 * result + (isNumber ? 13:31); result = 31 * result + (isDouble ? 13:31); result = 31 * result + (isByteArray ? 13:31); result = 31 * result + (isBinary ? 13:31); result = 31 * result + (isBoolean ? 13:31); result = 31 * result + (isDate ? 13:31); result = 31 * result + (isDateTime ? 13:31); + result = 31 * result + (isUuid ? 13:31); result = 31 * result + (isListContainer ? 13:31); result = 31 * result + (isMapContainer ? 13:31); result = 31 * result + (isFile ? 13:31); diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenProperty.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenProperty.java index 058e3775271..e50364ce1cd 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenProperty.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenProperty.java @@ -39,7 +39,7 @@ public class CodegenProperty implements Cloneable { public boolean hasMore, required, secondaryParam; public boolean hasMoreNonReadOnly; // for model constructor, true if next properyt is not readonly public boolean isPrimitiveType, isContainer, isNotContainer; - public boolean isString, isInteger, isLong, isFloat, isDouble, isByteArray, isBinary, isFile, isBoolean, isDate, isDateTime; + public boolean isString, isNumeric, isInteger, isLong, isNumber, isFloat, isDouble, isByteArray, isBinary, isFile, isBoolean, isDate, isDateTime, isUuid; public boolean isListContainer, isMapContainer; public boolean isEnum; public boolean isReadOnly = false; @@ -60,6 +60,7 @@ public class CodegenProperty implements Cloneable { public String xmlPrefix; public String xmlName; public String xmlNamespace; + public boolean isXmlWrapped = false; @Override @@ -114,8 +115,10 @@ public int hashCode() result = prime * result + ((vendorExtensions == null) ? 0 : vendorExtensions.hashCode()); result = prime * result + ((hasValidation ? 13:31)); result = prime * result + ((isString ? 13:31)); + result = prime * result + ((isNumeric ? 13:31)); result = prime * result + ((isInteger ? 13:31)); result = prime * result + ((isLong ?13:31)); + result = prime * result + ((isNumber ? 13:31)); result = prime * result + ((isFloat ? 13:31)); result = prime * result + ((isDouble ? 13:31)); result = prime * result + ((isByteArray ? 13:31)); @@ -124,6 +127,7 @@ public int hashCode() result = prime * result + ((isBoolean ? 13:31)); result = prime * result + ((isDate ? 13:31)); result = prime * result + ((isDateTime ? 13:31)); + result = prime * result + ((isUuid ? 13:31)); result = prime * result + ((isMapContainer ? 13:31)); result = prime * result + ((isListContainer ? 13:31)); result = prime * result + Objects.hashCode(isInherited); @@ -135,6 +139,7 @@ public int hashCode() result = prime * result + ((xmlPrefix == null) ? 0 : xmlPrefix.hashCode()); result = prime * result + ((xmlName == null) ? 0 : xmlName.hashCode()); result = prime * result + ((xmlNamespace == null) ? 0 : xmlNamespace.hashCode()); + result = prime * result + ((isXmlWrapped ? 13:31)); return result; } @@ -259,12 +264,18 @@ public boolean equals(Object obj) { return false; } + if (this.isNumeric != other.isNumeric) { + return false; + } if (this.isInteger != other.isInteger) { return false; } if (this.isLong != other.isLong) { return false; } + if (this.isNumber != other.isNumber) { + return false; + } if (this.isFloat != other.isFloat) { return false; } @@ -283,6 +294,9 @@ public boolean equals(Object obj) { if (this.isDateTime != other.isDateTime) { return false; } + if (this.isUuid != other.isUuid) { + return false; + } if (this.isBinary != other.isBinary) { return false; } @@ -322,6 +336,9 @@ public boolean equals(Object obj) { if (!Objects.equals(this.xmlNamespace, other.xmlNamespace)) { return false; } + if (!Objects.equals(this.isXmlWrapped, other.isXmlWrapped)) { + return false; + } return true; } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenResponse.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenResponse.java index 05901ba7236..6a0dfa44c4e 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenResponse.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenResponse.java @@ -11,7 +11,7 @@ public class CodegenResponse { public List> examples; public String dataType, baseType, containerType; public boolean hasHeaders; - public boolean isString, isInteger, isLong, isFloat, isDouble, isByteArray, isBoolean, isDate, isDateTime; + public boolean isString, isNumeric, isInteger, isLong, isNumber, isFloat, isDouble, isByteArray, isBoolean, isDate, isDateTime, isUuid; public boolean isDefault; public boolean simpleType; public boolean primitiveType; @@ -69,12 +69,13 @@ public boolean equals(Object o) { return false; if (isFile != that.isFile) return false; + if (isNumeric != that.isNumeric) + return false; if (schema != null ? !schema.equals(that.schema) : that.schema != null) return false; if (vendorExtensions != null ? !vendorExtensions.equals(that.vendorExtensions) : that.vendorExtensions != null) return false; return jsonSchema != null ? jsonSchema.equals(that.jsonSchema) : that.jsonSchema == null; - } @Override @@ -88,6 +89,7 @@ public int hashCode() { result = 31 * result + (baseType != null ? baseType.hashCode() : 0); result = 31 * result + (containerType != null ? containerType.hashCode() : 0); result = 31 * result + (isDefault ? 13:31); + result = 31 * result + (isNumeric ? 13:31); result = 31 * result + (simpleType ? 13:31); result = 31 * result + (primitiveType ? 13:31); result = 31 * result + (isMapContainer ? 13:31); diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java index 391a128bc69..dfdcf14e996 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java @@ -1,5 +1,18 @@ package io.swagger.codegen; +import javax.annotation.Nullable; +import java.io.File; +import java.util.*; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.common.base.Function; import com.google.common.collect.Lists; import com.samskivert.mustache.Mustache.Compiler; @@ -48,30 +61,6 @@ import io.swagger.models.properties.StringProperty; import io.swagger.models.properties.UUIDProperty; import io.swagger.util.Json; -import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.StringEscapeUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; -import java.util.TreeSet; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public class DefaultCodegen { protected static final Logger LOGGER = LoggerFactory.getLogger(DefaultCodegen.class); @@ -118,7 +107,7 @@ public class DefaultCodegen { // Then translated back during JSON encoding and decoding protected Map specialCharReplacements = new HashMap(); // When a model is an alias for a simple type - protected Map typeAliases = new HashMap<>(); + protected Map typeAliases = null; protected String ignoreFilePathOverride; @@ -206,7 +195,7 @@ public Map postProcessAllModels(Map objs) { // TODO Determine what to do if the parent discriminator name == the grandparent discriminator name while (parent != null) { if (parent.children == null) { - parent.children = new ArrayList(); + parent.children = new ArrayList(); } parent.children.add(cm); if (parent.discriminator == null) { @@ -344,6 +333,12 @@ public Map postProcessOperations(Map objs) { return objs; } + // override with any special post-processing + @SuppressWarnings("static-method") + public Map postProcessOperationsWithModels(Map objs, List allModels) { + return objs; + } + // override with any special post-processing @SuppressWarnings("static-method") public Map postProcessSupportingFileData(Map objs) { @@ -986,7 +981,7 @@ public String toInstantiationType(Property p) { String type = additionalProperties2.getType(); if (null == type) { LOGGER.error("No Type defined for Additional Property " + additionalProperties2 + "\n" // - + "\tIn Property: " + p); + + "\tIn Property: " + p); } String inner = getSwaggerType(additionalProperties2); return instantiationTypes.get("map") + ""; @@ -1123,7 +1118,7 @@ public String getSwaggerType(Property p) { String datatype = null; if (p instanceof StringProperty && "number".equals(p.getFormat())) { datatype = "BigDecimal"; - } else if (p instanceof ByteArrayProperty) { + } else if ((p instanceof ByteArrayProperty) || (p instanceof StringProperty && "byte".equals(p.getFormat()))) { datatype = "ByteArray"; } else if (p instanceof BinaryProperty) { datatype = "binary"; @@ -1230,6 +1225,36 @@ public String getAlias(String name) { return name; } + /** + * Output the Getter name for boolean property, e.g. getActive + * + * @param name the name of the property + * @return getter name based on naming convention + */ + public String toBooleanGetter(String name) { + return "get" + getterAndSetterCapitalize(name); + } + + /** + * Output the Getter name, e.g. getSize + * + * @param name the name of the property + * @return getter name based on naming convention + */ + public String toGetter(String name) { + return "get" + getterAndSetterCapitalize(name); + } + + /** + * Output the Getter name, e.g. getSize + * + * @param name the name of the property + * @return setter name based on naming convention + */ + public String toSetter(String name) { + return "set" + getterAndSetterCapitalize(name); + } + /** * Output the API (class) name (capitalized) ending with "Api" * Return DefaultApi if name is empty @@ -1275,6 +1300,10 @@ public CodegenModel fromModel(String name, Model model) { * @return Codegen Model object */ public CodegenModel fromModel(String name, Model model, Map allDefinitions) { + if (typeAliases == null) { + // Only do this once during first call + typeAliases = getAllAliases(allDefinitions); + } CodegenModel m = CodegenModelFactory.newInstance(CodegenModelType.MODEL); if (reservedWords.contains(name)) { m.name = escapeReservedWord(name); @@ -1290,15 +1319,16 @@ public CodegenModel fromModel(String name, Model model, Map allDe m.modelJson = Json.pretty(model); m.externalDocs = model.getExternalDocs(); m.vendorExtensions = model.getVendorExtensions(); + m.isAlias = typeAliases.containsKey(name); if (model instanceof ModelImpl) { - ModelImpl modelImpl = (ModelImpl) model; + ModelImpl modelImpl = (ModelImpl) model; m.discriminator = modelImpl.getDiscriminator(); if (modelImpl.getXml() != null) { - m.xmlPrefix = modelImpl.getXml().getPrefix(); - m.xmlNamespace = modelImpl.getXml().getNamespace(); - m.xmlName = modelImpl.getXml().getName(); + m.xmlPrefix = modelImpl.getXml().getPrefix(); + m.xmlNamespace = modelImpl.getXml().getNamespace(); + m.xmlName = modelImpl.getXml().getName(); } } @@ -1323,14 +1353,14 @@ public CodegenModel fromModel(String name, Model model, Map allDe int modelImplCnt = 0; // only one inline object allowed in a ComposedModel for (Model innerModel: ((ComposedModel)model).getAllOf()) { if (innerModel instanceof ModelImpl) { - ModelImpl modelImpl = (ModelImpl) innerModel; + ModelImpl modelImpl = (ModelImpl) innerModel; if (m.discriminator == null) { m.discriminator = modelImpl.getDiscriminator(); } if (modelImpl.getXml() != null) { - m.xmlPrefix = modelImpl.getXml().getPrefix(); - m.xmlNamespace = modelImpl.getXml().getNamespace(); - m.xmlName = modelImpl.getXml().getName(); + m.xmlPrefix = modelImpl.getXml().getPrefix(); + m.xmlNamespace = modelImpl.getXml().getNamespace(); + m.xmlName = modelImpl.getXml().getName(); } if (modelImplCnt++ > 1) { LOGGER.warn("More than one inline schema specified in allOf:. Only the first one is recognized. All others are ignored."); @@ -1407,10 +1437,6 @@ public CodegenModel fromModel(String name, Model model, Map allDe ModelImpl impl = (ModelImpl) model; if (impl.getType() != null) { Property p = PropertyBuilder.build(impl.getType(), impl.getFormat(), null); - if (!impl.getType().equals("object") && impl.getEnum() == null) { - typeAliases.put(name, impl.getType()); - m.isAlias = true; - } m.dataType = getSwaggerType(p); } if(impl.getEnum() != null && impl.getEnum().size() > 0) { @@ -1518,8 +1544,8 @@ public CodegenProperty fromProperty(String name, Property p) { property.description = escapeText(p.getDescription()); property.unescapedDescription = p.getDescription(); property.title = p.getTitle(); - property.getter = "get" + getterAndSetterCapitalize(name); - property.setter = "set" + getterAndSetterCapitalize(name); + property.getter = toGetter(name); + property.setter = toSetter(name); String example = toExampleValue(p); if(!"null".equals(example)) { property.example = example; @@ -1531,12 +1557,12 @@ public CodegenProperty fromProperty(String name, Property p) { property.isReadOnly = p.getReadOnly(); } if (p.getXml() != null) { - if (p.getXml().getAttribute() != null) { - property.isXmlAttribute = p.getXml().getAttribute(); - } - property.xmlPrefix = p.getXml().getPrefix(); - property.xmlName = p.getXml().getName(); - property.xmlNamespace = p.getXml().getNamespace(); + if (p.getXml().getAttribute() != null) { + property.isXmlAttribute = p.getXml().getAttribute(); + } + property.xmlPrefix = p.getXml().getPrefix(); + property.xmlName = p.getXml().getName(); + property.xmlNamespace = p.getXml().getNamespace(); } property.vendorExtensions = p.getVendorExtensions(); @@ -1544,18 +1570,18 @@ public CodegenProperty fromProperty(String name, Property p) { if (p instanceof AbstractNumericProperty) { AbstractNumericProperty np = (AbstractNumericProperty) p; if (np.getMinimum() != null) { - if (p instanceof BaseIntegerProperty) { // int, long - property.minimum = String.valueOf(np.getMinimum().longValue()); - } else { // double, decimal - property.minimum = String.valueOf(np.getMinimum()); - } + if (p instanceof BaseIntegerProperty) { // int, long + property.minimum = String.valueOf(np.getMinimum().longValue()); + } else { // double, decimal + property.minimum = String.valueOf(np.getMinimum()); + } } if (np.getMaximum() != null) { - if (p instanceof BaseIntegerProperty) { // int, long - property.maximum = String.valueOf(np.getMaximum().longValue()); - } else { // double, decimal - property.maximum = String.valueOf(np.getMaximum()); - } + if (p instanceof BaseIntegerProperty) { // int, long + property.maximum = String.valueOf(np.getMaximum().longValue()); + } else { // double, decimal + property.maximum = String.valueOf(np.getMaximum()); + } } if (np.getExclusiveMinimum() != null) { @@ -1579,7 +1605,7 @@ public CodegenProperty fromProperty(String name, Property p) { allowableValues.put("max", np.getMaximum()); } if(allowableValues.size() > 0) { - property.allowableValues = allowableValues; + property.allowableValues = allowableValues; } } @@ -1610,19 +1636,7 @@ public CodegenProperty fromProperty(String name, Property p) { if (p instanceof BaseIntegerProperty && !(p instanceof IntegerProperty) && !(p instanceof LongProperty)) { BaseIntegerProperty sp = (BaseIntegerProperty) p; property.isInteger = true; - /*if (sp.getEnum() != null) { - List _enum = sp.getEnum(); - property._enum = new ArrayList(); - for(Integer i : _enum) { - property._enum.add(i.toString()); - } - property.isEnum = true; - - // legacy support - Map allowableValues = new HashMap(); - allowableValues.put("values", _enum); - property.allowableValues = allowableValues; - }*/ + property.isNumeric = true; } if (p instanceof IntegerProperty) { IntegerProperty sp = (IntegerProperty) p; @@ -1631,7 +1645,7 @@ public CodegenProperty fromProperty(String name, Property p) { List _enum = sp.getEnum(); property._enum = new ArrayList(); for(Integer i : _enum) { - property._enum.add(i.toString()); + property._enum.add(i.toString()); } property.isEnum = true; @@ -1644,11 +1658,12 @@ public CodegenProperty fromProperty(String name, Property p) { if (p instanceof LongProperty) { LongProperty sp = (LongProperty) p; property.isLong = true; + property.isNumeric = true; if (sp.getEnum() != null) { List _enum = sp.getEnum(); property._enum = new ArrayList(); for(Long i : _enum) { - property._enum.add(i.toString()); + property._enum.add(i.toString()); } property.isEnum = true; @@ -1660,6 +1675,7 @@ public CodegenProperty fromProperty(String name, Property p) { } if (p instanceof BooleanProperty) { property.isBoolean = true; + property.getter = toBooleanGetter(name); } if (p instanceof BinaryProperty) { property.isBinary = true; @@ -1668,7 +1684,9 @@ public CodegenProperty fromProperty(String name, Property p) { property.isFile = true; } if (p instanceof UUIDProperty) { - property.isString = true; + property.isString =true; + property.isUuid = true; + } if (p instanceof ByteArrayProperty) { property.isByteArray = true; @@ -1676,29 +1694,17 @@ public CodegenProperty fromProperty(String name, Property p) { // type is number and without format if (p instanceof DecimalProperty && !(p instanceof DoubleProperty) && !(p instanceof FloatProperty)) { DecimalProperty sp = (DecimalProperty) p; - property.isFloat = true; - /*if (sp.getEnum() != null) { - List _enum = sp.getEnum(); - property._enum = new ArrayList(); - for(Double i : _enum) { - property._enum.add(i.toString()); - } - property.isEnum = true; - - // legacy support - Map allowableValues = new HashMap(); - allowableValues.put("values", _enum); - property.allowableValues = allowableValues; - }*/ + property.isNumber = true; } if (p instanceof DoubleProperty) { DoubleProperty sp = (DoubleProperty) p; property.isDouble = true; + property.isNumeric = true; if (sp.getEnum() != null) { List _enum = sp.getEnum(); property._enum = new ArrayList(); for(Double i : _enum) { - property._enum.add(i.toString()); + property._enum.add(i.toString()); } property.isEnum = true; @@ -1711,11 +1717,12 @@ public CodegenProperty fromProperty(String name, Property p) { if (p instanceof FloatProperty) { FloatProperty sp = (FloatProperty) p; property.isFloat = true; + property.isNumeric = true; if (sp.getEnum() != null) { List _enum = sp.getEnum(); property._enum = new ArrayList(); for(Float i : _enum) { - property._enum.add(i.toString()); + property._enum.add(i.toString()); } property.isEnum = true; @@ -1733,7 +1740,7 @@ public CodegenProperty fromProperty(String name, Property p) { List _enum = sp.getEnum(); property._enum = new ArrayList(); for(String i : _enum) { - property._enum.add(i); + property._enum.add(i); } property.isEnum = true; @@ -1750,7 +1757,7 @@ public CodegenProperty fromProperty(String name, Property p) { List _enum = sp.getEnum(); property._enum = new ArrayList(); for(String i : _enum) { - property._enum.add(i); + property._enum.add(i); } property.isEnum = true; @@ -1773,11 +1780,17 @@ public CodegenProperty fromProperty(String name, Property p) { property.baseType = getSwaggerType(p); - if (p instanceof ArrayProperty) { + if (p instanceof ArrayProperty) { property.isContainer = true; property.isListContainer = true; property.containerType = "array"; property.baseType = getSwaggerType(p); + if (p.getXml() != null) { + property.isXmlWrapped = p.getXml().getWrapped() == null ? false : p.getXml().getWrapped(); + property.xmlPrefix= p.getXml().getPrefix(); + property.xmlNamespace = p.getXml().getNamespace(); + property.xmlName = p.getXml().getName(); + } // handle inner property ArrayProperty ap = (ArrayProperty) p; property.maxItems = ap.getMaxItems(); @@ -1788,7 +1801,7 @@ public CodegenProperty fromProperty(String name, Property p) { } CodegenProperty cp = fromProperty(itemName, ap.getItems()); updatePropertyForArray(property, cp); - } else if (p instanceof MapProperty) { + } else if (p instanceof MapProperty) { MapProperty ap = (MapProperty) p; property.isContainer = true; @@ -1877,7 +1890,7 @@ protected void updatePropertyForMap(CodegenProperty property, CodegenProperty in protected Boolean isPropertyInnerMostEnum(CodegenProperty property) { CodegenProperty currentProperty = property; while (currentProperty != null && (Boolean.TRUE.equals(currentProperty.isMapContainer) - || Boolean.TRUE.equals(currentProperty.isListContainer))) { + || Boolean.TRUE.equals(currentProperty.isListContainer))) { currentProperty = currentProperty.items; } @@ -1887,7 +1900,7 @@ protected Boolean isPropertyInnerMostEnum(CodegenProperty property) { protected Map getInnerEnumAllowableValues(CodegenProperty property) { CodegenProperty currentProperty = property; while (currentProperty != null && (Boolean.TRUE.equals(currentProperty.isMapContainer) - || Boolean.TRUE.equals(currentProperty.isListContainer))) { + || Boolean.TRUE.equals(currentProperty.isListContainer))) { currentProperty = currentProperty.items; } @@ -1902,7 +1915,7 @@ protected Map getInnerEnumAllowableValues(CodegenProperty proper protected void updateDataTypeWithEnumForArray(CodegenProperty property) { CodegenProperty baseItem = property.items; while (baseItem != null && (Boolean.TRUE.equals(baseItem.isMapContainer) - || Boolean.TRUE.equals(baseItem.isListContainer))) { + || Boolean.TRUE.equals(baseItem.isListContainer))) { baseItem = baseItem.items; } if (baseItem != null) { @@ -1927,7 +1940,7 @@ protected void updateDataTypeWithEnumForArray(CodegenProperty property) { protected void updateDataTypeWithEnumForMap(CodegenProperty property) { CodegenProperty baseItem = property.items; while (baseItem != null && (Boolean.TRUE.equals(baseItem.isMapContainer) - || Boolean.TRUE.equals(baseItem.isListContainer))) { + || Boolean.TRUE.equals(baseItem.isListContainer))) { baseItem = baseItem.items; } @@ -2022,9 +2035,11 @@ public CodegenOperation fromOperation(String path, op.summary = escapeText(operation.getSummary()); op.unescapedNotes = operation.getDescription(); op.notes = escapeText(operation.getDescription()); - op.tags = operation.getTags(); op.hasConsumes = false; op.hasProduces = false; + if (operation.isDeprecated() != null) { + op.isDeprecated = operation.isDeprecated(); + } List consumes = new ArrayList(); if (operation.getConsumes() != null) { @@ -2135,6 +2150,10 @@ public CodegenOperation fromOperation(String path, ArrayProperty ap = (ArrayProperty) responseProperty; CodegenProperty innerProperty = fromProperty("response", ap.getItems()); op.returnBaseType = innerProperty.baseType; + } else if (responseProperty instanceof MapProperty) { + MapProperty ap = (MapProperty) responseProperty; + CodegenProperty innerProperty = fromProperty("response", ap.getAdditionalProperties()); + op.returnBaseType = innerProperty.baseType; } else { if (cm.complexType != null) { op.returnBaseType = cm.complexType; @@ -2185,6 +2204,7 @@ public CodegenOperation fromOperation(String path, List headerParams = new ArrayList(); List cookieParams = new ArrayList(); List formParams = new ArrayList(); + List requiredParams = new ArrayList(); if (parameters != null) { for (Parameter param : parameters) { @@ -2207,13 +2227,6 @@ public CodegenOperation fromOperation(String path, } } - // set isPrimitiveType and baseType for allParams - /*if (languageSpecificPrimitives.contains(p.baseType)) { - p.isPrimitiveType = true; - p.baseType = getSwaggerType(p); - }*/ - - allParams.add(p); // Issue #2561 (neilotoole) : Moved setting of isParam flags // from here to fromParameter(). @@ -2228,33 +2241,42 @@ public CodegenOperation fromOperation(String path, } else if (param instanceof BodyParameter) { bodyParam = p; bodyParams.add(p.copy()); + if(definitions != null) { + op.requestBodyExamples = new ExampleGenerator(definitions).generate(null, operation.getConsumes(), bodyParam.dataType); + } } else if (param instanceof FormParameter) { formParams.add(p.copy()); } - if (!p.required) { + + if (p.required) { //required parameters + requiredParams.add(p.copy()); + } else { // optional parameters op.hasOptionalParams = true; } } } + for (String i : imports) { if (needToImport(i)) { op.imports.add(i); } } + op.bodyParam = bodyParam; op.httpMethod = httpMethod.toUpperCase(); // move "required" parameters in front of "optional" parameters if (sortParamsByRequiredFlag) { - Collections.sort(allParams, new Comparator() { - @Override - public int compare(CodegenParameter one, CodegenParameter another) { - if (one.required == another.required) return 0; - else if (one.required) return -1; - else return 1; - } - }); + Collections.sort(allParams, new Comparator() { + @Override + public int compare(CodegenParameter one, CodegenParameter another) { + if (one.required == another.required) return 0; + else if (one.required) return -1; + else return 1; + } + }); } + op.allParams = addHasMore(allParams); op.bodyParams = addHasMore(bodyParams); op.pathParams = addHasMore(pathParams); @@ -2262,13 +2284,15 @@ public int compare(CodegenParameter one, CodegenParameter another) { op.headerParams = addHasMore(headerParams); // op.cookieParams = cookieParams; op.formParams = addHasMore(formParams); + op.requiredParams = addHasMore(requiredParams); + op.externalDocs = operation.getExternalDocs(); // legacy support op.nickname = op.operationId; if (op.allParams.size() > 0) { op.hasParams = true; } - op.externalDocs = operation.getExternalDocs(); + op.hasRequiredParams = op.requiredParams.size() > 0; // set Restful Flag op.isRestfulShow = op.isRestfulShow(); @@ -2321,20 +2345,29 @@ public CodegenResponse fromResponse(String responseCode, Response response) { } r.dataType = cm.datatype; - if (Boolean.TRUE.equals(cm.isString)) { + if (Boolean.TRUE.equals(cm.isString) && Boolean.TRUE.equals(cm.isUuid)) { + r.isUuid = true; + } else if (Boolean.TRUE.equals(cm.isByteArray)) { + r.isByteArray = true; + } else if (Boolean.TRUE.equals(cm.isString)) { r.isString = true; } else if (Boolean.TRUE.equals(cm.isBoolean)) { r.isBoolean = true; } else if (Boolean.TRUE.equals(cm.isLong)) { r.isLong = true; + r.isNumeric = true; } else if (Boolean.TRUE.equals(cm.isInteger)) { r.isInteger = true; + r.isNumeric = true; + } else if (Boolean.TRUE.equals(cm.isNumber)) { + r.isNumber = true; + r.isNumeric = true; } else if (Boolean.TRUE.equals(cm.isDouble)) { r.isDouble = true; + r.isNumeric = true; } else if (Boolean.TRUE.equals(cm.isFloat)) { r.isFloat = true; - } else if (Boolean.TRUE.equals(cm.isByteArray)) { - r.isByteArray = true; + r.isNumeric = true; } else if (Boolean.TRUE.equals(cm.isBinary)) { r.isBinary = true; } else if (Boolean.TRUE.equals(cm.isFile)) { @@ -2579,7 +2612,9 @@ public CodegenParameter fromParameter(Parameter param, Set imports) { // recursively add import CodegenProperty innerCp = cp; while(innerCp != null) { - imports.add(innerCp.complexType); + if(innerCp.complexType != null) { + imports.add(innerCp.complexType); + } innerCp = innerCp.items; } @@ -2643,7 +2678,9 @@ public CodegenParameter fromParameter(Parameter param, Set imports) { // set the example value // if not specified in x-example, generate a default value if (p.vendorExtensions.containsKey("x-example")) { - p.example = Objects.toString(p.vendorExtensions.get("x-example")); + p.example = Json.pretty(p.vendorExtensions.get("x-example")); + } else if (Boolean.TRUE.equals(p.isUuid) && (Boolean.TRUE.equals(p.isString))) { + p.example = "38400000-8cf0-11bd-b23e-10b96e4ef00d"; } else if (Boolean.TRUE.equals(p.isString)) { p.example = p.paramName + "_example"; } else if (Boolean.TRUE.equals(p.isBoolean)) { @@ -2654,6 +2691,8 @@ public CodegenParameter fromParameter(Parameter param, Set imports) { p.example = "56"; } else if (Boolean.TRUE.equals(p.isFloat)) { p.example = "3.4"; + } else if (Boolean.TRUE.equals(p.isNumber)) { + p.example = "8.14"; } else if (Boolean.TRUE.equals(p.isDouble)) { p.example = "1.2"; } else if (Boolean.TRUE.equals(p.isBinary)) { @@ -2679,11 +2718,19 @@ public CodegenParameter fromParameter(Parameter param, Set imports) { } public boolean isDataTypeBinary(String dataType) { - return dataType.toLowerCase().startsWith("byte"); + if (dataType != null) { + return dataType.toLowerCase().startsWith("byte"); + } else { + return false; + } } public boolean isDataTypeFile(String dataType) { - return dataType.toLowerCase().equals("file"); + if (dataType != null) { + return dataType.toLowerCase().equals("file"); + } else { + return false; + } } /** @@ -2786,10 +2833,10 @@ public int compare(CodegenSecurity one, CodegenSecurity another) { } protected void setReservedWordsLowerCase(List words) { - reservedWords = new HashSet(); - for (String word : words) { - reservedWords.add(word.toLowerCase()); - } + reservedWords = new HashSet(); + for (String word : words) { + reservedWords.add(word.toLowerCase()); + } } protected boolean isReservedWord(String word) { @@ -2839,7 +2886,7 @@ protected String getOrGenerateOperationId(Operation operation, String path, Stri * @return true if the library/module/package of the corresponding type needs to be imported */ protected boolean needToImport(String type) { - return !defaultIncludes.contains(type) + return StringUtils.isNotBlank(type) && !defaultIncludes.contains(type) && !languageSpecificPrimitives.contains(type); } @@ -2967,6 +3014,8 @@ public static String underscore(String word) { word = word.replaceAll(firstPattern, replacementPattern); word = word.replaceAll(secondPattern, replacementPattern); word = word.replace('-', '_'); + // replace space with underscore + word = word.replace(' ', '_'); word = word.toLowerCase(); return word; } @@ -3015,7 +3064,7 @@ private void addVars(CodegenModel m, Map properties, List properties, List required, - Map allProperties, List allRequired) { + Map allProperties, List allRequired) { m.hasRequired = false; if (properties != null && !properties.isEmpty()) { @@ -3057,6 +3106,7 @@ private void addVars(CodegenModel m, List vars, Map vars, Map getAllAliases(Map allDefinitions) { + Map aliases = new HashMap<>(); + if (allDefinitions != null) { + for (Map.Entry entry : allDefinitions.entrySet()) { + String swaggerName = entry.getKey(); + Model m = entry.getValue(); + if (m instanceof ModelImpl) { + ModelImpl impl = (ModelImpl) m; + if (impl.getType() != null && + !impl.getType().equals("object") && + impl.getEnum() == null) { + aliases.put(swaggerName, impl.getType()); + } + } + } + } + return aliases; + } + /** * Remove characters not suitable for variable or method name from the input and camelize it * @@ -3190,9 +3266,23 @@ public static String camelize(String word, boolean lowercaseFirstLetter) { word = m.replaceAll(rep); } - // Remove all underscores + // Remove all underscores (underscore_case to camelCase) p = Pattern.compile("(_)(.)"); m = p.matcher(word); + while (m.find()) { + String original = m.group(2); + String upperCase = original.toUpperCase(); + if (original.equals(upperCase)) { + word = word.replaceFirst("_", ""); + } else { + word = m.replaceFirst(upperCase); + } + m = p.matcher(word); + } + + // Remove all hyphens (hyphen-case to camelCase) + p = Pattern.compile("(-)(.)"); + m = p.matcher(word); while (m.find()) { word = m.replaceFirst(m.group(2).toUpperCase()); m = p.matcher(word); @@ -3271,8 +3361,17 @@ public Map supportedLibraries() { * @param library Library template */ public void setLibrary(String library) { - if (library != null && !supportedLibraries.containsKey(library)) - throw new RuntimeException("unknown library: " + library); + if (library != null && !supportedLibraries.containsKey(library)) { + StringBuilder sb = new StringBuilder("Unknown library: " + library + "\nAvailable libraries:"); + if(supportedLibraries.size() == 0) { + sb.append("\n ").append("NONE"); + } else { + for (String lib : supportedLibraries.keySet()) { + sb.append("\n ").append(lib); + } + } + throw new RuntimeException(sb.toString()); + } this.library = library; } @@ -3429,20 +3528,14 @@ public String sanitizeName(String name) { * @return Sanitized tag */ public String sanitizeTag(String tag) { - // remove spaces and make strong case - String[] parts = tag.split(" "); - StringBuilder buf = new StringBuilder(); - for (String part : parts) { - if (StringUtils.isNotEmpty(part)) { - buf.append(StringUtils.capitalize(part)); - } - } - String returnTag = buf.toString().replaceAll("[^a-zA-Z0-9_]", ""); - if (returnTag.matches("\\d.*")) { - return "_" + returnTag; - } else { - return returnTag; + tag = camelize(sanitizeName(tag)); + + // tag starts with numbers + if (tag.matches("^\\d.*")) { + tag = "Class" + tag; } + + return tag; } /** @@ -3488,7 +3581,12 @@ public void setParameterBooleanFlagWithCodegenProperty(CodegenParameter paramete return; } - if (Boolean.TRUE.equals(property.isString)) { + if (Boolean.TRUE.equals(property.isUuid) && Boolean.TRUE.equals(property.isString)) { + parameter.isUuid = true; + } else if (Boolean.TRUE.equals(property.isByteArray)) { + parameter.isByteArray = true; + parameter.isPrimitiveType = true; + } else if (Boolean.TRUE.equals(property.isString)) { parameter.isString = true; parameter.isPrimitiveType = true; } else if (Boolean.TRUE.equals(property.isBoolean)) { @@ -3506,16 +3604,14 @@ public void setParameterBooleanFlagWithCodegenProperty(CodegenParameter paramete } else if (Boolean.TRUE.equals(property.isFloat)) { parameter.isFloat = true; parameter.isPrimitiveType = true; - } else if (Boolean.TRUE.equals(property.isByteArray)) { - parameter.isByteArray = true; + } else if (Boolean.TRUE.equals(property.isNumber)) { + parameter.isNumber = true; parameter.isPrimitiveType = true; } else if (Boolean.TRUE.equals(property.isBinary)) { parameter.isByteArray = true; parameter.isPrimitiveType = true; } else if (Boolean.TRUE.equals(property.isFile)) { parameter.isFile = true; - // file is *not* a primitive type - //parameter.isPrimitiveType = true; } else if (Boolean.TRUE.equals(property.isDate)) { parameter.isDate = true; parameter.isPrimitiveType = true; @@ -3610,7 +3706,7 @@ public String addRegularExpressionDelimiter(String pattern) { * writes it back to additionalProperties to be usable as a boolean in * mustache files. * - * @param propertyKey + * @param propertyKey property key * @return property value as boolean */ public boolean convertPropertyToBooleanAndWriteBack(String propertyKey) { diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultGenerator.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultGenerator.java index 5a1ea3e1227..2c166e69577 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultGenerator.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultGenerator.java @@ -3,8 +3,8 @@ import com.samskivert.mustache.Mustache; import com.samskivert.mustache.Template; import io.swagger.codegen.ignore.CodegenIgnoreProcessor; -import io.swagger.codegen.utils.ImplementationVersion; import io.swagger.codegen.languages.AbstractJavaCodegen; +import io.swagger.codegen.utils.ImplementationVersion; import io.swagger.models.*; import io.swagger.models.auth.OAuth2Definition; import io.swagger.models.auth.SecuritySchemeDefinition; @@ -12,6 +12,7 @@ import io.swagger.util.Json; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,8 +20,6 @@ import java.io.*; import java.util.*; -import org.apache.commons.lang3.StringUtils; - public class DefaultGenerator extends AbstractGenerator implements Generator { protected final Logger LOGGER = LoggerFactory.getLogger(DefaultGenerator.class); protected CodegenConfig config; @@ -34,9 +33,11 @@ public class DefaultGenerator extends AbstractGenerator implements Generator { private Boolean generateApiDocumentation = null; private Boolean generateModelTests = null; private Boolean generateModelDocumentation = null; + private Boolean generateSwaggerMetadata = true; private String basePath; private String basePathWithoutHost; private String contextPath; + private Map generatorPropertyDefaults = new HashMap<>(); @Override public Generator opts(ClientOptInput opts) { @@ -46,22 +47,54 @@ public Generator opts(ClientOptInput opts) { this.config.additionalProperties().putAll(opts.getOpts().getProperties()); String ignoreFileLocation = this.config.getIgnoreFilePathOverride(); - if(ignoreFileLocation != null) { + if (ignoreFileLocation != null) { final File ignoreFile = new File(ignoreFileLocation); - if(ignoreFile.exists() && ignoreFile.canRead()) { + if (ignoreFile.exists() && ignoreFile.canRead()) { this.ignoreProcessor = new CodegenIgnoreProcessor(ignoreFile); } else { LOGGER.warn("Ignore file specified at {} is not valid. This will fall back to an existing ignore file if present in the output directory.", ignoreFileLocation); } } - if(this.ignoreProcessor == null) { + if (this.ignoreProcessor == null) { this.ignoreProcessor = new CodegenIgnoreProcessor(this.config.getOutputDir()); } return this; } + /** + * Programmatically disable the output of .swagger-codegen/VERSION, .swagger-codegen-ignore, + * or other metadata files used by Swagger Codegen. + * @param generateSwaggerMetadata true: enable outputs, false: disable outputs + */ + @SuppressWarnings("WeakerAccess") + public void setGenerateSwaggerMetadata(Boolean generateSwaggerMetadata) { + this.generateSwaggerMetadata = generateSwaggerMetadata; + } + + /** + * Set generator properties otherwise pulled from system properties. + * Useful for running tests in parallel without relying on System.properties. + * @param key The system property key + * @param value The system property value + */ + @SuppressWarnings("WeakerAccess") + public void setGeneratorPropertyDefault(final String key, final String value) { + this.generatorPropertyDefaults.put(key, value); + } + + private Boolean getGeneratorPropertyDefaultSwitch(final String key, final Boolean defaultValue) { + String result = null; + if (this.generatorPropertyDefaults.containsKey(key)) { + result = this.generatorPropertyDefaults.get(key); + } + if (result != null) { + return Boolean.valueOf(result); + } + return defaultValue; + } + private String getScheme() { String scheme; if (swagger.getSchemes() != null && swagger.getSchemes().size() > 0) { @@ -73,7 +106,7 @@ private String getScheme() { return scheme; } - private String getHost(){ + private String getHost() { StringBuilder hostBuilder = new StringBuilder(); hostBuilder.append(getScheme()); hostBuilder.append("://"); @@ -89,32 +122,32 @@ private String getHost(){ } private void configureGeneratorProperties() { - // allows generating only models by specifying a CSV of models to generate, or empty for all - generateApis = System.getProperty("apis") != null ? true:null; - generateModels = System.getProperty("models") != null ? true: null; - generateSupportingFiles = System.getProperty("supportingFiles") != null ? true:null; + // NOTE: Boolean.TRUE is required below rather than `true` because of JVM boxing constraints and type inference. + generateApis = System.getProperty(CodegenConstants.APIS) != null ? Boolean.TRUE : getGeneratorPropertyDefaultSwitch(CodegenConstants.APIS, null); + generateModels = System.getProperty(CodegenConstants.MODELS) != null ? Boolean.TRUE : getGeneratorPropertyDefaultSwitch(CodegenConstants.MODELS, null); + generateSupportingFiles = System.getProperty(CodegenConstants.SUPPORTING_FILES) != null ? Boolean.TRUE : getGeneratorPropertyDefaultSwitch(CodegenConstants.SUPPORTING_FILES, null); if (generateApis == null && generateModels == null && generateSupportingFiles == null) { // no specifics are set, generate everything generateApis = generateModels = generateSupportingFiles = true; } else { - if(generateApis == null) { + if (generateApis == null) { generateApis = false; } - if(generateModels == null) { + if (generateModels == null) { generateModels = false; } - if(generateSupportingFiles == null) { + if (generateSupportingFiles == null) { generateSupportingFiles = false; } } // model/api tests and documentation options rely on parent generate options (api or model) and no other options. // They default to true in all scenarios and can only be marked false explicitly - generateModelTests = System.getProperty("modelTests") != null ? Boolean.valueOf(System.getProperty("modelTests")): true; - generateModelDocumentation = System.getProperty("modelDocs") != null ? Boolean.valueOf(System.getProperty("modelDocs")):true; - generateApiTests = System.getProperty("apiTests") != null ? Boolean.valueOf(System.getProperty("apiTests")): true; - generateApiDocumentation = System.getProperty("apiDocs") != null ? Boolean.valueOf(System.getProperty("apiDocs")):true; + generateModelTests = System.getProperty(CodegenConstants.MODEL_TESTS) != null ? Boolean.valueOf(System.getProperty(CodegenConstants.MODEL_TESTS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.MODEL_TESTS, true); + generateModelDocumentation = System.getProperty(CodegenConstants.MODEL_DOCS) != null ? Boolean.valueOf(System.getProperty(CodegenConstants.MODEL_DOCS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.MODEL_DOCS, true); + generateApiTests = System.getProperty(CodegenConstants.API_TESTS) != null ? Boolean.valueOf(System.getProperty(CodegenConstants.API_TESTS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.API_TESTS, true); + generateApiDocumentation = System.getProperty(CodegenConstants.API_DOCS) != null ? Boolean.valueOf(System.getProperty(CodegenConstants.API_DOCS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.API_DOCS, true); // Additional properties added for tests to exclude references in project related files @@ -124,7 +157,7 @@ private void configureGeneratorProperties() { config.additionalProperties().put(CodegenConstants.GENERATE_API_DOCS, generateApiDocumentation); config.additionalProperties().put(CodegenConstants.GENERATE_MODEL_DOCS, generateModelDocumentation); - if(!generateApiTests && !generateModelTests) { + if (!generateApiTests && !generateModelTests) { config.additionalProperties().put(CodegenConstants.EXCLUDE_TESTS, true); } if (System.getProperty("debugSwagger") != null) { @@ -134,6 +167,7 @@ private void configureGeneratorProperties() { config.preprocessSwagger(swagger); config.additionalProperties().put("generatorVersion", ImplementationVersion.read()); config.additionalProperties().put("generatedDate", DateTime.now().toString()); + config.additionalProperties().put("generatedYear", String.valueOf(DateTime.now().getYear())); config.additionalProperties().put("generatorClass", config.getClass().getName()); config.additionalProperties().put("inputSpec", config.getInputSpec()); if (swagger.getVendorExtensions() != null) { @@ -160,7 +194,7 @@ private void configureSwaggerInfo() { LOGGER.error("Missing required field info version. Default appVersion set to 1.0.0"); config.additionalProperties().put("appVersion", "1.0.0"); } - + if (StringUtils.isEmpty(info.getDescription())) { // set a default description if none if provided config.additionalProperties().put("appDescription", @@ -198,7 +232,7 @@ private void configureSwaggerInfo() { } } - private void generateModelTests(List files, Map models, String modelName) throws IOException{ + private void generateModelTests(List files, Map models, String modelName) throws IOException { // to generate model test files for (String templateName : config.modelTestTemplateFiles().keySet()) { String suffix = config.modelTestTemplateFiles().get(templateName); @@ -243,15 +277,15 @@ private void generateModels(List files, List allModels) { String modelNames = System.getProperty("models"); Set modelsToGenerate = null; - if(modelNames != null && !modelNames.isEmpty()) { + if (modelNames != null && !modelNames.isEmpty()) { modelsToGenerate = new HashSet(Arrays.asList(modelNames.split(","))); } Set modelKeys = definitions.keySet(); - if(modelsToGenerate != null && !modelsToGenerate.isEmpty()) { + if (modelsToGenerate != null && !modelsToGenerate.isEmpty()) { Set updatedKeys = new HashSet(); - for(String m : modelKeys) { - if(modelsToGenerate.contains(m)) { + for (String m : modelKeys) { + if (modelsToGenerate.contains(m)) { updatedKeys.add(m); } } @@ -259,7 +293,7 @@ private void generateModels(List files, List allModels) { } // store all processed models - Map allProcessedModels = new TreeMap(new Comparator() { + Map allProcessedModels = new TreeMap(new Comparator() { @Override public int compare(String o1, String o2) { Model model1 = definitions.get(o1); @@ -300,7 +334,7 @@ private Model getParent(Model model) { return definitions.get(interf.getSimpleRef()); } } - if(parent != null) { + if (parent != null) { return definitions.get(parent.getReference()); } } @@ -313,7 +347,7 @@ private Model getParent(Model model) { for (String name : modelKeys) { try { //don't generate models that have an import mapping - if(config.importMapping().containsKey(name)) { + if (config.importMapping().containsKey(name)) { LOGGER.info("Model " + name + " not imported due to import mapping"); continue; } @@ -333,11 +367,12 @@ private Model getParent(Model model) { allProcessedModels = config.postProcessAllModels(allProcessedModels); // generate files based on processed models - for (String modelName: allProcessedModels.keySet()) { - Map models = (Map)allProcessedModels.get(modelName); + for (String modelName : allProcessedModels.keySet()) { + Map models = (Map) allProcessedModels.get(modelName); + models.put("modelPackage", config.modelPackage()); try { //don't generate models that have an import mapping - if(config.importMapping().containsKey(modelName)) { + if (config.importMapping().containsKey(modelName)) { continue; } Map modelTemplate = (Map) ((List) models.get("models")).get(0); @@ -359,14 +394,14 @@ private Model getParent(Model model) { continue; } File written = processTemplateToFile(models, templateName, filename); - if(written != null) { + if (written != null) { files.add(written); } } - if(generateModelTests) { + if (generateModelTests) { generateModelTests(files, models, modelName); } - if(generateModelDocumentation) { + if (generateModelDocumentation) { // to generate model documentation files generateModelDocumentation(files, models, modelName); } @@ -381,20 +416,20 @@ private Model getParent(Model model) { } - private void generateApis(List files, List allOperations) { + private void generateApis(List files, List allOperations, List allModels) { if (!generateApis) { return; } Map> paths = processPaths(swagger.getPaths()); Set apisToGenerate = null; String apiNames = System.getProperty("apis"); - if(apiNames != null && !apiNames.isEmpty()) { + if (apiNames != null && !apiNames.isEmpty()) { apisToGenerate = new HashSet(Arrays.asList(apiNames.split(","))); } - if(apisToGenerate != null && !apisToGenerate.isEmpty()) { + if (apisToGenerate != null && !apisToGenerate.isEmpty()) { Map> updatedPaths = new TreeMap>(); - for(String m : paths.keySet()) { - if(apisToGenerate.contains(m)) { + for (String m : paths.keySet()) { + if (apisToGenerate.contains(m)) { updatedPaths.put(m, paths.get(m)); } } @@ -409,12 +444,13 @@ public int compare(CodegenOperation one, CodegenOperation another) { return ObjectUtils.compare(one.operationId, another.operationId); } }); - Map operation = processOperations(config, tag, ops); + Map operation = processOperations(config, tag, ops, allModels); operation.put("basePath", basePath); operation.put("basePathWithoutHost", basePathWithoutHost); operation.put("contextPath", contextPath); operation.put("baseName", tag); + operation.put("apiPackage", config.apiPackage()); operation.put("modelPackage", config.modelPackage()); operation.putAll(config.additionalProperties()); operation.put("classname", config.toApiName(tag)); @@ -422,7 +458,7 @@ public int compare(CodegenOperation one, CodegenOperation another) { operation.put("importPath", config.toApiImport(tag)); operation.put("classFilename", config.toApiFilename(tag)); - if(!config.vendorExtensions().isEmpty()) { + if (!config.vendorExtensions().isEmpty()) { operation.put("vendorExtensions", config.vendorExtensions()); } @@ -452,12 +488,12 @@ public int compare(CodegenOperation one, CodegenOperation another) { } File written = processTemplateToFile(operation, templateName, filename); - if(written != null) { + if (written != null) { files.add(written); } } - if(generateApiTests) { + if (generateApiTests) { // to generate api test files for (String templateName : config.apiTestTemplateFiles().keySet()) { String filename = config.apiTestFilename(templateName, tag); @@ -475,7 +511,7 @@ public int compare(CodegenOperation one, CodegenOperation another) { } - if(generateApiDocumentation) { + if (generateApiDocumentation) { // to generate api documentation files for (String templateName : config.apiDocTemplateFiles().keySet()) { String filename = config.apiDocFilename(templateName, tag); @@ -508,7 +544,7 @@ private void generateSupportingFiles(List files, Map bundl } Set supportingFilesToGenerate = null; String supportingFiles = System.getProperty("supportingFiles"); - if(supportingFiles!= null && !supportingFiles.isEmpty()) { + if (supportingFiles != null && !supportingFiles.isEmpty()) { supportingFilesToGenerate = new HashSet(Arrays.asList(supportingFiles.split(","))); } @@ -528,20 +564,20 @@ private void generateSupportingFiles(List files, Map bundl continue; } String templateFile; - if( support instanceof GlobalSupportingFile) { - templateFile = config.getCommonTemplateDir() + File.separator + support.templateFile; + if (support instanceof GlobalSupportingFile) { + templateFile = config.getCommonTemplateDir() + File.separator + support.templateFile; } else { templateFile = getFullTemplateFile(config, support.templateFile); } boolean shouldGenerate = true; - if(supportingFilesToGenerate != null && !supportingFilesToGenerate.isEmpty()) { + if (supportingFilesToGenerate != null && !supportingFilesToGenerate.isEmpty()) { shouldGenerate = supportingFilesToGenerate.contains(support.destinationFilename); } - if (!shouldGenerate){ + if (!shouldGenerate) { continue; } - if(ignoreProcessor.allowsFile(new File(outputFilename))) { + if (ignoreProcessor.allowsFile(new File(outputFilename))) { if (templateFile.endsWith("mustache")) { String template = readTemplate(templateFile); Mustache.Compiler compiler = Mustache.compiler(); @@ -593,8 +629,8 @@ public Reader getTemplate(String name) { final String swaggerCodegenIgnore = ".swagger-codegen-ignore"; String ignoreFileNameTarget = config.outputFolder() + File.separator + swaggerCodegenIgnore; File ignoreFile = new File(ignoreFileNameTarget); - if(!ignoreFile.exists()) { - String ignoreFileNameSource = File.separator + config.getCommonTemplateDir() + File.separator + swaggerCodegenIgnore; + if (generateSwaggerMetadata && !ignoreFile.exists()) { + String ignoreFileNameSource = File.separator + config.getCommonTemplateDir() + File.separator + swaggerCodegenIgnore; String ignoreFileContents = readResourceContents(ignoreFileNameSource); try { writeToFile(ignoreFileNameTarget, ignoreFileContents); @@ -604,13 +640,15 @@ public Reader getTemplate(String name) { files.add(ignoreFile); } - final String swaggerVersionMetadata = config.outputFolder() + File.separator + ".swagger-codegen" + File.separator + "VERSION"; - File swaggerVersionMetadataFile = new File(swaggerVersionMetadata); - try { - writeToFile(swaggerVersionMetadata, ImplementationVersion.read()); - files.add(swaggerVersionMetadataFile); - } catch (IOException e) { - throw new RuntimeException("Could not generate supporting file '" + swaggerVersionMetadata + "'", e); + if(generateSwaggerMetadata) { + final String swaggerVersionMetadata = config.outputFolder() + File.separator + ".swagger-codegen" + File.separator + "VERSION"; + File swaggerVersionMetadataFile = new File(swaggerVersionMetadata); + try { + writeToFile(swaggerVersionMetadata, ImplementationVersion.read()); + files.add(swaggerVersionMetadataFile); + } catch (IOException e) { + throw new RuntimeException("Could not generate supporting file '" + swaggerVersionMetadata + "'", e); + } } /* @@ -648,7 +686,7 @@ private Map buildSupportFileBundle(List allOperations, L bundle.put("swagger", this.swagger); bundle.put("basePath", basePath); - bundle.put("basePathWithoutHost",basePathWithoutHost); + bundle.put("basePathWithoutHost", basePathWithoutHost); bundle.put("scheme", getScheme()); bundle.put("contextPath", contextPath); bundle.put("apiInfo", apis); @@ -697,7 +735,7 @@ public List generate() { generateModels(files, allModels); // apis List allOperations = new ArrayList(); - generateApis(files, allOperations); + generateApis(files, allOperations, allModels); // supporting files Map bundle = buildSupportFileBundle(allOperations, allModels); @@ -706,9 +744,10 @@ public List generate() { return files; } + private File processTemplateToFile(Map templateData, String templateName, String outputFilename) throws IOException { String adjustedOutputFilename = outputFilename.replaceAll("//", "/").replace('/', File.separatorChar); - if(ignoreProcessor.allowsFile(new File(adjustedOutputFilename))) { + if (ignoreProcessor.allowsFile(new File(adjustedOutputFilename))) { String templateFile = getFullTemplateFile(config, templateName); String template = readTemplate(templateFile); Mustache.Compiler compiler = Mustache.compiler(); @@ -732,7 +771,7 @@ public Reader getTemplate(String name) { } private static void processMimeTypes(List mimeTypeList, Map operation, String source) { - if (mimeTypeList == null || mimeTypeList.isEmpty()){ + if (mimeTypeList == null || mimeTypeList.isEmpty()) { return; } List> c = new ArrayList>(); @@ -776,11 +815,37 @@ private void processOperation(String resourcePath, String httpMethod, Operation if (System.getProperty("debugOperations") != null) { LOGGER.info("processOperation: resourcePath= " + resourcePath + "\t;" + httpMethod + " " + operation + "\n"); } - List tags = operation.getTags(); - if (tags == null) { - tags = new ArrayList(); - tags.add("default"); + List tags = new ArrayList(); + + List tagNames = operation.getTags(); + List swaggerTags = swagger.getTags(); + if (tagNames != null) { + if (swaggerTags == null) { + for (String tagName : tagNames) { + tags.add(new Tag().name(tagName)); + } + } else { + for (String tagName : tagNames) { + boolean foundTag = false; + for (Tag tag : swaggerTags) { + if (tag.getName().equals(tagName)) { + tags.add(tag); + foundTag = true; + break; + } + } + + if (!foundTag) { + tags.add(new Tag().name(tagName)); + } + } + } + } + + if (tags.isEmpty()) { + tags.add(new Tag().name("default")); } + /* build up a set of parameter "ids" defined at the operation level per the swagger 2.0 spec "A unique parameter is defined by a combination of a name and location" @@ -803,12 +868,11 @@ private void processOperation(String resourcePath, String httpMethod, Operation } } - for (String tag : tags) { + for (Tag tag : tags) { try { CodegenOperation codegenOperation = config.fromOperation(resourcePath, httpMethod, operation, swagger.getDefinitions(), swagger); - codegenOperation.tags = new ArrayList(); - codegenOperation.tags.add(config.sanitizeTag(tag)); - config.addOperationToGroup(config.sanitizeTag(tag), resourcePath, operation, codegenOperation, operations); + codegenOperation.tags = new ArrayList(tags); + config.addOperationToGroup(config.sanitizeTag(tag.getName()), resourcePath, operation, codegenOperation, operations); List>> securities = operation.getSecurity(); if (securities == null && swagger.getSecurity() != null) { @@ -821,7 +885,7 @@ private void processOperation(String resourcePath, String httpMethod, Operation continue; } Map authMethods = new HashMap(); - for (Map> security: securities) { + for (Map> security : securities) { for (String securityName : security.keySet()) { SecuritySchemeDefinition securityDefinition = swagger.getSecurityDefinitions().get(securityName); if (securityDefinition == null) { @@ -850,8 +914,7 @@ private void processOperation(String resourcePath, String httpMethod, Operation codegenOperation.authMethods = config.fromSecurity(authMethods); codegenOperation.hasAuthMethods = true; } - } - catch (Exception ex) { + } catch (Exception ex) { String msg = "Could not process operation:\n" // + " Tag: " + tag + "\n"// + " Operation: " + operation.getOperationId() + "\n" // @@ -869,7 +932,7 @@ private static String generateParameterId(Parameter parameter) { } - private Map processOperations(CodegenConfig config, String tag, List ops) { + private Map processOperations(CodegenConfig config, String tag, List ops, List allModels) { Map operations = new HashMap(); Map objs = new HashMap(); objs.put("classname", config.toApiName(tag)); @@ -917,6 +980,7 @@ private Map processOperations(CodegenConfig config, String tag, operations.put("hasImport", true); } config.postProcessOperations(operations); + config.postProcessOperationsWithModels(operations, allModels); if (objs.size() > 0) { List os = (List) objs.get("operation"); @@ -961,7 +1025,7 @@ private Map processModels(CodegenConfig config, Map> imports = new ArrayList>(); - for(String s: importSet) { + for (String s : importSet) { Map item = new HashMap(); item.put("import", s); imports.add(item); diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/InlineModelResolver.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/InlineModelResolver.java index 34b0906fd10..143fa9568f6 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/InlineModelResolver.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/InlineModelResolver.java @@ -68,9 +68,13 @@ public void flatten(Swagger swagger) { Model innerModel = modelFromProperty(op, modelName); String existing = matchGenerated(innerModel); if (existing != null) { - am.setItems(new RefProperty(existing)); + RefProperty refProperty = new RefProperty(existing); + refProperty.setRequired(op.getRequired()); + am.setItems(refProperty); } else { - am.setItems(new RefProperty(modelName)); + RefProperty refProperty = new RefProperty(modelName); + refProperty.setRequired(op.getRequired()); + am.setItems(refProperty); addGenerated(modelName, innerModel); swagger.addDefinition(modelName, innerModel); } @@ -94,9 +98,13 @@ public void flatten(Swagger swagger) { Model model = modelFromProperty(op, modelName); String existing = matchGenerated(model); if (existing != null) { - response.setSchema(this.makeRefProperty(existing, property)); + Property refProperty = this.makeRefProperty(existing, property); + refProperty.setRequired(op.getRequired()); + response.setSchema(refProperty); } else { - response.setSchema(this.makeRefProperty(modelName, property)); + Property refProperty = this.makeRefProperty(modelName, property); + refProperty.setRequired(op.getRequired()); + response.setSchema(refProperty); addGenerated(modelName, model); swagger.addDefinition(modelName, model); } @@ -114,9 +122,13 @@ public void flatten(Swagger swagger) { Model innerModel = modelFromProperty(op, modelName); String existing = matchGenerated(innerModel); if (existing != null) { - ap.setItems(this.makeRefProperty(existing, op)); + Property refProperty = this.makeRefProperty(existing, op); + refProperty.setRequired(op.getRequired()); + ap.setItems(refProperty); } else { - ap.setItems(this.makeRefProperty(modelName, op)); + Property refProperty = this.makeRefProperty(modelName, op); + refProperty.setRequired(op.getRequired()); + ap.setItems(refProperty); addGenerated(modelName, innerModel); swagger.addDefinition(modelName, innerModel); } @@ -135,9 +147,13 @@ public void flatten(Swagger swagger) { Model innerModel = modelFromProperty(op, modelName); String existing = matchGenerated(innerModel); if (existing != null) { - mp.setAdditionalProperties(new RefProperty(existing)); + RefProperty refProperty = new RefProperty(existing); + refProperty.setRequired(op.getRequired()); + mp.setAdditionalProperties(refProperty); } else { - mp.setAdditionalProperties(new RefProperty(modelName)); + RefProperty refProperty = new RefProperty(modelName); + refProperty.setRequired(op.getRequired()); + mp.setAdditionalProperties(refProperty); addGenerated(modelName, innerModel); swagger.addDefinition(modelName, innerModel); } @@ -161,7 +177,7 @@ public void flatten(Swagger swagger) { Map properties = m.getProperties(); flattenProperties(properties, modelName); - + fixStringModel(m); } else if (model instanceof ArrayModel) { ArrayModel m = (ArrayModel) model; Property inner = m.getItems(); @@ -174,9 +190,13 @@ public void flatten(Swagger swagger) { if (existing == null) { swagger.addDefinition(innerModelName, innerModel); addGenerated(innerModelName, innerModel); - m.setItems(new RefProperty(innerModelName)); + RefProperty refProperty = new RefProperty(innerModelName); + refProperty.setRequired(op.getRequired()); + m.setItems(refProperty); } else { - m.setItems(new RefProperty(existing)); + RefProperty refProperty = new RefProperty(existing); + refProperty.setRequired(op.getRequired()); + m.setItems(refProperty); } } } @@ -191,6 +211,21 @@ public void flatten(Swagger swagger) { } } + /** + * This function fix models that are string (mostly enum). Before this fix, the example + * would look something like that in the doc: "\"example from def\"" + * @param m Model implementation + */ + private void fixStringModel(ModelImpl m) { + if (m.getType() != null && m.getType().equals("string") && m.getExample() != null) { + String example = m.getExample().toString(); + if (example.substring(0, 1).equals("\"") && + example.substring(example.length() - 1).equals("\"")) { + m.setExample(example.substring(1, example.length() - 1)); + } + } + } + private String resolveModelName(String title, String key) { if (title == null) { return uniqueName(key); @@ -256,9 +291,13 @@ public void flattenProperties(Map properties, String path) { String existing = matchGenerated(model); if (existing != null) { - propsToUpdate.put(key, new RefProperty(existing)); + RefProperty refProperty = new RefProperty(existing); + refProperty.setRequired(op.getRequired()); + propsToUpdate.put(key, refProperty); } else { - propsToUpdate.put(key, new RefProperty(modelName)); + RefProperty refProperty = new RefProperty(modelName); + refProperty.setRequired(op.getRequired()); + propsToUpdate.put(key, refProperty); modelsToAdd.put(modelName, model); addGenerated(modelName, model); swagger.addDefinition(modelName, model); @@ -275,9 +314,13 @@ public void flattenProperties(Map properties, String path) { Model innerModel = modelFromProperty(op, modelName); String existing = matchGenerated(innerModel); if (existing != null) { - ap.setItems(new RefProperty(existing)); + RefProperty refProperty = new RefProperty(existing); + refProperty.setRequired(op.getRequired()); + ap.setItems(refProperty); } else { - ap.setItems(new RefProperty(modelName)); + RefProperty refProperty = new RefProperty(modelName); + refProperty.setRequired(op.getRequired()); + ap.setItems(refProperty); addGenerated(modelName, innerModel); swagger.addDefinition(modelName, innerModel); } @@ -295,9 +338,13 @@ public void flattenProperties(Map properties, String path) { Model innerModel = modelFromProperty(op, modelName); String existing = matchGenerated(innerModel); if (existing != null) { - mp.setAdditionalProperties(new RefProperty(existing)); + RefProperty refProperty = new RefProperty(existing); + refProperty.setRequired(op.getRequired()); + mp.setAdditionalProperties(refProperty); } else { - mp.setAdditionalProperties(new RefProperty(modelName)); + RefProperty refProperty = new RefProperty(modelName); + refProperty.setRequired(op.getRequired()); + mp.setAdditionalProperties(refProperty); addGenerated(modelName, innerModel); swagger.addDefinition(modelName, innerModel); } @@ -384,9 +431,9 @@ public Model modelFromProperty(MapProperty object, @SuppressWarnings("unused") S /** * Make a RefProperty - * - * @param ref - * @param property + * + * @param ref new property name + * @param property Property * @return */ public Property makeRefProperty(String ref, Property property) { @@ -397,9 +444,9 @@ public Property makeRefProperty(String ref, Property property) { /** * Copy vendor extensions from Property to another Property - * - * @param source - * @param target + * + * @param source source property + * @param target target property */ public void copyVendorExtensions(Property source, AbstractProperty target) { Map vendorExtensions = source.getVendorExtensions(); diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/examples/ExampleGenerator.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/examples/ExampleGenerator.java index 08d5065cfd6..0ab60e5befa 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/examples/ExampleGenerator.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/examples/ExampleGenerator.java @@ -1,39 +1,17 @@ package io.swagger.codegen.examples; -import static io.swagger.models.properties.StringProperty.Format.URI; -import static io.swagger.models.properties.StringProperty.Format.URL; - import io.swagger.models.Model; import io.swagger.models.ModelImpl; -import io.swagger.models.properties.ArrayProperty; -import io.swagger.models.properties.BaseIntegerProperty; -import io.swagger.models.properties.BooleanProperty; -import io.swagger.models.properties.DateProperty; -import io.swagger.models.properties.DateTimeProperty; -import io.swagger.models.properties.DecimalProperty; -import io.swagger.models.properties.DoubleProperty; -import io.swagger.models.properties.FileProperty; -import io.swagger.models.properties.FloatProperty; -import io.swagger.models.properties.LongProperty; -import io.swagger.models.properties.MapProperty; -import io.swagger.models.properties.ObjectProperty; -import io.swagger.models.properties.Property; -import io.swagger.models.properties.RefProperty; -import io.swagger.models.properties.StringProperty; -import io.swagger.models.properties.UUIDProperty; +import io.swagger.models.properties.*; import io.swagger.util.Json; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.Set; +import java.util.*; + +import static io.swagger.models.properties.StringProperty.Format.URI; +import static io.swagger.models.properties.StringProperty.Format.URL; public class ExampleGenerator { private static final Logger logger = LoggerFactory.getLogger(ExampleGenerator.class); @@ -68,7 +46,7 @@ public List> generate(Map examples, List kv = new HashMap<>(); kv.put(CONTENT_TYPE, mediaType); if (property != null && mediaType.startsWith(MIME_TYPE_JSON)) { - String example = Json.pretty(resolvePropertyToExample(mediaType, property, processedModels)); + String example = Json.pretty(resolvePropertyToExample("", mediaType, property, processedModels)); if (example != null) { kv.put(EXAMPLE, example); @@ -98,7 +76,54 @@ public List> generate(Map examples, List processedModels) { + public List> generate(Map examples, List mediaTypes, String modelName) { + List> output = new ArrayList<>(); + Set processedModels = new HashSet<>(); + if (examples == null) { + if (mediaTypes == null) { + // assume application/json for this + mediaTypes = Collections.singletonList(MIME_TYPE_JSON); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + } + for (String mediaType : mediaTypes) { + Map kv = new HashMap<>(); + kv.put(CONTENT_TYPE, mediaType); + if (modelName != null && mediaType.startsWith(MIME_TYPE_JSON)) { + final Model model = this.examples.get(modelName); + if (model != null) { + + String example = Json.pretty(resolveModelToExample(modelName, mediaType, model, processedModels)); + + if (example != null) { + kv.put(EXAMPLE, example); + output.add(kv); + } + } + } else if (modelName != null && mediaType.startsWith(MIME_TYPE_XML)) { + final Model model = this.examples.get(modelName); + String example = new XmlExampleGenerator(this.examples).toXml(model, 0, Collections.emptySet()); + if (example != null) { + kv.put(EXAMPLE, example); + output.add(kv); + } + } + } + } else { + for (Map.Entry entry : examples.entrySet()) { + final Map kv = new HashMap<>(); + kv.put(CONTENT_TYPE, entry.getKey()); + kv.put(EXAMPLE, Json.pretty(entry.getValue())); + output.add(kv); + } + } + if (output.size() == 0) { + Map kv = new HashMap<>(); + kv.put(OUTPUT, NONE); + output.add(kv); + } + return output; + } + + private Object resolvePropertyToExample(String propertyName, String mediaType, Property property, Set processedModels) { logger.debug("Resolving example for property {}...", property); if (property.getExample() != null) { logger.debug("Example set in swagger spec, returning example: '{}'", property.getExample().toString()); @@ -120,8 +145,8 @@ private Object resolvePropertyToExample(String mediaType, Property property, Set logger.debug("URI or URL format, without default or enum, generating random one."); return "http://example.com/aeiou"; } - logger.debug("No values found, using default string 'aeiou' as example"); - return "aeiou"; + logger.debug("No values found, using property name " + propertyName + " as example"); + return propertyName; } else if (property instanceof BooleanProperty) { Boolean defaultValue = ((BooleanProperty) property).getDefault(); if (defaultValue != null) { @@ -131,9 +156,13 @@ private Object resolvePropertyToExample(String mediaType, Property property, Set } else if (property instanceof ArrayProperty) { Property innerType = ((ArrayProperty) property).getItems(); if (innerType != null) { - return new Object[]{ - resolvePropertyToExample(mediaType, innerType, processedModels) - }; + int arrayLength = null == ((ArrayProperty) property).getMaxItems() ? 2 : ((ArrayProperty) property).getMaxItems(); + Object[] objectProperties = new Object[arrayLength]; + Object objProperty = resolvePropertyToExample(propertyName, mediaType, innerType, processedModels); + for(int i=0; i < arrayLength; i++) { + objectProperties[i] = objProperty; + } + return objectProperties; } } else if (property instanceof DateProperty) { return "2000-01-23"; @@ -165,10 +194,10 @@ private Object resolvePropertyToExample(String mediaType, Property property, Set Map mp = new HashMap(); if (property.getName() != null) { mp.put(property.getName(), - resolvePropertyToExample(mediaType, ((MapProperty) property).getAdditionalProperties(), processedModels)); + resolvePropertyToExample(propertyName, mediaType, ((MapProperty) property).getAdditionalProperties(), processedModels)); } else { mp.put("key", - resolvePropertyToExample(mediaType, ((MapProperty) property).getAdditionalProperties(), processedModels)); + resolvePropertyToExample(propertyName, mediaType, ((MapProperty) property).getAdditionalProperties(), processedModels)); } return mp; } else if (property instanceof ObjectProperty) { @@ -203,7 +232,7 @@ private double randomNumber(Double min, Double max) { private Object resolveModelToExample(String name, String mediaType, Model model, Set processedModels) { if (processedModels.contains(name)) { - return ""; + return model.getExample(); } if (model instanceof ModelImpl) { processedModels.add(name); @@ -219,11 +248,12 @@ private Object resolveModelToExample(String name, String mediaType, Model model, logger.debug("Creating example from model values"); for (String propertyName : impl.getProperties().keySet()) { Property property = impl.getProperties().get(propertyName); - values.put(propertyName, resolvePropertyToExample(mediaType, property, processedModels)); + values.put(propertyName, resolvePropertyToExample(propertyName, mediaType, property, processedModels)); } + impl.setExample(values); } return values; } return ""; } -} \ No newline at end of file +} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/ignore/CodegenIgnoreProcessor.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/ignore/CodegenIgnoreProcessor.java index 0de8d29862f..76179951d96 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/ignore/CodegenIgnoreProcessor.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/ignore/CodegenIgnoreProcessor.java @@ -45,7 +45,7 @@ public CodegenIgnoreProcessor(final String baseDirectory, final String ignoreFil if (directory.exists() && directory.isDirectory()) { loadFromFile(targetIgnoreFile); } else { - LOGGER.warn("Output directory does not exist, or is inaccessible. No file (.swager-codegen-ignore) will be evaluated."); + LOGGER.warn("Output directory does not exist, or is inaccessible. No file (.swagger-codegen-ignore) will be evaluated."); } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractAdaCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractAdaCodegen.java new file mode 100644 index 00000000000..eb6e0263ac9 --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractAdaCodegen.java @@ -0,0 +1,163 @@ +package io.swagger.codegen.languages; + +import io.swagger.codegen.CodegenConfig; +import io.swagger.codegen.CodegenProperty; +import io.swagger.codegen.DefaultCodegen; +import io.swagger.models.properties.Property; + +import java.util.Arrays; + +abstract public class AbstractAdaCodegen extends DefaultCodegen implements CodegenConfig { + + public AbstractAdaCodegen() { + super(); + + /* + * Reserved words. Override this with reserved words specific to your language + */ + setReservedWordsLowerCase( + Arrays.asList( + "abort", + "abs", + "abstract", + "accept", + "access", + "aliased", + "all", + "and", + "array", + "at", + "begin", + "body", + "case", + "constant", + "declare", + "delay", + "digits", + "do", + "else", + "elsif", + "end", + "entry", + "exception", + "exit", + "for", + "function", + "generic", + "goto", + "if", + "in", + "interface", + "is", + "limited", + "loop", + "mod", + "new", + "not", + "null", + "of", + "or", + "others", + "out", + "overriding", + "package", + "pragma", + "private", + "procedure", + "protected", + "raise", + "range", + "record", + "rem", + "renames", + "requeue", + "return", + "reverse", + "select", + "separate", + "some", + "subtype", + "synchronized", + "tagged", + "task", + "terminate", + "then", + "type", + "until", + "use", + "when", + "while", + "with", + "xor") + ); + } + + /** + * Turn a parameter name, operation name into an Ada identifier. + * + * Ada programming standard avoid the camelcase syntax and prefer the underscore + * notation. We also have to make sure the identifier is not a reserved keyword. + * When this happens, we add the configurable prefix. The function translates: + * + * body - P_Body + * petId - Pet_Id + * updatePetWithForm - Update_Pet_With_Form + * + * @param name the parameter name. + * @param prefix the optional prefix in case the parameter name is a reserved keyword. + * @return the Ada identifier to be used. + */ + protected String toAdaIdentifier(String name, String prefix) { + // We cannot use reserved keywords for identifiers + if (isReservedWord(name)) { + LOGGER.warn("Identifier '" + name + "' is a reserved word, renamed to " + prefix + name); + name = prefix + name; + } + StringBuilder result = new StringBuilder(); + boolean needUpperCase = true; + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if (needUpperCase) { + needUpperCase = false; + result.append(Character.toUpperCase(c)); + + } else if (Character.isUpperCase((c))) { + if (!needUpperCase) { + result.append('_'); + } + result.append(c); + needUpperCase = false; + } else { + result.append(c); + if (c == '_') { + needUpperCase = true; + } + } + } + return result.toString(); + } + + @Override + public String toOperationId(String operationId) { + return toAdaIdentifier(sanitizeName(operationId), "Call_"); + } + + @Override + public String toVarName(String name) { + return toAdaIdentifier(sanitizeName(name), "P_"); + } + + @Override + public String toParamName(String name) { + return toAdaIdentifier(super.toParamName(name), "P_"); + } + + @Override + public CodegenProperty fromProperty(String name, Property p) { + CodegenProperty property = super.fromProperty(name, p); + String nameInCamelCase = property.nameInCamelCase; + nameInCamelCase = sanitizeName(nameInCamelCase); + property.nameInCamelCase = nameInCamelCase; + return property; + } +} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractCSharpCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractCSharpCodegen.java index 51e5867dbfe..7145186b730 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractCSharpCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractCSharpCodegen.java @@ -1,6 +1,7 @@ package io.swagger.codegen.languages; import io.swagger.codegen.*; +import io.swagger.codegen.utils.ModelUtils; import io.swagger.models.properties.*; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -20,6 +21,8 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co protected boolean returnICollection = false; protected boolean netCoreProjectFileFlag = false; + protected String modelPropertyNaming = "PascalCase"; + protected String packageVersion = "1.0.0"; protected String packageName = "IO.Swagger"; protected String packageTitle = "Swagger Library"; @@ -47,6 +50,8 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co public AbstractCSharpCodegen() { super(); + supportsInheritance = true; + // C# does not use import mapping importMapping.clear(); @@ -271,24 +276,33 @@ public void processOpts() { // {{useDateTimeOffset}} if (additionalProperties.containsKey(CodegenConstants.USE_DATETIME_OFFSET)) { - useDateTimeOffset(Boolean.valueOf(additionalProperties.get(CodegenConstants.USE_DATETIME_OFFSET).toString())); + useDateTimeOffset(convertPropertyToBooleanAndWriteBack(CodegenConstants.USE_DATETIME_OFFSET)); + } else { + additionalProperties.put(CodegenConstants.USE_DATETIME_OFFSET, useDateTimeOffsetFlag); } - additionalProperties.put(CodegenConstants.USE_DATETIME_OFFSET, useDateTimeOffsetFlag); if (additionalProperties.containsKey(CodegenConstants.USE_COLLECTION)) { - setUseCollection(Boolean.valueOf(additionalProperties.get(CodegenConstants.USE_COLLECTION).toString())); + setUseCollection(convertPropertyToBooleanAndWriteBack(CodegenConstants.USE_COLLECTION)); + } else { + additionalProperties.put(CodegenConstants.USE_COLLECTION, useCollection); } if (additionalProperties.containsKey(CodegenConstants.RETURN_ICOLLECTION)) { - setReturnICollection(Boolean.valueOf(additionalProperties.get(CodegenConstants.RETURN_ICOLLECTION).toString())); + setReturnICollection(convertPropertyToBooleanAndWriteBack(CodegenConstants.RETURN_ICOLLECTION)); + } else { + additionalProperties.put(CodegenConstants.RETURN_ICOLLECTION, returnICollection); } if (additionalProperties.containsKey(CodegenConstants.OPTIONAL_EMIT_DEFAULT_VALUES)) { - setOptionalEmitDefaultValue(Boolean.valueOf(additionalProperties.get(CodegenConstants.OPTIONAL_EMIT_DEFAULT_VALUES).toString())); + setOptionalEmitDefaultValue(convertPropertyToBooleanAndWriteBack(CodegenConstants.OPTIONAL_EMIT_DEFAULT_VALUES)); + } else { + additionalProperties.put(CodegenConstants.OPTIONAL_EMIT_DEFAULT_VALUES, optionalEmitDefaultValue); } if (additionalProperties.containsKey(CodegenConstants.NETCORE_PROJECT_FILE)) { - setNetCoreProjectFileFlag(Boolean.valueOf(additionalProperties.get(CodegenConstants.NETCORE_PROJECT_FILE).toString())); + setNetCoreProjectFileFlag(convertPropertyToBooleanAndWriteBack(CodegenConstants.NETCORE_PROJECT_FILE)); + } else { + additionalProperties.put(CodegenConstants.NETCORE_PROJECT_FILE, netCoreProjectFileFlag); } if (additionalProperties.containsKey(CodegenConstants.INTERFACE_PREFIX)) { @@ -305,6 +319,11 @@ public void processOpts() { additionalProperties.put(CodegenConstants.INTERFACE_PREFIX, interfacePrefix); } + @Override + public void postProcessModelProperty(CodegenModel model, CodegenProperty property) { + super.postProcessModelProperty(model, property); + } + @Override public Map postProcessModels(Map objs) { List models = (List) objs.get("models"); @@ -324,6 +343,61 @@ public Map postProcessModels(Map objs) { return postProcessModelsEnum(objs); } + /** + * Invoked by {@link DefaultGenerator} after all models have been post-processed, allowing for a last pass of codegen-specific model cleanup. + * + * @param objs Current state of codegen object model. + * @return An in-place modified state of the codegen object model. + */ + @Override + public Map postProcessAllModels(Map objs) { + final Map processed = super.postProcessAllModels(objs); + postProcessEnumRefs(processed); + return processed; + } + + /** + * C# differs from other languages in that Enums are not _true_ objects; enums are compiled to integral types. + * So, in C#, an enum is considers more like a user-defined primitive. + * + * When working with enums, we can't always assume a RefModel is a nullable type (where default(YourType) == null), + * so this post processing runs through all models to find RefModel'd enums. Then, it runs through all vars and modifies + * those vars referencing RefModel'd enums to work the same as inlined enums rather than as objects. + * @param models + */ + private void postProcessEnumRefs(final Map models) { + Map enumRefs = new HashMap(); + for (Map.Entry entry : models.entrySet()) { + CodegenModel model = ModelUtils.getModelByName(entry.getKey(), models); + if (model.isEnum) { + enumRefs.put(entry.getKey(), model); + } + } + + for (Map.Entry entry : models.entrySet()) { + String swaggerName = entry.getKey(); + CodegenModel model = ModelUtils.getModelByName(swaggerName, models); + if (model != null) { + for (CodegenProperty var : model.allVars) { + if (enumRefs.containsKey(var.datatype)) { + // Handle any enum properties referred to by $ref. + // This is different in C# than most other generators, because enums in C# are compiled to integral types, + // while enums in many other languages are true objects. + CodegenModel refModel = enumRefs.get(var.datatype); + var.allowableValues = refModel.allowableValues; + updateCodegenPropertyEnum(var); + + // We do these after updateCodegenPropertyEnum to avoid generalities that don't mesh with C#. + var.isPrimitiveType = true; + var.isEnum = true; + } + } + } else { + LOGGER.warn("Expected to retrieve model %s by name, but no model was found. Check your -Dmodels inclusions.", swaggerName); + } + } + } + @Override public Map postProcessOperations(Map objs) { super.postProcessOperations(objs); @@ -564,6 +638,12 @@ public String toDefaultValue(Property p) { public String getSwaggerType(Property p) { String swaggerType = super.getSwaggerType(p); String type; + + if (swaggerType == null) { + swaggerType = ""; // set swagger type to empty string if null + } + + // TODO avoid using toLowerCase as typeMapping should be case-sensitive if (typeMapping.containsKey(swaggerType.toLowerCase())) { type = typeMapping.get(swaggerType.toLowerCase()); if (languageSpecificPrimitives.contains(type)) { @@ -600,6 +680,11 @@ private String getNullableTypeFor(String swaggerType) { @Override public String toModelName(String name) { + // We need to check if import-mapping has a different model for this class, so we use it + // instead of the auto-generated one. + if (importMapping.containsKey(name)) { + return importMapping.get(name); + } if (!StringUtils.isEmpty(modelNamePrefix)) { name = modelNamePrefix + "_" + name; } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractCppCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractCppCodegen.java index 672b80eff38..8fa66fa6aa8 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractCppCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractCppCodegen.java @@ -5,8 +5,107 @@ import io.swagger.codegen.DefaultCodegen; import io.swagger.models.properties.Property; +import java.util.Arrays; + abstract public class AbstractCppCodegen extends DefaultCodegen implements CodegenConfig { + public AbstractCppCodegen() { + super(); + + /* + * Reserved words. Override this with reserved words specific to your language + */ + setReservedWordsLowerCase( + Arrays.asList( + "alignas", + "alignof", + "and", + "and_eq", + "asm", + "auto", + "bitand", + "bitor", + "bool", + "break", + "case", + "catch", + "char", + "char16_t", + "char32_t", + "class", + "compl", + "concept", + "const", + "constexpr", + "const_cast", + "continue", + "decltype", + "default", + "delete", + "do", + "double", + "dynamic_cast", + "else", + "enum", + "explicit", + "export", + "extern", + "false", + "float", + "for", + "friend", + "goto", + "if", + "inline", + "int", + "long", + "mutable", + "namespace", + "new", + "noexcept", + "not", + "not_eq", + "nullptr", + "operator", + "or", + "or_eq", + "private", + "protected", + "public", + "register", + "reinterpret_cast", + "requires", + "return", + "short", + "signed", + "sizeof", + "static", + "static_assert", + "static_cast", + "struct", + "switch", + "template", + "this", + "thread_local", + "throw", + "true", + "try", + "typedef", + "typeid", + "typename", + "union", + "unsigned", + "using", + "virtual", + "void", + "volatile", + "wchar_t", + "while", + "xor", + "xor_eq") + ); + } + @Override public String toVarName(String name) { if (typeMapping.keySet().contains(name) || typeMapping.values().contains(name) @@ -15,6 +114,10 @@ public String toVarName(String name) { return sanitizeName(name); } + if (isReservedWord(name)) { + return escapeReservedWord(name); + } + if (name.length() > 1) { return sanitizeName(Character.toUpperCase(name.charAt(0)) + name.substring(1)); } @@ -22,6 +125,30 @@ public String toVarName(String name) { return sanitizeName(name); } + /** + * 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 sanitizeName("_" + name); + } + + @Override + public String toOperationId(String operationId) { + if (isReservedWord(operationId)) { + LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + escapeReservedWord(operationId)); + return escapeReservedWord(operationId); + } + return sanitizeName(super.toOperationId(operationId)); + } + @Override public String toParamName(String name) { return sanitizeName(super.toParamName(name)); @@ -39,4 +166,14 @@ public CodegenProperty fromProperty(String name, Property p) { property.nameInCamelCase = nameInCamelCase; return property; } + + /** + * Output the Getter name for boolean property, e.g. isActive + * + * @param name the name of the property + * @return getter name based on naming convention + */ + public String toBooleanGetter(String name) { + return "is" + getterAndSetterCapitalize(name); + } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractEiffelCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractEiffelCodegen.java new file mode 100644 index 00000000000..723e2fb1f7e --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractEiffelCodegen.java @@ -0,0 +1,606 @@ +package io.swagger.codegen.languages; + +import static com.google.common.base.Strings.isNullOrEmpty; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; + +import io.swagger.codegen.CliOption; +import io.swagger.codegen.CodegenConfig; +import io.swagger.codegen.CodegenConstants; +import io.swagger.codegen.CodegenModel; +import io.swagger.codegen.CodegenOperation; +import io.swagger.codegen.CodegenParameter; +import io.swagger.codegen.CodegenProperty; +import io.swagger.codegen.DefaultCodegen; +import io.swagger.codegen.utils.ModelUtils; +import io.swagger.models.Model; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.MapProperty; +import io.swagger.models.properties.Property; +import io.swagger.util.Json; + +public abstract class AbstractEiffelCodegen extends DefaultCodegen implements CodegenConfig { + + private final Set parentModels = new HashSet<>(); + private final Multimap childrenByParent = ArrayListMultimap.create(); + + public AbstractEiffelCodegen(){ + super(); + setReservedWordsLowerCase(Arrays.asList( + // language reserved words + "across", "agent", "alias", "all", "and", "as", "assign", "attribute", "check", "class", "convert", + "create", "Current", "debug", "deferred", "do", "else", "elseif", "end", "ensure", "expanded", "export", + "external", "False", "feature", "from", "frozen", "if", "implies", "inherit", "inspect", "invariant", + "like", "local", "loop", "not", "note", "obsolete", "old", "once", "only", "or", "Precursor", + "redefine", "rename", "require", "rescue", "Result", "retry", "select", "separate", "then", "True", + "TUPLE", "undefine", "until", "variant", "Void", "when", "xor")); + + defaultIncludes = new HashSet(Arrays.asList("map", "array")); + + languageSpecificPrimitives = new HashSet( + Arrays.asList("BOOLEAN", "INTEGER_8", "INTEGER_16", "INTEGER_32", "INTEGER_64", "NATURAL_8", + "NATURAL_16", "NATURAL_32", "NATURAL_64", "REAL_32", "REAL_64")); + + instantiationTypes.clear(); + + typeMapping.clear(); + typeMapping.put("integer", "INTEGER_32"); + typeMapping.put("long", "INTEGER_64"); + typeMapping.put("number", "REAL_32"); + typeMapping.put("float", "REAL_32"); + typeMapping.put("double", "REAL_64"); + typeMapping.put("boolean", "BOOLEAN"); + typeMapping.put("string", "STRING_32"); + typeMapping.put("UUID", "UUID"); // + typeMapping.put("date", "DATE"); + typeMapping.put("DateTime", "DATE_TIME"); + typeMapping.put("date-time", "DATE_TIME"); + typeMapping.put("password", "STRING"); + typeMapping.put("File", "FILE"); + typeMapping.put("file", "FILE"); + typeMapping.put("binary", "STRING_32"); + typeMapping.put("ByteArray", "ARRAY [NATURAL_8]"); + typeMapping.put("object", "ANY"); + typeMapping.put("map", "STRING_TABLE"); + typeMapping.put("array", "LIST"); + typeMapping.put("list", "LIST"); + + instantiationTypes.put("array", "ARRAYED_LIST"); + instantiationTypes.put("list", "ARRAYED_LIST"); + instantiationTypes.put("map", "STRING_TABLE"); + + + cliOptions.clear(); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Eiffel Cluster name (convention: lowercase).") + .defaultValue("swagger")); + cliOptions + .add(new CliOption(CodegenConstants.PACKAGE_VERSION, "Eiffel package version.").defaultValue("1.0.0")); + cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, + "hides the timestamp when files were generated").defaultValue(Boolean.TRUE.toString())); + } + + @Override + public String escapeReservedWord(String name) { + // Can't start with an underscore, as our fields need to start with an + // UppercaseLetter so that Go treats them as public/visible. + + // Options? + // - MyName + // - AName + // - TheName + // - XName + // - X_Name + // ... or maybe a suffix? + // - Name_ ... think this will work. + if (this.reservedWordsMappings().containsKey(name)) { + return this.reservedWordsMappings().get(name); + } + if (name.matches("^\\d.*")) {// prepend var_ + return "var_" + name; + } + return "var_" + name; + } + + @Override + public String toVarName(String name) { + // replace - with _ e.g. created-at => created_at + name = sanitizeName(name.replaceAll("-", "_")); + + // if it's all uppper case, do nothing + if (name.matches("^[A-Z_]*$")) { + return name; + } + + // pet_id + // petId => pet_id + name = unCamelize(name); + + if (name.startsWith("_")){ + name = "var" + name; + } + + // for reserved word + if (isReservedWord(name)) { + name = escapeReservedWord(name); + } + + // for reserved word or word starting with number, append + if (name.matches("^\\d.*")) { + name = escapeReservedWord(name); + } + + return name; + } + + @Override + public String toParamName(String name) { + // params should be lowercase. E.g. "person: PERSON" + return toVarName(name).toLowerCase(); + } + + @Override + public String toModelName(String name) { + // phone_number => PHONE_NUMBER + return toModelFilename(name).toUpperCase(); + } + + @Override + public String toModelFilename(String name) { + if (!StringUtils.isEmpty(modelNamePrefix)) { + name = modelNamePrefix + "_" + name; + } + + if (!StringUtils.isEmpty(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 + 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(String name) { + // replace - with _ e.g. created-at => created_at + name = name.replaceAll("-", "_"); // FIXME: a parameter should not be + // assigned. Also declare the + // methods parameters as 'final'. + + // e.g. PetApi.go => pet_api.go + return underscore(name) + "_api"; + } + + @Override + public String toApiTestFilename(String name) { + return toApiName(name).toLowerCase() + "_test"; + } + + @Override + public String toApiName(String name) { + if (name.length() == 0) { + return "DEFAULT_API"; + } + return name.toUpperCase() + "_API"; + } + + /** + * Overrides postProcessParameter to add a vendor extension + * "x-exportParamName". This is useful when paramName starts with a + * lowercase letter, but we need that param to be exportable (starts with an + * Uppercase letter). + * + * @param parameter + * CodegenParameter object to be processed. + */ + @Override + public void postProcessParameter(CodegenParameter parameter) { + + // Give the base class a chance to process + super.postProcessParameter(parameter); + + char firstChar = parameter.paramName.charAt(0); + + if (Character.isUpperCase(firstChar)) { + // First char is already uppercase, just use paramName. + parameter.vendorExtensions.put("x-exportParamName", parameter.paramName); + + } + + // It's a lowercase first char, let's convert it to uppercase + StringBuilder sb = new StringBuilder(parameter.paramName); + sb.setCharAt(0, Character.toUpperCase(firstChar)); + parameter.vendorExtensions.put("x-exportParamName", sb.toString()); + } + + @Override + public void postProcessModelProperty(CodegenModel model, CodegenProperty property) { + if (!isNullOrEmpty(model.parent)) { + parentModels.add(model.parent); + if (!childrenByParent.containsEntry(model.parent, model)) { + childrenByParent.put(model.parent, model); + } + } + if (!isNullOrEmpty(model.parentSchema)) { + model.parentSchema = model.parentSchema.toLowerCase(); + } + } + + @Override + public String toModelDocFilename(String name) { + return toModelName(name); + } + + @Override + public String toApiDocFilename(String name) { + return toApiName(name); + } + + @Override + public String getTypeDeclaration(Property p) { + if (p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + Property inner = ap.getItems(); + return "LIST [" + getTypeDeclaration(inner) + "]"; + } else if (p instanceof MapProperty) { + MapProperty mp = (MapProperty) p; + Property inner = mp.getAdditionalProperties(); + + return getSwaggerType(p) + "[" + getTypeDeclaration(inner) + "]"; + } + // return super.getTypeDeclaration(p); + + // Not using the supertype invocation, because we want to UpperCamelize + // the type. + String swaggerType = getSwaggerType(p); + if (typeMapping.containsKey(swaggerType)) { + return typeMapping.get(swaggerType); + } + + if (typeMapping.containsValue(swaggerType)) { + return swaggerType; + } + + if (languageSpecificPrimitives.contains(swaggerType)) { + return swaggerType; + } + + return toModelName(swaggerType); + } + + @Override + public String getSwaggerType(Property p) { + String swaggerType = super.getSwaggerType(p); + String type = null; + if (typeMapping.containsKey(swaggerType)) { + type = typeMapping.get(swaggerType); + if (languageSpecificPrimitives.contains(type)) + return (type); + } else + type = swaggerType; + return type; + } + + @Override + public String toOperationId(String operationId) { + String sanitizedOperationId = sanitizeName(operationId); + + // method name cannot use reserved keyword, e.g. return + if (isReservedWord(sanitizedOperationId)) { + LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + + camelize("call_" + operationId)); + sanitizedOperationId = "call_" + sanitizedOperationId; + } + // method name from updateSomething to update_Something. + sanitizedOperationId = unCamelize(sanitizedOperationId); + + return toEiffelFeatureStyle(sanitizedOperationId); + } + + @Override + public Map postProcessOperations(Map objs) { + @SuppressWarnings("unchecked") + Map objectMap = (Map) objs.get("operations"); + @SuppressWarnings("unchecked") + List operations = (List) objectMap.get("operation"); + for (CodegenOperation operation : operations) { + // http method verb conversion (e.g. PUT => Put) + + operation.httpMethod = camelize(operation.httpMethod.toLowerCase()); + } + + // remove model imports to avoid error + List> imports = (List>) objs.get("imports"); + if (imports == null) + return objs; + + Iterator> iterator = imports.iterator(); + while (iterator.hasNext()) { + String _import = iterator.next().get("import"); + if (_import.startsWith(apiPackage())) + iterator.remove(); + } + // if the return type is not primitive, import encoding/json + for (CodegenOperation operation : operations) { + if (operation.returnBaseType != null && needToImport(operation.returnBaseType)) { + imports.add(createMapping("import", "encoding/json")); + break; // just need to import once + } + } + + // this will only import "fmt" if there are items in pathParams + for (CodegenOperation operation : operations) { + if (operation.pathParams != null && operation.pathParams.size() > 0) { + imports.add(createMapping("import", "fmt")); + break; // just need to import once + } + } + + // recursively add import for mapping one type to multiple imports + List> recursiveImports = (List>) objs.get("imports"); + if (recursiveImports == null) + return objs; + + ListIterator> listIterator = imports.listIterator(); + while (listIterator.hasNext()) { + String _import = listIterator.next().get("import"); + // if the import package happens to be found in the importMapping + // (key) + // add the corresponding import package to the list + if (importMapping.containsKey(_import)) { + listIterator.add(createMapping("import", importMapping.get(_import))); + } + } + + return objs; + } + + @Override + public Map postProcessModels(Map objs) { + // remove model imports to avoid error +// List> imports = (List>) objs.get("imports"); +// final String prefix = modelPackage(); +// Iterator> iterator = imports.iterator(); +// while (iterator.hasNext()) { +// String _import = iterator.next().get("import"); +// if (_import.startsWith(prefix)) +// iterator.remove(); +// } +// +// // recursively add import for mapping one type to multiple imports +// List> recursiveImports = (List>) objs.get("imports"); +// if (recursiveImports == null) +// return objs; +// +// ListIterator> listIterator = imports.listIterator(); +// while (listIterator.hasNext()) { +// String _import = listIterator.next().get("import"); +// // if the import package happens to be found in the importMapping +// // (key) +// // add the corresponding import package to the list +// if (importMapping.containsKey(_import)) { +// listIterator.add(createMapping("import", importMapping.get(_import))); +// } +// } +// +// return objs; + // process enum in models + return postProcessModelsEnum(objs); + } + + @Override + public Map postProcessAllModels(final Map models) { + + final Map processed = super.postProcessAllModels(models); + postProcessParentModels(models); + return processed; + } + + private void postProcessParentModels(final Map models) { + for (final String parent : parentModels) { + final CodegenModel parentModel = ModelUtils.getModelByName(parent, models); + final Collection childrenModels = childrenByParent.get(parent); + for (final CodegenModel child : childrenModels) { + processParentPropertiesInChildModel(parentModel, child); + } + } + } + + /** + * Sets the child property's isInherited flag to true if it is an inherited + * property + */ + private void processParentPropertiesInChildModel(final CodegenModel parent, final CodegenModel child) { + final Map childPropertiesByName = new HashMap<>(child.vars.size()); + for (final CodegenProperty childProperty : child.vars) { + childPropertiesByName.put(childProperty.name, childProperty); + } + if (parent != null) { + for (final CodegenProperty parentProperty : parent.vars) { + final CodegenProperty duplicatedByParent = childPropertiesByName.get(parentProperty.name); + if (duplicatedByParent != null) { + duplicatedByParent.isInherited = true; + } + } + } + } + + @Override + public CodegenModel fromModel(String name, Model model, Map allDefinitions) { + CodegenModel codegenModel = super.fromModel(name, model, allDefinitions); + if (allDefinitions != null && codegenModel.parentSchema != null && codegenModel.hasEnums) { + final Model parentModel = allDefinitions.get(codegenModel.parentSchema); + final CodegenModel parentCodegenModel = super.fromModel(codegenModel.parent, parentModel); + codegenModel = AbstractEiffelCodegen.reconcileInlineEnums(codegenModel, parentCodegenModel); + } + return codegenModel; + } + + private static CodegenModel reconcileInlineEnums(CodegenModel codegenModel, CodegenModel parentCodegenModel) { + // This generator uses inline classes to define enums, which breaks when + // dealing with models that have subTypes. To clean this up, we will analyze + // the parent and child models, look for enums that match, and remove + // them from the child models and leave them in the parent. + // Because the child models extend the parents, the enums will be available via the parent. + + // Only bother with reconciliation if the parent model has enums. + if (!parentCodegenModel.hasEnums) { + return codegenModel; + } + + // Get the properties for the parent and child models + final List parentModelCodegenProperties = parentCodegenModel.vars; + List codegenProperties = codegenModel.vars; + + // Iterate over all of the parent model properties + boolean removedChildEnum = false; + for (CodegenProperty parentModelCodegenPropery : parentModelCodegenProperties) { + // Look for enums + if (parentModelCodegenPropery.isEnum) { + // Now that we have found an enum in the parent class, + // and search the child class for the same enum. + Iterator iterator = codegenProperties.iterator(); + while (iterator.hasNext()) { + CodegenProperty codegenProperty = iterator.next(); + if (codegenProperty.isEnum && codegenProperty.equals(parentModelCodegenPropery)) { + // We found an enum in the child class that is + // a duplicate of the one in the parent, so remove it. + iterator.remove(); + removedChildEnum = true; + } + } + } + } + + if(removedChildEnum) { + // If we removed an entry from this model's vars, we need to ensure hasMore is updated + int count = 0, numVars = codegenProperties.size(); + for(CodegenProperty codegenProperty : codegenProperties) { + count += 1; + codegenProperty.hasMore = (count < numVars) ? true : false; + } + codegenModel.vars = codegenProperties; + } + return codegenModel; + } + + + @Override + protected boolean needToImport(String type) { + return !defaultIncludes.contains(type) && !languageSpecificPrimitives.contains(type); + } + + @Override + public String escapeQuotationMark(String input) { + // remove " to avoid code injection + return input.replace("\"", ""); + } + + @Override + public String escapeUnsafeCharacters(String input) { + return input.replace("*/", "*_/").replace("/*", "/_*"); + } + + public Map createMapping(String key, String value) { + Map customImport = new HashMap(); + customImport.put(key, value); + + return customImport; + } + + @Override + public String toInstantiationType(Property p) { + if (p instanceof MapProperty) { + MapProperty ap = (MapProperty) p; + Property additionalProperties2 = ap.getAdditionalProperties(); + String type = additionalProperties2.getType(); + if (null == type) { + LOGGER.error("No Type defined for Additional Property " + additionalProperties2 + "\n" // + + "\tIn Property: " + p); + } + String inner = toModelName(getSwaggerType(additionalProperties2)); + return instantiationTypes.get("map") + " [" + inner + "]"; + } else if (p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + String inner = toModelName(getSwaggerType(ap.getItems())); + return instantiationTypes.get("array") + " [" + inner + "]"; + } else { + return null; + } + } + + public String unCamelize(String name) { + return name.replaceAll("(.)(\\p{Upper})", "$1_$2").toLowerCase(); + } + + public String toEiffelFeatureStyle(String operationId) { + if (operationId.startsWith("get_")) { + return operationId.substring(4, operationId.length()); + } else { + return operationId; + } + } + + /** + * Update property for array(list) container + * @param property Codegen property + * @param innerProperty Codegen inner property of map or list + */ + @Override + protected void updatePropertyForArray(CodegenProperty property, CodegenProperty innerProperty) { + if (innerProperty == null) { + LOGGER.warn("skipping invalid array property " + Json.pretty(property)); + return; + } + property.dataFormat = innerProperty.dataFormat; + if (!languageSpecificPrimitives.contains(innerProperty.baseType)) { + property.complexType = innerProperty.baseType; + } else { + property.isPrimitiveType = true; + } + property.items = innerProperty; + // inner item is Enum + if (isPropertyInnerMostEnum(property)) { + // We use the data type instead of the Enum class. + // at the moment is not supported. + + // isEnum is set to true when the type is an enum + // or the inner type of an array/map is an enum + //property.isEnum = true; + // update datatypeWithEnum and default value for array + // e.g. List => List + //updateDataTypeWithEnumForArray(property); + // set allowable values to enum values (including array/map of enum) + //property.allowableValues = getInnerEnumAllowableValues(property); + } + + } + +} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractJavaCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractJavaCodegen.java index 20350b2b177..4b51baeac63 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractJavaCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractJavaCodegen.java @@ -13,6 +13,9 @@ import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.common.base.Strings; import io.swagger.codegen.CliOption; @@ -27,6 +30,7 @@ import io.swagger.models.Operation; import io.swagger.models.Path; import io.swagger.models.Swagger; +import io.swagger.models.parameters.BodyParameter; import io.swagger.models.parameters.FormParameter; import io.swagger.models.parameters.Parameter; import io.swagger.models.properties.ArrayProperty; @@ -42,6 +46,7 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements CodegenConfig { + static Logger LOGGER = LoggerFactory.getLogger(AbstractJavaCodegen.class); public static final String FULL_JAVA_UTIL = "fullJavaUtil"; public static final String DEFAULT_LIBRARY = ""; public static final String DATE_LIBRARY = "dateLibrary"; @@ -49,9 +54,9 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code public static final String WITH_XML = "withXml"; public static final String SUPPORT_JAVA6 = "supportJava6"; + protected String dateLibrary = "threetenbp"; protected boolean java8Mode = false; protected boolean withXml = false; - protected String dateLibrary = "joda"; protected String invokerPackage = "io.swagger"; protected String groupId = "io.swagger"; protected String artifactId = "swagger-java"; @@ -93,8 +98,9 @@ public AbstractJavaCodegen() { setReservedWordsLowerCase( Arrays.asList( // used as internal variables, can collide with parameter names - "localVarPath", "localVarQueryParams", "localVarHeaderParams", "localVarFormParams", - "localVarPostBody", "localVarAccepts", "localVarAccept", "localVarContentTypes", + "localVarPath", "localVarQueryParams", "localVarCollectionQueryParams", + "localVarHeaderParams", "localVarFormParams", "localVarPostBody", + "localVarAccepts", "localVarAccept", "localVarContentTypes", "localVarContentType", "localVarAuthNames", "localReturnType", "ApiClient", "ApiException", "ApiResponse", "Configuration", "StringUtil", @@ -149,14 +155,15 @@ public AbstractJavaCodegen() { .SERIALIZE_BIG_DECIMAL_AS_STRING_DESC)); cliOptions.add(CliOption.newBoolean(FULL_JAVA_UTIL, "whether to use fully qualified name for classes under java.util. This option only works for Java API client")); cliOptions.add(new CliOption("hideGenerationTimestamp", "hides the timestamp when files were generated")); - cliOptions.add(CliOption.newBoolean(WITH_XML, "whether to include support for application/xml content type. This option only works for Java API client (resttemplate)")); + cliOptions.add(CliOption.newBoolean(WITH_XML, "whether to include support for application/xml content type and include XML annotations in the model (works with libraries that provide support for JSON and XML)")); CliOption dateLibrary = new CliOption(DATE_LIBRARY, "Option. Date library to use"); Map dateOptions = new HashMap(); - dateOptions.put("java8", "Java 8 native - note: this also sets \"" + JAVA8_MODE + "\" to true"); + dateOptions.put("java8", "Java 8 native JSR310 (preferred for jdk 1.8+) - note: this also sets \"" + JAVA8_MODE + "\" to true"); + dateOptions.put("threetenbp", "Backport of JSR310 (preferred for jdk < 1.8)"); dateOptions.put("java8-localdatetime", "Java 8 using LocalDateTime (for legacy app only)"); - dateOptions.put("joda", "Joda"); - dateOptions.put("legacy", "Legacy java.util.Date"); + dateOptions.put("joda", "Joda (for legacy app only)"); + dateOptions.put("legacy", "Legacy java.util.Date (if you really have a good reason not to use threetenbp"); dateLibrary.setEnum(dateOptions); cliOptions.add(dateLibrary); @@ -374,11 +381,6 @@ public void processOpts() { // used later in recursive import in postProcessingModels importMapping.put("com.fasterxml.jackson.annotation.JsonProperty", "com.fasterxml.jackson.annotation.JsonCreator"); - if(additionalProperties.containsKey(DATE_LIBRARY)) { - setDateLibrary(additionalProperties.get(DATE_LIBRARY).toString()); - additionalProperties.put(dateLibrary, "true"); - } - if(additionalProperties.containsKey(JAVA8_MODE)) { setJava8Mode(Boolean.parseBoolean(additionalProperties.get(JAVA8_MODE).toString())); if ( java8Mode ) { @@ -393,15 +395,26 @@ public void processOpts() { } } - if("joda".equals(dateLibrary)) { + if (additionalProperties.containsKey(DATE_LIBRARY)) { + setDateLibrary(additionalProperties.get("dateLibrary").toString()); + } + + if ("threetenbp".equals(dateLibrary)) { + additionalProperties.put("threetenbp", "true"); + additionalProperties.put("jsr310", "true"); + typeMapping.put("date", "LocalDate"); + typeMapping.put("DateTime", "OffsetDateTime"); + importMapping.put("LocalDate", "org.threeten.bp.LocalDate"); + importMapping.put("OffsetDateTime", "org.threeten.bp.OffsetDateTime"); + } else if ("joda".equals(dateLibrary)) { additionalProperties.put("joda", "true"); typeMapping.put("date", "LocalDate"); typeMapping.put("DateTime", "DateTime"); - importMapping.put("LocalDate", "org.joda.time.LocalDate"); importMapping.put("DateTime", "org.joda.time.DateTime"); } else if (dateLibrary.startsWith("java8")) { additionalProperties.put("java8", "true"); + additionalProperties.put("jsr310", "true"); typeMapping.put("date", "LocalDate"); importMapping.put("LocalDate", "java.time.LocalDate"); if ("java8-localdatetime".equals(dateLibrary)) { @@ -484,6 +497,19 @@ public String toApiTestFilename(String name) { return toApiName(name) + "Test"; } + @Override + public String toApiName(String name) { + if (name.length() == 0) { + return "DefaultApi"; + } + return camelize(name) + "Api"; + } + + @Override + public String toApiFilename(String name) { + return toApiName(name); + } + @Override public String toVarName(String name) { // sanitize name @@ -611,7 +637,7 @@ public String getTypeDeclaration(Property p) { @Override public String getAlias(String name) { - if (typeAliases.containsKey(name)) { + if (typeAliases != null && typeAliases.containsKey(name)) { return typeAliases.get(name); } return name; @@ -630,19 +656,39 @@ public String toDefaultValue(Property p) { if (ap.getItems() == null) { return null; } - return String.format(pattern, getTypeDeclaration(ap.getItems())); + + String typeDeclaration = getTypeDeclaration(ap.getItems()); + Object java8obj = additionalProperties.get("java8"); + if (java8obj != null) { + Boolean java8 = Boolean.valueOf(java8obj.toString()); + if (java8 != null && java8) { + typeDeclaration = ""; + } + } + + return String.format(pattern, typeDeclaration); } else if (p instanceof MapProperty) { final MapProperty ap = (MapProperty) p; final String pattern; if (fullJavaUtil) { - pattern = "new java.util.HashMap()"; + pattern = "new java.util.HashMap<%s>()"; } else { - pattern = "new HashMap()"; + pattern = "new HashMap<%s>()"; } if (ap.getAdditionalProperties() == null) { return null; } - return String.format(pattern, getTypeDeclaration(ap.getAdditionalProperties())); + + String typeDeclaration = String.format("String, %s", getTypeDeclaration(ap.getAdditionalProperties())); + Object java8obj = additionalProperties.get("java8"); + if (java8obj != null) { + Boolean java8 = Boolean.valueOf(java8obj.toString()); + if (java8 != null && java8) { + typeDeclaration = ""; + } + } + + return String.format(pattern, typeDeclaration); } else if (p instanceof IntegerProperty) { IntegerProperty dp = (IntegerProperty) p; if (dp.getDefault() != null) { @@ -829,10 +875,12 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert } } - if ("array".equals(property.containerType)) { - model.imports.add("ArrayList"); - } else if ("map".equals(property.containerType)) { - model.imports.add("HashMap"); + if (!fullJavaUtil) { + if ("array".equals(property.containerType)) { + model.imports.add("ArrayList"); + } else if ("map".equals(property.containerType)) { + model.imports.add("HashMap"); + } } if(!BooleanUtils.toBoolean(model.isEnum)) { @@ -894,13 +942,16 @@ public void preprocessSwagger(Swagger swagger) { } for (Operation operation : path.getOperations()) { boolean hasFormParameters = false; + boolean hasBodyParameters = false; for (Parameter parameter : operation.getParameters()) { if (parameter instanceof FormParameter) { hasFormParameters = true; } + if (parameter instanceof BodyParameter) { + hasBodyParameters = true; + } } - //only add content-Type if its no a GET-Method - if(path.getGet() != null || ! operation.equals(path.getGet())){ + if (hasBodyParameters || hasFormParameters){ String defaultContentType = hasFormParameters ? "application/x-www-form-urlencoded" : "application/json"; String contentType = operation.getConsumes() == null || operation.getConsumes().isEmpty() ? defaultContentType : operation.getConsumes().get(0); operation.setVendorExtension("x-contentType", contentType); @@ -988,9 +1039,11 @@ public String toEnumVarName(String value, String datatype) { @Override public String toEnumValue(String value, String datatype) { - if ("Integer".equals(datatype) || "Long".equals(datatype) || - "Double".equals(datatype)) { + if ("Integer".equals(datatype) || "Double".equals(datatype)) { return value; + } else if ("Long".equals(datatype)) { + // add l to number, e.g. 2048 => 2048l + return value + "l"; } else if ("Float".equals(datatype)) { // add f to number, e.g. 3.14 => 3.14f return value + "f"; @@ -1215,9 +1268,25 @@ public void writePropertyBack(String propertyKey, boolean value) { additionalProperties.put(propertyKey, value); } + /** + * Output the Getter name for boolean property, e.g. isActive + * + * @param name the name of the property + * @return getter name based on naming convention + */ + public String toBooleanGetter(String name) { + return "is" + getterAndSetterCapitalize(name); + } + @Override public String sanitizeTag(String tag) { - return camelize(sanitizeName(tag)); + tag = camelize(underscore(sanitizeName(tag))); + + // tag starts with numbers + if (tag.matches("^\\d.*")) { + tag = "Class" + tag; + } + return tag; } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractJavaJAXRSServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractJavaJAXRSServerCodegen.java index 1b753f8ebe0..ab1ddb7ea82 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractJavaJAXRSServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractJavaJAXRSServerCodegen.java @@ -140,6 +140,15 @@ public Map postProcessOperations(Map objs) { @SuppressWarnings("unchecked") List ops = (List) operations.get("operation"); for ( CodegenOperation operation : ops ) { + if (operation.hasConsumes == Boolean.TRUE) { + Map firstType = operation.consumes.get(0); + if (firstType != null) { + if ("multipart/form-data".equals(firstType.get("mediaType"))) { + operation.isMultipart = Boolean.TRUE; + } + } + } + boolean isMultipartPost = false; List> consumes = operation.consumes; if(consumes != null) { @@ -166,39 +175,32 @@ public Map postProcessOperations(Map objs) { resp.code = "200"; } - // set vendorExtensions.x-java-is-response-void to true as dataType is set to "void" - if (resp.dataType == null) { + if (resp.baseType == null) { + resp.dataType = "void"; + resp.baseType = "Void"; + // set vendorExtensions.x-java-is-response-void to true as baseType is set to "Void" resp.vendorExtensions.put("x-java-is-response-void", true); } + if ("array".equals(resp.containerType)) { + resp.containerType = "List"; + } else if ("map".equals(resp.containerType)) { + resp.containerType = "Map"; + } } } - if ( operation.returnType == null ) { + if ( operation.returnBaseType == null ) { operation.returnType = "void"; - // set vendorExtensions.x-java-is-response-void to true as returnType is set to "void" + operation.returnBaseType = "Void"; + // set vendorExtensions.x-java-is-response-void to true as returnBaseType is set to "Void" operation.vendorExtensions.put("x-java-is-response-void", true); - } else if ( operation.returnType.startsWith("List") ) { - String rt = operation.returnType; - int end = rt.lastIndexOf(">"); - if ( end > 0 ) { - operation.returnType = rt.substring("List<".length(), end).trim(); - operation.returnContainer = "List"; - } - } else if ( operation.returnType.startsWith("Map") ) { - String rt = operation.returnType; - int end = rt.lastIndexOf(">"); - if ( end > 0 ) { - operation.returnType = rt.substring("Map<".length(), end).split(",")[1].trim(); - operation.returnContainer = "Map"; - } - } else if ( operation.returnType.startsWith("Set") ) { - String rt = operation.returnType; - int end = rt.lastIndexOf(">"); - if ( end > 0 ) { - operation.returnType = rt.substring("Set<".length(), end).trim(); - operation.returnContainer = "Set"; - } + } + + if ("array".equals(operation.returnContainer)) { + operation.returnContainer = "List"; + } else if ("map".equals(operation.returnContainer)) { + operation.returnContainer = "Map"; } } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractScalaCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractScalaCodegen.java index cd9a5fd3432..3ef8b2e06bb 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractScalaCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractScalaCodegen.java @@ -20,6 +20,7 @@ import io.swagger.models.properties.MapProperty; import io.swagger.models.properties.Property; import io.swagger.models.properties.StringProperty; +import org.apache.commons.lang3.StringUtils; public abstract class AbstractScalaCodegen extends DefaultCodegen { @@ -31,18 +32,18 @@ public AbstractScalaCodegen() { super(); languageSpecificPrimitives.addAll(Arrays.asList( - "String", - "boolean", - "Boolean", - "Double", - "Int", - "Long", - "Float", - "Object", - "Any", - "List", - "Seq", - "Map")); + "String", + "boolean", + "Boolean", + "Double", + "Int", + "Long", + "Float", + "Object", + "Any", + "List", + "Seq", + "Map")); cliOptions.add(new CliOption(CodegenConstants.MODEL_PACKAGE, CodegenConstants.MODEL_PACKAGE_DESC)); cliOptions.add(new CliOption(CodegenConstants.API_PACKAGE, CodegenConstants.API_PACKAGE_DESC)); @@ -67,8 +68,8 @@ public String getSourceFolder() { } @Override - public String escapeReservedWord(String name) { - if(this.reservedWordsMappings().containsKey(name)) { + public String escapeReservedWord(String name) { + if (this.reservedWordsMappings().containsKey(name)) { return this.reservedWordsMappings().get(name); } return "_" + name; @@ -93,7 +94,7 @@ public String getTypeDeclaration(Property p) { } else if (p instanceof MapProperty) { MapProperty mp = (MapProperty) p; Property inner = mp.getAdditionalProperties(); - + return getSwaggerType(p) + "[String, " + getTypeDeclaration(inner) + "]"; } return super.getTypeDeclaration(p); @@ -184,4 +185,22 @@ public String escapeUnsafeCharacters(String input) { return input.replace("*/", "*_/").replace("/*", "/_*"); } + protected String formatIdentifier(String name, boolean capitalized) { + String identifier = camelize(sanitizeName(name), true); + if (capitalized) { + identifier = StringUtils.capitalize(identifier); + } + if (identifier.matches("[a-zA-Z_$][\\w_$]+") && !isReservedWord(identifier)) { + return identifier; + } + return escapeReservedWord(identifier); + } + + protected String stripPackageName(String input) { + if (StringUtils.isEmpty(input) || input.lastIndexOf(".") < 0) + return input; + + int lastIndexOfDot = input.lastIndexOf("."); + return input.substring(lastIndexOfDot + 1); + } } \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractTypeScriptClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractTypeScriptClientCodegen.java index 888e76279dc..3904f9d46dc 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractTypeScriptClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AbstractTypeScriptClientCodegen.java @@ -3,10 +3,10 @@ import org.apache.commons.lang3.StringUtils; import java.io.File; -import java.util.List; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.TreeSet; @@ -18,11 +18,20 @@ import io.swagger.codegen.CodegenType; import io.swagger.codegen.DefaultCodegen; import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.BooleanProperty; +import io.swagger.models.properties.DateProperty; +import io.swagger.models.properties.DateTimeProperty; +import io.swagger.models.properties.DoubleProperty; import io.swagger.models.properties.FileProperty; +import io.swagger.models.properties.FloatProperty; +import io.swagger.models.properties.IntegerProperty; +import io.swagger.models.properties.LongProperty; import io.swagger.models.properties.MapProperty; import io.swagger.models.properties.Property; +import io.swagger.models.properties.StringProperty; public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen implements CodegenConfig { + private static final String UNDEFINED_VALUE = "undefined"; protected String modelPropertyNaming= "camelCase"; protected Boolean supportsES6 = true; @@ -43,7 +52,7 @@ public AbstractTypeScriptClientCodegen() { // Typescript reserved words "abstract", "await", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "debugger", "default", "delete", "do", "double", "else", "enum", "export", "extends", "false", "final", "finally", "float", "for", "function", "goto", "if", "implements", "import", "in", "instanceof", "int", "interface", "let", "long", "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "transient", "true", "try", "typeof", "var", "void", "volatile", "while", "with", "yield")); - languageSpecificPrimitives = new HashSet(Arrays.asList( + languageSpecificPrimitives = new HashSet<>(Arrays.asList( "string", "String", "boolean", @@ -57,8 +66,10 @@ public AbstractTypeScriptClientCodegen() { "Date", "number", "any", - "Error" - )); + "File", + "Error", + "Map" + )); languageGenericTypes = new HashSet(Arrays.asList( "Array" @@ -82,12 +93,15 @@ public AbstractTypeScriptClientCodegen() { typeMapping.put("object", "any"); typeMapping.put("integer", "number"); typeMapping.put("Map", "any"); + typeMapping.put("date", "string"); typeMapping.put("DateTime", "Date"); //TODO binary should be mapped to byte array // mapped to String as a workaround typeMapping.put("binary", "string"); typeMapping.put("ByteArray", "string"); typeMapping.put("UUID", "string"); + typeMapping.put("File", "any"); + typeMapping.put("Error", "Error"); cliOptions.add(new CliOption(CodegenConstants.MODEL_PROPERTY_NAMING, CodegenConstants.MODEL_PROPERTY_NAMING_DESC).defaultValue("camelCase")); cliOptions.add(new CliOption(CodegenConstants.SUPPORTS_ES6, CodegenConstants.SUPPORTS_ES6_DESC).defaultValue("false")); @@ -114,7 +128,7 @@ public CodegenType getTag() { } @Override - public String escapeReservedWord(String name) { + public String escapeReservedWord(String name) { if(this.reservedWordsMappings().containsKey(name)) { return this.reservedWordsMappings().get(name); } @@ -188,6 +202,7 @@ public String toModelName(String name) { LOGGER.warn(name + " (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); @@ -215,6 +230,49 @@ public String getTypeDeclaration(Property p) { return super.getTypeDeclaration(p); } + @Override + public String toDefaultValue(Property p) { + if (p instanceof StringProperty) { + StringProperty sp = (StringProperty) p; + if (sp.getDefault() != null) { + return "\"" + sp.getDefault() + "\""; + } + return UNDEFINED_VALUE; + } else if (p instanceof BooleanProperty) { + return UNDEFINED_VALUE; + } else if (p instanceof DateProperty) { + return UNDEFINED_VALUE; + } else if (p instanceof DateTimeProperty) { + return UNDEFINED_VALUE; + } else if (p instanceof DoubleProperty) { + DoubleProperty dp = (DoubleProperty) p; + if (dp.getDefault() != null) { + return dp.getDefault().toString(); + } + return UNDEFINED_VALUE; + } else if (p instanceof FloatProperty) { + FloatProperty fp = (FloatProperty) p; + if (fp.getDefault() != null) { + return fp.getDefault().toString(); + } + return UNDEFINED_VALUE; + } else if (p instanceof IntegerProperty) { + IntegerProperty ip = (IntegerProperty) p; + if (ip.getDefault() != null) { + return ip.getDefault().toString(); + } + return UNDEFINED_VALUE; + } else if (p instanceof LongProperty) { + LongProperty lp = (LongProperty) p; + if (lp.getDefault() != null) { + return lp.getDefault().toString(); + } + return UNDEFINED_VALUE; + } else { + return UNDEFINED_VALUE; + } + } + @Override public String getSwaggerType(Property p) { String swaggerType = super.getSwaggerType(p); diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AdaCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AdaCodegen.java new file mode 100644 index 00000000000..309f9a2bada --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AdaCodegen.java @@ -0,0 +1,362 @@ +package io.swagger.codegen.languages; + +import java.io.File; +import java.util.*; + +import io.swagger.codegen.*; +import io.swagger.models.Model; +import io.swagger.models.Operation; +import io.swagger.models.Response; +import io.swagger.models.Swagger; +import io.swagger.models.properties.*; + +public class AdaCodegen extends AbstractAdaCodegen implements CodegenConfig { + protected String packageName = "swagger"; + protected String projectName = "Swagger"; + protected List> orderedModels; + protected Map> modelDepends; + + public AdaCodegen() { + super(); + + modelNameSuffix = "_Type"; + orderedModels = new ArrayList>(); + modelDepends = new HashMap>(); + embeddedTemplateDir = templateDir = "Ada"; + + // CLI options + addOption(CodegenConstants.PROJECT_NAME, "GNAT project name", + this.projectName); + addOption(CodegenConstants.PACKAGE_NAME, "Ada package name (convention: name.space.model).", + this.modelPackage); + addOption(CodegenConstants.MODEL_PACKAGE, "Ada package for models (convention: name.space.model).", + this.modelPackage); + addOption(CodegenConstants.API_PACKAGE, "Ada package for apis (convention: name.space.api).", + this.apiPackage); + + languageSpecificPrimitives = new HashSet( + Arrays.asList("integer", "boolean", "Integer", "Character", "Boolean", "long", "float", "double", "int32_t", "int64_t")); + + typeMapping = new HashMap(); + typeMapping.put("date", "Swagger.Date"); + typeMapping.put("DateTime", "Swagger.Datetime"); + typeMapping.put("string", "Swagger.UString"); + typeMapping.put("integer", "Integer"); + typeMapping.put("long", "Swagger.Long"); + typeMapping.put("boolean", "Boolean"); + typeMapping.put("array", "Swagger.Vector"); + typeMapping.put("map", "Swagger.Map"); + typeMapping.put("object", "Swagger.Object"); + typeMapping.put("number", "Swagger.Number"); + typeMapping.put("UUID", "Swagger.UString"); + typeMapping.put("file", "Swagger.Http_Content_Type"); + typeMapping.put("binary", "Swagger.Binary"); + + super.importMapping = new HashMap(); + } + + @Override + public CodegenType getTag() { + return CodegenType.CLIENT; + } + + @Override + public String getName() { + return "ada"; + } + + @Override + public String getHelp() { + return "Generates an Ada client implementation (beta)."; + } + + protected void addOption(String key, String description, String defaultValue) { + CliOption option = new CliOption(key, description); + if (defaultValue != null) + option.defaultValue(defaultValue); + cliOptions.add(option); + } + + public String toFilename(String name) { + return name.replace(".", "-").toLowerCase(); + } + + @Override + public void processOpts() { + super.processOpts(); + if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { + packageName = (String) additionalProperties.get(CodegenConstants.PACKAGE_NAME); + } + String serverPrefix = "src" + File.separator + "server" + File.separator + toFilename(modelPackage); + String clientPrefix = "src" + File.separator + "client" + File.separator + toFilename(modelPackage); + supportingFiles.add(new SupportingFile("model-spec.mustache", null, clientPrefix + "-models.ads")); + supportingFiles.add(new SupportingFile("model-body.mustache", null, clientPrefix + "-models.adb")); + supportingFiles.add(new SupportingFile("model-spec.mustache", null, serverPrefix + "-models.ads")); + supportingFiles.add(new SupportingFile("model-body.mustache", null, serverPrefix + "-models.adb")); + supportingFiles.add(new SupportingFile("client-spec.mustache", null, clientPrefix + "-clients.ads")); + supportingFiles.add(new SupportingFile("client-body.mustache", null, clientPrefix + "-clients.adb")); + supportingFiles.add(new SupportingFile("server-spec.mustache", null, serverPrefix + "-servers.ads")); + supportingFiles.add(new SupportingFile("server-body.mustache", null, serverPrefix + "-servers.adb")); + + // String title = swagger.getInfo().getTitle(); + supportingFiles.add(new SupportingFile("gnat-project.mustache", "", "project.gpr")); + + if (additionalProperties.containsKey(CodegenConstants.PROJECT_NAME)) { + projectName = (String) additionalProperties.get(CodegenConstants.PROJECT_NAME); + } else { + // default: set project based on package name + // e.g. petstore.api (package name) => petstore_api (project name) + projectName = packageName.replaceAll("\\.", "_"); + } + + /* + * Additional Properties. These values can be passed to the templates and + * are available in models, apis, and supporting files + */ + additionalProperties.put("package", this.modelPackage); + additionalProperties.put(CodegenConstants.PROJECT_NAME, projectName); + } + + @Override + public String apiFileFolder() { + return outputFolder + "/" + apiPackage().replace('.', File.separatorChar); + } + + @Override + public String modelFileFolder() { + return outputFolder + "/model/" + modelPackage().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) { + return "p_" + name; // add an underscore to the name + } + + @Override + public String escapeQuotationMark(String input) { + // remove " to avoid code injection + return input.replace("\"", ""); + } + + @Override + public String escapeUnsafeCharacters(String input) { + return input.replace("*/", "*_/").replace("/*", "/_*"); + } + + /** + * Optional - type declaration. This is a String which is used by the + * templates to instantiate your types. There is typically special handling + * for different property types + * + * @return a string value used as the `dataType` field for model templates, + * `returnType` for api templates + */ + @Override + public String getTypeDeclaration(Property p) { + String swaggerType = getSwaggerType(p); + + if (p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + Property inner = ap.getItems(); + return getTypeDeclaration(inner) + "_Vectors.Vector"; + } + if (p instanceof MapProperty) { + MapProperty mp = (MapProperty) p; + Property inner = mp.getAdditionalProperties(); + return "Swagger." + getTypeDeclaration(inner) + "_Map"; + } + if (typeMapping.containsKey(swaggerType)) { + return typeMapping.get(swaggerType); + } + // LOGGER.info("Swagger type " + swaggerType); + if (languageSpecificPrimitives.contains(swaggerType)) { + return swaggerType; + } + String modelType = toModelName(swaggerType); + if (p instanceof StringProperty || p instanceof DateProperty + || p instanceof DateTimeProperty || p instanceof FileProperty + || languageSpecificPrimitives.contains(modelType)) { + return modelType; + } + + return modelPackage + ".Models." + modelType; + } + + /** + * Overrides postProcessParameter to add a vendor extension "x-is-model-type". + * This boolean indicates that the parameter comes from the model package. + * + * @param parameter CodegenParameter object to be processed. + */ + @Override + public void postProcessParameter(CodegenParameter parameter){ + // Give the base class a chance to process + super.postProcessParameter(parameter); + + boolean isModel = parameter.dataType.startsWith(modelPackage); + if (!isModel && !parameter.isPrimitiveType && !parameter.isDate + && !parameter.isString && !parameter.isContainer && !parameter.isFile) { + isModel = true; + } + parameter.vendorExtensions.put("x-is-model-type", isModel); + } + + /** + * Post process the media types (produces and consumes) for Ada code generator. + * + * For each media type, add a adaMediaType member that gives the Ada enum constant + * for the corresponding type. + * + * @param types the list of media types. + * @return the number of media types. + */ + protected int postProcessMediaTypes(List> types) { + int count = 0; + if (types != null) { + for (Map media : types) { + String mt = media.get("mediaType"); + if (mt != null) { + mt = mt.replace('/', '_'); + media.put("adaMediaType", mt.toUpperCase()); + count++; + } + } + } + return count; + } + + @Override + public CodegenOperation fromOperation(String path, String httpMethod, Operation operation, + Map definitions, Swagger swagger) { + CodegenOperation op = super.fromOperation(path, httpMethod, operation, definitions, swagger); + + if (operation.getResponses() != null && !operation.getResponses().isEmpty()) { + Response methodResponse = findMethodResponse(operation.getResponses()); + + if (methodResponse != null) { + if (methodResponse.getSchema() != null) { + CodegenProperty cm = fromProperty("response", methodResponse.getSchema()); + op.vendorExtensions.put("x-codegen-response", cm); + if(cm.datatype == "HttpContent") { + op.vendorExtensions.put("x-codegen-response-ishttpcontent", true); + } + } + } + } + return op; + } + + @SuppressWarnings("unchecked") + @Override + public Map postProcessOperations(Map objs) { + Map operations = (Map) objs.get("operations"); + List operationList = (List) operations.get("operation"); + + for (CodegenOperation op1 : operationList) { + op1.vendorExtensions.put("x-has-uniq-produces", postProcessMediaTypes(op1.produces) == 1); + op1.vendorExtensions.put("x-has-uniq-consumes", postProcessMediaTypes(op1.consumes) == 1); + op1.vendorExtensions.put("x-has-notes", op1.notes.length() > 0); + } + return objs; + } + + @Override + public Map postProcessModels(Map objs) { + // Collect the model dependencies. + List> models = (List>) objs.get("models"); + for (Map model : models) { + Object v = model.get("model"); + if (v instanceof CodegenModel) { + CodegenModel m = (CodegenModel) v; + List d = new ArrayList(); + for (CodegenProperty p : m.allVars) { + boolean isModel = false; + CodegenProperty item = p; + if (p.isContainer) { + item = p.items; + } + if (item != null && !item.isString && !item.isPrimitiveType && !item.isContainer && !item.isInteger) { + if (!d.contains(item.datatype)) { + // LOGGER.info("Model " + m.name + " uses " + p.datatype); + d.add(item.datatype); + isModel = true; + } + } + p.vendorExtensions.put("x-is-model-type", isModel); + } + modelDepends.put(m.name, d); + orderedModels.add(model); + } + } + + // Sort the models according to dependencies so that model that depend + // on others appear at end of the list. + final Map> deps = modelDepends; + Collections.sort(orderedModels, new Comparator>() { + @Override + public int compare(Map lhs, Map rhs) { + Object v = lhs.get("model"); + String lhsName = ((CodegenModel) v).name; + v = rhs.get("model"); + String rhsName = ((CodegenModel) v).name; + List lhsList = deps.get(lhsName); + List rhsList = deps.get(rhsName); + if (lhsList == rhsList) { + // LOGGER.info("First compare " + lhsName + "<" + rhsName); + return lhsName.compareTo(rhsName); + } + // Put models without dependencies first. + if (lhsList == null) { + // LOGGER.info(" Empty " + lhsName + ", no check " + rhsName); + return -1; + } + if (rhsList == null) { + // LOGGER.info(" No check " + lhsName + ", empty " + rhsName); + return 1; + } + // Put models that depend on another after. + if (lhsList.contains(rhsName)) { + // LOGGER.info(" LSH " + lhsName + " uses " + rhsName); + return 1; + } + if (rhsList.contains(lhsName)) { + // LOGGER.info(" RHS " + rhsName + " uses " + lhsName); + return -1; + } + // Put models with less dependencies first. + if (lhsList.size() < rhsList.size()) { + // LOGGER.info(" LSH size " + lhsName + " < RHS size " + rhsName); + return -1; + } + if (lhsList.size() > rhsList.size()) { + // LOGGER.info(" LSH size " + lhsName + " > RHS size " + rhsName); + return 1; + } + // Sort models on their name. + // LOGGER.info("Compare " + lhsName + "<" + rhsName); + return lhsName.compareTo(rhsName); + } + }); + /* for (Map model : orderedModels) { + Object v = model.get("model"); + if (v instanceof CodegenModel) { + CodegenModel m = (CodegenModel) v; + LOGGER.info("Order: " + m.name); + } + }*/ + return postProcessModelsEnum(objs); + } + + @Override + public Map postProcessSupportingFileData(Map objs) { + objs.put("orderedModels", orderedModels); + return super.postProcessSupportingFileData(objs); + } +} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AkkaScalaClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AkkaScalaClientCodegen.java index 1ead8de061f..7d894aea2fa 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AkkaScalaClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AkkaScalaClientCodegen.java @@ -98,7 +98,9 @@ public AkkaScalaClientCodegen() { additionalProperties.put("fnEnumEntry", new EnumEntryLambda()); additionalProperties.put("onlyOneSuccess", onlyOneSuccess); + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml")); + supportingFiles.add(new SupportingFile("build.sbt.mustache", "", "build.sbt")); supportingFiles.add(new SupportingFile("reference.mustache", resourcesFolder, "reference.conf")); final String invokerFolder = (sourceFolder + File.separator + invokerPackage).replace(".", File.separator); supportingFiles.add(new SupportingFile("apiRequest.mustache", invokerFolder, "ApiRequest.scala")); @@ -230,17 +232,6 @@ public String toOperationId(String operationId) { return super.toOperationId(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, operationId)); } - private String formatIdentifier(String name, boolean capitalized) { - String identifier = camelize(sanitizeName(name), true); - if (capitalized) { - identifier = StringUtils.capitalize(identifier); - } - if (identifier.matches("[a-zA-Z_$][\\w_$]+") && !isReservedWord(identifier)) { - return identifier; - } - return escapeReservedWord(identifier); - } - @Override public String toParamName(String name) { return formatIdentifier(name, false); diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AndroidClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AndroidClientCodegen.java index 73a8d9859c7..18f90d49843 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AndroidClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AndroidClientCodegen.java @@ -22,6 +22,9 @@ public class AndroidClientCodegen extends DefaultCodegen implements CodegenConfig { private static final Logger LOGGER = LoggerFactory.getLogger(AndroidClientCodegen.class); public static final String USE_ANDROID_MAVEN_GRADLE_PLUGIN = "useAndroidMavenGradlePlugin"; + public static final String ANDROID_GRADLE_VERSION = "androidGradleVersion"; + public static final String ANDROID_SDK_VERSION = "androidSdkVersion"; + public static final String ANDROID_BUILD_TOOLS_VERSION = "androidBuildToolsVersion"; protected String invokerPackage = "io.swagger.client"; protected String groupId = "io.swagger"; protected String artifactId = "swagger-android-client"; @@ -29,6 +32,9 @@ public class AndroidClientCodegen extends DefaultCodegen implements CodegenConfi protected String projectFolder = "src/main"; protected String sourceFolder = projectFolder + "/java"; protected Boolean useAndroidMavenGradlePlugin = true; + protected String androidGradleVersion; + protected String androidSdkVersion; + protected String androidBuildToolsVersion; protected Boolean serializableModel = false; // requestPackage and authPackage are used by the "volley" template/library @@ -93,6 +99,9 @@ public AndroidClientCodegen() { cliOptions.add(new CliOption(CodegenConstants.SOURCE_FOLDER, CodegenConstants.SOURCE_FOLDER_DESC)); cliOptions.add(CliOption.newBoolean(USE_ANDROID_MAVEN_GRADLE_PLUGIN, "A flag to toggle android-maven gradle plugin.") .defaultValue(Boolean.TRUE.toString())); + cliOptions.add(new CliOption(ANDROID_GRADLE_VERSION, "gradleVersion version for use in the generated build.gradle")); + cliOptions.add(new CliOption(ANDROID_SDK_VERSION, "compileSdkVersion version for use in the generated build.gradle")); + cliOptions.add(new CliOption(ANDROID_BUILD_TOOLS_VERSION, "buildToolsVersion version for use in the generated build.gradle")); cliOptions.add(CliOption.newBoolean(CodegenConstants.SERIALIZABLE_MODEL, CodegenConstants.SERIALIZABLE_MODEL_DESC)); @@ -346,9 +355,13 @@ public void processOpts() { if (additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) { this.setInvokerPackage((String) additionalProperties.get(CodegenConstants.INVOKER_PACKAGE)); + this.setRequestPackage(invokerPackage + ".request"); + this.setAuthPackage(invokerPackage + ".auth"); } else { //not set, use default to be passed to template additionalProperties.put(CodegenConstants.INVOKER_PACKAGE, invokerPackage); + additionalProperties.put("requestPackage", requestPackage); + additionalProperties.put("authPackage", authPackage); } if (additionalProperties.containsKey(CodegenConstants.GROUP_ID)) { @@ -383,6 +396,18 @@ public void processOpts() { additionalProperties.put(USE_ANDROID_MAVEN_GRADLE_PLUGIN, useAndroidMavenGradlePlugin); } + if (additionalProperties.containsKey(ANDROID_GRADLE_VERSION)) { + this.setAndroidGradleVersion((String) additionalProperties.get(ANDROID_GRADLE_VERSION)); + } + + if (additionalProperties.containsKey(ANDROID_SDK_VERSION)) { + this.setAndroidSdkVersion((String) additionalProperties.get(ANDROID_SDK_VERSION)); + } + + if (additionalProperties.containsKey(ANDROID_BUILD_TOOLS_VERSION)) { + this.setAndroidBuildToolsVersion((String) additionalProperties.get(ANDROID_BUILD_TOOLS_VERSION)); + } + if (additionalProperties.containsKey(CodegenConstants.LIBRARY)) { this.setLibrary((String) additionalProperties.get(CodegenConstants.LIBRARY)); } @@ -496,14 +521,46 @@ public Boolean getUseAndroidMavenGradlePlugin() { return useAndroidMavenGradlePlugin; } + public String getAndroidGradleVersion() { + return androidGradleVersion; + } + + public String getAndroidSdkVersion() { + return androidSdkVersion; + } + + public String getAndroidBuildToolsVersion() { + return androidBuildToolsVersion; + } + public void setUseAndroidMavenGradlePlugin(Boolean useAndroidMavenGradlePlugin) { this.useAndroidMavenGradlePlugin = useAndroidMavenGradlePlugin; } + public void setAndroidGradleVersion(String androidGradleVersion) { + this.androidGradleVersion = androidGradleVersion; + } + + public void setAndroidSdkVersion(String androidSdkVersion) { + this.androidSdkVersion = androidSdkVersion; + } + + public void setAndroidBuildToolsVersion(String androidBuildToolsVersion) { + this.androidBuildToolsVersion = androidBuildToolsVersion; + } + public void setInvokerPackage(String invokerPackage) { this.invokerPackage = invokerPackage; } + public void setRequestPackage(String requestPackage) { + this.requestPackage = requestPackage; + } + + public void setAuthPackage(String authPackage) { + this.authPackage = authPackage; + } + public void setGroupId(String groupId) { this.groupId = groupId; } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AspNet5ServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AspNet5ServerCodegen.java deleted file mode 100644 index 83bbfbb6adb..00000000000 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AspNet5ServerCodegen.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.swagger.codegen.languages; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class AspNet5ServerCodegen extends AspNetCoreServerCodegen { - - @SuppressWarnings("hiding") - protected Logger LOGGER = LoggerFactory.getLogger(AspNet5ServerCodegen.class); - - public AspNet5ServerCodegen() { - super(); - - embeddedTemplateDir = templateDir = "aspnetcore"; - } - - @Override - public String getName() { - return "aspnet5"; - } - - @Override - public void processOpts() { - super.processOpts(); - - LOGGER.warn("aspnet5 is deprecated. Please use aspnetcore."); - } -} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AspNetCoreServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AspNetCoreServerCodegen.java index a5adfff4666..d742bcea8f3 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AspNetCoreServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AspNetCoreServerCodegen.java @@ -1,5 +1,6 @@ package io.swagger.codegen.languages; +import com.samskivert.mustache.Mustache; import io.swagger.codegen.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -102,7 +103,6 @@ public void processOpts() { String packageFolder = sourceFolder + File.separator + packageName; supportingFiles.add(new SupportingFile("NuGet.Config", "", "NuGet.Config")); - supportingFiles.add(new SupportingFile("global.json", "", "global.json")); supportingFiles.add(new SupportingFile("build.sh.mustache", "", "build.sh")); supportingFiles.add(new SupportingFile("build.bat.mustache", "", "build.bat")); supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); @@ -111,12 +111,12 @@ public void processOpts() { supportingFiles.add(new SupportingFile("gitignore", packageFolder, ".gitignore")); supportingFiles.add(new SupportingFile("appsettings.json", packageFolder, "appsettings.json")); - supportingFiles.add(new SupportingFile("project.json.mustache", packageFolder, "project.json")); supportingFiles.add(new SupportingFile("Startup.mustache", packageFolder, "Startup.cs")); supportingFiles.add(new SupportingFile("Program.mustache", packageFolder, "Program.cs")); + supportingFiles.add(new SupportingFile("validateModel.mustache", packageFolder + File.separator + "Attributes", "ValidateModelStateAttribute.cs")); supportingFiles.add(new SupportingFile("web.config", packageFolder, "web.config")); - supportingFiles.add(new SupportingFile("Project.xproj.mustache", packageFolder, this.packageName + ".xproj")); + supportingFiles.add(new SupportingFile("Project.csproj.mustache", packageFolder, this.packageName + ".csproj")); supportingFiles.add(new SupportingFile("Properties" + File.separator + "launchSettings.json", packageFolder + File.separator + "Properties", "launchSettings.json")); @@ -168,7 +168,12 @@ protected void processOperation(CodegenOperation operation) { // Converts, for example, PUT to HttpPut for controller attributes operation.httpMethod = "Http" + operation.httpMethod.substring(0, 1) + operation.httpMethod.substring(1).toLowerCase(); } - + + @Override + public Mustache.Compiler processCompiler(Mustache.Compiler compiler) { + // To avoid unexpected behaviors when options are passed programmatically such as { "useCollection": "" } + return super.processCompiler(compiler).emptyStringIsFalse(true); + } @Override public Map postProcessOperations(Map objs) { super.postProcessOperations(objs); diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AsyncScalaClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AsyncScalaClientCodegen.java deleted file mode 100644 index 03bc9505264..00000000000 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AsyncScalaClientCodegen.java +++ /dev/null @@ -1,106 +0,0 @@ -package io.swagger.codegen.languages; - -import io.swagger.codegen.CliOption; -import io.swagger.codegen.CodegenConfig; -import io.swagger.codegen.CodegenConstants; -import io.swagger.codegen.CodegenType; -import io.swagger.codegen.SupportingFile; - -import java.io.File; -import java.util.Arrays; -import java.util.HashMap; - -public class AsyncScalaClientCodegen extends AbstractScalaCodegen implements CodegenConfig { - protected String groupId = "io.swagger"; - protected String artifactId = "swagger-async-scala-client"; - protected String artifactVersion = "1.0.0"; - protected String clientName = "SwaggerClient"; - protected String authScheme = ""; - protected boolean authPreemptive; - protected boolean asyncHttpClient = !authScheme.isEmpty(); - - public AsyncScalaClientCodegen() { - super(); - outputFolder = "generated-code/async-scala"; - modelTemplateFiles.put("model.mustache", ".scala"); - apiTemplateFiles.put("api.mustache", ".scala"); - embeddedTemplateDir = templateDir = "asyncscala"; - apiPackage = "io.swagger.client.api"; - modelPackage = "io.swagger.client.model"; - - setReservedWordsLowerCase( - Arrays.asList( - // local variable names used in API methods (endpoints) - "config", "path", "contentTypes", "contentType", "queryParams", "headerParams", - "formParams", "postBody", "resFuture", "client", "reader", - - // scala reserved words - "abstract", "case", "catch", "class", "def", "do", "else", "extends", - "false", "final", "finally", "for", "forSome", "if", "implicit", - "import", "lazy", "match", "new", "null", "object", "override", "package", - "private", "protected", "return", "sealed", "super", "this", "throw", - "trait", "try", "true", "type", "val", "var", "while", "with", "yield") - ); - - additionalProperties.put(CodegenConstants.INVOKER_PACKAGE, invokerPackage); - additionalProperties.put(CodegenConstants.GROUP_ID, groupId); - additionalProperties.put(CodegenConstants.ARTIFACT_ID, artifactId); - additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, artifactVersion); - additionalProperties.put("asyncHttpClient", asyncHttpClient); - additionalProperties.put("authScheme", authScheme); - additionalProperties.put("authPreemptive", authPreemptive); - additionalProperties.put("clientName", clientName); - - supportingFiles.add(new SupportingFile("sbt.mustache", "", "build.sbt")); - supportingFiles.add(new SupportingFile("client.mustache", - (sourceFolder + File.separator + invokerPackage).replace(".", java.io.File.separator), clientName + ".scala")); - - importMapping.remove("List"); - importMapping.remove("Set"); - importMapping.remove("Map"); - - importMapping.put("DateTime", "org.joda.time.DateTime"); - importMapping.put("ListBuffer", "scala.collection.mutable.ListBuffer"); - - typeMapping = new HashMap(); - typeMapping.put("enum", "NSString"); - typeMapping.put("array", "List"); - typeMapping.put("set", "Set"); - typeMapping.put("boolean", "Boolean"); - typeMapping.put("string", "String"); - typeMapping.put("int", "Int"); - typeMapping.put("long", "Long"); - typeMapping.put("float", "Float"); - typeMapping.put("byte", "Byte"); - typeMapping.put("short", "Short"); - typeMapping.put("char", "Char"); - typeMapping.put("long", "Long"); - typeMapping.put("double", "Double"); - typeMapping.put("object", "Any"); - typeMapping.put("file", "File"); - - instantiationTypes.put("array", "ListBuffer"); - instantiationTypes.put("map", "HashMap"); - } - - @Override - public CodegenType getTag() { - return CodegenType.CLIENT; - } - - @Override - public String getName() { - return "async-scala"; - } - - @Override - public String getHelp() { - return "Generates an Asynchronous Scala client library."; - } - - @Override - public String escapeQuotationMark(String input) { - // remove " to avoid code injection - return input.replace("\"", ""); - } -} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/BashClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/BashClientCodegen.java index de7ae732d8b..487cb5c2e9e 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/BashClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/BashClientCodegen.java @@ -39,7 +39,8 @@ public class BashClientCodegen extends DefaultCodegen implements CodegenConfig { protected String hostEnvironmentVariable; protected String basicAuthEnvironmentVariable; protected String apiKeyAuthEnvironmentVariable; - + protected String apiDocPath = "docs/"; + protected String modelDocPath = "docs/"; public static final String CURL_OPTIONS = "curlOptions"; public static final String PROCESS_MARKDOWN = "processMarkdown"; @@ -105,6 +106,13 @@ public BashClientCodegen() { apiTemplateFiles.clear(); + /** + * docs files. + */ + modelDocTemplateFiles.put("model_doc.mustache", ".md"); + apiDocTemplateFiles.put("api_doc.mustache", ".md"); + + /** * Templates location for client script and bash completion template. */ @@ -169,6 +177,7 @@ public BashClientCodegen() { typeMapping.put("int", "integer"); typeMapping.put("float", "float"); typeMapping.put("number", "integer"); + typeMapping.put("date", "string"); typeMapping.put("DateTime", "string"); typeMapping.put("long", "integer"); typeMapping.put("short", "integer"); @@ -185,12 +194,22 @@ public BashClientCodegen() { * are available in models, apis, and supporting files. */ additionalProperties.put("apiVersion", apiVersion); + // make api and model doc path available in mustache template + additionalProperties.put("apiDocPath", apiDocPath); + additionalProperties.put("modelDocPath", modelDocPath); /** * Language Specific Primitives. These types will not trigger imports by * the client generator */ - languageSpecificPrimitives = new HashSet(); + languageSpecificPrimitives.clear(); + languageSpecificPrimitives.add("array"); + languageSpecificPrimitives.add("map"); + languageSpecificPrimitives.add("boolean"); + languageSpecificPrimitives.add("integer"); + languageSpecificPrimitives.add("float"); + languageSpecificPrimitives.add("string"); + languageSpecificPrimitives.add("binary"); } @@ -239,15 +258,15 @@ public void processOpts() { } supportingFiles.add(new SupportingFile( - "client.mustache", "", scriptName)); + "client.mustache", "", scriptName)); supportingFiles.add(new SupportingFile( - "bash-completion.mustache", "", scriptName+".bash-completion")); + "bash-completion.mustache", "", scriptName+".bash-completion")); supportingFiles.add(new SupportingFile( - "zsh-completion.mustache", "", "_"+scriptName)); + "zsh-completion.mustache", "", "_"+scriptName)); supportingFiles.add(new SupportingFile( - "README.mustache", "", "README.md")); + "README.mustache", "", "README.md")); supportingFiles.add(new SupportingFile( - "Dockerfile.mustache", "", "Dockerfile")); + "Dockerfile.mustache", "", "Dockerfile")); } public void setCurlOptions(String curlOptions) { @@ -288,7 +307,7 @@ public void setApiKeyAuthEnvironmentVariable(String /** * 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 reseved words. + * matches the reserved words. * * @return the escaped term */ @@ -314,6 +333,25 @@ public String apiFileFolder() { return outputFolder; } + @Override + public String apiDocFileFolder() { + return (outputFolder + "/" + apiDocPath); + } + + @Override + public String modelDocFileFolder() { + return (outputFolder + "/" + modelDocPath); + } + + @Override + public String toModelDocFilename(String name) { + return toModelName(name); + } + + @Override + public String toApiDocFilename(String name) { + return toApiName(name); + } /** * Optional - type declaration. This is a String which is used by the @@ -355,8 +393,9 @@ public String getSwaggerType(Property p) { if(languageSpecificPrimitives.contains(type)) return type; } - else + else { type = swaggerType; + } return toModelName(type); } @@ -656,4 +695,69 @@ public void preprocessSwagger(Swagger swagger) { } + @Override + public void setParameterExampleValue(CodegenParameter p) { + String example; + + if (p.defaultValue == null) { + example = p.example; + } else { + example = p.defaultValue; + } + + String type = p.baseType; + if (type == null) { + type = p.dataType; + } + + if ("string".equalsIgnoreCase(type)) { + if (example == null) { + example = p.paramName + "_example"; + } + example = "'" + escapeText(example) + "'"; + } else if ("integer".equals(type)) { + if (example == null) { + example = "56"; + } + } else if ("float".equalsIgnoreCase(type)) { + if (example == null) { + example = "3.4"; + } + } else if ("boolean".equalsIgnoreCase(type)) { + if (example == null) { + example = "True"; + } + } else if ("file".equalsIgnoreCase(type)) { + if (example == null) { + example = "/path/to/file"; + } + example = "'" + escapeText(example) + "'"; + } else if ("date".equalsIgnoreCase(type)) { + if (example == null) { + example = "2013-10-20"; + } + example = "'" + escapeText(example) + "'"; + } else if ("datetime".equalsIgnoreCase(type)) { + if (example == null) { + example = "2013-10-20T19:20:30+01:00"; + } + example = "'" + escapeText(example) + "'"; + } else if (!languageSpecificPrimitives.contains(type)) { + // type is a model class, e.g. User + example = type; + } else { + LOGGER.warn("Type " + type + " not handled properly in setParameterExampleValue"); + } + + if (example == null) { + example = "NULL"; + } else if (Boolean.TRUE.equals(p.isListContainer)) { + example = "[" + example + "]"; + } else if (Boolean.TRUE.equals(p.isMapContainer)) { + example = "{'key': " + example + "}"; + } + + p.example = example; + } + } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/CSharpClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/CSharpClientCodegen.java index 12ed9e74ccf..e28fccd1065 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/CSharpClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/CSharpClientCodegen.java @@ -1,39 +1,31 @@ package io.swagger.codegen.languages; import com.google.common.collect.ImmutableMap; - -import com.sun.org.apache.bcel.internal.classfile.Code; - -import io.swagger.codegen.CodegenConstants; -import io.swagger.codegen.CodegenType; -import io.swagger.codegen.CodegenModel; -import io.swagger.codegen.CodegenParameter; -import io.swagger.codegen.SupportingFile; -import io.swagger.codegen.CodegenProperty; -import io.swagger.codegen.CodegenOperation; -import io.swagger.codegen.CliOption; +import com.samskivert.mustache.Mustache; +import io.swagger.codegen.*; import io.swagger.models.Model; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.HashMap; -import java.util.Iterator; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; +import java.util.*; + import static org.apache.commons.lang3.StringUtils.isEmpty; public class CSharpClientCodegen extends AbstractCSharpCodegen { - @SuppressWarnings({"unused", "hiding"}) + @SuppressWarnings({"hiding"}) private static final Logger LOGGER = LoggerFactory.getLogger(CSharpClientCodegen.class); private static final String NET45 = "v4.5"; + private static final String NET40 = "v4.0"; private static final String NET35 = "v3.5"; + // TODO: v5.0 is PCL, not netstandard version 1.3, and not a specific .NET Framework. This needs to be updated, + // especially because it will conflict with .NET Framework 5.0 when released, and PCL 5 refers to Framework 4.0. + // We should support either NETSTANDARD, PCL, or Both… but the concepts shouldn't be mixed. private static final String NETSTANDARD = "v5.0"; private static final String UWP = "uwp"; - private static final String DATA_TYPE_WITH_ENUM_EXTENSION = "plainDatatypeWithEnum"; + + // Defines the sdk option for targeted frameworks, which differs from targetFramework and targetFrameworkNuget + private static final String MCS_NET_VERSION_KEY = "x-mcs-sdk"; protected String packageGuid = "{" + java.util.UUID.randomUUID().toString().toUpperCase() + "}"; protected String clientPackage = "IO.Swagger.Client"; @@ -41,12 +33,18 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen { protected String apiDocPath = "docs/"; protected String modelDocPath = "docs/"; + // Defines TargetFrameworkVersion in csproj files protected String targetFramework = NET45; + + // Defines nuget identifiers for target framework protected String targetFrameworkNuget = "net45"; protected boolean supportsAsync = Boolean.TRUE; protected boolean supportsUWP = Boolean.FALSE; protected boolean netStandard = Boolean.FALSE; protected boolean generatePropertyChanged = Boolean.FALSE; + protected boolean hideGenerationTimestamp = Boolean.TRUE; + + protected boolean validatable = Boolean.TRUE; protected Map regexModifiers; protected final Map frameworks; @@ -55,6 +53,7 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen { public CSharpClientCodegen() { super(); + supportsInheritance = true; modelTemplateFiles.put("model.mustache", ".cs"); apiTemplateFiles.put("api.mustache", ".cs"); @@ -90,6 +89,7 @@ public CSharpClientCodegen() { ); frameworks = new ImmutableMap.Builder() .put(NET35, ".NET Framework 3.5 compatible") + .put(NET40, ".NET Framework 4.0 compatible") .put(NET45, ".NET Framework 4.5+ compatible") .put(NETSTANDARD, ".NET Standard 1.3 compatible") .put(UWP, "Universal Windows Platform (IMPORTANT: this will be decommissioned and replaced by v5.0)") @@ -98,6 +98,9 @@ public CSharpClientCodegen() { framework.setEnum(frameworks); cliOptions.add(framework); + CliOption modelPropertyNaming = new CliOption(CodegenConstants.MODEL_PROPERTY_NAMING, CodegenConstants.MODEL_PROPERTY_NAMING_DESC); + cliOptions.add(modelPropertyNaming.defaultValue("PascalCase")); + // CLI Switches addSwitch(CodegenConstants.HIDE_GENERATION_TIMESTAMP, CodegenConstants.HIDE_GENERATION_TIMESTAMP_DESC, @@ -155,6 +158,10 @@ public CSharpClientCodegen() { CodegenConstants.NETCORE_PROJECT_FILE_DESC, this.netCoreProjectFileFlag); + addSwitch(CodegenConstants.VALIDATABLE, + CodegenConstants.VALIDATABLE_DESC, + this.validatable); + regexModifiers = new HashMap(); regexModifiers.put('i', "IgnoreCase"); regexModifiers.put('m', "Multiline"); @@ -166,35 +173,43 @@ public CSharpClientCodegen() { public void processOpts() { super.processOpts(); + /* + * NOTE: When supporting boolean additionalProperties, you should read the value and write it back as a boolean. + * This avoids oddities where additionalProperties contains "false" rather than false, which will cause the + * templating engine to behave unexpectedly. + * + * Use the pattern: + * if (additionalProperties.containsKey(prop)) convertPropertyToBooleanAndWriteBack(prop); + */ + + if (additionalProperties.containsKey(CodegenConstants.MODEL_PROPERTY_NAMING)) { + setModelPropertyNaming((String) additionalProperties.get(CodegenConstants.MODEL_PROPERTY_NAMING)); + } + // default HIDE_GENERATION_TIMESTAMP to true - if (!additionalProperties.containsKey(CodegenConstants.HIDE_GENERATION_TIMESTAMP)) { - additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, Boolean.TRUE.toString()); + if (additionalProperties.containsKey(CodegenConstants.HIDE_GENERATION_TIMESTAMP)) { + setHideGenerationTimestamp(convertPropertyToBooleanAndWriteBack(CodegenConstants.HIDE_GENERATION_TIMESTAMP)); } else { - additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, - Boolean.valueOf(additionalProperties().get(CodegenConstants.HIDE_GENERATION_TIMESTAMP).toString())); + additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, hideGenerationTimestamp); } - if(isEmpty(apiPackage)) { - apiPackage = "Api"; + if (isEmpty(apiPackage)) { + setApiPackage("Api"); } - if(isEmpty(modelPackage)) { - modelPackage = "Model"; + if (isEmpty(modelPackage)) { + setModelPackage("Model"); } clientPackage = "Client"; Boolean excludeTests = false; - if(additionalProperties.containsKey(CodegenConstants.EXCLUDE_TESTS)) { - excludeTests = Boolean.valueOf(additionalProperties.get(CodegenConstants.EXCLUDE_TESTS).toString()); + if (additionalProperties.containsKey(CodegenConstants.EXCLUDE_TESTS)) { + excludeTests = convertPropertyToBooleanAndWriteBack(CodegenConstants.EXCLUDE_TESTS); } - additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage); - additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage); - additionalProperties.put("clientPackage", clientPackage); - additionalProperties.put("emitDefaultValue", optionalEmitDefaultValue); - - if (!additionalProperties.containsKey("validatable")) { - // default validatable to true if not set - additionalProperties.put("validatable", true); + if (additionalProperties.containsKey(CodegenConstants.VALIDATABLE)) { + setValidatable(convertPropertyToBooleanAndWriteBack(CodegenConstants.VALIDATABLE)); + } else { + additionalProperties.put(CodegenConstants.VALIDATABLE, validatable); } if (additionalProperties.containsKey(CodegenConstants.DOTNET_FRAMEWORK)) { @@ -202,86 +217,116 @@ public void processOpts() { } else { // Ensure default is set. setTargetFramework(NET45); - additionalProperties.put("targetFramework", this.targetFramework); + additionalProperties.put(CodegenConstants.DOTNET_FRAMEWORK, this.targetFramework); } if (NET35.equals(this.targetFramework)) { + // This is correct, mono will require you build .NET 3.5 sources using 4.0 SDK + additionalProperties.put(MCS_NET_VERSION_KEY, "4"); + additionalProperties.put("net35", true); + if (additionalProperties.containsKey(CodegenConstants.SUPPORTS_ASYNC)) { + LOGGER.warn(".NET 3.5 generator does not support async."); + additionalProperties.remove(CodegenConstants.SUPPORTS_ASYNC); + } + setTargetFrameworkNuget("net35"); + setValidatable(Boolean.FALSE); setSupportsAsync(Boolean.FALSE); - if(additionalProperties.containsKey("supportsAsync")){ - additionalProperties.remove("supportsAsync"); + } else if (NETSTANDARD.equals(this.targetFramework)) { + // TODO: NETSTANDARD here is misrepresenting a PCL v5.0 which supports .NET Framework 4.6+, .NET Core 1.0, and Windows Universal 10.0 + additionalProperties.put(MCS_NET_VERSION_KEY, "4.6-api"); + if (additionalProperties.containsKey("supportsUWP")) { + LOGGER.warn(".NET " + NETSTANDARD + " generator does not support UWP."); + additionalProperties.remove("supportsUWP"); } - additionalProperties.put("validatable", false); - } else if (NETSTANDARD.equals(this.targetFramework)){ + + // TODO: NETSTANDARD=v5.0 and targetFrameworkNuget=netstandard1.3. These need to sync. setTargetFrameworkNuget("netstandard1.3"); setSupportsAsync(Boolean.TRUE); setSupportsUWP(Boolean.FALSE); setNetStandard(Boolean.TRUE); - additionalProperties.put("supportsAsync", this.supportsAsync); - additionalProperties.put("supportsUWP", this.supportsUWP); - additionalProperties.put("netStandard", this.netStandard); //Tests not yet implemented for .NET Standard codegen //Todo implement it excludeTests = true; - if(additionalProperties.containsKey(CodegenConstants.EXCLUDE_TESTS)){ - additionalProperties.remove(CodegenConstants.EXCLUDE_TESTS); - } - additionalProperties.put(CodegenConstants.EXCLUDE_TESTS, excludeTests); - } else if (UWP.equals(this.targetFramework)){ + } else if (UWP.equals(this.targetFramework)) { setTargetFrameworkNuget("uwp"); setSupportsAsync(Boolean.TRUE); setSupportsUWP(Boolean.TRUE); - additionalProperties.put("supportsAsync", this.supportsUWP); - additionalProperties.put("supportsUWP", this.supportsAsync); + } else if (NET40.equals(this.targetFramework)) { + additionalProperties.put(MCS_NET_VERSION_KEY, "4"); + additionalProperties.put("isNet40", true); + + if (additionalProperties.containsKey(CodegenConstants.SUPPORTS_ASYNC)) { + LOGGER.warn(".NET " + NET40 + " generator does not support async."); + additionalProperties.remove(CodegenConstants.SUPPORTS_ASYNC); + } + setTargetFrameworkNuget("net40"); + setSupportsAsync(Boolean.FALSE); } else { + additionalProperties.put(MCS_NET_VERSION_KEY, "4.5.2-api"); setTargetFrameworkNuget("net45"); setSupportsAsync(Boolean.TRUE); - additionalProperties.put("supportsAsync", this.supportsAsync); } - if(additionalProperties.containsKey(CodegenConstants.GENERATE_PROPERTY_CHANGED)) { - if(NET35.equals(targetFramework)) { + if (additionalProperties.containsKey(CodegenConstants.GENERATE_PROPERTY_CHANGED)) { + if (NET35.equals(targetFramework)) { LOGGER.warn(CodegenConstants.GENERATE_PROPERTY_CHANGED + " is only supported by generated code for .NET 4+."); - } else if(NETSTANDARD.equals(targetFramework)) { + additionalProperties.remove(CodegenConstants.GENERATE_PROPERTY_CHANGED); + } else if (NETSTANDARD.equals(targetFramework)) { LOGGER.warn(CodegenConstants.GENERATE_PROPERTY_CHANGED + " is not supported in .NET Standard generated code."); - } else if(Boolean.TRUE.equals(netCoreProjectFileFlag)) { + additionalProperties.remove(CodegenConstants.GENERATE_PROPERTY_CHANGED); + } else if (Boolean.TRUE.equals(netCoreProjectFileFlag)) { LOGGER.warn(CodegenConstants.GENERATE_PROPERTY_CHANGED + " is not supported in .NET Core csproj project format."); - } else { - setGeneratePropertyChanged(Boolean.valueOf(additionalProperties.get(CodegenConstants.GENERATE_PROPERTY_CHANGED).toString())); - } - - if(Boolean.FALSE.equals(this.generatePropertyChanged)) { additionalProperties.remove(CodegenConstants.GENERATE_PROPERTY_CHANGED); + } else { + setGeneratePropertyChanged(convertPropertyToBooleanAndWriteBack(CodegenConstants.GENERATE_PROPERTY_CHANGED)); } } + additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage); + additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage); + additionalProperties.put("clientPackage", clientPackage); + + additionalProperties.put(CodegenConstants.EXCLUDE_TESTS, excludeTests); + additionalProperties.put(CodegenConstants.VALIDATABLE, this.validatable); + additionalProperties.put(CodegenConstants.SUPPORTS_ASYNC, this.supportsAsync); + additionalProperties.put("supportsUWP", this.supportsUWP); + additionalProperties.put("netStandard", this.netStandard); additionalProperties.put("targetFrameworkNuget", this.targetFrameworkNuget); + // TODO: either remove this and update templates to match the "optionalEmitDefaultValues" property, or rename that property. + additionalProperties.put("emitDefaultValue", optionalEmitDefaultValue); + if (additionalProperties.containsKey(CodegenConstants.OPTIONAL_PROJECT_FILE)) { - setOptionalProjectFileFlag(Boolean.valueOf( - additionalProperties.get(CodegenConstants.OPTIONAL_PROJECT_FILE).toString())); + setOptionalProjectFileFlag(convertPropertyToBooleanAndWriteBack(CodegenConstants.OPTIONAL_PROJECT_FILE)); + } else { + additionalProperties.put(CodegenConstants.OPTIONAL_PROJECT_FILE, optionalProjectFileFlag); } if (additionalProperties.containsKey(CodegenConstants.OPTIONAL_PROJECT_GUID)) { setPackageGuid((String) additionalProperties.get(CodegenConstants.OPTIONAL_PROJECT_GUID)); + } else { + additionalProperties.put(CodegenConstants.OPTIONAL_PROJECT_GUID, packageGuid); } - additionalProperties.put("packageGuid", packageGuid); if (additionalProperties.containsKey(CodegenConstants.OPTIONAL_METHOD_ARGUMENT)) { - setOptionalMethodArgumentFlag(Boolean.valueOf(additionalProperties - .get(CodegenConstants.OPTIONAL_METHOD_ARGUMENT).toString())); + setOptionalMethodArgumentFlag(convertPropertyToBooleanAndWriteBack(CodegenConstants.OPTIONAL_METHOD_ARGUMENT)); + } else { + additionalProperties.put(CodegenConstants.OPTIONAL_METHOD_ARGUMENT, optionalMethodArgumentFlag); } - additionalProperties.put("optionalMethodArgument", optionalMethodArgumentFlag); if (additionalProperties.containsKey(CodegenConstants.OPTIONAL_ASSEMBLY_INFO)) { - setOptionalAssemblyInfoFlag(Boolean.valueOf(additionalProperties - .get(CodegenConstants.OPTIONAL_ASSEMBLY_INFO).toString())); + setOptionalAssemblyInfoFlag(convertPropertyToBooleanAndWriteBack(CodegenConstants.OPTIONAL_ASSEMBLY_INFO)); + } else { + additionalProperties.put(CodegenConstants.OPTIONAL_ASSEMBLY_INFO, optionalAssemblyInfoFlag); } if (additionalProperties.containsKey(CodegenConstants.NON_PUBLIC_API)) { - setNonPublicApi(Boolean.valueOf(additionalProperties.get(CodegenConstants.NON_PUBLIC_API).toString())); + setNonPublicApi(convertPropertyToBooleanAndWriteBack(CodegenConstants.NON_PUBLIC_API)); + } else { + additionalProperties.put(CodegenConstants.NON_PUBLIC_API, isNonPublicApi()); } final String testPackageName = testPackageName(); @@ -314,7 +359,14 @@ public void processOpts() { clientPackageDir, "ExceptionFactory.cs")); supportingFiles.add(new SupportingFile("SwaggerDateConverter.mustache", clientPackageDir, "SwaggerDateConverter.cs")); - if(Boolean.FALSE.equals(this.netStandard) && Boolean.FALSE.equals(this.netCoreProjectFileFlag)) { + + if (NET40.equals(this.targetFramework)) { + // .net 4.0 doesn't include ReadOnlyDictionary… + supportingFiles.add(new SupportingFile("ReadOnlyDictionary.mustache", + clientPackageDir, "ReadOnlyDictionary.cs")); + } + + if (Boolean.FALSE.equals(this.netStandard) && Boolean.FALSE.equals(this.netCoreProjectFileFlag)) { supportingFiles.add(new SupportingFile("compile.mustache", "", "build.bat")); supportingFiles.add(new SupportingFile("compile-mono.sh.mustache", "", "build.sh")); @@ -322,12 +374,17 @@ public void processOpts() { supportingFiles.add(new SupportingFile("packages.config.mustache", packageFolder + File.separator, "packages.config")); // .travis.yml for travis-ci.org CI supportingFiles.add(new SupportingFile("travis.mustache", "", ".travis.yml")); - } else if(Boolean.FALSE.equals(this.netCoreProjectFileFlag)) { + } else if (Boolean.FALSE.equals(this.netCoreProjectFileFlag)) { supportingFiles.add(new SupportingFile("project.json.mustache", packageFolder + File.separator, "project.json")); } + supportingFiles.add(new SupportingFile("IReadableConfiguration.mustache", + clientPackageDir, "IReadableConfiguration.cs")); + supportingFiles.add(new SupportingFile("GlobalConfiguration.mustache", + clientPackageDir, "GlobalConfiguration.cs")); + // Only write out test related files if excludeTests is unset or explicitly set to false (see start of this method) - if(Boolean.FALSE.equals(excludeTests)) { + if (Boolean.FALSE.equals(excludeTests)) { // shell script to run the nunit test supportingFiles.add(new SupportingFile("mono_nunit_test.mustache", "", "mono_nunit_test.sh")); @@ -335,50 +392,69 @@ public void processOpts() { apiTestTemplateFiles.put("api_test.mustache", ".cs"); if (Boolean.FALSE.equals(this.netCoreProjectFileFlag)) { - supportingFiles.add(new SupportingFile("packages_test.config.mustache", testPackageFolder + File.separator, "packages.config")); - } + supportingFiles.add(new SupportingFile("packages_test.config.mustache", testPackageFolder + File.separator, "packages.config")); + } + + if (NET40.equals(this.targetFramework)) { + // Include minimal tests for modifications made to JsonSubTypes, since code is quite different for .net 4.0 from original implementation + supportingFiles.add(new SupportingFile("JsonSubTypesTests.mustache", + testPackageFolder + File.separator + "Client", + "JsonSubTypesTests.cs")); + } } - if(Boolean.TRUE.equals(generatePropertyChanged)) { + if (Boolean.TRUE.equals(generatePropertyChanged)) { supportingFiles.add(new SupportingFile("FodyWeavers.xml", packageFolder, "FodyWeavers.xml")); } supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); - // apache v2 license - // UPDATE (20160612) no longer needed as the Apache v2 LICENSE is added globally - //supportingFiles.add(new SupportingFile("LICENSE", "", "LICENSE")); if (optionalAssemblyInfoFlag && Boolean.FALSE.equals(this.netCoreProjectFileFlag)) { supportingFiles.add(new SupportingFile("AssemblyInfo.mustache", packageFolder + File.separator + "Properties", "AssemblyInfo.cs")); } if (optionalProjectFileFlag) { supportingFiles.add(new SupportingFile("Solution.mustache", "", packageName + ".sln")); - - if(Boolean.TRUE.equals(this.netCoreProjectFileFlag)) { + + if (Boolean.TRUE.equals(this.netCoreProjectFileFlag)) { supportingFiles.add(new SupportingFile("netcore_project.mustache", packageFolder, packageName + ".csproj")); } else { - supportingFiles.add(new SupportingFile("Project.mustache", packageFolder, packageName + ".csproj")); - if(Boolean.FALSE.equals(this.netStandard)) { - supportingFiles.add(new SupportingFile("nuspec.mustache", packageFolder, packageName + ".nuspec")); - } + supportingFiles.add(new SupportingFile("Project.mustache", packageFolder, packageName + ".csproj")); + if (Boolean.FALSE.equals(this.netStandard)) { + supportingFiles.add(new SupportingFile("nuspec.mustache", packageFolder, packageName + ".nuspec")); + } } - if(Boolean.FALSE.equals(excludeTests)) { + if (Boolean.FALSE.equals(excludeTests)) { // NOTE: This exists here rather than previous excludeTests block because the test project is considered an optional project file. - if(Boolean.TRUE.equals(this.netCoreProjectFileFlag)) { + if (Boolean.TRUE.equals(this.netCoreProjectFileFlag)) { supportingFiles.add(new SupportingFile("netcore_testproject.mustache", testPackageFolder, testPackageName + ".csproj")); } else { - supportingFiles.add(new SupportingFile("TestProject.mustache", testPackageFolder, testPackageName + ".csproj")); + supportingFiles.add(new SupportingFile("TestProject.mustache", testPackageFolder, testPackageName + ".csproj")); + } } } - } additionalProperties.put("apiDocPath", apiDocPath); additionalProperties.put("modelDocPath", modelDocPath); } + public void setModelPropertyNaming(String naming) { + if ("original".equals(naming) || "camelCase".equals(naming) || + "PascalCase".equals(naming) || "snake_case".equals(naming)) { + this.modelPropertyNaming = naming; + } else { + throw new IllegalArgumentException("Invalid model property naming '" + + naming + "'. Must be 'original', 'camelCase', " + + "'PascalCase' or 'snake_case'"); + } + } + + public String getModelPropertyNaming() { + return this.modelPropertyNaming; + } + @Override public Map postProcessOperations(Map objs) { super.postProcessOperations(objs); @@ -428,10 +504,54 @@ public void setOptionalAssemblyInfoFlag(boolean flag) { @Override public CodegenModel fromModel(String name, Model model, Map allDefinitions) { CodegenModel codegenModel = super.fromModel(name, model, allDefinitions); - if (allDefinitions != null && codegenModel != null && codegenModel.parent != null && codegenModel.hasEnums) { + if (allDefinitions != null && codegenModel != null && codegenModel.parent != null) { final Model parentModel = allDefinitions.get(toModelName(codegenModel.parent)); - final CodegenModel parentCodegenModel = super.fromModel(codegenModel.parent, parentModel); - codegenModel = this.reconcileInlineEnums(codegenModel, parentCodegenModel); + if (parentModel != null) { + final CodegenModel parentCodegenModel = super.fromModel(codegenModel.parent, parentModel); + if (codegenModel.hasEnums) { + codegenModel = this.reconcileInlineEnums(codegenModel, parentCodegenModel); + } + + Map propertyHash = new HashMap<>(codegenModel.vars.size()); + for (final CodegenProperty property : codegenModel.vars) { + propertyHash.put(property.name, property); + } + + for (final CodegenProperty property : codegenModel.readWriteVars) { + if (property.defaultValue == null && property.baseName.equals(parentCodegenModel.discriminator)) { + property.defaultValue = "\"" + name + "\""; + } + } + + CodegenProperty last = null; + for (final CodegenProperty property : parentCodegenModel.vars) { + // helper list of parentVars simplifies templating + if (!propertyHash.containsKey(property.name)) { + final CodegenProperty parentVar = property.clone(); + parentVar.isInherited = true; + parentVar.hasMore = true; + last = parentVar; + LOGGER.info("adding parent variable {}", property.name); + codegenModel.parentVars.add(parentVar); + } + } + + if (last != null) { + last.hasMore = false; + } + } + } + + // Cleanup possible duplicates. Currently, readWriteVars can contain the same property twice. May or may not be isolated to C#. + if (codegenModel != null && codegenModel.readWriteVars != null && codegenModel.readWriteVars.size() > 1) { + int length = codegenModel.readWriteVars.size() - 1; + for (int i = length; i > (length / 2); i--) { + final CodegenProperty codegenProperty = codegenModel.readWriteVars.get(i); + // If the property at current index is found earlier in the list, remove this last instance. + if (codegenModel.readWriteVars.indexOf(codegenProperty) < i) { + codegenModel.readWriteVars.remove(i); + } + } } return codegenModel; @@ -445,11 +565,6 @@ public void setPackageGuid(String packageGuid) { this.packageGuid = packageGuid; } - @Override - public Map postProcessModels(Map objMap) { - return super.postProcessModels(objMap); - } - @Override public void postProcessParameter(CodegenParameter parameter) { postProcessPattern(parameter.pattern, parameter.vendorExtensions); @@ -462,7 +577,6 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert super.postProcessModelProperty(model, property); } - /* * The swagger pattern spec follows the Perl convention and style of modifiers. .NET * does not support this syntax directly so we need to convert the pattern to a .NET compatible @@ -471,13 +585,13 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert * See https://github.com/swagger-api/swagger-codegen/pull/2794 for Python's initial implementation from which this is copied. */ public void postProcessPattern(String pattern, Map vendorExtensions) { - if(pattern != null) { + if (pattern != null) { int i = pattern.lastIndexOf('/'); //Must follow Perl /pattern/modifiers convention - if(pattern.charAt(0) != '/' || i < 2) { + if (pattern.charAt(0) != '/' || i < 2) { throw new IllegalArgumentException("Pattern must follow the Perl " - + "/pattern/modifiers convention. "+pattern+" is not valid."); + + "/pattern/modifiers convention. " + pattern + " is not valid."); } String regex = pattern.substring(1, i).replace("'", "\'"); @@ -486,8 +600,8 @@ public void postProcessPattern(String pattern, Map vendorExtensi // perl requires an explicit modifier to be culture specific and .NET is the reverse. modifiers.add("CultureInvariant"); - for(char c : pattern.substring(i).toCharArray()) { - if(regexModifiers.containsKey(c)) { + for (char c : pattern.substring(i).toCharArray()) { + if (regexModifiers.containsKey(c)) { String modifier = regexModifiers.get(c); modifiers.add(modifier); } else if (c == 'l') { @@ -501,7 +615,7 @@ public void postProcessPattern(String pattern, Map vendorExtensi } public void setTargetFramework(String dotnetFramework) { - if(!frameworks.containsKey(dotnetFramework)){ + if (!frameworks.containsKey(dotnetFramework)) { LOGGER.warn("Invalid .NET framework version, defaulting to " + this.targetFramework); } else { this.targetFramework = dotnetFramework; @@ -543,12 +657,12 @@ private CodegenModel reconcileInlineEnums(CodegenModel codegenModel, CodegenMode } } - if(removedChildEnum) { + if (removedChildEnum) { // If we removed an entry from this model's vars, we need to ensure hasMore is updated int count = 0, numVars = codegenProperties.size(); - for(CodegenProperty codegenProperty : codegenProperties) { + for (CodegenProperty codegenProperty : codegenProperties) { count += 1; - codegenProperty.hasMore = (count < numVars) ? true : null; + codegenProperty.hasMore = count < numVars; } codegenModel.vars = codegenProperties; } @@ -569,8 +683,8 @@ public String toEnumVarName(String value, String datatype) { } // number - if ("int?".equals(datatype) || "long?".equals(datatype) || - "double?".equals(datatype) || "float?".equals(datatype)) { + if ("int?".equals(datatype) || "long?".equals(datatype) || + "double?".equals(datatype) || "float?".equals(datatype)) { String varName = "NUMBER_" + value; varName = varName.replaceAll("-", "MINUS_"); varName = varName.replaceAll("\\+", "PLUS_"); @@ -591,6 +705,38 @@ public String toEnumVarName(String value, String datatype) { } } + @Override + public String toVarName(String name) { + // sanitize name + name = sanitizeName(name); + + // if it's all uppper case, do nothing + if (name.matches("^[A-Z_]*$")) { + return name; + } + + name = getNameUsingModelPropertyNaming(name); + + // for reserved word or word starting with number, append _ + if (isReservedWord(name) || name.matches("^\\d.*")) { + name = escapeReservedWord(name); + } + + return name; + } + + public String getNameUsingModelPropertyNaming(String name) { + switch (CodegenConstants.MODEL_PROPERTY_NAMING_TYPE.valueOf(getModelPropertyNaming())) { + case original: return name; + case camelCase: return camelize(name, true); + case PascalCase: return camelize(name); + case snake_case: return underscore(name); + default: throw new IllegalArgumentException("Invalid model property naming '" + + name + "'. Must be 'original', 'camelCase', " + + "'PascalCase' or 'snake_case'"); + } + } + public void setPackageName(String packageName) { this.packageName = packageName; @@ -604,22 +750,26 @@ public void setTargetFrameworkNuget(String targetFrameworkNuget) { this.targetFrameworkNuget = targetFrameworkNuget; } - public void setSupportsAsync(Boolean supportsAsync){ + public void setSupportsAsync(Boolean supportsAsync) { this.supportsAsync = supportsAsync; } - public void setSupportsUWP(Boolean supportsUWP){ + public void setSupportsUWP(Boolean supportsUWP) { this.supportsUWP = supportsUWP; } - public void setNetStandard(Boolean netStandard){ + public void setNetStandard(Boolean netStandard) { this.netStandard = netStandard; } - public void setGeneratePropertyChanged(final Boolean generatePropertyChanged){ + public void setGeneratePropertyChanged(final Boolean generatePropertyChanged) { this.generatePropertyChanged = generatePropertyChanged; } + public void setHideGenerationTimestamp(boolean hideGenerationTimestamp) { + this.hideGenerationTimestamp = hideGenerationTimestamp; + } + public boolean isNonPublicApi() { return nonPublicApi; } @@ -628,6 +778,10 @@ public void setNonPublicApi(final boolean nonPublicApi) { this.nonPublicApi = nonPublicApi; } + public void setValidatable(boolean validatable) { + this.validatable = validatable; + } + @Override public String toModelDocFilename(String name) { return toModelFilename(name); @@ -652,4 +806,10 @@ public String apiTestFileFolder() { public String modelTestFileFolder() { return outputFolder + File.separator + testFolder + File.separator + testPackageName() + File.separator + modelPackage(); } + + @Override + public Mustache.Compiler processCompiler(Mustache.Compiler compiler) { + // To avoid unexpected behaviors when options are passed programmatically such as { "supportsAsync": "" } + return super.processCompiler(compiler).emptyStringIsFalse(true); + } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/CppRestClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/CppRestClientCodegen.java index 872e1f571d1..315a6f0f27e 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/CppRestClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/CppRestClientCodegen.java @@ -55,7 +55,7 @@ public class CppRestClientCodegen extends AbstractCppCodegen { /** * Configures the type of generator. - * + * * @return the CodegenType for this generator * @see io.swagger.codegen.CodegenType */ @@ -66,7 +66,7 @@ public CodegenType getTag() { /** * Configures a friendly name for the generator. This will be used by the * generator to select the library with the -l flag. - * + * * @return the friendly name for the generator */ public String getName() { @@ -76,7 +76,7 @@ public String getName() { /** * Returns human-friendly help for the generator. Provide the consumer with * help tips, parameters here - * + * * @return A string value for the help message */ public String getHelp() { @@ -111,10 +111,10 @@ public CppRestClientCodegen() { "The default include statement that should be placed in all headers for including things like the declspec (convention: #include \"Commons.h\" ", this.defaultInclude); - reservedWords = new HashSet(); - supportingFiles.add(new SupportingFile("modelbase-header.mustache", "", "ModelBase.h")); supportingFiles.add(new SupportingFile("modelbase-source.mustache", "", "ModelBase.cpp")); + supportingFiles.add(new SupportingFile("object-header.mustache", "", "Object.h")); + supportingFiles.add(new SupportingFile("object-source.mustache", "", "Object.cpp")); supportingFiles.add(new SupportingFile("apiclient-header.mustache", "", "ApiClient.h")); supportingFiles.add(new SupportingFile("apiclient-source.mustache", "", "ApiClient.cpp")); supportingFiles.add(new SupportingFile("apiconfiguration-header.mustache", "", "ApiConfiguration.h")); @@ -181,24 +181,14 @@ public void processOpts() { additionalProperties.put("modelNamespaceDeclarations", modelPackage.split("\\.")); additionalProperties.put("modelNamespace", modelPackage.replaceAll("\\.", "::")); + additionalProperties.put("modelHeaderGuardPrefix", modelPackage.replaceAll("\\.", "_").toUpperCase()); additionalProperties.put("apiNamespaceDeclarations", apiPackage.split("\\.")); additionalProperties.put("apiNamespace", apiPackage.replaceAll("\\.", "::")); + additionalProperties.put("apiHeaderGuardPrefix", apiPackage.replaceAll("\\.", "_").toUpperCase()); additionalProperties.put("declspec", declspec); additionalProperties.put("defaultInclude", defaultInclude); } - /** - * 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 reseved words - * - * @return the escaped term - */ - @Override - public String escapeReservedWord(String name) { - return "_" + name; // add an underscore to the name - } - /** * Location to write model files. You can use the modelPackage() as defined * when the class is instantiated @@ -326,7 +316,7 @@ public String getTypeDeclaration(Property p) { @Override public String toDefaultValue(Property p) { if (p instanceof StringProperty) { - return "U(\"\")"; + return "utility::conversions::to_string_t(\"\")"; } else if (p instanceof BooleanProperty) { return "false"; } else if (p instanceof DateProperty) { diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/CsharpDotNet2ClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/CsharpDotNet2ClientCodegen.java index 73914ea5376..fe71ba3006b 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/CsharpDotNet2ClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/CsharpDotNet2ClientCodegen.java @@ -1,26 +1,16 @@ package io.swagger.codegen.languages; -import io.swagger.codegen.CodegenConfig; +import io.swagger.codegen.CliOption; import io.swagger.codegen.CodegenConstants; import io.swagger.codegen.CodegenType; -import io.swagger.codegen.DefaultCodegen; import io.swagger.codegen.SupportingFile; -import io.swagger.models.properties.ArrayProperty; -import io.swagger.models.properties.MapProperty; -import io.swagger.models.properties.Property; -import io.swagger.codegen.CliOption; - -import org.apache.commons.lang3.StringUtils; import java.io.File; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; public class CsharpDotNet2ClientCodegen extends AbstractCSharpCodegen { public static final String CLIENT_PACKAGE = "clientPackage"; protected String clientPackage = "IO.Swagger.Client"; - protected String apiDocPath = "docs/"; + protected String apiDocPath = "docs/"; protected String modelDocPath = "docs/"; public CsharpDotNet2ClientCodegen() { @@ -33,9 +23,10 @@ public CsharpDotNet2ClientCodegen() { modelTemplateFiles.put("model.mustache", ".cs"); apiTemplateFiles.put("api.mustache", ".cs"); - setApiPackage("IO.Swagger.Api"); - setModelPackage("IO.Swagger.Model"); - setSourceFolder("src" + File.separator + "main" + File.separator + this.getName()); + setApiPackage(packageName + ".Api"); + setModelPackage(packageName + ".Model"); + setClientPackage(packageName + ".Client"); + setSourceFolder("src" + File.separator + "main" + File.separator + "CsharpDotNet2"); modelDocTemplateFiles.put("model_doc.mustache", ".md"); apiDocTemplateFiles.put("api_doc.mustache", ".md"); @@ -57,7 +48,7 @@ public void processOpts() { super.processOpts(); if (additionalProperties.containsKey(CLIENT_PACKAGE)) { - this.setClientPackage((String) additionalProperties.get(CLIENT_PACKAGE)); + setClientPackage((String) additionalProperties.get(CLIENT_PACKAGE)); } else { additionalProperties.put(CLIENT_PACKAGE, getClientPackage()); } @@ -90,8 +81,8 @@ public String modelPackage() { return packageName + ".Model"; } - public String getClientPackage(){ - return packageName + ".Client"; + public String getClientPackage() { + return clientPackage; } public void setClientPackage(String clientPackage) { @@ -105,7 +96,7 @@ public CodegenType getTag() { @Override public String getName() { - return "CsharpDotNet2"; + return "csharp-dotnet2"; } @Override diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/DartClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/DartClientCodegen.java index da10196bfda..478431a455d 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/DartClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/DartClientCodegen.java @@ -3,27 +3,35 @@ import io.swagger.codegen.CliOption; import io.swagger.codegen.CodegenConfig; import io.swagger.codegen.CodegenConstants; +import io.swagger.codegen.CodegenModel; +import io.swagger.codegen.CodegenProperty; import io.swagger.codegen.CodegenType; import io.swagger.codegen.DefaultCodegen; import io.swagger.codegen.SupportingFile; +import io.swagger.models.Model; import io.swagger.models.properties.ArrayProperty; import io.swagger.models.properties.MapProperty; import io.swagger.models.properties.Property; import java.io.File; +import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; public class DartClientCodegen extends DefaultCodegen implements CodegenConfig { public static final String BROWSER_CLIENT = "browserClient"; public static final String PUB_NAME = "pubName"; public static final String PUB_VERSION = "pubVersion"; public static final String PUB_DESCRIPTION = "pubDescription"; + public static final String USE_ENUM_EXTENSION = "useEnumExtension"; protected boolean browserClient = true; protected String pubName = "swagger"; protected String pubVersion = "1.0.0"; protected String pubDescription = "Swagger API client"; + protected boolean useEnumExtension = false; protected String sourceFolder = ""; protected String apiDocPath = "docs/"; protected String modelDocPath = "docs/"; @@ -95,6 +103,7 @@ public DartClientCodegen() { cliOptions.add(new CliOption(PUB_NAME, "Name in generated pubspec")); cliOptions.add(new CliOption(PUB_VERSION, "Version in generated pubspec")); cliOptions.add(new CliOption(PUB_DESCRIPTION, "Description in generated pubspec")); + cliOptions.add(new CliOption(USE_ENUM_EXTENSION, "Allow the 'x-enum-values' extension for enums")); cliOptions.add(new CliOption(CodegenConstants.SOURCE_FOLDER, "source folder for generated code")); } @@ -145,6 +154,13 @@ public void processOpts() { additionalProperties.put(PUB_DESCRIPTION, pubDescription); } + if (additionalProperties.containsKey(USE_ENUM_EXTENSION)) { + this.setUseEnumExtension(convertPropertyToBooleanAndWriteBack(USE_ENUM_EXTENSION)); + } else { + // Not set, use to be passed to template. + additionalProperties.put(USE_ENUM_EXTENSION, useEnumExtension); + } + if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) { this.setSourceFolder((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER)); } @@ -177,16 +193,11 @@ public void processOpts() { supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); - } - @Override - public String escapeReservedWord(String name) { - if(this.reservedWordsMappings().containsKey(name)) { - return this.reservedWordsMappings().get(name); - } - return "_" + name; + public String escapeReservedWord(String name) { + return name + "_"; } @Override @@ -223,8 +234,11 @@ public String toVarName(String name) { // pet_id => petId name = camelize(name, true); - // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (name.matches("^\\d.*")) { + name = "n" + name; + } + + if (isReservedWord(name)) { name = escapeReservedWord(name); } @@ -300,6 +314,117 @@ public String getSwaggerType(Property p) { return toModelName(type); } + @Override + public Map postProcessModels(Map objs) { + return postProcessModelsEnum(objs); + } + + @Override + public Map postProcessModelsEnum(Map objs) { + List models = (List) objs.get("models"); + for (Object _mo : models) { + Map mo = (Map) _mo; + CodegenModel cm = (CodegenModel) mo.get("model"); + boolean succes = buildEnumFromVendorExtension(cm) || + buildEnumFromValues(cm); + for (CodegenProperty var : cm.vars) { + updateCodegenPropertyEnum(var); + } + } + return objs; + } + + /** + * Builds the set of enum members from their declared value. + * + * @return {@code true} if the enum was built + */ + private boolean buildEnumFromValues(CodegenModel cm) { + if (!cm.isEnum || cm.allowableValues == null) { + return false; + } + Map allowableValues = cm.allowableValues; + List values = (List) allowableValues.get("values"); + List> enumVars = + new ArrayList>(); + String commonPrefix = findCommonPrefixOfVars(values); + int truncateIdx = commonPrefix.length(); + for (Object value : values) { + Map enumVar = new HashMap(); + String enumName; + if (truncateIdx == 0) { + enumName = value.toString(); + } else { + enumName = value.toString().substring(truncateIdx); + if ("".equals(enumName)) { + enumName = value.toString(); + } + } + enumVar.put("name", toEnumVarName(enumName, cm.dataType)); + enumVar.put("value", toEnumValue(value.toString(), cm.dataType)); + enumVars.add(enumVar); + } + cm.allowableValues.put("enumVars", enumVars); + return true; + } + + /** + * Builds the set of enum members from a vendor extension. + * + * @return {@code true} if the enum was built + */ + private boolean buildEnumFromVendorExtension(CodegenModel cm) { + if (!cm.isEnum || cm.allowableValues == null || + !useEnumExtension || + !cm.vendorExtensions.containsKey("x-enum-values")) { + return false; + } + Object extension = cm.vendorExtensions.get("x-enum-values"); + List> values = + (List>) extension; + List> enumVars = + new ArrayList>(); + for (Map value : values) { + Map enumVar = new HashMap(); + String name = camelize((String) value.get("identifier"), true); + if (isReservedWord(name)) { + name = escapeReservedWord(name); + } + enumVar.put("name", name); + enumVar.put("value", toEnumValue( + value.get("numericValue").toString(), cm.dataType)); + if (value.containsKey("description")) { + enumVar.put("description", value.get("description").toString()); + } + enumVars.add(enumVar); + } + cm.allowableValues.put("enumVars", enumVars); + return true; + } + + @Override + public String toEnumVarName(String value, String datatype) { + if (value.length() == 0) { + return "empty"; + } + String var = value.replaceAll("\\W+", "_"); + if ("number".equalsIgnoreCase(datatype) || + "int".equalsIgnoreCase(datatype)) { + var = "Number" + var; + } + return escapeReservedWord(camelize(var, true)); + } + + @Override + public String toEnumValue(String value, String datatype) { + if ("number".equalsIgnoreCase(datatype) || + "int".equalsIgnoreCase(datatype)) { + return value; + } else { + return "\"" + escapeText(value) + "\""; + } + } + @Override public String toOperationId(String operationId) { // method name cannot use reserved keyword, e.g. return @@ -328,6 +453,10 @@ public void setPubDescription(String pubDescription) { this.pubDescription = pubDescription; } + public void setUseEnumExtension(boolean useEnumExtension) { + this.useEnumExtension = useEnumExtension; + } + public void setSourceFolder(String sourceFolder) { this.sourceFolder = sourceFolder; } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/EiffelClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/EiffelClientCodegen.java index ba697be3c2c..28d7879a0be 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/EiffelClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/EiffelClientCodegen.java @@ -1,42 +1,19 @@ package io.swagger.codegen.languages; -import static com.google.common.base.Strings.isNullOrEmpty; - import java.io.File; -import java.util.Arrays; -import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; import java.util.Map; -import java.util.Set; import java.util.UUID; -import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; - -import io.swagger.codegen.CliOption; -import io.swagger.codegen.CodegenConfig; import io.swagger.codegen.CodegenConstants; -import io.swagger.codegen.CodegenModel; -import io.swagger.codegen.CodegenOperation; -import io.swagger.codegen.CodegenParameter; import io.swagger.codegen.CodegenProperty; import io.swagger.codegen.CodegenType; -import io.swagger.codegen.DefaultCodegen; import io.swagger.codegen.SupportingFile; -import io.swagger.codegen.utils.ModelUtils; -import io.swagger.models.properties.ArrayProperty; -import io.swagger.models.properties.MapProperty; -import io.swagger.models.properties.Property; -public class EiffelClientCodegen extends DefaultCodegen implements CodegenConfig { +public class EiffelClientCodegen extends AbstractEiffelCodegen { static Logger LOGGER = LoggerFactory.getLogger(EiffelClientCodegen.class); protected String libraryTarget = "swagger_eiffel_client"; @@ -49,9 +26,6 @@ public class EiffelClientCodegen extends DefaultCodegen implements CodegenConfig protected UUID uuid; protected UUID uuidTest; - private final Set parentModels = new HashSet<>(); - private final Multimap childrenByParent = ArrayListMultimap.create(); - @Override public CodegenType getTag() { return CodegenType.CLIENT; @@ -71,67 +45,13 @@ public EiffelClientCodegen() { super(); uuid = UUID.randomUUID(); uuidTest = UUID.randomUUID(); - ; outputFolder = "generated-code/Eiffel"; modelDocTemplateFiles.put("model_doc.mustache", ".md"); - modelTemplateFiles.put("model.mustache", ".e"); + modelTemplateFiles.put("model_generic.mustache", ".e"); apiTemplateFiles.put("api.mustache", ".e"); apiTestTemplateFiles.put("test/api_test.mustache", ".e"); apiDocTemplateFiles.put("api_doc.mustache", ".md"); embeddedTemplateDir = templateDir = "Eiffel"; - - setReservedWordsLowerCase(Arrays.asList( - // language reserved words - "across", "agent", "alias", "all", "and", "as", "assign", "attribute", "check", "class", "convert", - "create", "Current", "debug", "deferred", "do", "else", "elseif", "end", "ensure", "expanded", "export", - "external", "False", "feature", "from", "frozen", "if", "implies", "inherit", "inspect", "invariant", - "like", "local", "loop", "not", "note", "obsolete", "old", "once", "only", "or", "Precursor", - "redefine", "rename", "require", "rescue", "Result", "retry", "select", "separate", "then", "True", - "TUPLE", "undefine", "until", "variant", "Void", "when", "xor")); - - defaultIncludes = new HashSet(Arrays.asList("map", "array")); - - languageSpecificPrimitives = new HashSet( - Arrays.asList("BOOLEAN", "INTEGER_8", "INTEGER_16", "INTEGER_32", "INTEGER_64", "NATURAL_8", - "NATURAL_16", "NATURAL_32", "NATURAL_64", "REAL_32", "REAL_64")); - - instantiationTypes.clear(); - - typeMapping.clear(); - typeMapping.put("integer", "INTEGER_32"); - typeMapping.put("long", "INTEGER_64"); - typeMapping.put("number", "REAL_32"); - typeMapping.put("float", "REAL_32"); - typeMapping.put("double", "REAL_64"); - typeMapping.put("boolean", "BOOLEAN"); - typeMapping.put("string", "STRING_32"); - typeMapping.put("UUID", "UUID"); // - typeMapping.put("date", "DATE"); - typeMapping.put("DateTime", "DATE_TIME"); - typeMapping.put("date-time", "DATE_TIME"); - typeMapping.put("password", "STRING"); - typeMapping.put("File", "FILE"); - typeMapping.put("file", "FILE"); - typeMapping.put("binary", "STRING_32"); - typeMapping.put("ByteArray", "ARRAY [NATURAL_8]"); - typeMapping.put("object", "ANY"); - typeMapping.put("map", "STRING_TABLE"); - typeMapping.put("array", "LIST"); - typeMapping.put("list", "LIST"); - - //instantiationTypes.put("array", "ARRAY"); - //instantiationTypes.put("list", "ARRAYED_LIST"); - //instantiationTypes.put("map", "STRING_TABLE"); - - - cliOptions.clear(); - cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Eiffel Cluster name (convention: lowercase).") - .defaultValue("swagger")); - cliOptions - .add(new CliOption(CodegenConstants.PACKAGE_VERSION, "Eiffel package version.").defaultValue("1.0.0")); - cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, - "hides the timestamp when files were generated").defaultValue(Boolean.TRUE.toString())); - } @Override @@ -173,6 +93,7 @@ public void processOpts() { final String authFolder = ("src/framework/auth"); final String serializerFolder = ("src/framework/serialization"); supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("travis.mustache", "", ".travis.yml")); supportingFiles.add(new SupportingFile("ecf.mustache", "", "api_client.ecf")); supportingFiles.add(new SupportingFile("test/ecf_test.mustache", "test", "api_test.ecf")); supportingFiles.add(new SupportingFile("test/application.mustache", "test", "application.e")); @@ -205,24 +126,6 @@ public void processOpts() { } - @Override - public String escapeReservedWord(String name) { - // Can't start with an underscore, as our fields need to start with an - // UppercaseLetter so that Go treats them as public/visible. - - // Options? - // - MyName - // - AName - // - TheName - // - XName - // - X_Name - // ... or maybe a suffix? - // - Name_ ... think this will work. - if (this.reservedWordsMappings().containsKey(name)) { - return this.reservedWordsMappings().get(name); - } - return camelize(name) + '_'; - } @Override public String apiFileFolder() { @@ -237,141 +140,6 @@ public String apiTestFileFolder() { return outputFolder + File.separator + "test" + File.separator + "apis"; } - @Override - public String toVarName(String name) { - // replace - with _ e.g. created-at => created_at - name = sanitizeName(name.replaceAll("-", "_")); - - // if it's all uppper case, do nothing - if (name.matches("^[A-Z_]*$")) { - return name; - } - - // pet_id - // petId => pet_id - name = unCamelize(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 (isReservedWord(name) || name.matches("^\\d.*")) { - name = escapeReservedWord(name); - } - - return name; - } - - @Override - public String toParamName(String name) { - // params should be lowercase. E.g. "person: PERSON" - return toVarName(name).toLowerCase(); - } - - @Override - public String toModelName(String name) { - // phone_number => PHONE_NUMBER - return toModelFilename(name).toUpperCase(); - } - - @Override - public String toModelFilename(String name) { - if (!StringUtils.isEmpty(modelNamePrefix)) { - name = modelNamePrefix + "_" + name; - } - - if (!StringUtils.isEmpty(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 - 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(String name) { - // replace - with _ e.g. created-at => created_at - name = name.replaceAll("-", "_"); // FIXME: a parameter should not be - // assigned. Also declare the - // methods parameters as 'final'. - - // e.g. PetApi.go => pet_api.go - return underscore(name) + "_api"; - } - - @Override - public String toApiTestFilename(String name) { - return toApiName(name).toLowerCase() + "_test"; - } - - @Override - public String toApiName(String name) { - if (name.length() == 0) { - return "DEFAULT_API"; - } - return name.toUpperCase() + "_API"; - } - - /** - * Overrides postProcessParameter to add a vendor extension - * "x-exportParamName". This is useful when paramName starts with a - * lowercase letter, but we need that param to be exportable (starts with an - * Uppercase letter). - * - * @param parameter - * CodegenParameter object to be processed. - */ - @Override - public void postProcessParameter(CodegenParameter parameter) { - - // Give the base class a chance to process - super.postProcessParameter(parameter); - - char firstChar = parameter.paramName.charAt(0); - - if (Character.isUpperCase(firstChar)) { - // First char is already uppercase, just use paramName. - parameter.vendorExtensions.put("x-exportParamName", parameter.paramName); - - } - - // It's a lowercase first char, let's convert it to uppercase - StringBuilder sb = new StringBuilder(parameter.paramName); - sb.setCharAt(0, Character.toUpperCase(firstChar)); - parameter.vendorExtensions.put("x-exportParamName", sb.toString()); - } - - @Override - public void postProcessModelProperty(CodegenModel model, CodegenProperty property) { - if (!isNullOrEmpty(model.parent)) { - parentModels.add(model.parent); - if (!childrenByParent.containsEntry(model.parent, model)) { - childrenByParent.put(model.parent, model); - } - } - if (!isNullOrEmpty(model.parentSchema)) { - model.parentSchema = model.parentSchema.toLowerCase(); - } - } - @Override public String apiDocFileFolder() { return (outputFolder + "/" + apiDocPath).replace('/', File.separatorChar); @@ -382,242 +150,60 @@ 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(Property p) { - if (p instanceof ArrayProperty) { - ArrayProperty ap = (ArrayProperty) p; - Property inner = ap.getItems(); - return "LIST [" + getTypeDeclaration(inner) + "]"; - } else if (p instanceof MapProperty) { - MapProperty mp = (MapProperty) p; - Property inner = mp.getAdditionalProperties(); - - return getSwaggerType(p) + "[" + getTypeDeclaration(inner) + "]"; - } - // return super.getTypeDeclaration(p); - - // Not using the supertype invocation, because we want to UpperCamelize - // the type. - String swaggerType = getSwaggerType(p); - if (typeMapping.containsKey(swaggerType)) { - return typeMapping.get(swaggerType); - } - - if (typeMapping.containsValue(swaggerType)) { - return swaggerType; - } - - if (languageSpecificPrimitives.contains(swaggerType)) { - return swaggerType; - } - - return toModelName(swaggerType); + public void setPackageName(String packageName) { + this.packageName = packageName; } - @Override - public String getSwaggerType(Property p) { - String swaggerType = super.getSwaggerType(p); - String type = null; - if (typeMapping.containsKey(swaggerType)) { - type = typeMapping.get(swaggerType); - if (languageSpecificPrimitives.contains(type)) - return (type); - } else - type = swaggerType; - return type; + public void setPackageVersion(String packageVersion) { + this.packageVersion = packageVersion; } + @Override - public String toOperationId(String operationId) { - String sanitizedOperationId = sanitizeName(operationId); - - // method name cannot use reserved keyword, e.g. return - if (isReservedWord(sanitizedOperationId)) { - LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " - + camelize("call_" + operationId)); - sanitizedOperationId = "call_" + sanitizedOperationId; - } - // method name from updateSomething to update_Something. - sanitizedOperationId = unCamelize(sanitizedOperationId); - - return toEiffelFeatureStyle(sanitizedOperationId); + public String toEnumName(CodegenProperty property) { + return sanitizeName(property.name).toUpperCase() + "_ENUM"; } @Override - public Map postProcessOperations(Map objs) { - @SuppressWarnings("unchecked") - Map objectMap = (Map) objs.get("operations"); - @SuppressWarnings("unchecked") - List operations = (List) objectMap.get("operation"); - for (CodegenOperation operation : operations) { - // http method verb conversion (e.g. PUT => Put) - - operation.httpMethod = camelize(operation.httpMethod.toLowerCase()); - } - - // remove model imports to avoid error - List> imports = (List>) objs.get("imports"); - if (imports == null) - return objs; - - Iterator> iterator = imports.iterator(); - while (iterator.hasNext()) { - String _import = iterator.next().get("import"); - if (_import.startsWith(apiPackage())) - iterator.remove(); - } - // if the return type is not primitive, import encoding/json - for (CodegenOperation operation : operations) { - if (operation.returnBaseType != null && needToImport(operation.returnBaseType)) { - imports.add(createMapping("import", "encoding/json")); - break; // just need to import once - } - } - - // this will only import "fmt" if there are items in pathParams - for (CodegenOperation operation : operations) { - if (operation.pathParams != null && operation.pathParams.size() > 0) { - imports.add(createMapping("import", "fmt")); - break; // just need to import once - } - } - - // recursively add import for mapping one type to multiple imports - List> recursiveImports = (List>) objs.get("imports"); - if (recursiveImports == null) - return objs; - - ListIterator> listIterator = imports.listIterator(); - while (listIterator.hasNext()) { - String _import = listIterator.next().get("import"); - // if the import package happens to be found in the importMapping - // (key) - // add the corresponding import package to the list - if (importMapping.containsKey(_import)) { - listIterator.add(createMapping("import", importMapping.get(_import))); - } + public String toEnumVarName(String value, String datatype) { + if (value.length() == 0) { + return "EMPTY"; } - return objs; - } - - @Override - public Map postProcessModels(Map objs) { - // remove model imports to avoid error - List> imports = (List>) objs.get("imports"); - final String prefix = modelPackage(); - Iterator> iterator = imports.iterator(); - while (iterator.hasNext()) { - String _import = iterator.next().get("import"); - if (_import.startsWith(prefix)) - iterator.remove(); + // for symbol, e.g. $, # + if (getSymbolName(value) != null) { + return getSymbolName(value).toUpperCase(); } - // recursively add import for mapping one type to multiple imports - List> recursiveImports = (List>) objs.get("imports"); - if (recursiveImports == null) - return objs; - - ListIterator> listIterator = imports.listIterator(); - while (listIterator.hasNext()) { - String _import = listIterator.next().get("import"); - // if the import package happens to be found in the importMapping - // (key) - // add the corresponding import package to the list - if (importMapping.containsKey(_import)) { - listIterator.add(createMapping("import", importMapping.get(_import))); - } + // number + if ("INTEGER_32".equals(datatype) || "INTEGER_64".equals(datatype) || + "REAL_32".equals(datatype) || "REAL_64".equals(datatype)) { + String varName = "NUMBER_" + value; + varName = varName.replaceAll("-", "MINUS_"); + varName = varName.replaceAll("\\+", "PLUS_"); + varName = varName.replaceAll("\\.", "_DOT_"); + return varName; } - return objs; - } - - @Override - public Map postProcessAllModels(final Map models) { - - final Map processed = super.postProcessAllModels(models); - postProcessParentModels(models); - return processed; - } - - private void postProcessParentModels(final Map models) { - for (final String parent : parentModels) { - final CodegenModel parentModel = ModelUtils.getModelByName(parent, models); - final Collection childrenModels = childrenByParent.get(parent); - for (final CodegenModel child : childrenModels) { - processParentPropertiesInChildModel(parentModel, child); - } - } - } - - /** - * Sets the child property's isInherited flag to true if it is an inherited - * property - */ - private void processParentPropertiesInChildModel(final CodegenModel parent, final CodegenModel child) { - final Map childPropertiesByName = new HashMap<>(child.vars.size()); - for (final CodegenProperty childProperty : child.vars) { - childPropertiesByName.put(childProperty.name, childProperty); - } - for (final CodegenProperty parentProperty : parent.vars) { - final CodegenProperty duplicatedByParent = childPropertiesByName.get(parentProperty.name); - if (duplicatedByParent != null) { - duplicatedByParent.isInherited = true; - } + // string + String var = value.replaceAll("\\W+", "_").toLowerCase(); + if (var.matches("\\d.*")) { + return "val_" + var; + } else if (var.startsWith("_")){ + return "val" + var; + } else { + return "val_" + var; } } @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("/*", "/_*"); - } - - public Map createMapping(String key, String value) { - Map customImport = new HashMap(); - customImport.put(key, value); - - return customImport; - } - - public String unCamelize(String name) { - return name.replaceAll("(.)(\\p{Upper})", "$1_$2").toLowerCase(); - } - - public String toEiffelFeatureStyle(String operationId) { - if (operationId.startsWith("get_")) { - return operationId.substring(4, operationId.length()); + public String toEnumValue(String value, String datatype) { + if ("INTEGER_32".equals(datatype) || "INTEGER_64".equals(datatype) || + "REAL_32".equals(datatype) || "REAL_64".equals(datatype)) { + return value; } else { - return operationId; + return "\"" + escapeText(value) + "\""; } + } - } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ElixirClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ElixirClientCodegen.java index 21a237dd1b6..82861dbd0e5 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ElixirClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ElixirClientCodegen.java @@ -3,9 +3,11 @@ import com.samskivert.mustache.Mustache; import com.samskivert.mustache.Template; import io.swagger.codegen.*; -import io.swagger.models.properties.ArrayProperty; -import io.swagger.models.properties.MapProperty; -import io.swagger.models.properties.Property; +import io.swagger.models.properties.*; +import io.swagger.models.Info; +import io.swagger.models.Model; +import io.swagger.models.Swagger; +import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.io.Writer; @@ -14,25 +16,27 @@ import java.util.regex.Pattern; public class ElixirClientCodegen extends DefaultCodegen implements CodegenConfig { - // source folder where to write the files - protected String sourceFolder = "lib"; protected String apiVersion = "1.0.0"; + protected String moduleName; + protected static final String defaultModuleName = "Swagger.Client"; + + // This is the name of elixir project name; + protected static final String defaultPackageName = "swagger_client"; String supportedElixirVersion = "1.4"; List extraApplications = Arrays.asList(":logger"); List deps = Arrays.asList( - "{:tesla, \"~> 0.5.0\"}", + "{:tesla, \"~> 0.8\"}", "{:poison, \">= 1.0.0\"}" ); - public ElixirClientCodegen() { super(); // set the output folder here outputFolder = "generated-code/elixir"; - /** + /* * Models. You can write model files using the modelTemplateFiles map. * if you want to create one template for file, you can do so here. * for multiple files for model, just put another entry in the `modelTemplateFiles` with @@ -59,11 +63,18 @@ public ElixirClientCodegen() { /** * Reserved words. Override this with reserved words specific to your language + * Ref: https://github.com/itsgreggreg/elixir_quick_reference#reserved-words */ reservedWords = new HashSet( Arrays.asList( - "sample1", // replace with static values - "sample2") + "nil", + "true", + "false", + "__MODULE__", + "__FILE__", + "__DIR__", + "__ENV__", + "__CALLER__") ); /** @@ -93,6 +104,10 @@ public ElixirClientCodegen() { "test", "test_helper.exs") ); + supportingFiles.add(new SupportingFile("gitignore.mustache", + "", + ".gitignore") + ); /** * Language Specific Primitives. These types will not trigger imports by @@ -100,9 +115,43 @@ public ElixirClientCodegen() { */ languageSpecificPrimitives = new HashSet( Arrays.asList( - "Type1", // replace these with your types - "Type2") + "Integer", + "Float", + "Boolean", + "String", + "List", + "Atom", + "Map", + "Tuple", + "PID", + "DateTime" + ) ); + + // ref: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types + typeMapping = new HashMap(); + typeMapping.put("integer", "Integer"); + typeMapping.put("long", "Integer"); + typeMapping.put("number", "Float"); + typeMapping.put("float", "Float"); + typeMapping.put("double", "Float"); + typeMapping.put("string", "String"); + typeMapping.put("byte", "Integer"); + typeMapping.put("boolean", "Boolean"); + typeMapping.put("Date", "DateTime"); + typeMapping.put("DateTime", "DateTime"); + typeMapping.put("file", "String"); + typeMapping.put("map", "Map"); + typeMapping.put("array", "List"); + typeMapping.put("list", "List"); + // typeMapping.put("object", "Map"); + typeMapping.put("binary", "String"); + typeMapping.put("ByteArray", "String"); + typeMapping.put("UUID", "String"); + + cliOptions.add(new CliOption(CodegenConstants.INVOKER_PACKAGE, "The main namespace to use for all classes. e.g. Yay.Pets")); + cliOptions.add(new CliOption("licenseHeader", "The license header to prepend to the top of all source files.")); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Elixir package name (convention: lowercase).")); } /** @@ -153,6 +202,41 @@ public void execute(Template.Fragment fragment, Writer writer) throws IOExceptio writer.write(modulized(fragment.execute())); } }); + + if (additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) { + setModuleName((String) additionalProperties.get(CodegenConstants.INVOKER_PACKAGE)); + } + } + + @Override + public void preprocessSwagger(Swagger swagger) { + Info info = swagger.getInfo(); + if (moduleName == null) { + if (info.getTitle() != null) { + // default to the appName (from title field) + setModuleName(modulized(escapeText(info.getTitle()))); + } else { + setModuleName(defaultModuleName); + } + } + additionalProperties.put("moduleName", moduleName); + + if (!additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { + additionalProperties.put(CodegenConstants.PACKAGE_NAME, underscored(moduleName)); + } + + supportingFiles.add(new SupportingFile("connection.ex.mustache", + sourceFolder(), + "connection.ex")); + + supportingFiles.add(new SupportingFile("request_builder.ex.mustache", + sourceFolder(), + "request_builder.ex")); + + + supportingFiles.add(new SupportingFile("deserializer.ex.mustache", + sourceFolder(), + "deserializer.ex")); } @Override @@ -160,14 +244,14 @@ public Map postProcessOperations(Map objs) { Map operations = (Map) super.postProcessOperations(objs).get("operations"); List os = (List) operations.get("operation"); List newOs = new ArrayList(); - Pattern pattern = Pattern.compile("(.*)\\{([^\\}]+)\\}(.*)"); + Pattern pattern = Pattern.compile("\\{([^\\}]+)\\}([^\\{]*)"); for (CodegenOperation o : os) { ArrayList pathTemplateNames = new ArrayList(); Matcher matcher = pattern.matcher(o.path); StringBuffer buffer = new StringBuffer(); while (matcher.find()) { - String pathTemplateName = matcher.group(2); - matcher.appendReplacement(buffer, "$1" + "#{" + underscore(pathTemplateName) + "}" + "$3"); + String pathTemplateName = matcher.group(1); + matcher.appendReplacement(buffer, "#{" + underscore(pathTemplateName) + "}" + "$2"); pathTemplateNames.add(pathTemplateName); } ExtendedCodegenOperation eco = new ExtendedCodegenOperation(o); @@ -177,12 +261,29 @@ public Map postProcessOperations(Map objs) { eco.setReplacedPathName(buffer.toString()); } eco.setPathTemplateNames(pathTemplateNames); + + // detect multipart form types + if (eco.hasConsumes == Boolean.TRUE) { + Map firstType = eco.consumes.get(0); + if (firstType != null) { + if ("multipart/form-data".equals(firstType.get("mediaType"))) { + eco.isMultipart = Boolean.TRUE; + } + } + } + newOs.add(eco); } operations.put("operation", newOs); return objs; } + @Override + public CodegenModel fromModel(String name, Model model, Map allDefinitions) { + CodegenModel cm = super.fromModel(name, model, allDefinitions); + return new ExtendedCodegenModel(cm); + } + // We should use String.join if we can use Java8 String join(CharSequence charSequence, Iterable iterable) { StringBuilder buf = new StringBuilder(); @@ -213,7 +314,7 @@ String modulized(String words) { /** * 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 reseved words + * those terms here. This logic is only called if a variable matches the reserved words * * @return the escaped term */ @@ -222,12 +323,20 @@ public String escapeReservedWord(String name) { return "_" + name; // add an underscore to the name } + private String sourceFolder() { + ArrayList underscoredWords = new ArrayList(); + for (String word : moduleName.split("\\.")) { + underscoredWords.add(underscore(word)); + } + return "lib/" + join("/", underscoredWords); + } + /** * Location to write model files. You can use the modelPackage() as defined when the class is * instantiated */ public String modelFileFolder() { - return outputFolder + "/" + sourceFolder + "/" + underscored((String) additionalProperties.get("appName")) + "/" + "model"; + return outputFolder + "/" + sourceFolder() + "/" + "model"; } /** @@ -236,7 +345,7 @@ public String modelFileFolder() { */ @Override public String apiFileFolder() { - return outputFolder + "/" + sourceFolder + "/" + underscored((String) additionalProperties.get("appName")) + "/" + "api"; + return outputFolder + "/" + sourceFolder() + "/" + "api"; } @Override @@ -244,17 +353,60 @@ public String toApiName(String name) { if (name.length() == 0) { return "Default"; } - return initialCaps(name); + return camelize(name); } @Override public String toApiFilename(String name) { - return snakeCase(name); + // replace - with _ e.g. created-at => created_at + name = name.replaceAll("-", "_"); + + // e.g. PetApi.go => pet_api.go + return underscore(name); + } + + @Override + public String toModelName(String name) { + // camelize the model name + // phone_number => PhoneNumber + return camelize(toModelFilename(name)); } @Override public String toModelFilename(String name) { - return snakeCase(name); + if (!StringUtils.isEmpty(modelNamePrefix)) { + name = modelNamePrefix + "_" + name; + } + + if (!StringUtils.isEmpty(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 + 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 toOperationId(String operationId) { + // throw exception if method name is empty (should not occur as an auto-generated method name will be used) + if (StringUtils.isEmpty(operationId)) { + throw new RuntimeException("Empty method name (operationId) not allowed"); + } + + return camelize(sanitizeName(operationId)); } /** @@ -265,14 +417,77 @@ public String toModelFilename(String name) { */ @Override public String getTypeDeclaration(Property p) { + // SubClasses of AbstractProperty + // + // ArrayProperty + // MapProperty + // PasswordProperty + // StringProperty + // EmailProperty + // ByteArrayProperty + // DateProperty + // UUIDProperty + // DateTimeProperty + // ObjectProperty + // AbstractNumericProperty + // BaseIntegerProperty + // IntegerProperty + // LongProperty + // DecimalProperty + // DoubleProperty + // FloatProperty + // BinaryProperty + // BooleanProperty + // RefProperty + // FileProperty if (p instanceof ArrayProperty) { ArrayProperty ap = (ArrayProperty) p; Property inner = ap.getItems(); - return getSwaggerType(p) + "[" + getTypeDeclaration(inner) + "]"; + return "[" + getTypeDeclaration(inner) + "]"; } else if (p instanceof MapProperty) { MapProperty mp = (MapProperty) p; Property inner = mp.getAdditionalProperties(); - return getSwaggerType(p) + "[String, " + getTypeDeclaration(inner) + "]"; + return "%{optional(String.t) => " + getTypeDeclaration(inner) + "}"; + } else if (p instanceof PasswordProperty) { + return "String.t"; + } else if (p instanceof EmailProperty) { + return "String.t"; + } else if (p instanceof ByteArrayProperty) { + return "binary()"; + } else if (p instanceof StringProperty) { + return "String.t"; + } else if (p instanceof DateProperty) { + return "Date.t"; + } else if (p instanceof UUIDProperty) { + return "String.t"; + } else if (p instanceof DateTimeProperty) { + return "DateTime.t"; + } else if (p instanceof ObjectProperty) { + // How to map it? + return super.getTypeDeclaration(p); + } else if (p instanceof IntegerProperty) { + return "integer()"; + } else if (p instanceof LongProperty) { + return "integer()"; + } else if (p instanceof BaseIntegerProperty) { + return "integer()"; + } else if (p instanceof DoubleProperty) { + return "float()"; + } else if (p instanceof FloatProperty) { + return "float()"; + } else if (p instanceof DecimalProperty) { + return "float()"; + } else if (p instanceof AbstractNumericProperty) { + return "number()"; + } else if (p instanceof BinaryProperty) { + return "binary()"; + } else if (p instanceof BooleanProperty) { + return "boolean()"; + } else if (p instanceof RefProperty) { + // How to map it? + return super.getTypeDeclaration(p); + } else if (p instanceof FileProperty) { + return "String.t"; } return super.getTypeDeclaration(p); } @@ -374,6 +589,187 @@ public String getReplacedPathName() { public void setReplacedPathName(String replacedPathName) { this.replacedPathName = replacedPathName; } + + public String typespec() { + StringBuilder sb = new StringBuilder("@spec "); + sb.append(underscore(operationId)); + sb.append("(Tesla.Env.client, "); + + for (CodegenParameter param : allParams) { + if (param.required) { + buildTypespec(param, sb); + sb.append(", "); + } + } + + sb.append("keyword()) :: {:ok, "); + if (returnBaseType == null) { + sb.append("nil"); + } else if (returnSimpleType) { + if (!returnTypeIsPrimitive) { + sb.append(moduleName); + sb.append(".Model."); + } + sb.append(returnBaseType); + sb.append(".t"); + } else if (returnContainer == null) { + sb.append(returnBaseType); + sb.append(".t"); + } else { + if (returnContainer.equals("array")) { + sb.append("list("); + if (!returnTypeIsPrimitive) { + sb.append(moduleName); + sb.append(".Model."); + } + sb.append(returnBaseType); + sb.append(".t)"); + } else if (returnContainer.equals("map")) { + sb.append("map()"); + } + } + sb.append("} | {:error, Tesla.Env.t}"); + return sb.toString(); + } + + private void buildTypespec(CodegenParameter param, StringBuilder sb) { + if (param.dataType == null) { + sb.append("nil"); + } else if (param.isListContainer) { + // list() + sb.append("list("); + if (param.isBodyParam) { + buildTypespec(param.items.items, sb); + } else { + buildTypespec(param.items, sb); + } + sb.append(")"); + } else if (param.isMapContainer) { + // %{optional(String.t) => } + sb.append("%{optional(String.t) => "); + buildTypespec(param.items, sb); + sb.append("}"); + } else if (param.isPrimitiveType) { + // like `integer()`, `String.t` + sb.append(param.dataType); + } else if (param.isFile) { + sb.append("String.t"); + } else { + // .Model..t + sb.append(moduleName); + sb.append(".Model."); + sb.append(param.dataType); + sb.append(".t"); + } + } + private void buildTypespec(CodegenProperty property, StringBuilder sb) { + if (property.isListContainer) { + sb.append("list("); + buildTypespec(property.items, sb); + sb.append(")"); + } else if (property.isMapContainer) { + sb.append("%{optional(String.t) => "); + buildTypespec(property.items, sb); + sb.append("}"); + } else if (property.isPrimitiveType) { + sb.append(property.baseType); + sb.append(".t"); + } else { + sb.append(moduleName); + sb.append(".Model."); + sb.append(property.baseType); + sb.append(".t"); + } + } + + public String decodedStruct() { + // Let Poison decode the entire response into a generic blob + if (isMapContainer) { + return ""; + } + // Primitive return type, don't even try to decode + if (returnBaseType == null || (returnSimpleType && returnTypeIsPrimitive)) { + return "false"; + } + StringBuilder sb = new StringBuilder(); + if (isListContainer) { + sb.append("["); + } + sb.append("%"); + sb.append(moduleName); + sb.append(".Model."); + sb.append(returnBaseType); + sb.append("{}"); + if (isListContainer) { + sb.append("]"); + } + return sb.toString(); + } + } + + class ExtendedCodegenModel extends CodegenModel { + public boolean hasImports; + public ExtendedCodegenModel(CodegenModel cm) { + super(); + + // Copy all fields of CodegenModel + this.parent = cm.parent; + this.parentSchema = cm.parentSchema; + this.parentModel = cm.parentModel; + this.interfaceModels = cm.interfaceModels; + this.children = cm.children; + this.name = cm.name; + this.classname = cm.classname; + this.title = cm.title; + this.description = cm.description; + this.classVarName = cm.classVarName; + this.modelJson = cm.modelJson; + this.dataType = cm.dataType; + this.xmlPrefix = cm.xmlPrefix; + this.xmlNamespace = cm.xmlNamespace; + this.xmlName = cm.xmlName; + this.classFilename = cm.classFilename; + this.unescapedDescription = cm.unescapedDescription; + this.discriminator = cm.discriminator; + this.defaultValue = cm.defaultValue; + this.arrayModelType = cm.arrayModelType; + this.isAlias = cm.isAlias; + this.vars = cm.vars; + this.requiredVars = cm.requiredVars; + this.optionalVars = cm.optionalVars; + this.readOnlyVars = cm.readOnlyVars; + this.readWriteVars = cm.readWriteVars; + this.allVars = cm.allVars; + this.parentVars = cm.parentVars; + this.allowableValues = cm.allowableValues; + this.mandatory = cm.mandatory; + this.allMandatory = cm.allMandatory; + this.imports = cm.imports; + this.hasVars = cm.hasVars; + this.emptyVars = cm.emptyVars; + this.hasMoreModels = cm.hasMoreModels; + this.hasEnums = cm.hasEnums; + this.isEnum = cm.isEnum; + this.hasRequired = cm.hasRequired; + this.hasOptional = cm.hasOptional; + this.isArrayModel = cm.isArrayModel; + this.hasChildren = cm.hasChildren; + this.hasOnlyReadOnly = cm.hasOnlyReadOnly; + this.externalDocs = cm.externalDocs; + this.vendorExtensions = cm.vendorExtensions; + this.additionalPropertiesType = cm.additionalPropertiesType; + + this.hasImports = !this.imports.isEmpty(); + } + + public boolean hasComplexVars() { + for (CodegenProperty p : vars) { + if (!p.isPrimitiveType) { + return true; + } + } + return false; + } } @Override @@ -386,4 +782,8 @@ public String escapeUnsafeCharacters(String input) { // no need to escape as Elixir does not support multi-line comments return input; } + + public void setModuleName(String moduleName) { + this.moduleName = moduleName; + } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ErlangClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ErlangClientCodegen.java new file mode 100644 index 00000000000..d41daa8576c --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ErlangClientCodegen.java @@ -0,0 +1,417 @@ +package io.swagger.codegen.languages; + +import io.swagger.codegen.*; +import com.samskivert.mustache.Mustache; +import com.samskivert.mustache.Template; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.MapProperty; +import io.swagger.models.properties.Property; +import io.swagger.models.parameters.Parameter; + +import java.io.File; +import java.util.*; +import java.io.Writer; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ErlangClientCodegen extends DefaultCodegen implements CodegenConfig { + static Logger LOGGER = LoggerFactory.getLogger(ErlangClientCodegen.class); + + protected String packageName = "swagger"; + protected String packageVersion = "1.0.0"; + protected String sourceFolder = "src"; + + public CodegenType getTag() { + return CodegenType.CLIENT; + } + + public String getName() { + return "erlang-client"; + } + + public String getHelp() { + return "Generates an Erlang client library (beta)."; + } + + public ErlangClientCodegen() { + super(); + outputFolder = "generated-code/erlang"; + modelTemplateFiles.put("model.mustache", ".erl"); + apiTemplateFiles.put("api.mustache", ".erl"); + + embeddedTemplateDir = templateDir = "erlang-client"; + + setReservedWordsLowerCase( + Arrays.asList( + "after","and","andalso","band","begin","bnot","bor","bsl","bsr","bxor","case", + "catch","cond","div","end","fun","if","let","not","of","or","orelse","receive", + "rem","try","when","xor" + ) + ); + + instantiationTypes.clear(); + + typeMapping.clear(); + typeMapping.put("enum", "binary()"); + typeMapping.put("date", "calendar:date()"); + typeMapping.put("datetime", "calendar:datetime()"); + typeMapping.put("date-time", "calendar:datetime()"); + typeMapping.put("boolean", "boolean()"); + typeMapping.put("string", "binary()"); + typeMapping.put("integer", "integer()"); + typeMapping.put("int", "integer()"); + typeMapping.put("float", "integer()"); + typeMapping.put("long", "integer()"); + typeMapping.put("double", "float()"); + typeMapping.put("array", "list()"); + typeMapping.put("map", "maps:map()"); + typeMapping.put("number", "integer()"); + typeMapping.put("bigdecimal", "float()"); + typeMapping.put("List", "list()"); + typeMapping.put("object", "maps:map()"); + typeMapping.put("file", "binary()"); + typeMapping.put("binary", "binary()"); + typeMapping.put("bytearray", "binary()"); + typeMapping.put("byte", "binary()"); + typeMapping.put("uuid", "binary()"); + typeMapping.put("password", "binary()"); + + cliOptions.clear(); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Erlang application name (convention: lowercase).") + .defaultValue(this.packageName)); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Erlang application version") + .defaultValue(this.packageVersion)); + } + + @Override + public String getTypeDeclaration(String name) { + return name + ":" + name + "()"; + } + + @Override + public String getTypeDeclaration(Property p) { + String swaggerType = getSwaggerType(p); + if (typeMapping.containsKey(swaggerType)) { + return typeMapping.get(swaggerType); + } + return swaggerType; + } + + @Override + public String getSwaggerType(Property p) { + String swaggerType = super.getSwaggerType(p); + String type = null; + if(typeMapping.containsKey(swaggerType)) { + type = typeMapping.get(swaggerType); + if(languageSpecificPrimitives.contains(type)) + return (type); + } + else + type = getTypeDeclaration(toModelName(snakeCase(swaggerType))); + return type; + } + + @Override + public void processOpts() { + super.processOpts(); + + if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { + setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME)); + } + else { + setPackageName("swagger"); + } + + 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("length", new Mustache.Lambda() { + @Override + public void execute(Template.Fragment fragment, Writer writer) throws IOException { + writer.write(length(fragment.context())); + } + }); + + additionalProperties.put("qsEncode", new Mustache.Lambda() { + @Override + public void execute(Template.Fragment fragment, Writer writer) throws IOException { + writer.write(qsEncode(fragment.context())); + } + }); + + modelPackage = packageName; + apiPackage = packageName; + + supportingFiles.add(new SupportingFile("rebar.config.mustache","", "rebar.config")); + supportingFiles.add(new SupportingFile("app.src.mustache", "", "src" + File.separator + this.packageName + ".app.src")); + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + } + + public String qsEncode(Object o) { + String r = new String(); + CodegenParameter q = (CodegenParameter) o; + if (q.required) { + if (q.isListContainer) { + r += "[{<<\"" + q.baseName + "\">>, X} || X <- " + q.paramName + "]"; + } else { + r += "{<<\"" + q.baseName + "\">>, " + q.paramName + "}"; + } + } + return r; + } + + @Override + public String escapeReservedWord(String name) + { + // Can't start with an underscore, as our fields need to start with an + // UppercaseLetter so that Go treats them as public/visible. + + // Options? + // - MyName + // - AName + // - TheName + // - XName + // - X_Name + // ... or maybe a suffix? + // - Name_ ... think this will work. + if(this.reservedWordsMappings().containsKey(name)) { + return this.reservedWordsMappings().get(name); + } + return camelize(name) + '_'; + } + + @Override + public String apiFileFolder() { + return outputFolder + File.separator + sourceFolder + File.separator; + } + + @Override + public String modelFileFolder() { + return outputFolder + File.separator + sourceFolder + File.separator; + } + + @Override + public String toVarName(String name) { + // replace - with _ e.g. created-at => created_at + name = sanitizeName(name.replaceAll("-", "_")); + // for reserved word or word starting with number, append _ + if (isReservedWord(name)) + name = escapeReservedWord(name); + + return name; + } + + @Override + public String toParamName(String name) { + return camelize(toVarName(name)); + } + + @Override + public String toModelName(String name) { + return this.packageName + "_" + underscore(name.replaceAll("-", "_")); + } + + @Override + public String toApiName(String name) { + return this.packageName + "_" + underscore(name.replaceAll("-", "_")); + } + + @Override + public String toModelFilename(String name) { + return this.packageName + "_" + underscore(name); + } + + @Override + public String toApiFilename(String name) { + // replace - with _ e.g. created-at => created_at + name = name.replaceAll("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + + // e.g. PetApi.erl => pet_api.erl + return this.packageName + "_" + underscore(name) + "_api"; + } + + @Override + public String toOperationId(String operationId) { + // method name cannot use reserved keyword, e.g. return + if (isReservedWord(operationId)) { + LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + underscore(sanitizeName("call_" + operationId))); + operationId = "call_" + operationId; + } + + return underscore(operationId); + } + + @Override + public Map postProcessOperations(Map objs) { + Map operations = (Map) objs.get("operations"); + List os = (List) operations.get("operation"); + List newOs = new ArrayList(); + Pattern pattern = Pattern.compile("\\{([^\\}]+)\\}"); + for (CodegenOperation o : os) { + // force http method to lower case + o.httpMethod = o.httpMethod.toLowerCase(); + + if (o.isListContainer) { + o.returnType = "[" + o.returnBaseType + "]"; + } + + ArrayList pathTemplateNames = new ArrayList(); + Matcher matcher = pattern.matcher(o.path); + StringBuffer buffer = new StringBuffer(); + while (matcher.find()) { + String pathTemplateName = matcher.group(1); + matcher.appendReplacement(buffer, "\", " + camelize(pathTemplateName) + ", \""); + pathTemplateNames.add(pathTemplateName); + } + matcher.appendTail(buffer); + + ExtendedCodegenOperation eco = new ExtendedCodegenOperation(o); + if (buffer.toString().isEmpty()) { + eco.setReplacedPathName(o.path); + } else { + eco.setReplacedPathName(buffer.toString()); + } + eco.setPathTemplateNames(pathTemplateNames); + newOs.add(eco); + } + operations.put("operation", newOs); + return objs; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + public void setPackageVersion(String packageVersion) { + this.packageVersion = packageVersion; + } + + String length(Object os) { + int l = 1; + for (CodegenParameter o : ((ExtendedCodegenOperation) os).allParams) { + CodegenParameter q = (CodegenParameter) o; + if (q.required) + l++; + } + + return Integer.toString(l); + } + + int lengthRequired(List allParams) { + int l = 0; + for (CodegenParameter o : allParams) { + CodegenParameter q = (CodegenParameter) o; + if (q.required || q.isBodyParam) + l++; + } + + return l; + } + + @Override + public String escapeQuotationMark(String input) { + // remove " to avoid code injection + return input.replace("\"", ""); + } + + @Override + public String escapeUnsafeCharacters(String input) { + return input.replace("*/", "*_/").replace("/*", "/_*"); + } + + class ExtendedCodegenOperation extends CodegenOperation { + private List pathTemplateNames = new ArrayList(); + private String replacedPathName; + String arityRequired; + String arityOptional; + + public ExtendedCodegenOperation(CodegenOperation o) { + super(); + + // Copy all fields of CodegenOperation + this.responseHeaders.addAll(o.responseHeaders); + this.hasAuthMethods = o.hasAuthMethods; + this.hasConsumes = o.hasConsumes; + this.hasProduces = o.hasProduces; + this.hasParams = o.hasParams; + this.hasOptionalParams = o.hasOptionalParams; + this.returnTypeIsPrimitive = o.returnTypeIsPrimitive; + this.returnSimpleType = o.returnSimpleType; + this.subresourceOperation = o.subresourceOperation; + this.isMapContainer = o.isMapContainer; + this.isListContainer = o.isListContainer; + this.isMultipart = o.isMultipart; + this.hasMore = o.hasMore; + this.isResponseBinary = o.isResponseBinary; + this.hasReference = o.hasReference; + this.isRestfulIndex = o.isRestfulIndex; + this.isRestfulShow = o.isRestfulShow; + this.isRestfulCreate = o.isRestfulCreate; + this.isRestfulUpdate = o.isRestfulUpdate; + this.isRestfulDestroy = o.isRestfulDestroy; + this.isRestful = o.isRestful; + this.path = o.path; + this.operationId = o.operationId; + this.returnType = o.returnType; + this.httpMethod = o.httpMethod; + this.returnBaseType = o.returnBaseType; + this.returnContainer = o.returnContainer; + this.summary = o.summary; + this.unescapedNotes = o.unescapedNotes; + this.notes = o.notes; + this.baseName = o.baseName; + this.defaultResponse = o.defaultResponse; + this.discriminator = o.discriminator; + this.consumes = o.consumes; + this.produces = o.produces; + this.bodyParam = o.bodyParam; + this.allParams = o.allParams; + this.arityRequired = Integer.toString(lengthRequired(o.allParams)); + this.arityOptional = Integer.toString(lengthRequired(o.allParams)+1); + this.bodyParams = o.bodyParams; + this.pathParams = o.pathParams; + this.queryParams = o.queryParams; + this.headerParams = o.headerParams; + this.formParams = o.formParams; + this.authMethods = o.authMethods; + this.tags = o.tags; + this.responses = o.responses; + this.imports = o.imports; + this.examples = o.examples; + this.externalDocs = o.externalDocs; + this.vendorExtensions = o.vendorExtensions; + this.nickname = o.nickname; + this.operationIdLowerCase = o.operationIdLowerCase; + this.operationIdCamelCase = o.operationIdCamelCase; + } + + public List getPathTemplateNames() { + return pathTemplateNames; + } + + public void setPathTemplateNames(List pathTemplateNames) { + this.pathTemplateNames = pathTemplateNames; + } + + public String getReplacedPathName() { + return replacedPathName; + } + + public void setReplacedPathName(String replacedPathName) { + this.replacedPathName = replacedPathName; + } + } +} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ErlangServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ErlangServerCodegen.java index ebb8260d6f1..c3e32e30882 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ErlangServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ErlangServerCodegen.java @@ -177,7 +177,7 @@ public String toApiName(String name) { /** * 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 reseved words + * those terms here. This logic is only called if a variable matches the reserved words * * @return the escaped term */ diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/FinchServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/FinchServerCodegen.java index f807325c144..aa02184d833 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/FinchServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/FinchServerCodegen.java @@ -1,6 +1,5 @@ package io.swagger.codegen.languages; -import com.google.common.base.Strings; import io.swagger.codegen.*; import io.swagger.models.Model; import io.swagger.models.properties.ArrayProperty; @@ -81,7 +80,7 @@ public FinchServerCodegen() { typeMapping.put("long", "Long"); typeMapping.put("double", "Double"); typeMapping.put("number", "BigDecimal"); - typeMapping.put("date-time", "LocalDateTime"); + typeMapping.put("date-time", "ZonedDateTime"); typeMapping.put("date", "LocalDateTime"); typeMapping.put("file", "File"); typeMapping.put("array", "Seq"); @@ -90,7 +89,7 @@ public FinchServerCodegen() { typeMapping.put("object", "Object"); typeMapping.put("binary", "Array[Byte]"); typeMapping.put("Date", "LocalDateTime"); - typeMapping.put("DateTime", "LocalDateTime"); + typeMapping.put("DateTime", "ZonedDateTime"); additionalProperties.put("modelPackage", modelPackage()); additionalProperties.put("apiPackage", apiPackage()); @@ -154,6 +153,7 @@ public FinchServerCodegen() { importMapping.put("LocalDateTime", "java.time.LocalDateTime"); importMapping.put("LocalDate", "java.time.LocalDate"); importMapping.put("LocalTime", "java.time.LocalTime"); + importMapping.put("ZonedDateTime", "java.time.ZonedDateTime"); cliOptions.clear(); cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Finch package name (e.g. io.swagger).") @@ -206,68 +206,31 @@ public CodegenModel fromModel(String name, Model model, Map allDe return codegenModel; } + + + @Override public Map postProcessOperations(Map objs) { Map operations = (Map) objs.get("operations"); List operationList = (List) operations.get("operation"); for (CodegenOperation op : operationList) { - op.httpMethod = op.httpMethod.toLowerCase(); - - String path = new String(op.path); - // remove first / - if (path.startsWith("/")) { - path = path.substring(1); - } - // remove last / - if (path.endsWith("/")) { - path = path.substring(0, path.length()-1); - } - - String[] items = path.split("/", -1); - String scalaPath = ""; - int pathParamIndex = 0; - for (int i = 0; i < items.length; ++i) { - if (items[i].matches("^\\{(.*)\\}$")) { // wrap in {} - // find the datatype of the parameter - final CodegenParameter cp = op.pathParams.get(pathParamIndex); + // Converts GET /foo/bar => get("foo" :: "bar") + generateScalaPath(op); - // TODO: Handle non-primitives… - scalaPath = scalaPath + cp.dataType.toLowerCase(); + // Generates e.g. uuid :: header("boo") :: params("baa") under key "x-codegen-pathParams" + // Generates e.g. (id: UUID, headerBoo: String, paramBaa: String) under key "x-codegen-typedInputParams" + // Generates e.g. (id, headerBoo, paramBaa) under key "x-codegen-inputParams" + generateInputParameters(op); - pathParamIndex++; - } else { - scalaPath = scalaPath + "\"" + items[i] + "\""; - } + //Generate Auth parameters using security: definition + //Results in header("apiKey") or param("apiKey") + authParameters(op); - if (i != items.length -1) { - scalaPath = scalaPath + " :: "; - } - } - - for (CodegenParameter p : op.allParams) { - // TODO: This hacky, should be converted to mappings if possible to keep it clean. - // This could also be done using template imports - if(Boolean.TRUE.equals(p.isPrimitiveType)) { - p.vendorExtensions.put("x-codegen-normalized-path-type", p.dataType.toLowerCase()); - p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType); - } else if(Boolean.TRUE.equals(p.isBodyParam)) { - p.vendorExtensions.put("x-codegen-normalized-path-type", "jsonBody["+ p.dataType + "]"); - p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType); - } else if(Boolean.TRUE.equals(p.isContainer) || Boolean.TRUE.equals(p.isListContainer)) { - p.vendorExtensions.put("x-codegen-normalized-path-type", "params(\""+ p.paramName + "\")"); - p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType.replaceAll("^[^\\[]+", "Seq")); - } else if(Boolean.TRUE.equals(p.isFile)) { - p.vendorExtensions.put("x-codegen-normalized-path-type", "fileUpload(\""+ p.paramName + "\")"); - p.vendorExtensions.put("x-codegen-normalized-input-type", "FileUpload"); - } else { - p.vendorExtensions.put("x-codegen-normalized-path-type", p.dataType); - p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType); - } - } - - op.vendorExtensions.put("x-codegen-path", scalaPath); + //Concatenates all parameters + concatParameters(op); } + return objs; } @@ -317,4 +280,172 @@ public void setPackageName(String packageName) { this.packageName = packageName; } + + /** + * + * @param prim + * @param isRequired + * @param canBeOptional + * @return + */ + private String toPrimitive(String prim, Boolean isRequired, Boolean canBeOptional) { + + String converter = ".map(_.to" + prim + ")"; + return (canBeOptional ? (isRequired ? converter : ".map(_" + converter +")") : ""); + } + + //All path parameters are String initially, for primitives these need to be converted + private String toPathParameter(CodegenParameter p, String paramType, Boolean canBeOptional ) { + + Boolean isNotAString = !p.dataType.equals("String"); + + return paramType + (canBeOptional && !p.required ? "Option" : "") + "(\""+ p.baseName + "\")" + (isNotAString ? toPrimitive(p.dataType,p.required,canBeOptional) : "") ; + } + + private String toInputParameter(CodegenParameter p){ + return (p.required ? "" : "Option[")+p.dataType+(p.required ? "" : "]"); + } + + private String concat(String original, String addition, String op) { + return original + (original.isEmpty() ? "" : (addition.isEmpty() ? "" : op)) + addition; + } + + // a, b + private String csvConcat(String original, String addition) { + return concat(original, addition,", "); + } + // a :: b + private String colConcat(String original, String addition) { + return concat(original, addition," :: "); + } + + private void authParameters(CodegenOperation op) { + + String authParams = ""; + String authInputParams = ""; + String typedAuthInputParams = ""; + //Append apikey security to path params and create input parameters for functions + if(op.authMethods != null){ + + for(CodegenSecurity s : op.authMethods) { + if(s.isApiKey && s.isKeyInHeader){ + authParams = colConcat(authParams, "header(\""+ s.keyParamName + "\")"); + } else if(s.isApiKey && s.isKeyInQuery){ + authParams = colConcat(authParams, "param(\""+ s.keyParamName + "\")"); + } + if(s.isApiKey) { + typedAuthInputParams = csvConcat(typedAuthInputParams, "authParam"+ s.name + ": String"); + authInputParams = csvConcat(authInputParams,"authParam"+ s.name); + } + } + } + + op.vendorExtensions.put("x-codegen-authParams", authParams); + op.vendorExtensions.put("x-codegen-authInputParams", authInputParams); + op.vendorExtensions.put("x-codegen-typedAuthInputParams", typedAuthInputParams); + + } + + private void generateScalaPath(CodegenOperation op) { + op.httpMethod = op.httpMethod.toLowerCase(); + + String path = new String(op.path); + + // remove first / + if (path.startsWith("/")) { + path = path.substring(1); + } + + // remove last / + if (path.endsWith("/")) { + path = path.substring(0, path.length()-1); + } + + String[] items = path.split("/", -1); + String scalaPath = ""; + Integer pathParamIndex = 0; + + for (int i = 0; i < items.length; ++i) { + + if (items[i].matches("^\\{(.*)\\}$")) { // wrap in {} + // find the datatype of the parameter + final CodegenParameter cp = op.pathParams.get(pathParamIndex); + + // TODO: Handle non-primitives… + scalaPath = colConcat(scalaPath, cp.dataType.toLowerCase()); + + pathParamIndex++; + } else { + scalaPath = colConcat(scalaPath, "\"" + items[i] + "\""); + } + } + + op.vendorExtensions.put("x-codegen-path", scalaPath); + + } + + + private void concatParameters(CodegenOperation op) { + + String path = colConcat(colConcat(op.vendorExtensions.get("x-codegen-path").toString(),op.vendorExtensions.get("x-codegen-pathParams").toString()), op.vendorExtensions.get("x-codegen-authParams").toString()); + String parameters = csvConcat(op.vendorExtensions.get("x-codegen-inputParams").toString(), op.vendorExtensions.get("x-codegen-authInputParams").toString()); + String typedParameters = csvConcat(op.vendorExtensions.get("x-codegen-typedInputParams").toString(), op.vendorExtensions.get("x-codegen-typedAuthInputParams").toString()); + + // The input parameters for functions + op.vendorExtensions.put("x-codegen-paths",path); + op.vendorExtensions.put("x-codegen-params", parameters); + op.vendorExtensions.put("x-codegen-typedParams", typedParameters); + + } + + + private void generateInputParameters(CodegenOperation op) { + + String inputParams = ""; + String typedInputParams = ""; + String pathParams = ""; + + for (CodegenParameter p : op.allParams) { + // TODO: This hacky, should be converted to mappings if possible to keep it clean. + // This could also be done using template imports + + if(p.isBodyParam) { + p.vendorExtensions.put("x-codegen-normalized-path-type", "jsonBody["+ p.dataType + "]"); + p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType); + } else if(p.isContainer || p.isListContainer) { + p.vendorExtensions.put("x-codegen-normalized-path-type", toPathParameter(p,"params", false)); + p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType.replaceAll("^[^\\[]+", "Seq")); + } else if(p.isQueryParam) { + p.vendorExtensions.put("x-codegen-normalized-path-type", toPathParameter(p, "param",true)); + p.vendorExtensions.put("x-codegen-normalized-input-type", toInputParameter(p)); + } else if(p.isHeaderParam) { + p.vendorExtensions.put("x-codegen-normalized-path-type", toPathParameter(p,"header", true)); + p.vendorExtensions.put("x-codegen-normalized-input-type", toInputParameter(p)); + } else if(p.isFile) { + p.vendorExtensions.put("x-codegen-normalized-path-type", "fileUpload(\""+ p.paramName + "\")"); + p.vendorExtensions.put("x-codegen-normalized-input-type", "FileUpload"); + } else if(p.isPrimitiveType && !p.isPathParam) { + p.vendorExtensions.put("x-codegen-normalized-path-type", p.dataType.toLowerCase()); + p.vendorExtensions.put("x-codegen-normalized-input-type", toInputParameter(p)); + } else { + //Path paremeters are handled in generateScalaPath() + p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType); + } + if(p.vendorExtensions.get("x-codegen-normalized-path-type") != null){ + pathParams = colConcat(pathParams , p.vendorExtensions.get("x-codegen-normalized-path-type").toString()); + } + inputParams = csvConcat(inputParams, p.paramName); + typedInputParams = csvConcat(typedInputParams , p.paramName + ": " + p.vendorExtensions.get("x-codegen-normalized-input-type")); + + } + + // All body, path, query and header parameters + op.vendorExtensions.put("x-codegen-pathParams", pathParams); + + // The input parameters for functions + op.vendorExtensions.put("x-codegen-inputParams", inputParams); + op.vendorExtensions.put("x-codegen-typedInputParams", typedInputParams); + + } + } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/FlaskConnexionCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/FlaskConnexionCodegen.java index bf1d638bc5e..ebcfedbb7ce 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/FlaskConnexionCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/FlaskConnexionCodegen.java @@ -69,7 +69,7 @@ public FlaskConnexionCodegen() { typeMapping.put("file", "file"); typeMapping.put("UUID", "str"); - // from https://docs.python.org/release/2.5.4/ref/keywords.html + // from https://docs.python.org/3/reference/lexical_analysis.html#keywords setReservedWordsLowerCase( Arrays.asList( // @property @@ -78,7 +78,7 @@ public FlaskConnexionCodegen() { "and", "del", "from", "not", "while", "as", "elif", "global", "or", "with", "assert", "else", "if", "pass", "yield", "break", "except", "import", "print", "class", "exec", "in", "raise", "continue", "finally", "is", - "return", "def", "for", "lambda", "try", "self", "None")); + "return", "def", "for", "lambda", "try", "self", "None", "True", "False", "nonlocal")); // set the output folder here outputFolder = "generated-code/connexion"; @@ -93,14 +93,6 @@ public FlaskConnexionCodegen() { */ embeddedTemplateDir = templateDir = "flaskConnexion"; - // from https://docs.python.org/release/2.5.4/ref/keywords.html - setReservedWordsLowerCase( - Arrays.asList( - "and", "del", "from", "not", "while", "as", "elif", "global", "or", "with", - "assert", "else", "if", "pass", "yield", "break", "except", "import", - "print", "class", "exec", "in", "raise", "continue", "finally", "is", - "return", "def", "for", "lambda", "try")); - /* * Additional Properties. These values can be passed to the templates and * are available in models, apis, and supporting files @@ -257,7 +249,7 @@ public String toApiTestFilename(String name) { /** * 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 reseved words + * those terms here. This logic is only called if a variable matches the reserved words * * @return the escaped term */ @@ -481,13 +473,18 @@ public String toModelName(String name) { @Override public String toOperationId(String operationId) { - operationId = super.toOperationId(operationId); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. - // Use the part after the last dot, e.g. - // controllers.defaultController.addPet => addPet - operationId = operationId.replaceAll(".*\\.", ""); - // Need to underscore it since it has been processed via removeNonNameElementToCamelCase, e.g. - // addPet => add_pet - return underscore(operationId); + // throw exception if method name is empty (should not occur as an auto-generated method name will be used) + if (StringUtils.isEmpty(operationId)) { + throw new RuntimeException("Empty method name (operationId) not allowed"); + } + + // method name cannot use reserved keyword, e.g. return + if (isReservedWord(operationId)) { + LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + underscore(sanitizeName("call_" + operationId))); + operationId = "call_" + operationId; + } + + return underscore(sanitizeName(operationId)); } /** @@ -670,6 +667,12 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert postProcessPattern(property.pattern, property.vendorExtensions); } + @Override + public Map postProcessModels(Map objs) { + // process enum in models + return postProcessModelsEnum(objs); + } + @Override public void postProcessParameter(CodegenParameter parameter){ postProcessPattern(parameter.pattern, parameter.vendorExtensions); diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/GoClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/GoClientCodegen.java index 456d49ee3b9..5ca11adac5f 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/GoClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/GoClientCodegen.java @@ -97,7 +97,7 @@ public GoClientCodegen() { typeMapping.put("boolean", "bool"); typeMapping.put("string", "string"); typeMapping.put("UUID", "string"); - typeMapping.put("date", "time.Time"); + typeMapping.put("date", "string"); typeMapping.put("DateTime", "time.Time"); typeMapping.put("password", "string"); typeMapping.put("File", "*os.File"); @@ -151,7 +151,7 @@ public void processOpts() { additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName); additionalProperties.put(CodegenConstants.PACKAGE_VERSION, packageVersion); - + additionalProperties.put("apiDocPath", apiDocPath); additionalProperties.put("modelDocPath", modelDocPath); @@ -180,10 +180,10 @@ public String escapeReservedWord(String name) // - XName // - X_Name // ... or maybe a suffix? - // - Name_ ... think this will work. + // - Name_ ... think this will work. if(this.reservedWordsMappings().containsKey(name)) { return this.reservedWordsMappings().get(name); - } + } return camelize(name) + '_'; } @@ -274,8 +274,6 @@ public String toApiFilename(String name) { return underscore(name) + "_api"; } - - /** * Overrides postProcessParameter to add a vendor extension "x-exportParamName". * This is useful when paramName starts with a lowercase letter, but we need that @@ -303,7 +301,6 @@ public void postProcessParameter(CodegenParameter parameter){ parameter.vendorExtensions.put("x-exportParamName", sb.toString()); } - @Override public String apiDocFileFolder() { return (outputFolder + "/" + apiDocPath).replace('/', File.separatorChar); @@ -406,9 +403,10 @@ public Map postProcessOperations(Map objs) { if (_import.startsWith(apiPackage())) iterator.remove(); } - // if the return type is not primitive, import encoding/json + + // if their is a return type, import encoding/json for (CodegenOperation operation : operations) { - if(operation.returnBaseType != null && needToImport(operation.returnBaseType)) { + if(operation.returnBaseType != null ) { imports.add(createMapping("import", "encoding/json")); break; //just need to import once } @@ -422,7 +420,6 @@ public Map postProcessOperations(Map objs) { } } - // recursively add import for mapping one type to multiple imports List> recursiveImports = (List>) objs.get("imports"); if (recursiveImports == null) @@ -468,7 +465,7 @@ public Map postProcessModels(Map objs) { } } - return objs; + return postProcessModelsEnum(objs); } @Override @@ -502,4 +499,65 @@ public Map createMapping(String key, String value){ return customImport; } + + + @Override + public String toEnumValue(String value, String datatype) { + if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { + return value; + } else { + return escapeText(value); + } + } + + @Override + public String toEnumDefaultValue(String value, String datatype) { + return datatype + "_" + value; + } + + @Override + public String toEnumVarName(String name, String datatype) { + if (name.length() == 0) { + return "EMPTY"; + } + + // number + if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { + String varName = name; + varName = varName.replaceAll("-", "MINUS_"); + varName = varName.replaceAll("\\+", "PLUS_"); + varName = varName.replaceAll("\\.", "_DOT_"); + return varName; + } + + // for symbol, e.g. $, # + if (getSymbolName(name) != null) { + return getSymbolName(name).toUpperCase(); + } + + // string + String enumName = sanitizeName(underscore(name).toUpperCase()); + enumName = enumName.replaceFirst("^_", ""); + enumName = enumName.replaceFirst("_$", ""); + + if (isReservedWord(enumName) || enumName.matches("\\d.*")) { // reserved word or starts with number + return escapeReservedWord(enumName); + } else { + return enumName; + } + } + + @Override + public String toEnumName(CodegenProperty property) { + String enumName = underscore(toModelName(property.name)).toUpperCase(); + + // remove [] for array or map of enum + enumName = enumName.replace("[]", ""); + + if (enumName.matches("\\d.*")) { // starts with number + return "_" + enumName; + } else { + return enumName; + } + } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/GoServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/GoServerCodegen.java index c447b6561a4..8fd49394be4 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/GoServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/GoServerCodegen.java @@ -11,6 +11,11 @@ import io.swagger.codegen.*; import io.swagger.models.*; import io.swagger.util.Yaml; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.MapProperty; +import io.swagger.models.properties.Property; +import io.swagger.models.parameters.Parameter; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,7 +34,7 @@ public class GoServerCodegen extends DefaultCodegen implements CodegenConfig { protected int serverPort = 8080; protected String projectName = "swagger-server"; protected String apiPath = "go"; - + public GoServerCodegen() { super(); @@ -64,6 +69,11 @@ public GoServerCodegen() { */ setReservedWordsLowerCase( Arrays.asList( + // data type + "string", "bool", "uint", "uint8", "uint16", "uint32", "uint64", + "int", "int8", "int16", "int32", "int64", "float32", "float64", + "complex64", "complex128", "rune", "byte", "uintptr", + "break", "default", "func", "interface", "select", "case", "defer", "go", "map", "struct", "chan", "else", "goto", "package", "switch", @@ -109,7 +119,8 @@ public GoServerCodegen() { typeMapping.put("double", "float64"); typeMapping.put("boolean", "bool"); typeMapping.put("string", "string"); - typeMapping.put("date", "time.Time"); + typeMapping.put("UUID", "string"); + typeMapping.put("date", "string"); typeMapping.put("DateTime", "time.Time"); typeMapping.put("password", "string"); typeMapping.put("File", "*os.File"); @@ -118,6 +129,7 @@ public GoServerCodegen() { // the correct solution is to use []byte typeMapping.put("binary", "string"); typeMapping.put("ByteArray", "string"); + typeMapping.put("object", "interface{}"); typeMapping.put("UUID", "string"); importMapping = new HashMap(); @@ -149,8 +161,8 @@ public GoServerCodegen() { ); supportingFiles.add(new SupportingFile("main.mustache", "", "main.go")); supportingFiles.add(new SupportingFile("routers.mustache", apiPath, "routers.go")); - supportingFiles.add(new SupportingFile("logger.mustache", apiPath, "logger.go")); - supportingFiles.add(new SupportingFile("app.mustache", apiPath, "app.yaml")); + supportingFiles.add(new SupportingFile("logger.mustache", apiPath, "logger.go")); + supportingFiles.add(new SupportingFile("app.mustache", apiPath, "app.yaml")); writeOptional(outputFolder, new SupportingFile("README.mustache", apiPath, "README.md")); } @@ -203,7 +215,7 @@ public String toApiName(String name) { /** * 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 reseved words + * those terms here. This logic is only called if a variable matches the reserved words * * @return the escaped term */ @@ -211,7 +223,7 @@ public String toApiName(String name) { public String escapeReservedWord(String name) { if(this.reservedWordsMappings().containsKey(name)) { return this.reservedWordsMappings().get(name); - } + } return "_" + name; // add an underscore to the name } @@ -245,11 +257,11 @@ public String toOperationId(String operationId) { @Override public String toModelFilename(String name) { if (!StringUtils.isEmpty(modelNamePrefix)) { - name = modelNamePrefix + "_" + name; + name = modelNamePrefix + name; } if (!StringUtils.isEmpty(modelNameSuffix)) { - name = name + "_" + modelNameSuffix; + name = name + modelNameSuffix; } name = sanitizeName(name); @@ -260,7 +272,54 @@ public String toModelFilename(String name) { name = "model_" + name; // e.g. return => ModelReturn (after camelize) } - return underscore(name); + return camelize(name); + } + + @Override + public String getTypeDeclaration(Property p) { + if(p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + Property inner = ap.getItems(); + return "[]" + getTypeDeclaration(inner); + } + else if (p instanceof MapProperty) { + MapProperty mp = (MapProperty) p; + Property inner = mp.getAdditionalProperties(); + + return getSwaggerType(p) + "[string]" + getTypeDeclaration(inner); + } + //return super.getTypeDeclaration(p); + + // Not using the supertype invocation, because we want to UpperCamelize + // the type. + String swaggerType = getSwaggerType(p); + if (typeMapping.containsKey(swaggerType)) { + return typeMapping.get(swaggerType); + } + + if(typeMapping.containsValue(swaggerType)) { + return swaggerType; + } + + if(languageSpecificPrimitives.contains(swaggerType)) { + return swaggerType; + } + + return toModelName(swaggerType); + } + + @Override + public String getSwaggerType(Property p) { + String swaggerType = super.getSwaggerType(p); + String type = null; + if(typeMapping.containsKey(swaggerType)) { + type = typeMapping.get(swaggerType); + if(languageSpecificPrimitives.contains(type)) + return (type); + } + else + type = swaggerType; + return type; } @Override @@ -283,4 +342,69 @@ public String escapeUnsafeCharacters(String input) { return input.replace("*/", "*_/").replace("/*", "/_*"); } + @Override + public String toEnumValue(String value, String datatype) { + if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { + return value; + } else { + return "\'" + escapeText(value) + "\'"; + } + } + + @Override + public String toEnumDefaultValue(String value, String datatype) { + return datatype + "_" + value; + } + + @Override + public String toEnumVarName(String name, String datatype) { + if (name.length() == 0) { + return "EMPTY"; + } + + // number + if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { + String varName = name; + varName = varName.replaceAll("-", "MINUS_"); + varName = varName.replaceAll("\\+", "PLUS_"); + varName = varName.replaceAll("\\.", "_DOT_"); + return varName; + } + + // for symbol, e.g. $, # + if (getSymbolName(name) != null) { + return getSymbolName(name).toUpperCase(); + } + + // string + String enumName = sanitizeName(underscore(name).toUpperCase()); + enumName = enumName.replaceFirst("^_", ""); + enumName = enumName.replaceFirst("_$", ""); + + if (isReservedWord(enumName) || enumName.matches("\\d.*")) { // reserved word or starts with number + return escapeReservedWord(enumName); + } else { + return enumName; + } + } + + @Override + public String toEnumName(CodegenProperty property) { + String enumName = underscore(toModelName(property.name)).toUpperCase(); + + // remove [] for array or map of enum + enumName = enumName.replace("[]", ""); + + if (enumName.matches("\\d.*")) { // starts with number + return "_" + enumName; + } else { + return enumName; + } + } + + @Override + public Map postProcessModels(Map objs) { + // process enum in models + return postProcessModelsEnum(objs); + } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/HaskellHttpClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/HaskellHttpClientCodegen.java new file mode 100644 index 00000000000..f7a4da78871 --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/HaskellHttpClientCodegen.java @@ -0,0 +1,1194 @@ +package io.swagger.codegen.languages; + +import io.swagger.codegen.*; +import io.swagger.models.Model; +import io.swagger.models.Operation; +import io.swagger.models.Swagger; +import io.swagger.models.properties.*; + +import java.util.*; +import java.util.regex.Pattern; + +import io.swagger.models.auth.SecuritySchemeDefinition; +import io.swagger.codegen.CliOption; +import io.swagger.codegen.CodegenConstants; +import io.swagger.codegen.CodegenModel; +import io.swagger.codegen.CodegenOperation; +import io.swagger.codegen.CodegenProperty; +import io.swagger.codegen.SupportingFile; +import io.swagger.util.Yaml; +import com.fasterxml.jackson.core.JsonProcessingException; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.regex.Matcher; + +public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenConfig { + + // source folder where to write the files + protected String sourceFolder = "src"; + + protected String artifactId = "swagger-haskell-http-client"; + protected String artifactVersion = "1.0.0"; + + protected String defaultDateFormat = "%Y-%m-%d"; + + protected Boolean useMonadLogger = false; + protected Boolean genEnums = true; + + // CLI PROPS + public static final String PROP_ALLOW_FROMJSON_NULLS = "allowFromJsonNulls"; + public static final String PROP_ALLOW_TOJSON_NULLS = "allowToJsonNulls"; + public static final String PROP_DATETIME_FORMAT = "dateTimeFormat"; + public static final String PROP_DATE_FORMAT = "dateFormat"; + public static final String PROP_GENERATE_ENUMS = "generateEnums"; + public static final String PROP_GENERATE_FORM_URLENCODED_INSTANCES = "generateFormUrlEncodedInstances"; + public static final String PROP_GENERATE_LENSES = "generateLenses"; + public static final String PROP_GENERATE_MODEL_CONSTRUCTORS = "generateModelConstructors"; + public static final String PROP_INLINE_MIME_TYPES = "inlineMimeTypes"; + public static final String PROP_MODEL_DERIVING = "modelDeriving"; + public static final String PROP_STRICT_FIELDS = "strictFields"; + public static final String PROP_USE_MONAD_LOGGER = "useMonadLogger"; + + // protected String MODEL_IMPORTS = "modelImports"; + // protected String MODEL_EXTENSIONS = "modelExtensions"; + + private static final Pattern LEADING_UNDERSCORE = Pattern.compile("^_+"); + + static final String MEDIA_TYPE = "mediaType"; + static final String MIME_NO_CONTENT = "MimeNoContent"; + + // vendor extensions + static final String X_ALL_UNIQUE_PARAMS = "x-allUniqueParams"; + static final String X_COLLECTION_FORMAT = "x-collectionFormat"; + static final String X_HADDOCK_PATH = "x-haddockPath"; + static final String X_HAS_BODY_OR_FORM_PARAM = "x-hasBodyOrFormParam"; + static final String X_HAS_ENUM_SECTION = "x-hasEnumSection"; + static final String X_HAS_MIME_FORM_URL_ENCODED = "x-hasMimeFormUrlEncoded"; + static final String X_HAS_NEW_TAG = "x-hasNewTag"; + static final String X_HAS_OPTIONAL_PARAMS = "x-hasOptionalParams"; + static final String X_HAS_UNKNOWN_MIME_TYPES = "x-hasUnknownMimeTypes"; + static final String X_HAS_UNKNOWN_RETURN = "x-hasUnknownReturn"; + static final String X_INLINE_CONTENT_TYPE = "x-inlineContentType"; + static final String X_INLINE_ACCEPT = "x-inlineAccept"; + static final String X_IS_BODY_OR_FORM_PARAM = "x-isBodyOrFormParam"; + static final String X_IS_BODY_PARAM = "x-isBodyParam"; + static final String X_MEDIA_DATA_TYPE = "x-mediaDataType"; + static final String X_DATA_TYPE = "x-dataType"; + static final String X_ENUM_VALUES = "x-enumValues"; + static final String X_MEDIA_IS_JSON = "x-mediaIsJson"; + static final String X_MIME_TYPES = "x-mimeTypes"; + static final String X_OPERATION_TYPE = "x-operationType"; + static final String X_PARAM_NAME_TYPE = "x-paramNameType"; + static final String X_PATH = "x-path"; + static final String X_RETURN_TYPE = "x-returnType"; + static final String X_STRICT_FIELDS = "x-strictFields"; + static final String X_UNKNOWN_MIME_TYPES = "x-unknownMimeTypes"; + static final String X_USE_MONAD_LOGGER = "x-useMonadLogger"; + static final String X_NEWTYPE = "x-newtype"; + static final String X_ENUM = "x-enum"; + + + protected ArrayList> unknownMimeTypes = new ArrayList<>(); + protected Map> uniqueParamNameTypes = new HashMap<>(); + protected Map> modelMimeTypes = new HashMap<>(); + protected Map knownMimeDataTypes = new HashMap<>(); + protected Set typeNames = new HashSet(); + + public CodegenType getTag() { + return CodegenType.CLIENT; + } + public String getName() { + return "haskell-http-client"; + } + public String getHelp() { + return "Generates a Haskell http-client library."; + } + + + final private static Pattern JSON_MIME_PATTERN = Pattern.compile("(?i)application/.*json(;.*)?"); + + public HaskellHttpClientCodegen() { + super(); + + // override the mapping to keep the original mapping in Haskell + specialCharReplacements.put("-", "Dash"); + specialCharReplacements.put(">", "GreaterThan"); + specialCharReplacements.put("<", "LessThan"); + + // backslash and double quote need double the escapement for both Java and Haskell + specialCharReplacements.remove("\\"); + specialCharReplacements.remove("\""); + specialCharReplacements.put("\\\\", "Back_Slash"); + specialCharReplacements.put("\\\"", "Double_Quote"); + + // set the output folder here + outputFolder = "generated-code/haskell-http-client"; + + embeddedTemplateDir = templateDir = "haskell-http-client"; + apiPackage = "API"; + modelPackage = "Model"; + + // Haskell keywords and reserved function names, taken mostly from https://wiki.haskell.org/Keywords + setReservedWordsLowerCase( + Arrays.asList( + // Keywords + "as", "case", "of", + "class", "data", "family", + "default", "deriving", + "do", "forall", "foreign", "hiding", + "if", "then", "else", + "import", "infix", "infixl", "infixr", + "instance", "let", "in", + "mdo", "module", "newtype", + "proc", "qualified", "rec", + "type", "where", "pure", "return", + "Accept", "ContentType" + ) + ); + + additionalProperties.put("artifactId", artifactId); + additionalProperties.put("artifactVersion", artifactVersion); + + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("stack.mustache", "", "stack.yaml")); + supportingFiles.add(new SupportingFile("Setup.mustache", "", "Setup.hs")); + supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); + supportingFiles.add(new SupportingFile(".travis.yml", "", ".travis.yml")); + supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); + + supportingFiles.add(new SupportingFile("tests/ApproxEq.mustache", "tests", "ApproxEq.hs")); + supportingFiles.add(new SupportingFile("tests/Instances.mustache", "tests", "Instances.hs")); + supportingFiles.add(new SupportingFile("tests/PropMime.mustache", "tests", "PropMime.hs")); + supportingFiles.add(new SupportingFile("tests/Test.mustache", "tests", "Test.hs")); + + languageSpecificPrimitives = new HashSet( + Arrays.asList( + "Bool", + "String", + "Int", + "Integer", + "Float", + "Char", + "Double", + "List", + "FilePath", + "Text" + ) + ); + + typeMapping.clear(); + // prim + typeMapping.put("boolean", "Bool"); + typeMapping.put("int", "Int"); + typeMapping.put("long", "Integer"); + typeMapping.put("short", "Int"); + typeMapping.put("char", "Char"); + typeMapping.put("float", "Float"); + typeMapping.put("double", "Double"); + typeMapping.put("number", "Double"); + typeMapping.put("integer", "Int"); + typeMapping.put("file", "FilePath"); + // lib + typeMapping.put("string", "Text"); + typeMapping.put("UUID", "Text"); + typeMapping.put("any", "A.Value"); + typeMapping.put("set", "Set.Set"); + // newtype + typeMapping.put("binary", "Binary"); + typeMapping.put("ByteArray", "ByteArray"); + typeMapping.put("date", "Date"); + typeMapping.put("DateTime", "DateTime"); + + knownMimeDataTypes.put("application/json", "MimeJSON"); + knownMimeDataTypes.put("application/xml", "MimeXML"); + knownMimeDataTypes.put("application/x-www-form-urlencoded", "MimeFormUrlEncoded"); + knownMimeDataTypes.put("application/octet-stream", "MimeOctetStream"); + knownMimeDataTypes.put("multipart/form-data", "MimeMultipartFormData"); + knownMimeDataTypes.put("text/plain", "MimePlainText"); + knownMimeDataTypes.put("*/*", "MimeAny"); + + importMapping.clear(); + + cliOptions.add(CliOption.newString(CodegenConstants.MODEL_PACKAGE, CodegenConstants.MODEL_PACKAGE_DESC)); + cliOptions.add(CliOption.newString(CodegenConstants.API_PACKAGE, CodegenConstants.API_PACKAGE_DESC)); + + cliOptions.add(CliOption.newBoolean(PROP_ALLOW_FROMJSON_NULLS, "allow JSON Null during model decoding from JSON").defaultValue(Boolean.TRUE.toString())); + cliOptions.add(CliOption.newBoolean(PROP_ALLOW_TOJSON_NULLS, "allow emitting JSON Null during model encoding to JSON").defaultValue(Boolean.FALSE.toString())); + cliOptions.add(CliOption.newBoolean(PROP_GENERATE_LENSES, "Generate Lens optics for Models").defaultValue(Boolean.TRUE.toString())); + cliOptions.add(CliOption.newBoolean(PROP_GENERATE_MODEL_CONSTRUCTORS, "Generate smart constructors (only supply required fields) for models").defaultValue(Boolean.TRUE.toString())); + cliOptions.add(CliOption.newBoolean(PROP_GENERATE_ENUMS, "Generate specific datatypes for swagger enums").defaultValue(Boolean.TRUE.toString())); + cliOptions.add(CliOption.newBoolean(PROP_GENERATE_FORM_URLENCODED_INSTANCES, "Generate FromForm/ToForm instances for models that are used by operations that produce or consume application/x-www-form-urlencoded").defaultValue(Boolean.TRUE.toString())); + cliOptions.add(CliOption.newBoolean(PROP_INLINE_MIME_TYPES, "Inline (hardcode) the content-type and accept parameters on operations, when there is only 1 option").defaultValue(Boolean.FALSE.toString())); + + + cliOptions.add(CliOption.newString(PROP_MODEL_DERIVING, "Additional classes to include in the deriving() clause of Models")); + cliOptions.add(CliOption.newBoolean(PROP_STRICT_FIELDS, "Add strictness annotations to all model fields").defaultValue((Boolean.TRUE.toString()))); + cliOptions.add(CliOption.newBoolean(PROP_USE_MONAD_LOGGER, "Use the monad-logger package to provide logging (if false, use the katip logging package)").defaultValue((Boolean.FALSE.toString()))); + + cliOptions.add(CliOption.newString(PROP_DATETIME_FORMAT, "format string used to parse/render a datetime")); + cliOptions.add(CliOption.newString(PROP_DATE_FORMAT, "format string used to parse/render a date").defaultValue(defaultDateFormat)); + + cliOptions.add(CliOption.newBoolean(CodegenConstants.HIDE_GENERATION_TIMESTAMP, "hides the timestamp when files were generated").defaultValue(Boolean.TRUE.toString())); + + } + + public void setAllowFromJsonNulls(Boolean value) { + additionalProperties.put(PROP_ALLOW_FROMJSON_NULLS, value); + } + + public void setAllowToJsonNulls(Boolean value) { + additionalProperties.put(PROP_ALLOW_TOJSON_NULLS, value); + } + + public void setGenerateModelConstructors(Boolean value) { + additionalProperties.put(PROP_GENERATE_MODEL_CONSTRUCTORS, value); + } + public void setGenerateEnums(Boolean value) { + additionalProperties.put(PROP_GENERATE_ENUMS, value); + genEnums = value; + } + public void setGenerateFormUrlEncodedInstances(Boolean value) { + additionalProperties.put(PROP_GENERATE_FORM_URLENCODED_INSTANCES, value); + } + public void setInlineMimeTypes(Boolean value) { + additionalProperties.put(PROP_INLINE_MIME_TYPES, value); + } + + public void setGenerateLenses(Boolean value) { + additionalProperties.put(PROP_GENERATE_LENSES, value); + } + + public void setModelDeriving(String value) { + if (StringUtils.isBlank(value)) { + additionalProperties.remove(PROP_MODEL_DERIVING); + } else { + additionalProperties.put(PROP_MODEL_DERIVING, StringUtils.join(value.split(" "), ",")); + } + } + + public void setDateTimeFormat(String value) { + if (StringUtils.isBlank(value)) { + additionalProperties.remove(PROP_DATETIME_FORMAT); + } else { + additionalProperties.put(PROP_DATETIME_FORMAT, value); + } + + } + + public void setDateFormat(String value) { + if (StringUtils.isBlank(value)) { + additionalProperties.remove(PROP_DATE_FORMAT); + } else { + additionalProperties.put(PROP_DATE_FORMAT, value); + } + } + + public void setStrictFields(Boolean value) { + additionalProperties.put(X_STRICT_FIELDS, value); + } + + public void setUseMonadLogger(Boolean value) { + additionalProperties.put(X_USE_MONAD_LOGGER, value); + this.useMonadLogger = value; + } + + @Override + public void processOpts() { + super.processOpts(); + // default HIDE_GENERATION_TIMESTAMP to true + if (additionalProperties.containsKey(CodegenConstants.HIDE_GENERATION_TIMESTAMP)) { + convertPropertyToBooleanAndWriteBack(CodegenConstants.HIDE_GENERATION_TIMESTAMP); + } else { + additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, true); + } + + if (additionalProperties.containsKey(PROP_ALLOW_FROMJSON_NULLS)) { + setAllowFromJsonNulls(convertPropertyToBoolean(PROP_ALLOW_FROMJSON_NULLS)); + } else { + setAllowFromJsonNulls(true); + } + + if (additionalProperties.containsKey(PROP_ALLOW_TOJSON_NULLS)) { + setAllowToJsonNulls(convertPropertyToBoolean(PROP_ALLOW_TOJSON_NULLS)); + } else { + setAllowToJsonNulls(false); + } + + if (additionalProperties.containsKey(PROP_GENERATE_MODEL_CONSTRUCTORS)) { + setGenerateModelConstructors(convertPropertyToBoolean(PROP_GENERATE_MODEL_CONSTRUCTORS)); + } else { + setGenerateModelConstructors(true); + } + + if (additionalProperties.containsKey(PROP_GENERATE_ENUMS)) { + setGenerateEnums(convertPropertyToBoolean(PROP_GENERATE_ENUMS)); + } else { + setGenerateEnums(true); + } + + if (additionalProperties.containsKey(PROP_GENERATE_FORM_URLENCODED_INSTANCES)) { + setGenerateFormUrlEncodedInstances(convertPropertyToBoolean(PROP_GENERATE_FORM_URLENCODED_INSTANCES)); + } else { + setGenerateFormUrlEncodedInstances(true); + } + + if (additionalProperties.containsKey(PROP_INLINE_MIME_TYPES)) { + setInlineMimeTypes(convertPropertyToBoolean(PROP_INLINE_MIME_TYPES)); + } else { + setInlineMimeTypes(false); + } + + if (additionalProperties.containsKey(PROP_GENERATE_LENSES)) { + setGenerateLenses(convertPropertyToBoolean(PROP_GENERATE_LENSES)); + } else { + setGenerateLenses(true); + } + + if (additionalProperties.containsKey(PROP_MODEL_DERIVING)) { + setModelDeriving(additionalProperties.get(PROP_MODEL_DERIVING).toString()); + } else { + setModelDeriving(""); + } + + if (additionalProperties.containsKey(PROP_DATETIME_FORMAT)) { + setDateTimeFormat(additionalProperties.get(PROP_DATETIME_FORMAT).toString()); + } else { + setDateTimeFormat(null); // default should be null + } + + if (additionalProperties.containsKey(PROP_DATE_FORMAT)) { + setDateFormat(additionalProperties.get(PROP_DATE_FORMAT).toString()); + } else { + setDateFormat(defaultDateFormat); + } + + if (additionalProperties.containsKey(PROP_STRICT_FIELDS)) { + setStrictFields(convertPropertyToBoolean(PROP_STRICT_FIELDS)); + } else { + setStrictFields(true); + } + if (additionalProperties.containsKey(PROP_USE_MONAD_LOGGER)) { + setUseMonadLogger(convertPropertyToBoolean(PROP_USE_MONAD_LOGGER)); + } else { + setUseMonadLogger(false); + } + + } + + @Override + public void preprocessSwagger(Swagger swagger) { + // From the title, compute a reasonable name for the package and the API + String title = swagger.getInfo().getTitle(); + + // Drop any API suffix + if (title == null) { + title = "Swagger"; + } else { + title = title.trim(); + if (title.toUpperCase().endsWith("API")) { + title = title.substring(0, title.length() - 3); + } + } + + String[] words = title.split(" "); + + // The package name is made by appending the lowercased words of the title interspersed with dashes + List wordsLower = new ArrayList(); + for (String word : words) { + wordsLower.add(word.toLowerCase()); + } + String cabalName = StringUtils.join(wordsLower, "-"); + String pathsName = StringUtils.join(wordsLower, "_"); + + // The API name is made by appending the capitalized words of the title + List wordsCaps = new ArrayList(); + for (String word : words) { + wordsCaps.add(firstLetterToUpper(word)); + } + String apiName = StringUtils.join(wordsCaps, ""); + + // Set the filenames to write for the API + + // root + supportingFiles.add(new SupportingFile("haskell-http-client.cabal.mustache", "", cabalName + ".cabal")); + supportingFiles.add(new SupportingFile("swagger.mustache", "", "swagger.yaml")); + + // lib + supportingFiles.add(new SupportingFile("TopLevel.mustache", "lib/", apiName + ".hs")); + supportingFiles.add(new SupportingFile("Client.mustache", "lib/" + apiName, "Client.hs")); + + supportingFiles.add(new SupportingFile("API.mustache", "lib/" + apiName, "API.hs")); + supportingFiles.add(new SupportingFile("Core.mustache", "lib/" + apiName, "Core.hs")); + supportingFiles.add(new SupportingFile("Model.mustache", "lib/" + apiName, "Model.hs")); + supportingFiles.add(new SupportingFile("MimeTypes.mustache", "lib/" + apiName, "MimeTypes.hs")); + + // logger + supportingFiles.add(new SupportingFile(useMonadLogger ? "LoggingMonadLogger.mustache" : "LoggingKatip.mustache", "lib/" + apiName, "Logging.hs")); + + // modelTemplateFiles.put("API.mustache", ".hs"); + // apiTemplateFiles.put("Model.mustache", ".hs"); + + // lens + if ((boolean)additionalProperties.get(PROP_GENERATE_LENSES)) { + supportingFiles.add(new SupportingFile("ModelLens.mustache", "lib/" + apiName, "ModelLens.hs")); + } + + additionalProperties.put("title", apiName); + additionalProperties.put("titleLower", firstLetterToLower(apiName)); + additionalProperties.put("package", cabalName); + additionalProperties.put("pathsName", pathsName); + additionalProperties.put("requestType", apiName + "Request"); + additionalProperties.put("configType", apiName + "Config"); + additionalProperties.put("swaggerVersion", swagger.getSwagger()); + + super.preprocessSwagger(swagger); + } + + @Override + public Map postProcessSupportingFileData(Map objs) { + Swagger swagger = (Swagger)objs.get("swagger"); + if(swagger != null) { + try { + objs.put("swagger-yaml", Yaml.mapper().writeValueAsString(swagger)); + } catch (JsonProcessingException e) { + LOGGER.error(e.getMessage(), e); + } + } + return super.postProcessSupportingFileData(objs); + } + + + @Override + public String getTypeDeclaration(Property p) { + if (p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + Property inner = ap.getItems(); + return "[" + getTypeDeclaration(inner) + "]"; + } else if (p instanceof MapProperty) { + MapProperty mp = (MapProperty) p; + Property inner = mp.getAdditionalProperties(); + return "(Map.Map String " + getTypeDeclaration(inner) + ")"; + } + return super.getTypeDeclaration(p); + } + + @Override + public String getSwaggerType(Property p) { + String swaggerType = super.getSwaggerType(p); + + if (typeMapping.containsKey(swaggerType)) { + return typeMapping.get(swaggerType); + } else if (swaggerType == "object") { + return "A.Value"; + } else { + return toModelName(swaggerType); + } + } + + @Override + public String toInstantiationType(Property p) { + if (p instanceof MapProperty) { + MapProperty ap = (MapProperty) p; + Property additionalProperties2 = ap.getAdditionalProperties(); + String type = additionalProperties2.getType(); + if (null == type) { + LOGGER.error("No Type defined for Additional Property " + additionalProperties2 + "\n" // + + "\tIn Property: " + p); + } + String inner = getSwaggerType(additionalProperties2); + return "(Map.Map Text " + inner + ")"; + } else if (p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + String inner = getSwaggerType(ap.getItems()); + return inner; + } else { + return null; + } + } + @Override + public CodegenOperation fromOperation(String resourcePath, String httpMethod, Operation operation, Map definitions, Swagger swagger) { + CodegenOperation op = super.fromOperation(resourcePath, httpMethod, operation, definitions, swagger); + + // prevent aliasing/sharing of operation.vendorExtensions reference + op.vendorExtensions = new LinkedHashMap(); + + String operationType = toTypeName("Op", op.operationId); + op.vendorExtensions.put(X_OPERATION_TYPE, operationType); + typeNames.add(operationType); + + op.vendorExtensions.put(X_HADDOCK_PATH, String.format("%s %s", op.httpMethod, op.path.replace("/", "\\/"))); + op.vendorExtensions.put(X_HAS_BODY_OR_FORM_PARAM, op.getHasBodyParam() || op.getHasFormParams()); + + for (CodegenParameter param : op.allParams) { + param.vendorExtensions = new LinkedHashMap(); // prevent aliasing/sharing + param.vendorExtensions.put(X_OPERATION_TYPE, operationType); + param.vendorExtensions.put(X_IS_BODY_OR_FORM_PARAM, param.isBodyParam || param.isFormParam); + if (!StringUtils.isBlank(param.collectionFormat)) { + param.vendorExtensions.put(X_COLLECTION_FORMAT, mapCollectionFormat(param.collectionFormat)); + } + if(!param.required) { + op.vendorExtensions.put(X_HAS_OPTIONAL_PARAMS, true); + } + + if (typeMapping.containsKey(param.dataType) + || param.isMapContainer || param.isListContainer + || param.isPrimitiveType || param.isFile || param.isEnum) { + + String dataType = genEnums && param.isEnum ? param.datatypeWithEnum : param.dataType; + + String paramNameType = toDedupedName(toTypeName("Param", param.paramName), dataType, !param.isEnum); + param.vendorExtensions.put(X_PARAM_NAME_TYPE, paramNameType); + + HashMap props = new HashMap<>(); + props.put(X_IS_BODY_PARAM, param.isBodyParam); + addToUniques(X_NEWTYPE, paramNameType, dataType, props); + } + } + + processPathExpr(op); + + processProducesConsumes(op); + + processReturnType(op); + + return op; + } + + @Override + public List fromSecurity(Map schemes) { + List secs = super.fromSecurity(schemes); + for(CodegenSecurity sec : secs) { + String prefix = ""; + if(sec.isBasic) prefix = "AuthBasic"; + if(sec.isApiKey) prefix = "AuthApiKey"; + if(sec.isOAuth) prefix = "AuthOAuth"; + sec.name = prefix + toTypeName("",sec.name); + } + return secs; + } + + @Override + public Map postProcessOperations(Map objs) { + Map ret = super.postProcessOperations(objs); + + HashMap pathOps = (HashMap)ret.get("operations"); + ArrayList ops = (ArrayList)pathOps.get("operation"); + if(ops.size() > 0) { + ops.get(0).vendorExtensions.put(X_HAS_NEW_TAG, true); + } + + updateGlobalAdditionalProps(); + return ret; + } + + @Override + public Map postProcessAllModels(Map objs) { + updateGlobalAdditionalProps(); + return super.postProcessAllModels(objs); + } + + public void updateGlobalAdditionalProps() { + additionalProperties.put(X_HAS_UNKNOWN_MIME_TYPES, !unknownMimeTypes.isEmpty()); + + Collections.sort(unknownMimeTypes, new Comparator>() { + @Override + public int compare(Map o1, Map o2) { + return o1.get(MEDIA_TYPE).compareTo(o2.get(MEDIA_TYPE)); + } + }); + additionalProperties.put(X_UNKNOWN_MIME_TYPES, unknownMimeTypes); + + ArrayList> params = new ArrayList<>(uniqueParamNameTypes.values()); + Collections.sort(params, new Comparator>() { + @Override + public int compare(Map o1, Map o2) { + return + ((String) o1.get(X_PARAM_NAME_TYPE)) + .compareTo( + (String) o2.get(X_PARAM_NAME_TYPE)); + } + }); + additionalProperties.put(X_ALL_UNIQUE_PARAMS, params); + } + + @Override + public Map postProcessOperationsWithModels(Map objs, List allModels) { + for (Object o : allModels) { + HashMap h = (HashMap) o; + CodegenModel m = (CodegenModel) h.get("model"); + if (modelMimeTypes.containsKey(m.classname)) { + Set mimeTypes = modelMimeTypes.get(m.classname); + m.vendorExtensions.put(X_MIME_TYPES, mimeTypes); + if ((boolean)additionalProperties.get(PROP_GENERATE_FORM_URLENCODED_INSTANCES) && mimeTypes.contains("MimeFormUrlEncoded")) { + Boolean hasMimeFormUrlEncoded = true; + for (CodegenProperty v : m.vars) { + if (!(v.isPrimitiveType || v.isString || v.isDate || v.isDateTime)) { + hasMimeFormUrlEncoded = false; + } + } + if (hasMimeFormUrlEncoded) { + m.vendorExtensions.put(X_HAS_MIME_FORM_URL_ENCODED, true); + } + } + } + + } + return objs; + } + + @Override + public CodegenModel fromModel(String name, Model mod, Map allDefinitions) { + CodegenModel model = super.fromModel(name, mod, allDefinitions); + + while (typeNames.contains(model.classname)) { + model.classname = generateNextName(model.classname); + } + typeNames.add(model.classname); + + // From the model name, compute the prefix for the fields. + String prefix = StringUtils.uncapitalize(model.classname); + for (CodegenProperty prop : model.vars) { + prop.name = toVarName(prefix, prop.name); + } + + return model; + } + + @Override + public String escapeReservedWord(String name) { + if (this.reservedWordsMappings().containsKey(name)) { + return this.reservedWordsMappings().get(name); + } + return "_" + name; + } + + @Override + public String escapeQuotationMark(String input) { + // remove " to avoid code injection + return input.replace("\"", ""); + } + + @Override + public String escapeUnsafeCharacters(String input) { + return input.replace("{-", "{_-").replace("-}", "-_}"); + } + + @Override + public boolean isDataTypeFile(String dataType) { + return dataType != null && dataType.equals("FilePath"); + } + + @Override + public boolean isDataTypeBinary(final String dataType) { + return dataType != null && dataType.equals("B.ByteString"); + } + + private void processReturnType(CodegenOperation op) { + String returnType = op.returnType; + if (returnType == null || returnType.equals("null")) { + if(op.hasProduces) { + returnType = "res"; + op.vendorExtensions.put(X_HAS_UNKNOWN_RETURN, true); + } else { + returnType = "NoContent"; + if(!op.vendorExtensions.containsKey(X_INLINE_ACCEPT)) { + SetNoContent(op, X_INLINE_ACCEPT); + } + } + } + if (returnType.indexOf(" ") >= 0) { + returnType = "(" + returnType + ")"; + } + op.vendorExtensions.put(X_RETURN_TYPE, returnType); + } + + private void processProducesConsumes(CodegenOperation op) { + if (!(Boolean) op.vendorExtensions.get(X_HAS_BODY_OR_FORM_PARAM)) { + SetNoContent(op, X_INLINE_CONTENT_TYPE); + } + if (op.hasConsumes) { + for (Map m : op.consumes) { + processMediaType(op, m); + processInlineConsumesContentType(op, m); + + } + if (isMultipartOperation(op.consumes)) { + op.isMultipart = Boolean.TRUE; + } + } + if (op.hasProduces) { + for (Map m : op.produces) { + processMediaType(op,m); + processInlineProducesContentType(op, m); + } + } + } + + private void processInlineConsumesContentType(CodegenOperation op, Map m) { + if (op.vendorExtensions.containsKey(X_INLINE_CONTENT_TYPE)) return; + if ((boolean) additionalProperties.get(PROP_INLINE_MIME_TYPES) + && op.consumes.size() == 1) { + op.vendorExtensions.put(X_INLINE_CONTENT_TYPE, m); + for (CodegenParameter param : op.allParams) { + if (param.isBodyParam && param.required) { + param.vendorExtensions.put(X_INLINE_CONTENT_TYPE, m); + } + } + } + } + + private void processInlineProducesContentType(CodegenOperation op, Map m) { + if ((boolean) additionalProperties.get(PROP_INLINE_MIME_TYPES) + && op.produces.size() == 1) { + op.vendorExtensions.put(X_INLINE_ACCEPT, m); + } + } + + private void SetNoContent(CodegenOperation op, String inlineExtentionName) { + Map m = new HashMap<>(); + m.put(X_MEDIA_DATA_TYPE, MIME_NO_CONTENT); + op.vendorExtensions.put(inlineExtentionName, m); + } + + private String toDedupedName(String paramNameType, String dataType, Boolean appendDataType) { + if (appendDataType + && uniqueParamNameTypes.containsKey(paramNameType) + && !isDuplicate(paramNameType, dataType)) { + paramNameType = paramNameType + dataType; + } + + while (typeNames.contains(paramNameType)) { + if (isDuplicate(paramNameType, dataType)) { + break; + } + paramNameType = generateNextName(paramNameType); + } + + typeNames.add(paramNameType); + return paramNameType; + } + + public Boolean isDuplicate(String paramNameType, String dataType) { + Map lastParam = this.uniqueParamNameTypes.get(paramNameType); + if (lastParam != null) { + String comparisonKey = lastParam.containsKey(X_ENUM) ? X_ENUM_VALUES : X_DATA_TYPE; + String lastParamDataType = (String) lastParam.get(comparisonKey); + if (lastParamDataType != null && lastParamDataType.equals(dataType)) { + return true; + } + } + return false; + } + + private Pair isDuplicateEnumValues(String enumValues) { + for (Map vs : uniqueParamNameTypes.values()) { + if (enumValues.equals(vs.get(X_ENUM_VALUES))) { + return Pair.of(true, (String) vs.get(X_PARAM_NAME_TYPE)); + } + } + return Pair.of(false, null); + } + + + private void addToUniques(String xGroup, String paramNameType, String dataType, Map props) { + HashMap m = new HashMap<>(); + m.put(X_PARAM_NAME_TYPE, paramNameType); + m.put(X_DATA_TYPE, dataType); + m.put(xGroup, true); + m.putAll(props); + uniqueParamNameTypes.put(paramNameType, m); + } + + private void addEnumToUniques(String paramNameType, String datatype, String enumValues, Map allowableValues, String description) { + HashMap props = new HashMap<>(); + props.put("allowableValues", allowableValues); + if(StringUtils.isNotBlank(description)) { + props.put("description", description); + } + props.put(X_ENUM_VALUES, enumValues); + addToUniques(X_ENUM, paramNameType, datatype, props); + additionalProperties.put(X_HAS_ENUM_SECTION, true); + } + + + // build the parameterized path segments, according to pathParams + private void processPathExpr(CodegenOperation op) { + String xPath = "[\"" + escapeText(op.path) + "\"]"; + if (op.getHasPathParams()) { + for (CodegenParameter param : op.pathParams) { + xPath = xPath.replaceAll("\\{" + param.baseName + "\\}", "\",toPath " + param.paramName + ",\""); + } + xPath = xPath.replaceAll(",\"\",", ","); + xPath = xPath.replaceAll("\"\",", ","); + xPath = xPath.replaceAll(",\"\"", ","); + xPath = xPath.replaceAll("^\\[,", "["); + xPath = xPath.replaceAll(",\\]$", "]"); + } + op.vendorExtensions.put(X_PATH, xPath); + } + + + private void processMediaType(CodegenOperation op, Map m) { + String mediaType = m.get(MEDIA_TYPE); + + if (StringUtils.isBlank(mediaType)) return; + + String mimeType = getMimeDataType(mediaType); + typeNames.add(mimeType); + m.put(X_MEDIA_DATA_TYPE, mimeType); + if (isJsonMimeType(mediaType)) { + m.put(X_MEDIA_IS_JSON, "true"); + } + + if (!knownMimeDataTypes.containsValue(mimeType) && !unknownMimeTypesContainsType(mimeType)) { + unknownMimeTypes.add(m); + } + for (CodegenParameter param : op.allParams) { + if (param.isBodyParam || param.isFormParam && (!param.isPrimitiveType && !param.isListContainer && !param.isMapContainer)) { + Set mimeTypes = modelMimeTypes.containsKey(param.dataType) ? modelMimeTypes.get(param.dataType) : new HashSet(); + mimeTypes.add(mimeType); + modelMimeTypes.put(param.dataType, mimeTypes); + } + } + } + + private Boolean unknownMimeTypesContainsType(String mimeType) { + for(Map m : unknownMimeTypes) { + String mimeType0 = m.get(X_MEDIA_DATA_TYPE); + if(mimeType0 != null && mimeType0.equals(mimeType)) { + return true; + } + } + + return false; + } + + public String firstLetterToUpper(String word) { + if (word.length() == 0) { + return word; + } else if (word.length() == 1) { + return word.substring(0, 1).toUpperCase(); + } else { + return word.substring(0, 1).toUpperCase() + word.substring(1); + } + } + + public String firstLetterToLower(String word) { + if (word.length() == 0) { + return word; + } else if (word.length() == 1) { + return word.substring(0, 1).toLowerCase(); + } else { + return word.substring(0, 1).toLowerCase() + word.substring(1); + } + } + + private String mapCollectionFormat(String collectionFormat) { + switch (collectionFormat) { + case "csv": + return "CommaSeparated"; + case "tsv": + return "TabSeparated"; + case "ssv": + return "SpaceSeparated"; + case "pipes": + return "PipeSeparated"; + case "multi": + return "MultiParamArray"; + default: + throw new UnsupportedOperationException(); + } + } + + private String getMimeDataType(String mimeType) { + if (StringUtils.isBlank(mimeType)) { + return MIME_NO_CONTENT; + } + if (knownMimeDataTypes.containsKey(mimeType)) { + return knownMimeDataTypes.get(mimeType); + } + String shortenedName = mimeType.replaceFirst("application/",""); + return "Mime" + toTypeName("", shortenedName); + } + + private static String generateNextName(String name) { + Pattern pattern = Pattern.compile("\\d+\\z"); + Matcher matcher = pattern.matcher(name); + if (matcher.find()) { + String numStr = matcher.group(); + int num = Integer.parseInt(numStr) + 1; + return name.substring(0, name.length() - numStr.length()) + num; + } else { + return name + "2"; + } + } + private static boolean isMultipartOperation(List> consumes) { + for(Map consume : consumes) { + if (consume != null) { + if ("multipart/form-data".equals(consume.get(MEDIA_TYPE))) { + return true; + } + } + } + return false; + } + + @Override + public String toVarName(String name) { + return toVarName("", name); + } + public String toVarName(String prefix, String name) { + Boolean hasPrefix = !StringUtils.isBlank(prefix); + name = underscore(sanitizeName(name.replaceAll("-", "_"))); + name = camelize(name, !hasPrefix); + if(hasPrefix) { + return prefix + name; + } else { + if (name.matches("^\\d.*")) + name = escapeReservedWord(name); + if (isReservedWord(name)) + name = escapeReservedWord(name); + return name; + } + } + + @Override + public String toParamName(String name) { + return toVarName(name); + } + + @Override + public String toModelName(String name) { + return toTypeName("Model", name); + } + @Override + public String toModelFilename(String name) { + return toTypeName("Model", name); + } + public String toTypeName(String prefix, String name) { + name = escapeIdentifier(prefix, camelize(sanitizeName(name))); + return name; + } + @Override + public String toOperationId(String operationId) { + if (StringUtils.isEmpty(operationId)) { + throw new RuntimeException("Empty method/operation name (operationId) not allowed"); + } + operationId = escapeIdentifier("op",camelize(sanitizeName(operationId), true)); + String uniqueName = operationId; + String uniqueNameType = toTypeName("Op", operationId); + while (typeNames.contains(uniqueNameType)) { + uniqueName = generateNextName(uniqueName); + uniqueNameType = toTypeName("Op", uniqueName); + } + typeNames.add(uniqueNameType); + if(!operationId.equals(uniqueName)) { + LOGGER.warn("generated unique operationId `" + uniqueName + "`"); + } + return uniqueName; + } + public String escapeIdentifier(String prefix, String name) { + if(StringUtils.isBlank(prefix)) return name; + + if (isReservedWord(name)) { + name = prefix + name; + } + if (name.matches("^\\d.*")) { + name = prefix + name; // e.g. 200Response => Model200Response (after camelize) + } + if (languageSpecificPrimitives.contains(name)) { + name = prefix + name; + } + if (typeMapping.containsValue(name)) { + name = prefix + name; + } + return name; + } + static boolean isJsonMimeType(String mime) { + return mime != null && JSON_MIME_PATTERN.matcher(mime).matches(); + } + + @Override + public String toDefaultValue(Property p) { + if (p instanceof StringProperty) { + StringProperty dp = (StringProperty) p; + if (dp.getDefault() != null) { + return "\"" + escapeText(dp.getDefault()) + "\""; + } + } else if (p instanceof BooleanProperty) { + BooleanProperty dp = (BooleanProperty) p; + if (dp.getDefault() != null) { + if (dp.getDefault().toString().equalsIgnoreCase("false")) + return "False"; + else + return "True"; + } + } + + return null; + } + + @Override + public Map postProcessModels(Map objs) { + List models = (List) objs.get("models"); + for (Object _mo : models) { + Map mo = (Map) _mo; + CodegenModel cm = (CodegenModel) mo.get("model"); + cm.isEnum = genEnums && cm.isEnum; + if(cm.isAlias) { + cm.vendorExtensions.put(X_DATA_TYPE, cm.dataType); + } + for (CodegenProperty var : cm.vars) { + String datatype = genEnums && !StringUtils.isBlank(var.datatypeWithEnum) + ? var.datatypeWithEnum + : var.datatype; + var.vendorExtensions.put(X_DATA_TYPE, datatype); + } + } + return postProcessModelsEnum(objs); + } + + @Override + public Map postProcessModelsEnum(Map objs) { + Map objsEnum = super.postProcessModelsEnum(objs); + if (genEnums) { + List models = (List) objsEnum.get("models"); + for (Object _mo : models) { + Map mo = (Map) _mo; + CodegenModel cm = (CodegenModel) mo.get("model"); + if (cm.isEnum && cm.allowableValues != null) { + updateAllowableValuesNames(cm.classname, cm.allowableValues); + addEnumToUniques(cm.classname, cm.dataType, cm.allowableValues.values().toString(), cm.allowableValues, cm.description); + } + } + } + return objsEnum; + } + + @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) { + + // this replacement is/needs to be language-specific + property.datatypeWithEnum = property.datatypeWithEnum.replace(baseItem.baseType + ")", toEnumName(baseItem) + ")"); + + property.enumName = toEnumName(property); + if (property.defaultValue != null) { + property.defaultValue = property.defaultValue.replace(", " + property.items.baseType, ", " + toEnumName(property.items)); + } + } + } + + @Override + public String toEnumName(CodegenProperty var) { + if (!genEnums) return super.toEnumName(var); + + if (var.items != null && var.items.isEnum) { + return toEnumName(var.items); + } + String paramNameType = "E'" + toTypeName("", var.name); + String enumValues = var._enum.toString(); + + Pair duplicateEnum = isDuplicateEnumValues(enumValues); + if (duplicateEnum.getLeft()) { + paramNameType = duplicateEnum.getRight(); + } else { + paramNameType = toDedupedName(paramNameType, enumValues, false); + var.datatypeWithEnum = paramNameType; + updateCodegenPropertyEnum(var); + addEnumToUniques(paramNameType, var.datatype, enumValues, var.allowableValues, var.description); + } + + return paramNameType; + } + + @Override + public void updateCodegenPropertyEnum(CodegenProperty var) { + super.updateCodegenPropertyEnum(var); + if (!genEnums) return; + updateCodegenPropertyEnumValues(var, var.datatypeWithEnum); + } + + public void updateCodegenPropertyEnumValues(CodegenProperty var, String paramNameType) { + if (var.items != null && var.items.allowableValues != null) { + updateCodegenPropertyEnumValues(var.items, var.items.datatypeWithEnum); + return; + } + if(var.isEnum && var.allowableValues != null) { + updateAllowableValuesNames(paramNameType, var.allowableValues); + } + } + + private void updateAllowableValuesNames(String paramNameType, Map allowableValues) { + if (allowableValues == null) { + return; + } + for (Map enumVar : (List>) allowableValues.get("enumVars")) { + enumVar.put("name", paramNameType + enumVar.get("name")); + } + } + + @Override + public String toEnumVarName(String value, String datatype) { + if (!genEnums) return super.toEnumVarName(value, datatype); + + List num = new ArrayList<>(Arrays.asList("integer","int","double","long","float")); + if (value.length() == 0) { + return "'Empty"; + } + + // for symbol, e.g. $, # + if (getSymbolName(value) != null) { + return "'" + StringUtils.capitalize(sanitizeName(getSymbolName(value))); + } + + // number + if (num.contains(datatype.toLowerCase())) { + String varName = "Num" + value; + varName = varName.replaceAll("-", "Minus_"); + varName = varName.replaceAll("\\+", "Plus_"); + varName = varName.replaceAll("\\.", "_Dot_"); + return "'" + StringUtils.capitalize(sanitizeName(varName)); + } + + return "'" + StringUtils.capitalize(sanitizeName(value)); + } + + @Override + public String toEnumValue(String value, String datatype) { + List num = new ArrayList<>(Arrays.asList("integer","int","double","long","float")); + if(num.contains(datatype.toLowerCase())) { + return value; + } else { + return "\"" + escapeText(value) + "\""; + } + } + + // override with any special text escaping logic + @SuppressWarnings("static-method") + public String escapeText(String input) { + if (input == null) { + return input; + } + + // remove \t, \n, \r + // replace \ with \\ + // replace " with \" + // outter unescape to retain the original multi-byte characters + // finally escalate characters avoiding code injection + return escapeUnsafeCharacters( + StringEscapeUtils.unescapeJava( + StringEscapeUtils.escapeJava(input) + .replace("\\/", "/")) + .replaceAll("[\\t\\n\\r]"," ") + .replace("\\", "\\\\") + .replace("\"", "\\\"")); + } +} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/HaskellServantCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/HaskellServantCodegen.java index fdd68a5330a..d04c63e4ed3 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/HaskellServantCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/HaskellServantCodegen.java @@ -148,6 +148,7 @@ public HaskellServantCodegen() { typeMapping.put("integer", "Int"); typeMapping.put("any", "Value"); typeMapping.put("UUID", "Text"); + typeMapping.put("ByteArray", "Text"); importMapping.clear(); importMapping.put("Map", "qualified Data.Map as Map"); @@ -158,12 +159,12 @@ public HaskellServantCodegen() { /** * 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 reseved words + * 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) { + public String escapeReservedWord(String name) { if(this.reservedWordsMappings().containsKey(name)) { return this.reservedWordsMappings().get(name); } @@ -515,7 +516,7 @@ public CodegenModel fromModel(String name, Model mod, Map allDefi // Create newtypes for things with non-object types String dataOrNewtype = "data"; - // check if it's a ModelImpl before casting + // check if it's a ModelImpl before casting if (!(mod instanceof ModelImpl)) { return model; } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JMeterCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JMeterCodegen.java index 6ebfbea6f17..39125284b98 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JMeterCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JMeterCodegen.java @@ -115,7 +115,7 @@ public void preprocessSwagger(Swagger swagger) { /** * 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 reseved words + * those terms here. This logic is only called if a variable matches the reserved words * * @return the escaped term */ diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaCXFClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaCXFClientCodegen.java index 5edd70e2ca1..649b09d17c4 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaCXFClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaCXFClientCodegen.java @@ -11,20 +11,17 @@ import io.swagger.codegen.CliOption; import io.swagger.codegen.CodegenModel; import io.swagger.codegen.CodegenOperation; -import io.swagger.codegen.CodegenParameter; import io.swagger.codegen.CodegenProperty; -import io.swagger.codegen.CodegenResponse; import io.swagger.codegen.CodegenType; import io.swagger.codegen.SupportingFile; import io.swagger.codegen.languages.features.BeanValidationFeatures; import io.swagger.codegen.languages.features.GzipTestFeatures; -import io.swagger.codegen.languages.features.JaxbFeatures; import io.swagger.codegen.languages.features.LoggingTestFeatures; import io.swagger.codegen.languages.features.UseGenericResponseFeatures; import io.swagger.models.Operation; public class JavaCXFClientCodegen extends AbstractJavaCodegen - implements BeanValidationFeatures, UseGenericResponseFeatures, JaxbFeatures, GzipTestFeatures, LoggingTestFeatures { + implements BeanValidationFeatures, UseGenericResponseFeatures, GzipTestFeatures, LoggingTestFeatures { private static final Logger LOGGER = LoggerFactory.getLogger(JavaCXFClientCodegen.class); @@ -34,8 +31,6 @@ public class JavaCXFClientCodegen extends AbstractJavaCodegen */ protected static final String JAXRS_TEMPLATE_DIRECTORY_NAME = "JavaJaxRS"; - protected boolean useJaxbAnnotations = true; - protected boolean useBeanValidation = false; protected boolean useGenericResponse = false; @@ -73,8 +68,6 @@ public JavaCXFClientCodegen() embeddedTemplateDir = templateDir = JAXRS_TEMPLATE_DIRECTORY_NAME + File.separator + "cxf"; - cliOptions.add(CliOption.newBoolean(USE_JAXB_ANNOTATIONS, "Use JAXB annotations for XML")); - cliOptions.add(CliOption.newBoolean(USE_BEANVALIDATION, "Use BeanValidation API annotations")); cliOptions.add(CliOption.newBoolean(USE_GZIP_FEATURE_FOR_TESTS, "Use Gzip Feature for tests")); @@ -89,11 +82,6 @@ public void processOpts() { super.processOpts(); - if (additionalProperties.containsKey(USE_JAXB_ANNOTATIONS)) { - boolean useJaxbAnnotationsProp = convertPropertyToBooleanAndWriteBack(USE_JAXB_ANNOTATIONS); - this.setUseJaxbAnnotations(useJaxbAnnotationsProp); - } - if (additionalProperties.containsKey(USE_BEANVALIDATION)) { boolean useBeanValidationProp = convertPropertyToBooleanAndWriteBack(USE_BEANVALIDATION); this.setUseBeanValidation(useBeanValidationProp); @@ -177,11 +165,6 @@ public void setUseBeanValidation(boolean useBeanValidation) { this.useBeanValidation = useBeanValidation; } - - public void setUseJaxbAnnotations(boolean useJaxbAnnotations) { - this.useJaxbAnnotations = useJaxbAnnotations; - } - public void setUseGzipFeatureForTests(boolean useGzipFeatureForTests) { this.useGzipFeatureForTests = useGzipFeatureForTests; } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaCXFServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaCXFServerCodegen.java index 4aa26394c6e..a8f25d69206 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaCXFServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaCXFServerCodegen.java @@ -15,20 +15,17 @@ import io.swagger.codegen.SupportingFile; import io.swagger.codegen.languages.features.CXFServerFeatures; import io.swagger.codegen.languages.features.GzipTestFeatures; -import io.swagger.codegen.languages.features.JaxbFeatures; import io.swagger.codegen.languages.features.LoggingTestFeatures; import io.swagger.codegen.languages.features.UseGenericResponseFeatures; import io.swagger.models.Operation; public class JavaCXFServerCodegen extends AbstractJavaJAXRSServerCodegen - implements CXFServerFeatures, GzipTestFeatures, LoggingTestFeatures, JaxbFeatures, UseGenericResponseFeatures -{ + implements CXFServerFeatures, GzipTestFeatures, LoggingTestFeatures, UseGenericResponseFeatures +{ private static final Logger LOGGER = LoggerFactory.getLogger(JavaCXFServerCodegen.class); protected boolean addConsumesProducesJson = true; - protected boolean useJaxbAnnotations = true; - protected boolean generateSpringApplication = false; protected boolean useSpringAnnotationConfig = false; @@ -86,8 +83,6 @@ public JavaCXFServerCodegen() embeddedTemplateDir = templateDir = JAXRS_TEMPLATE_DIRECTORY_NAME + File.separator + "cxf"; - cliOptions.add(CliOption.newBoolean(USE_JAXB_ANNOTATIONS, "Use JAXB annotations for XML")); - cliOptions.add(CliOption.newBoolean(GENERATE_SPRING_APPLICATION, "Generate Spring application")); cliOptions.add(CliOption.newBoolean(USE_SPRING_ANNOTATION_CONFIG, "Use Spring Annotation Config")); @@ -124,11 +119,6 @@ public void processOpts() { super.processOpts(); - if (additionalProperties.containsKey(USE_JAXB_ANNOTATIONS)) { - boolean useJaxbAnnotationsProp = convertPropertyToBooleanAndWriteBack(USE_JAXB_ANNOTATIONS); - this.setUseJaxbAnnotations(useJaxbAnnotationsProp); - } - if (additionalProperties.containsKey(ADD_CONSUMES_PRODUCES_JSON)) { this.setAddConsumesProducesJson(convertPropertyToBooleanAndWriteBack(ADD_CONSUMES_PRODUCES_JSON)); } @@ -288,10 +278,6 @@ public void setGenerateSpringBootApplication(boolean generateSpringBootApplicati this.generateSpringBootApplication = generateSpringBootApplication; } - public void setUseJaxbAnnotations(boolean useJaxbAnnotations) { - this.useJaxbAnnotations = useJaxbAnnotations; - } - public void setGenerateJbossDeploymentDescriptor(boolean generateJbossDeploymentDescriptor) { this.generateJbossDeploymentDescriptor = generateJbossDeploymentDescriptor; } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java index 8d3892ee536..9ef0e34aa99 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java @@ -1,9 +1,13 @@ package io.swagger.codegen.languages; +import static java.util.Collections.sort; + +import com.google.common.collect.LinkedListMultimap; import io.swagger.codegen.*; import io.swagger.codegen.languages.features.BeanValidationFeatures; import io.swagger.codegen.languages.features.GzipFeatures; import io.swagger.codegen.languages.features.PerformBeanValidationFeatures; + import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -25,10 +29,14 @@ public class JavaClientCodegen extends AbstractJavaCodegen public static final String USE_RX_JAVA = "useRxJava"; public static final String USE_RX_JAVA2 = "useRxJava2"; public static final String DO_NOT_USE_RX = "doNotUseRx"; - public static final String USE_PLAY24_WS = "usePlay24WS"; + public static final String USE_PLAY_WS = "usePlayWS"; + public static final String PLAY_VERSION = "playVersion"; public static final String PARCELABLE_MODEL = "parcelableModel"; public static final String USE_RUNTIME_EXCEPTION = "useRuntimeException"; + public static final String PLAY_24 = "play24"; + public static final String PLAY_25 = "play25"; + public static final String RETROFIT_1 = "retrofit"; public static final String RETROFIT_2 = "retrofit2"; @@ -36,13 +44,15 @@ public class JavaClientCodegen extends AbstractJavaCodegen protected boolean useRxJava = false; protected boolean useRxJava2 = false; protected boolean doNotUseRx = true; // backwards compatibility for swagger configs that specify neither rx1 nor rx2 (mustache does not allow for boolean operators so we need this extra field) - protected boolean usePlay24WS = false; + protected boolean usePlayWS = false; + protected String playVersion = PLAY_25; protected boolean parcelableModel = false; protected boolean useBeanValidation = false; protected boolean performBeanValidation = false; protected boolean useGzipFeature = false; protected boolean useRuntimeException = false; + public JavaClientCodegen() { super(); outputFolder = "generated-code" + File.separator + "java"; @@ -55,7 +65,8 @@ public JavaClientCodegen() { cliOptions.add(CliOption.newBoolean(USE_RX_JAVA, "Whether to use the RxJava adapter with the retrofit2 library.")); cliOptions.add(CliOption.newBoolean(USE_RX_JAVA2, "Whether to use the RxJava2 adapter with the retrofit2 library.")); cliOptions.add(CliOption.newBoolean(PARCELABLE_MODEL, "Whether to generate models for Android that implement Parcelable with the okhttp-gson library.")); - cliOptions.add(CliOption.newBoolean(USE_PLAY24_WS, "Use Play! 2.4 Async HTTP client (Play WS API)")); + cliOptions.add(CliOption.newBoolean(USE_PLAY_WS, "Use Play! Async HTTP client (Play WS API)")); + cliOptions.add(CliOption.newString(PLAY_VERSION, "Version of Play! Framework (possible values \"play24\", \"play25\")")); cliOptions.add(CliOption.newBoolean(SUPPORT_JAVA6, "Whether to support Java6 with the Jersey1 library.")); cliOptions.add(CliOption.newBoolean(USE_BEANVALIDATION, "Use BeanValidation API annotations")); cliOptions.add(CliOption.newBoolean(PERFORM_BEANVALIDATION, "Perform BeanValidation")); @@ -70,6 +81,8 @@ public JavaClientCodegen() { supportedLibraries.put(RETROFIT_2, "HTTP client: OkHttp 3.8.0. JSON processing: Gson 2.6.1 (Retrofit 2.3.0). Enable the RxJava adapter using '-DuseRxJava[2]=true'. (RxJava 1.x or 2.x)"); supportedLibraries.put("resttemplate", "HTTP client: Spring RestTemplate 4.3.9-RELEASE. JSON processing: Jackson 2.8.9"); supportedLibraries.put("resteasy", "HTTP client: Resteasy client 3.1.3.Final. JSON processing: Jackson 2.8.9"); + supportedLibraries.put("vertx", "HTTP client: VertX client 3.2.4. JSON processing: Jackson 2.8.9"); + supportedLibraries.put("google-api-client", "HTTP client: Google API client 1.23.0. JSON processing: Jackson 2.8.9"); CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "library template (sub-template) to use"); libraryOption.setEnum(supportedLibraries); @@ -110,10 +123,15 @@ public void processOpts() { if (!useRxJava && !useRxJava2) { additionalProperties.put(DO_NOT_USE_RX, true); } - if (additionalProperties.containsKey(USE_PLAY24_WS)) { - this.setUsePlay24WS(Boolean.valueOf(additionalProperties.get(USE_PLAY24_WS).toString())); + if (additionalProperties.containsKey(USE_PLAY_WS)) { + this.setUsePlayWS(Boolean.valueOf(additionalProperties.get(USE_PLAY_WS).toString())); + } + additionalProperties.put(USE_PLAY_WS, usePlayWS); + + if (additionalProperties.containsKey(PLAY_VERSION)) { + this.setPlayVersion(additionalProperties.get(PLAY_VERSION).toString()); } - additionalProperties.put(USE_PLAY24_WS, usePlay24WS); + additionalProperties.put(PLAY_VERSION, playVersion); if (additionalProperties.containsKey(PARCELABLE_MODEL)) { this.setParcelableModel(Boolean.valueOf(additionalProperties.get(PARCELABLE_MODEL).toString())); @@ -139,6 +157,7 @@ public void processOpts() { final String invokerFolder = (sourceFolder + '/' + invokerPackage).replace(".", "/"); final String authFolder = (sourceFolder + '/' + invokerPackage + ".auth").replace(".", "/"); + final String apiFolder = (sourceFolder + '/' + apiPackage).replace(".", "/"); //Common files writeOptional(outputFolder, new SupportingFile("pom.mustache", "", "pom.xml")); @@ -154,10 +173,13 @@ public void processOpts() { supportingFiles.add(new SupportingFile("StringUtil.mustache", invokerFolder, "StringUtil.java")); } - supportingFiles.add(new SupportingFile("auth/HttpBasicAuth.mustache", authFolder, "HttpBasicAuth.java")); - supportingFiles.add(new SupportingFile("auth/ApiKeyAuth.mustache", authFolder, "ApiKeyAuth.java")); - supportingFiles.add(new SupportingFile("auth/OAuth.mustache", authFolder, "OAuth.java")); - supportingFiles.add(new SupportingFile("auth/OAuthFlow.mustache", authFolder, "OAuthFlow.java")); + // google-api-client doesn't use the Swagger auth, because it uses Google Credential directly (HttpRequestInitializer) + if (!"google-api-client".equals(getLibrary())) { + supportingFiles.add(new SupportingFile("auth/HttpBasicAuth.mustache", authFolder, "HttpBasicAuth.java")); + supportingFiles.add(new SupportingFile("auth/ApiKeyAuth.mustache", authFolder, "ApiKeyAuth.java")); + supportingFiles.add(new SupportingFile("auth/OAuth.mustache", authFolder, "OAuth.java")); + supportingFiles.add(new SupportingFile("auth/OAuthFlow.mustache", authFolder, "OAuthFlow.java")); + } supportingFiles.add(new SupportingFile( "gradlew.mustache", "", "gradlew") ); supportingFiles.add(new SupportingFile( "gradlew.bat.mustache", "", "gradlew.bat") ); supportingFiles.add(new SupportingFile( "gradle-wrapper.properties.mustache", @@ -178,7 +200,7 @@ public void processOpts() { apiDocTemplateFiles.remove("api_doc.mustache"); } - if (!("feign".equals(getLibrary()) || "resttemplate".equals(getLibrary()) || usesAnyRetrofitLibrary())) { + if (!("feign".equals(getLibrary()) || "resttemplate".equals(getLibrary()) || usesAnyRetrofitLibrary() || "google-api-client".equals(getLibrary()))) { supportingFiles.add(new SupportingFile("apiException.mustache", invokerFolder, "ApiException.java")); supportingFiles.add(new SupportingFile("Configuration.mustache", invokerFolder, "Configuration.java")); supportingFiles.add(new SupportingFile("Pair.mustache", invokerFolder, "Pair.java")); @@ -202,6 +224,9 @@ public void processOpts() { supportingFiles.add(new SupportingFile("auth/OAuthOkHttpClient.mustache", authFolder, "OAuthOkHttpClient.java")); supportingFiles.add(new SupportingFile("CollectionFormats.mustache", invokerFolder, "CollectionFormats.java")); additionalProperties.put("gson", "true"); + if ("retrofit2".equals(getLibrary()) && !usePlayWS) { + supportingFiles.add(new SupportingFile("JSON.mustache", invokerFolder, "JSON.java")); + } } else if ("jersey2".equals(getLibrary()) || "resteasy".equals(getLibrary())) { supportingFiles.add(new SupportingFile("JSON.mustache", invokerFolder, "JSON.java")); additionalProperties.put("jackson", "true"); @@ -210,12 +235,23 @@ public void processOpts() { } else if("resttemplate".equals(getLibrary())) { additionalProperties.put("jackson", "true"); supportingFiles.add(new SupportingFile("auth/Authentication.mustache", authFolder, "Authentication.java")); + } else if("vertx".equals(getLibrary())) { + typeMapping.put("file", "AsyncFile"); + importMapping.put("AsyncFile", "io.vertx.core.file.AsyncFile"); + setJava8Mode(true); + additionalProperties.put("java8", "true"); + additionalProperties.put("jackson", "true"); + apiTemplateFiles.put("apiImpl.mustache", "Impl.java"); + apiTemplateFiles.put("rxApiImpl.mustache", ".java"); + supportingFiles.remove(new SupportingFile("manifest.mustache", projectFolder, "AndroidManifest.xml")); + } else if ("google-api-client".equals(getLibrary())) { + additionalProperties.put("jackson", "true"); } else { LOGGER.error("Unknown library option (-l/--library): " + getLibrary()); } - if (Boolean.TRUE.equals(additionalProperties.get(USE_PLAY24_WS))) { - // remove unsupported auth + if (usePlayWS) { + // remove unsupported auth Iterator iter = supportingFiles.iterator(); while (iter.hasNext()) { SupportingFile sf = iter.next(); @@ -224,25 +260,40 @@ public void processOpts() { } } - // auth - supportingFiles.add(new SupportingFile("play24/auth/ApiKeyAuth.mustache", authFolder, "ApiKeyAuth.java")); + apiTemplateFiles.remove("api.mustache"); + + if (PLAY_24.equals(playVersion)) { + additionalProperties.put(PLAY_24, true); + apiTemplateFiles.put("play24/api.mustache", ".java"); + + supportingFiles.add(new SupportingFile("play24/ApiClient.mustache", invokerFolder, "ApiClient.java")); + supportingFiles.add(new SupportingFile("play24/Play24CallFactory.mustache", invokerFolder, "Play24CallFactory.java")); + supportingFiles.add(new SupportingFile("play24/Play24CallAdapterFactory.mustache", invokerFolder, + "Play24CallAdapterFactory.java")); + } else { + additionalProperties.put(PLAY_25, true); + apiTemplateFiles.put("play25/api.mustache", ".java"); + + supportingFiles.add(new SupportingFile("play25/ApiClient.mustache", invokerFolder, "ApiClient.java")); + supportingFiles.add(new SupportingFile("play25/Play25CallFactory.mustache", invokerFolder, "Play25CallFactory.java")); + supportingFiles.add(new SupportingFile("play25/Play25CallAdapterFactory.mustache", invokerFolder, + "Play25CallAdapterFactory.java")); + additionalProperties.put("java8", "true"); + } + + supportingFiles.add(new SupportingFile("play-common/auth/ApiKeyAuth.mustache", authFolder, "ApiKeyAuth.java")); supportingFiles.add(new SupportingFile("auth/Authentication.mustache", authFolder, "Authentication.java")); supportingFiles.add(new SupportingFile("Pair.mustache", invokerFolder, "Pair.java")); - - // api client - supportingFiles.add(new SupportingFile("play24/ApiClient.mustache", invokerFolder, "ApiClient.java")); - - // adapters - supportingFiles - .add(new SupportingFile("play24/Play24CallFactory.mustache", invokerFolder, "Play24CallFactory.java")); - supportingFiles.add(new SupportingFile("play24/Play24CallAdapterFactory.mustache", invokerFolder, - "Play24CallAdapterFactory.java")); + additionalProperties.put("jackson", "true"); additionalProperties.remove("gson"); } - if (additionalProperties.containsKey("jackson") ) { + if (additionalProperties.containsKey("jackson")) { supportingFiles.add(new SupportingFile("RFC3339DateFormat.mustache", invokerFolder, "RFC3339DateFormat.java")); + if ("threetenbp".equals(dateLibrary) && !usePlayWS) { + supportingFiles.add(new SupportingFile("CustomInstantDeserializer.mustache", invokerFolder, "CustomInstantDeserializer.java")); + } } } @@ -254,6 +305,7 @@ private boolean usesRetrofit2Library() { return getLibrary() != null && getLibrary().contains(RETROFIT_2); } + @SuppressWarnings("unchecked") @Override public Map postProcessOperations(Map objs) { super.postProcessOperations(objs); @@ -274,10 +326,34 @@ public Map postProcessOperations(Map objs) { if (operation.returnType == null) { operation.returnType = "Void"; } - if (usesRetrofit2Library() && StringUtils.isNotEmpty(operation.path) && operation.path.startsWith("/")) + if (usesRetrofit2Library() && StringUtils.isNotEmpty(operation.path) && operation.path.startsWith("/")){ operation.path = operation.path.substring(1); + } + + // sorting operation parameters to make sure path params are parsed before query params + if (operation.allParams != null) { + sort(operation.allParams, new Comparator() { + @Override + public int compare(CodegenParameter one, CodegenParameter another) { + if (one.isPathParam && another.isQueryParam) { + return -1; + } + if (one.isQueryParam && another.isPathParam){ + return 1; + } + + return 0; + } + }); + Iterator iterator = operation.allParams.iterator(); + while (iterator.hasNext()){ + CodegenParameter param = iterator.next(); + param.hasMore = iterator.hasNext(); + } + } } } + } // camelize path variables for Feign client @@ -285,10 +361,8 @@ public Map postProcessOperations(Map objs) { Map operations = (Map) objs.get("operations"); List operationList = (List) operations.get("operation"); for (CodegenOperation op : operationList) { - String path = new String(op.path); + String path = op.path; String[] items = path.split("/", -1); - String opsPath = ""; - int pathParamIndex = 0; for (int i = 0; i < items.length; ++i) { if (items[i].matches("^\\{(.*)\\}$")) { // wrap in {} @@ -303,6 +377,20 @@ public Map postProcessOperations(Map objs) { return objs; } + @Override + public String apiFilename(String templateName, String tag) { + if("vertx".equals(getLibrary())) { + String suffix = apiTemplateFiles().get(templateName); + String subFolder = ""; + if (templateName.startsWith("rx")) { + subFolder = "/rxjava"; + } + return apiFileFolder() + subFolder + '/' + toApiFilename(tag) + suffix; + } else { + return super.apiFilename(templateName, tag); + } + } + /** * Prioritizes consumes mime-type list by moving json-vendor and json mime-types up front, but * otherwise preserves original consumes definition order. @@ -378,6 +466,25 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert } } + @SuppressWarnings("unchecked") + @Override + public Map postProcessAllModels(Map objs) { + Map allProcessedModels = super.postProcessAllModels(objs); + if(!additionalProperties.containsKey("gsonFactoryMethod")) { + List allModels = new ArrayList(); + for (String name: allProcessedModels.keySet()) { + Map models = (Map)allProcessedModels.get(name); + try { + allModels.add(((List) models.get("models")).get(0)); + } catch (Exception e){ + e.printStackTrace(); + } + } + additionalProperties.put("parent", modelInheritanceSupportInGson(allModels)); + } + return allProcessedModels; + } + @Override public Map postProcessModelsEnum(Map objs) { objs = super.postProcessModelsEnum(objs); @@ -400,6 +507,34 @@ public Map postProcessModelsEnum(Map objs) { return objs; } + private List> modelInheritanceSupportInGson(List allModels) { + LinkedListMultimap byParent = LinkedListMultimap.create(); + for (Object m : allModels) { + Map entry = (Map) m; + CodegenModel parent = ((CodegenModel)entry.get("model")).parentModel; + if(null!= parent) { + byParent.put(parent, ((CodegenModel)entry.get("model"))); + } + } + List> parentsList = new ArrayList<>(); + for (CodegenModel parentModel : byParent.keySet()) { + List> childrenList = new ArrayList<>(); + Map parent = new HashMap<>(); + parent.put("classname", parentModel.classname); + List childrenModels = byParent.get(parentModel); + for (CodegenModel model : childrenModels) { + Map child = new HashMap<>(); + child.put("name", model.name); + child.put("classname", model.classname); + childrenList.add(child); + } + parent.put("children", childrenList); + parent.put("discriminator", parentModel.discriminator); + parentsList.add(parent); + } + return parentsList; + } + public void setUseRxJava(boolean useRxJava) { this.useRxJava = useRxJava; doNotUseRx = false; @@ -414,10 +549,13 @@ public void setDoNotUseRx(boolean doNotUseRx) { this.doNotUseRx = doNotUseRx; } - public void setUsePlay24WS(boolean usePlay24WS) { - this.usePlay24WS = usePlay24WS; + public void setUsePlayWS(boolean usePlayWS) { + this.usePlayWS = usePlayWS; } + public void setPlayVersion(String playVersion) { + this.playVersion = playVersion; + } public void setParcelableModel(boolean parcelableModel) { this.parcelableModel = parcelableModel; diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaInflectorServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaInflectorServerCodegen.java index 1c856c95382..cf1e2b816fb 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaInflectorServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaInflectorServerCodegen.java @@ -1,16 +1,22 @@ package io.swagger.codegen.languages; import com.fasterxml.jackson.core.JsonProcessingException; -import io.swagger.codegen.*; +import io.swagger.codegen.CodegenModel; +import io.swagger.codegen.CodegenOperation; +import io.swagger.codegen.CodegenProperty; +import io.swagger.codegen.CodegenType; +import io.swagger.codegen.SupportingFile; import io.swagger.models.Operation; import io.swagger.models.Swagger; import io.swagger.util.Yaml; import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class JavaInflectorServerCodegen extends AbstractJavaCodegen { @@ -24,7 +30,7 @@ public JavaInflectorServerCodegen() { sourceFolder = "src/gen/java"; apiTestTemplateFiles.clear(); // TODO: add test template embeddedTemplateDir = templateDir = "JavaInflector"; - invokerPackage = "io.swagger.handler"; + invokerPackage = "io.swagger.controllers"; artifactId = "swagger-inflector-server"; dateLibrary = "legacy"; //TODO: add joda support @@ -35,7 +41,7 @@ public JavaInflectorServerCodegen() { apiDocTemplateFiles.remove("api_doc.mustache"); - apiPackage = System.getProperty("swagger.codegen.inflector.apipackage", "io.swagger.handler"); + apiPackage = System.getProperty("swagger.codegen.inflector.apipackage", "io.swagger.controllers"); modelPackage = System.getProperty("swagger.codegen.inflector.modelpackage", "io.swagger.model"); additionalProperties.put("title", title); @@ -173,6 +179,11 @@ public Map postProcessModelsEnum(Map objs) { return objs; } + @Override + protected String getOrGenerateOperationId(Operation operation, String path, String httpMethod) { + return super.getOrGenerateOperationId(operation, path, httpMethod.toUpperCase()); + } + public String apiFilename(String templateName, String tag) { String result = super.apiFilename(templateName, tag); @@ -204,7 +215,7 @@ public String toApiName(String name) { if (name.length() == 0) { return "DefaultController"; } - name = name.replaceAll("[^a-zA-Z0-9]+", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + name = name.replaceAll("[^a-zA-Z0-9]+", "_"); return camelize(name)+ "Controller"; } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaJAXRSSpecServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaJAXRSSpecServerCodegen.java index 55b2b87da64..ba0c837be39 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaJAXRSSpecServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaJAXRSSpecServerCodegen.java @@ -15,15 +15,19 @@ import io.swagger.codegen.CodegenOperation; import io.swagger.codegen.CodegenProperty; import io.swagger.codegen.SupportingFile; -import io.swagger.codegen.languages.features.BeanValidationFeatures; import io.swagger.models.Operation; import io.swagger.models.Swagger; -import io.swagger.models.properties.Property; import io.swagger.util.Json; public class JavaJAXRSSpecServerCodegen extends AbstractJavaJAXRSServerCodegen { + public static final String INTERFACE_ONLY = "interfaceOnly"; + public static final String GENERATE_POM = "generatePom"; + + private boolean interfaceOnly = false; + private boolean generatePom = true; + public JavaJAXRSSpecServerCodegen() { super(); @@ -69,19 +73,34 @@ public JavaJAXRSSpecServerCodegen() library.setEnum(supportedLibraries); cliOptions.add(library); + cliOptions.add(CliOption.newBoolean(GENERATE_POM, "Whether to generate pom.xml if the file does not already exist.").defaultValue(String.valueOf(generatePom))); + cliOptions.add(CliOption.newBoolean(INTERFACE_ONLY, "Whether to generate only API interface stubs without the server files.").defaultValue(String.valueOf(interfaceOnly))); } @Override public void processOpts() { + if (additionalProperties.containsKey(GENERATE_POM)) { + generatePom = Boolean.valueOf(additionalProperties.get(GENERATE_POM).toString()); + } + if (additionalProperties.containsKey(INTERFACE_ONLY)) { + interfaceOnly = Boolean.valueOf(additionalProperties.get(INTERFACE_ONLY).toString()); + } + if (interfaceOnly) { + // Change default artifactId if genereating interfaces only, before command line options are applied in base class. + artifactId = "swagger-jaxrs-client"; + } + super.processOpts(); supportingFiles.clear(); // Don't need extra files provided by AbstractJAX-RS & Java Codegen - writeOptional(outputFolder, new SupportingFile("pom.mustache", "", "pom.xml")); - - writeOptional(outputFolder, new SupportingFile("RestApplication.mustache", - (sourceFolder + '/' + invokerPackage).replace(".", "/"), "RestApplication.java")); - + if (generatePom) { + writeOptional(outputFolder, new SupportingFile("pom.mustache", "", "pom.xml")); + } + if (!interfaceOnly) { + writeOptional(outputFolder, new SupportingFile("RestApplication.mustache", + (sourceFolder + '/' + invokerPackage).replace(".", "/"), "RestApplication.java")); + } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaJerseyServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaJerseyServerCodegen.java index 8cbd3a485d5..0c0aa767f8f 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaJerseyServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaJerseyServerCodegen.java @@ -18,6 +18,9 @@ public class JavaJerseyServerCodegen extends AbstractJavaJAXRSServerCodegen { * Default library template to use. (Default:{@value #DEFAULT_LIBRARY}) */ public static final String DEFAULT_LIBRARY = LIBRARY_JERSEY2; + public static final String USE_TAGS = "useTags"; + + protected boolean useTags = false; public JavaJerseyServerCodegen() { super(); @@ -46,6 +49,7 @@ public JavaJerseyServerCodegen() { cliOptions.add(library); cliOptions.add(CliOption.newBoolean(SUPPORT_JAVA6, "Whether to support Java6 with the Jersey1/2 library.")); + cliOptions.add(CliOption.newBoolean(USE_TAGS, "use tags for creating interface and controller classnames")); } @Override @@ -89,6 +93,10 @@ public void processOpts() { if ( additionalProperties.containsKey(CodegenConstants.IMPL_FOLDER)) { implFolder = (String) additionalProperties.get(CodegenConstants.IMPL_FOLDER); } + + if (additionalProperties.containsKey(USE_TAGS)) { + this.setUseTags(Boolean.valueOf(additionalProperties.get(USE_TAGS).toString())); + } if ("joda".equals(dateLibrary)) { supportingFiles.add(new SupportingFile("JodaDateTimeProvider.mustache", (sourceFolder + '/' + apiPackage).replace(".", "/"), "JodaDateTimeProvider.java")); @@ -136,30 +144,38 @@ public Map postProcessModelsEnum(Map objs) { @Override public void addOperationToGroup(String tag, String resourcePath, Operation operation, CodegenOperation co, Map> operations) { - String basePath = resourcePath; - if (basePath.startsWith("/")) { - basePath = basePath.substring(1); - } - int pos = basePath.indexOf("/"); - if (pos > 0) { - basePath = basePath.substring(0, pos); - } + if (useTags) { + super.addOperationToGroup(tag, resourcePath, operation, co, operations); + } else { + String basePath = resourcePath; + if (basePath.startsWith("/")) { + basePath = basePath.substring(1); + } + int pos = basePath.indexOf("/"); + if (pos > 0) { + basePath = basePath.substring(0, pos); + } - if (basePath == "") { - basePath = "default"; - } else { - if (co.path.startsWith("/" + basePath)) { - co.path = co.path.substring(("/" + basePath).length()); + if (basePath == "") { + basePath = "default"; + } else { + if (co.path.startsWith("/" + basePath)) { + co.path = co.path.substring(("/" + basePath).length()); + } + co.subresourceOperation = !co.path.isEmpty(); } - co.subresourceOperation = !co.path.isEmpty(); - } - List opList = operations.get(basePath); - if (opList == null) { - opList = new ArrayList(); - operations.put(basePath, opList); + List opList = operations.get(basePath); + if (opList == null) { + opList = new ArrayList(); + operations.put(basePath, opList); + } + opList.add(co); + co.baseName = basePath; } - opList.add(co); - co.baseName = basePath; } - + + public void setUseTags(boolean useTags) { + this.useTags = useTags; + } + } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaPlayFrameworkCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaPlayFrameworkCodegen.java index 3577bd43fd1..45aa089a97e 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaPlayFrameworkCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaPlayFrameworkCodegen.java @@ -10,6 +10,8 @@ import java.io.File; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class JavaPlayFrameworkCodegen extends AbstractJavaCodegen implements BeanValidationFeatures { @@ -49,7 +51,7 @@ public JavaPlayFrameworkCodegen() { additionalProperties.put(CONFIG_PACKAGE, configPackage); additionalProperties.put(BASE_PACKAGE, basePackage); - + additionalProperties.put("java8", true); additionalProperties.put("jackson", "true"); cliOptions.add(new CliOption(TITLE, "server title name or client service name")); @@ -104,39 +106,33 @@ public void processOpts() { if (additionalProperties.containsKey(CONTROLLER_ONLY)) { this.setControllerOnly(convertPropertyToBoolean(CONTROLLER_ONLY)); - } else { - writePropertyBack(CONTROLLER_ONLY, controllerOnly); } + writePropertyBack(CONTROLLER_ONLY, controllerOnly); if (additionalProperties.containsKey(USE_BEANVALIDATION)) { this.setUseBeanValidation(convertPropertyToBoolean(USE_BEANVALIDATION)); - } else { - writePropertyBack(USE_BEANVALIDATION, useBeanValidation); } + writePropertyBack(USE_BEANVALIDATION, useBeanValidation); if (additionalProperties.containsKey(USE_INTERFACES)) { this.setUseInterfaces(convertPropertyToBoolean(USE_INTERFACES)); - } else { - writePropertyBack(USE_INTERFACES, useInterfaces); } + writePropertyBack(USE_INTERFACES, useInterfaces); if (additionalProperties.containsKey(HANDLE_EXCEPTIONS)) { this.setHandleExceptions(convertPropertyToBoolean(HANDLE_EXCEPTIONS)); - } else { - writePropertyBack(HANDLE_EXCEPTIONS, handleExceptions); } + writePropertyBack(HANDLE_EXCEPTIONS, handleExceptions); if (additionalProperties.containsKey(WRAP_CALLS)) { this.setWrapCalls(convertPropertyToBoolean(WRAP_CALLS)); - } else { - writePropertyBack(WRAP_CALLS, wrapCalls); } + writePropertyBack(WRAP_CALLS, wrapCalls); if (additionalProperties.containsKey(USE_SWAGGER_UI)) { this.setUseSwaggerUI(convertPropertyToBoolean(USE_SWAGGER_UI)); - } else { - writePropertyBack(USE_SWAGGER_UI, useSwaggerUI); } + writePropertyBack(USE_SWAGGER_UI, useSwaggerUI); //We don't use annotation anymore importMapping.remove("ApiModelProperty"); @@ -157,6 +153,9 @@ public void processOpts() { supportingFiles.add(new SupportingFile("routes.mustache", "conf", "routes")); //App/Utils folder + if (!this.controllerOnly && this.useInterfaces) { + supportingFiles.add(new SupportingFile("module.mustache", "app", "Module.java")); + } supportingFiles.add(new SupportingFile("swaggerUtils.mustache", "app/swagger", "SwaggerUtils.java")); if (this.handleExceptions) { supportingFiles.add(new SupportingFile("errorHandler.mustache", "app/swagger", "ErrorHandler.java")); @@ -177,9 +176,9 @@ public void processOpts() { apiTemplateFiles.put("newApiController.mustache", "Controller.java"); if (!this.controllerOnly) { apiTemplateFiles.put("newApi.mustache", "ControllerImp.java"); - } - if (this.useInterfaces) { - apiTemplateFiles.put("newApiInterface.mustache", "ControllerImpInterface.java"); + if (this.useInterfaces) { + apiTemplateFiles.put("newApiInterface.mustache", "ControllerImpInterface.java"); + } } additionalProperties.put("javaVersion", "1.8"); @@ -264,16 +263,27 @@ public Map postProcessOperations(Map objs) { } } - if (operation.path.contains("{")) { - operation.path = operation.path.replace("{", ":").replace("}", ""); + Pattern pathVariableMatcher = Pattern.compile("\\{([^}]+)}"); + Matcher match = pathVariableMatcher.matcher(operation.path); + while (match.find()) { + String completeMatch = match.group(); + String replacement = ":" + camelize(match.group(1), true); + operation.path = operation.path.replace(completeMatch, replacement); } if (operation.returnType != null) { + if (operation.returnType.equals("Boolean")) { + operation.vendorExtensions.put("missingReturnInfoIfNeeded", "true"); + } + if (operation.returnType.equals("BigDecimal")) { + operation.vendorExtensions.put("missingReturnInfoIfNeeded", "1.0"); + } if (operation.returnType.startsWith("List")) { String rt = operation.returnType; int end = rt.lastIndexOf(">"); if (end > 0) { operation.returnType = rt.substring("List<".length(), end).trim(); + operation.returnTypeIsPrimitive = languageSpecificPrimitives().contains(operation.returnType) || operation.returnType == null; operation.returnContainer = "List"; } } else if (operation.returnType.startsWith("Map")) { @@ -281,6 +291,7 @@ public Map postProcessOperations(Map objs) { int end = rt.lastIndexOf(">"); if (end > 0) { operation.returnType = rt.substring("Map<".length(), end).split(",")[1].trim(); + operation.returnTypeIsPrimitive = languageSpecificPrimitives().contains(operation.returnType) || operation.returnType == null; operation.returnContainer = "Map"; } } else if (operation.returnType.startsWith("Set")) { @@ -288,6 +299,7 @@ public Map postProcessOperations(Map objs) { int end = rt.lastIndexOf(">"); if (end > 0) { operation.returnType = rt.substring("Set<".length(), end).trim(); + operation.returnTypeIsPrimitive = languageSpecificPrimitives().contains(operation.returnType) || operation.returnType == null; operation.returnContainer = "Set"; } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaResteasyEapServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaResteasyEapServerCodegen.java index f9c9cad1380..50f6511ff33 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaResteasyEapServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaResteasyEapServerCodegen.java @@ -133,54 +133,7 @@ public void addOperationToGroup(String tag, String resourcePath, Operation opera @Override public Map postProcessOperations(Map objs) { - - Map operations = (Map) objs.get("operations"); - if (operations != null) { - List ops = (List) operations.get("operation"); - for (CodegenOperation operation : ops) { - if (operation.hasConsumes == Boolean.TRUE) { - Map firstType = operation.consumes.get(0); - if (firstType != null) { - if ("multipart/form-data".equals(firstType.get("mediaType"))) { - operation.isMultipart = Boolean.TRUE; - } - } - } - List responses = operation.responses; - if (responses != null) { - for (CodegenResponse resp : responses) { - if ("0".equals(resp.code)) { - resp.code = "200"; - } - } - } - if (operation.returnType == null) { - operation.returnType = "Void"; - } else if (operation.returnType.startsWith("List")) { - String rt = operation.returnType; - int end = rt.lastIndexOf(">"); - if (end > 0) { - operation.returnType = rt.substring("List<".length(), end).trim(); - operation.returnContainer = "List"; - } - } else if (operation.returnType.startsWith("Map")) { - String rt = operation.returnType; - int end = rt.lastIndexOf(">"); - if (end > 0) { - operation.returnType = rt.substring("Map<".length(), end).split(",")[1].trim(); - operation.returnContainer = "Map"; - } - } else if (operation.returnType.startsWith("Set")) { - String rt = operation.returnType; - int end = rt.lastIndexOf(">"); - if (end > 0) { - operation.returnType = rt.substring("Set<".length(), end).trim(); - operation.returnContainer = "Set"; - } - } - } - } - return objs; + return super.postProcessOperations(objs); } @Override diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaResteasyServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaResteasyServerCodegen.java index 7627884c407..4e9241a91cf 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaResteasyServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaResteasyServerCodegen.java @@ -132,54 +132,7 @@ public void addOperationToGroup(String tag, String resourcePath, Operation opera @Override public Map postProcessOperations(Map objs) { - - Map operations = (Map) objs.get("operations"); - if (operations != null) { - List ops = (List) operations.get("operation"); - for (CodegenOperation operation : ops) { - if (operation.hasConsumes == Boolean.TRUE) { - Map firstType = operation.consumes.get(0); - if (firstType != null) { - if ("multipart/form-data".equals(firstType.get("mediaType"))) { - operation.isMultipart = Boolean.TRUE; - } - } - } - List responses = operation.responses; - if (responses != null) { - for (CodegenResponse resp : responses) { - if ("0".equals(resp.code)) { - resp.code = "200"; - } - } - } - if (operation.returnType == null) { - operation.returnType = "Void"; - } else if (operation.returnType.startsWith("List")) { - String rt = operation.returnType; - int end = rt.lastIndexOf(">"); - if (end > 0) { - operation.returnType = rt.substring("List<".length(), end).trim(); - operation.returnContainer = "List"; - } - } else if (operation.returnType.startsWith("Map")) { - String rt = operation.returnType; - int end = rt.lastIndexOf(">"); - if (end > 0) { - operation.returnType = rt.substring("Map<".length(), end).split(",")[1].trim(); - operation.returnContainer = "Map"; - } - } else if (operation.returnType.startsWith("Set")) { - String rt = operation.returnType; - int end = rt.lastIndexOf(">"); - if (end > 0) { - operation.returnType = rt.substring("Set<".length(), end).trim(); - operation.returnContainer = "Set"; - } - } - } - } - return objs; + return super.postProcessOperations(objs); } @Override diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavascriptClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavascriptClientCodegen.java index 548155035f2..7c82c09a4cf 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavascriptClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavascriptClientCodegen.java @@ -52,7 +52,6 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo public static final String MODULE_NAME = "moduleName"; public static final String PROJECT_DESCRIPTION = "projectDescription"; public static final String PROJECT_VERSION = "projectVersion"; - public static final String PROJECT_LICENSE_NAME = "projectLicenseName"; public static final String USE_PROMISES = "usePromises"; public static final String USE_INHERITANCE = "useInheritance"; public static final String EMIT_MODEL_METHODS = "emitModelMethods"; @@ -84,7 +83,7 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo protected String moduleName; protected String projectDescription; protected String projectVersion; - protected String projectLicenseName; + protected String licenseName; protected String invokerPackage; protected String sourceFolder = "src"; @@ -96,7 +95,7 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo protected String modelDocPath = "docs/"; protected String apiTestPath = "api/"; protected String modelTestPath = "model/"; - protected boolean useES6 = false; // default is ES5 + protected boolean useES6 = true; // default is ES6 public JavascriptClientCodegen() { super(); @@ -179,7 +178,7 @@ public JavascriptClientCodegen() { "description of the project (Default: using info.description or \"Client library of \")")); cliOptions.add(new CliOption(PROJECT_VERSION, "version of the project (Default: using info.version or \"1.0.0\")")); - cliOptions.add(new CliOption(PROJECT_LICENSE_NAME, + cliOptions.add(new CliOption(CodegenConstants.LICENSE_NAME, "name of the license the project uses (Default: using info.license.name)")); cliOptions.add(new CliOption(USE_PROMISES, "use Promises as return values from the client API, instead of superagent callbacks") @@ -197,7 +196,7 @@ public JavascriptClientCodegen() { .defaultValue(Boolean.TRUE.toString())); cliOptions.add(new CliOption(USE_ES6, "use JavaScript ES6 (ECMAScript 6)") - .defaultValue(Boolean.FALSE.toString())); + .defaultValue(Boolean.TRUE.toString())); } @Override @@ -217,6 +216,11 @@ public String getHelp() { @Override public void processOpts() { + if (additionalProperties.containsKey(USE_ES6)) { + setUseES6(convertPropertyToBooleanAndWriteBack(USE_ES6)); + } else { + setUseES6(true); // default to ES6 + } super.processOpts(); // default HIDE_GENERATION_TIMESTAMP to true @@ -239,8 +243,8 @@ public void processOpts() { if (additionalProperties.containsKey(PROJECT_VERSION)) { setProjectVersion(((String) additionalProperties.get(PROJECT_VERSION))); } - if (additionalProperties.containsKey(PROJECT_LICENSE_NAME)) { - setProjectLicenseName(((String) additionalProperties.get(PROJECT_LICENSE_NAME))); + if (additionalProperties.containsKey(CodegenConstants.LICENSE_NAME)) { + setLicenseName(((String) additionalProperties.get(CodegenConstants.LICENSE_NAME))); } if (additionalProperties.containsKey(CodegenConstants.LOCAL_VARIABLE_PREFIX)) { setLocalVariablePrefix((String) additionalProperties.get(CodegenConstants.LOCAL_VARIABLE_PREFIX)); @@ -266,11 +270,6 @@ public void processOpts() { if (additionalProperties.containsKey(EMIT_JS_DOC)) { setEmitJSDoc(convertPropertyToBooleanAndWriteBack(EMIT_JS_DOC)); } - if (additionalProperties.containsKey(USE_ES6)) { - setUseES6(convertPropertyToBooleanAndWriteBack(USE_ES6)); - } else { - setUseES6(false); - } } @Override @@ -291,12 +290,11 @@ public void preprocessSwagger(Swagger swagger) { // when projectDescription is not specified, use info.description projectDescription = sanitizeName(info.getDescription()); } - if (additionalProperties.get(PROJECT_LICENSE_NAME) == null) { - // when projectLicense is not specified, use info.license - if (info.getLicense() != null) { - License license = info.getLicense(); - additionalProperties.put(PROJECT_LICENSE_NAME, sanitizeName(license.getName())); - } + + // when licenceName is not specified, use info.license + if (additionalProperties.get(CodegenConstants.LICENSE_NAME) == null && info.getLicense() != null) { + License license = info.getLicense(); + licenseName = license.getName(); } } @@ -313,11 +311,15 @@ public void preprocessSwagger(Swagger swagger) { if (projectDescription == null) { projectDescription = "Client library of " + projectName; } + if (StringUtils.isBlank(licenseName)) { + licenseName = "Unlicense"; + } additionalProperties.put(PROJECT_NAME, projectName); additionalProperties.put(MODULE_NAME, moduleName); additionalProperties.put(PROJECT_DESCRIPTION, escapeText(projectDescription)); additionalProperties.put(PROJECT_VERSION, projectVersion); + additionalProperties.put(CodegenConstants.LICENSE_NAME, licenseName); additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage); additionalProperties.put(CodegenConstants.INVOKER_PACKAGE, invokerPackage); additionalProperties.put(CodegenConstants.LOCAL_VARIABLE_PREFIX, localVariablePrefix); @@ -422,8 +424,8 @@ public void setProjectVersion(String projectVersion) { this.projectVersion = projectVersion; } - public void setProjectLicenseName(String projectLicenseName) { - this.projectLicenseName = projectLicenseName; + public void setLicenseName(String licenseName) { + this.licenseName = licenseName; } public void setUsePromises(boolean usePromises) { diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/KotlinClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/KotlinClientCodegen.java index 2df436d195a..e014f8a2504 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/KotlinClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/KotlinClientCodegen.java @@ -11,6 +11,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.Map; public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig { static Logger LOGGER = LoggerFactory.getLogger(KotlinClientCodegen.class); @@ -22,6 +23,7 @@ public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig protected String packageName = "io.swagger.client"; protected String apiDocPath = "docs/"; protected String modelDocPath = "docs/"; + protected CodegenConstants.ENUM_PROPERTY_NAMING_TYPE enumPropertyNaming = CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.camelCase; /** * Constructs an instance of `KotlinClientCodegen`. @@ -55,17 +57,26 @@ public KotlinClientCodegen() { )); // this includes hard reserved words defined by https://github.com/JetBrains/kotlin/blob/master/core/descriptors/src/org/jetbrains/kotlin/renderer/KeywordStringsGenerated.java - // as well as select soft (contextual) keywords + // as well as keywords from https://kotlinlang.org/docs/reference/keyword-reference.html reservedWords = new HashSet(Arrays.asList( "abstract", + "annotation", "as", "break", "case", "catch", "class", + "companion", + "const", + "constructor", "continue", + "crossinline", + "data", + "delegate", "do", "else", + "enum", + "external", "false", "final", "finally", @@ -73,20 +84,33 @@ public KotlinClientCodegen() { "fun", "if", "in", + "infix", + "init", + "inline", + "inner", "interface", + "internal", "is", "it", + "lateinit", "lazy", + "noinline", "null", "object", + "open", + "operator", + "out", "override", "package", "private", "protected", "public", + "reified", "return", "sealed", "super", + "suspend", + "tailrec", "this", "throw", "true", @@ -95,6 +119,7 @@ public KotlinClientCodegen() { "typeof", "val", "var", + "vararg", "when", "while" )); @@ -148,11 +173,17 @@ public KotlinClientCodegen() { importMapping.put("LocalDate", "java.time.LocalDate"); importMapping.put("LocalTime", "java.time.LocalTime"); + specialCharReplacements.put(";", "Semicolon"); + cliOptions.clear(); + cliOptions.add(new CliOption(CodegenConstants.SOURCE_FOLDER, CodegenConstants.SOURCE_FOLDER_DESC).defaultValue(sourceFolder)); cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Client package name (e.g. io.swagger).").defaultValue(this.packageName)); cliOptions.add(new CliOption(CodegenConstants.GROUP_ID, "Client package's organization (i.e. maven groupId).").defaultValue(groupId)); cliOptions.add(new CliOption(CodegenConstants.ARTIFACT_ID, "Client artifact id (name of generated jar).").defaultValue(artifactId)); cliOptions.add(new CliOption(CodegenConstants.ARTIFACT_VERSION, "Client package version.").defaultValue(artifactVersion)); + + CliOption enumPropertyNamingOpt = new CliOption(CodegenConstants.ENUM_PROPERTY_NAMING, CodegenConstants.ENUM_PROPERTY_NAMING_DESC); + cliOptions.add(enumPropertyNamingOpt.defaultValue(enumPropertyNaming.name())); } public CodegenType getTag() { @@ -191,8 +222,22 @@ public void setSourceFolder(String sourceFolder) { public void processOpts() { super.processOpts(); + if (additionalProperties.containsKey(CodegenConstants.ENUM_PROPERTY_NAMING)) { + setEnumPropertyNaming((String) additionalProperties.get(CodegenConstants.ENUM_PROPERTY_NAMING)); + } + + if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) { + this.setSourceFolder((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER)); + } else { + additionalProperties.put(CodegenConstants.SOURCE_FOLDER, sourceFolder); + } + if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { this.setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME)); + if (!additionalProperties.containsKey(CodegenConstants.MODEL_PACKAGE)) + this.setModelPackage(packageName + ".models"); + if (!additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) + this.setApiPackage(packageName + ".apis"); } else { additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName); } @@ -219,14 +264,6 @@ public void processOpts() { LOGGER.warn(CodegenConstants.INVOKER_PACKAGE + " with " + this.getName() + " generator is ignored. Use " + CodegenConstants.PACKAGE_NAME + "."); } - if (additionalProperties.containsKey(CodegenConstants.MODEL_PACKAGE)) { - LOGGER.warn(CodegenConstants.MODEL_PACKAGE + " with " + this.getName() + " generator is ignored. Setting this value independently of " + CodegenConstants.PACKAGE_NAME + " is not currently supported."); - } - - if (additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) { - LOGGER.warn(CodegenConstants.API_PACKAGE + " with " + this.getName() + " generator is ignored. Setting this value independently of " + CodegenConstants.PACKAGE_NAME + " is not currently supported."); - } - additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage()); additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage()); @@ -251,6 +288,27 @@ public void processOpts() { supportingFiles.add(new SupportingFile("infrastructure/Errors.kt.mustache", infrastructureFolder, "Errors.kt")); } + /** + * Sets the naming convention for Kotlin enum properties + * + * @param enumPropertyNamingType The string representation of the naming convention, as defined by {@link CodegenConstants.ENUM_PROPERTY_NAMING_TYPE} + */ + public void setEnumPropertyNaming(final String enumPropertyNamingType) { + try { + this.enumPropertyNaming = CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.valueOf(enumPropertyNamingType); + } catch (IllegalArgumentException ex) { + StringBuilder sb = new StringBuilder(enumPropertyNamingType + " is an invalid enum property naming option. Please choose from:"); + for (CodegenConstants.ENUM_PROPERTY_NAMING_TYPE t : CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.values()) { + sb.append("\n ").append(t.name()); + } + throw new RuntimeException(sb.toString()); + } + } + + public CodegenConstants.ENUM_PROPERTY_NAMING_TYPE getEnumPropertyNaming() { + return this.enumPropertyNaming; + } + @Override public String escapeUnsafeCharacters(String input) { return input.replace("*/", "*_/").replace("/*", "/_*"); @@ -285,7 +343,8 @@ public String modelFileFolder() { @Override public String escapeReservedWord(String name) { - return "_" + name; + // TODO: Allow enum escaping as an option (e.g. backticks vs append/prepend underscore vs match model property escaping). + return String.format("`%s`", name); } /** @@ -296,12 +355,25 @@ public String escapeReservedWord(String name) { * @return capitalized model name */ @Override - public String toModelName(String name) { - if(!name.startsWith("kotlin.") && !name.startsWith("java.")) { - return initialCaps(modelNamePrefix + name + modelNameSuffix); - } else { + public String toModelName(final String name) { + // Allow for explicitly configured kotlin.* and java.* types + if (name.startsWith("kotlin.") || name.startsWith("java.")) { return name; } + + // If importMapping contains name, assume this is a legitimate model name. + if (importMapping.containsKey(name)) { + return importMapping.get(name); + } + + String modifiedName = name.replaceAll("\\.", ""); + modifiedName = sanitizeKotlinSpecificNames(modifiedName); + + if (reservedWords.contains(modifiedName)) { + modifiedName = escapeReservedWord(modifiedName); + } + + return titleCase(modifiedName); } /** @@ -377,4 +449,89 @@ public String toModelImport(String name) { return name; } + + @Override + public Map postProcessModels(Map objs) { + return postProcessModelsEnum(super.postProcessModels(objs)); + } + + /** + * Return the sanitized variable name for enum + * + * @param value enum variable name + * @param datatype data type + * @return the sanitized variable name for enum + */ + @Override + public String toEnumVarName(String value, String datatype) { + String modified; + if (value.length() == 0) { + modified = "EMPTY"; + } else { + modified = value; + modified = sanitizeKotlinSpecificNames(modified); + } + + switch (getEnumPropertyNaming()) { + case original: + // NOTE: This is provided as a last-case allowance, but will still result in reserved words being escaped. + modified = value; + break; + case camelCase: + // NOTE: Removes hyphens and underscores + modified = camelize(modified, true); + break; + case PascalCase: + // NOTE: Removes hyphens and underscores + String result = camelize(modified); + modified = titleCase(result); + break; + case snake_case: + // NOTE: Removes hyphens + modified = underscore(modified); + break; + case UPPERCASE: + modified = modified.toUpperCase(); + break; + } + + if (reservedWords.contains(modified)) { + return escapeReservedWord(modified); + } + + return modified; + } + + private String titleCase(final String input) { + return input.substring(0, 1).toUpperCase() + input.substring(1); + } + + /** + * Sanitize against Kotlin specific naming conventions, which may differ from those required by {@link DefaultCodegen#sanitizeName}. + * + * @param name string to be sanitize + * @return sanitized string + */ + private String sanitizeKotlinSpecificNames(final String name) { + String word = name; + for (Map.Entry specialCharacters : specialCharReplacements.entrySet()) { + // Underscore is the only special character we'll allow + if (!specialCharacters.getKey().equals("_")) { + word = word.replaceAll("\\Q" + specialCharacters.getKey() + "\\E", specialCharacters.getValue()); + } + } + + // Fallback, replace unknowns with underscore. + word = word.replaceAll("\\W+", "_"); + if (word.matches("\\d.*")) { + word = "_" + word; + } + + // _, __, and ___ are reserved in Kotlin. Treat all names with only underscores consistently, regardless of count. + if (word.matches("^_*$")) { + word = word.replaceAll("\\Q_\\E", "Underscore"); + } + + return word; + } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/LuaClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/LuaClientCodegen.java new file mode 100644 index 00000000000..30288b8baf1 --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/LuaClientCodegen.java @@ -0,0 +1,522 @@ +package io.swagger.codegen.languages; + +import io.swagger.codegen.*; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.MapProperty; +import io.swagger.models.properties.Property; +import io.swagger.models.parameters.Parameter; + +import java.io.File; +import java.util.*; + +import org.apache.commons.lang3.StringUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LuaClientCodegen extends DefaultCodegen implements CodegenConfig { + static Logger LOGGER = LoggerFactory.getLogger(LuaClientCodegen.class); + + protected String packageName = "swagger"; + protected String packageVersion = "1.0.0"; + protected String apiDocPath = "docs/"; + protected String modelDocPath = "docs/"; + + public CodegenType getTag() { + return CodegenType.CLIENT; + } + + public String getName() { + return "lua"; + } + + public String getHelp() { + return "Generates a Lua client library (beta)."; + } + + public LuaClientCodegen() { + super(); + outputFolder = "generated-code/lua"; + modelTemplateFiles.put("model.mustache", ".lua"); + apiTemplateFiles.put("api.mustache", ".lua"); + + modelDocTemplateFiles.put("model_doc.mustache", ".md"); + apiDocTemplateFiles.put("api_doc.mustache", ".md"); + + embeddedTemplateDir = templateDir = "lua"; + + setReservedWordsLowerCase( + Arrays.asList( + // data type + "nil", "string", "boolean", "number", "userdata", "thread", + "table", + + // reserved words: http://www.lua.org/manual/5.1/manual.html#2.1 + "and", "break", "do", "else", "elseif", + "end", "false", "for", "function", "if", + "in", "local", "nil", "not", "or", + "repeat", "return", "then", "true", "until", "while" + ) + ); + + defaultIncludes = new HashSet( + Arrays.asList( + "map", + "array") + ); + + languageSpecificPrimitives = new HashSet( + Arrays.asList( + "nil", + "string", + "boolean", + "number") + ); + + instantiationTypes.clear(); + /*instantiationTypes.put("array", "LuaArray"); + instantiationTypes.put("map", "LuaMap");*/ + + typeMapping.clear(); + typeMapping.put("integer", "number"); + typeMapping.put("long", "number"); + typeMapping.put("number", "number"); + typeMapping.put("float", "number"); + typeMapping.put("double", "number"); + typeMapping.put("boolean", "boolean"); + typeMapping.put("string", "string"); + typeMapping.put("UUID", "string"); + typeMapping.put("date", "string"); + typeMapping.put("DateTime", "string"); + typeMapping.put("password", "string"); + typeMapping.put("file", "TODO_FILE_MAPPING"); + // map binary to string as a workaround + // the correct solution is to use []byte + typeMapping.put("binary", "string"); + typeMapping.put("ByteArray", "string"); + typeMapping.put("object", "TODO_OBJECT_MAPPING"); + + importMapping = new HashMap(); + importMapping.put("time.Time", "time"); + importMapping.put("*os.File", "os"); + importMapping.put("os", "io/ioutil"); + + cliOptions.clear(); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Lua package name (convention: lowercase).") + .defaultValue("swagger")); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_VERSION, "Lua package version.") + .defaultValue("1.0.0")); + cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, "hides the timestamp when files were generated") + .defaultValue(Boolean.TRUE.toString())); + + } + + @Override + public void processOpts() { + super.processOpts(); + + // default HIDE_GENERATION_TIMESTAMP to true + if (!additionalProperties.containsKey(CodegenConstants.HIDE_GENERATION_TIMESTAMP)) { + additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, Boolean.TRUE.toString()); + } else { + additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, + Boolean.valueOf(additionalProperties().get(CodegenConstants.HIDE_GENERATION_TIMESTAMP).toString())); + } + + if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { + setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME)); + } else { + setPackageName("swagger"); + } + + 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); + + apiTestTemplateFiles.clear(); // TODO: add api test template + modelTestTemplateFiles.clear(); // TODO: add model test template + + apiDocTemplateFiles.clear(); // TODO: add api doc template + modelDocTemplateFiles.clear(); // TODO: add model doc template + + modelPackage = packageName; + apiPackage = packageName; + + //supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); + supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); + //supportingFiles.add(new SupportingFile("configuration.mustache", "", "configuration.lua")); + //supportingFiles.add(new SupportingFile("api_client.mustache", "", "api_client.lua")); + //supportingFiles.add(new SupportingFile("api_response.mustache", "", "api_response.lua")); + //supportingFiles.add(new SupportingFile(".travis.yml", "", ".travis.yml")); + } + + @Override + public String escapeReservedWord(String name) + { + // Can't start with an underscore, as our fields need to start with an + // UppercaseLetter so that Lua treats them as public/visible. + + // Options? + // - MyName + // - AName + // - TheName + // - XName + // - X_Name + // ... or maybe a suffix? + // - Name_ ... think this will work. + if(this.reservedWordsMappings().containsKey(name)) { + return this.reservedWordsMappings().get(name); + } + return camelize(name) + '_'; + } + + @Override + public String apiFileFolder() { + return outputFolder + File.separator + "api" + File.separator; + } + + public String modelFileFolder() { + return outputFolder + File.separator + "model" + File.separator; + } + + @Override + public String toVarName(String name) { + // replace - with _ e.g. created-at => created_at + name = sanitizeName(name.replaceAll("-", "_")); + + // if it's all uppper case, do nothing + if (name.matches("^[A-Z_]*$")) + return name; + + // convert variable name to snake case + // 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) { + return toModelFilename(name); + } + + @Override + public String toModelFilename(String name) { + if (!StringUtils.isEmpty(modelNamePrefix)) { + name = modelNamePrefix + "_" + name; + } + + if (!StringUtils.isEmpty(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 + 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(String name) { + // replace - with _ e.g. created-at => created_at + name = name.replaceAll("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + + // e.g. PetApi.lua => pet_api.lua + return underscore(name) + "_api"; + } + + /** + * Overrides postProcessParameter to add a vendor extension "x-exportParamName". + * This is useful when paramName starts with a lowercase letter, but we need that + * param to be exportable (starts with an Uppercase letter). + * + * @param parameter CodegenParameter object to be processed. + */ + @Override + public void postProcessParameter(CodegenParameter parameter){ + + //// Give the base class a chance to process + //super.postProcessParameter(parameter); + + //char firstChar = parameter.paramName.charAt(0); + + //if (Character.isUpperCase(firstChar)) { + // // First char is already uppercase, just use paramName. + // parameter.vendorExtensions.put("x-exportParamName", parameter.paramName); + + //} + + //// It's a lowercase first char, let's convert it to uppercase + //StringBuilder sb = new StringBuilder(parameter.paramName); + //sb.setCharAt(0, Character.toUpperCase(firstChar)); + //parameter.vendorExtensions.put("x-exportParamName", sb.toString()); + } + + @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 toApiName(String name) { + return underscore(super.toApiName(name)); + } + + @Override + public String getTypeDeclaration(Property p) { + if(p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + Property inner = ap.getItems(); + return getTypeDeclaration(inner); + } else if (p instanceof MapProperty) { + MapProperty mp = (MapProperty) p; + Property inner = mp.getAdditionalProperties(); + return getTypeDeclaration(inner); + } + + // Not using the supertype invocation, because we want to UpperCamelize + // the type. + String swaggerType = getSwaggerType(p); + if (typeMapping.containsKey(swaggerType)) { + return typeMapping.get(swaggerType); + } + + if (typeMapping.containsValue(swaggerType)) { + return swaggerType; + } + + if (languageSpecificPrimitives.contains(swaggerType)) { + return swaggerType; + } + + return toModelName(swaggerType); + } + + @Override + public String getSwaggerType(Property p) { + String swaggerType = super.getSwaggerType(p); + String type = null; + if (typeMapping.containsKey(swaggerType)) { + type = typeMapping.get(swaggerType); + if (languageSpecificPrimitives.contains(type)) + return (type); + } else { + type = swaggerType; + } + return type; + } + + @Override + public String toOperationId(String operationId) { + String sanitizedOperationId = sanitizeName(operationId); + + // method name cannot use reserved keyword, e.g. return + if (isReservedWord(sanitizedOperationId)) { + LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + underscore("call_" + operationId)); + sanitizedOperationId = "call_" + sanitizedOperationId; + } + + return underscore(sanitizedOperationId); + } + + @Override + public Map postProcessOperations(Map objs) { + @SuppressWarnings("unchecked") + Map objectMap = (Map) objs.get("operations"); + @SuppressWarnings("unchecked") + List operations = (List) objectMap.get("operation"); + for (CodegenOperation op: operations) { + + String[] items = op.path.split("/", -1); + String luaPath = ""; + int pathParamIndex = 0; + + for (int i = 0; i < items.length; ++i) { + if (items[i].matches("^\\{(.*)\\}$")) { // wrap in {} + // find the datatype of the parameter + //final CodegenParameter cp = op.pathParams.get(pathParamIndex); + // TODO: Handle non-primitives… + //luaPath = luaPath + cp.dataType.toLowerCase(); + luaPath = luaPath + "/%s"; + pathParamIndex++; + } else if (items[i].length() != 0) { + luaPath = luaPath + "/" + items[i]; + } else { + //luaPath = luaPath + "/"; + } + } + op.vendorExtensions.put("x-codegen-path", luaPath); + } + return objs; + } + + @Override + public Map postProcessModels(Map objs) { + // remove model imports to avoid error + List> imports = (List>) objs.get("imports"); + final String prefix = modelPackage(); + Iterator> iterator = imports.iterator(); + while (iterator.hasNext()) { + String _import = iterator.next().get("import"); + if (_import.startsWith(prefix)) + iterator.remove(); + } + + // recursively add import for mapping one type to multiple imports + List> recursiveImports = (List>) objs.get("imports"); + if (recursiveImports == null) + return objs; + + ListIterator> listIterator = imports.listIterator(); + while (listIterator.hasNext()) { + String _import = listIterator.next().get("import"); + // if the import package happens to be found in the importMapping (key) + // add the corresponding import package to the list + if (importMapping.containsKey(_import)) { + listIterator.add(createMapping("import", importMapping.get(_import))); + } + } + + return postProcessModelsEnum(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("]]", "] ]"); + } + + public Map createMapping(String key, String value){ + Map customImport = new HashMap(); + customImport.put(key, value); + + return customImport; + } + + @Override + public String toEnumValue(String value, String datatype) { + if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { + return value; + } else { + return escapeText(value); + } + } + + @Override + public String toEnumDefaultValue(String value, String datatype) { + return datatype + "_" + value; + } + + @Override + public String toEnumVarName(String name, String datatype) { + if (name.length() == 0) { + return "EMPTY"; + } + + // number + if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { + String varName = name; + varName = varName.replaceAll("-", "MINUS_"); + varName = varName.replaceAll("\\+", "PLUS_"); + varName = varName.replaceAll("\\.", "_DOT_"); + return varName; + } + + // for symbol, e.g. $, # + if (getSymbolName(name) != null) { + return getSymbolName(name).toUpperCase(); + } + + // string + String enumName = sanitizeName(underscore(name).toUpperCase()); + enumName = enumName.replaceFirst("^_", ""); + enumName = enumName.replaceFirst("_$", ""); + + if (isReservedWord(enumName) || enumName.matches("\\d.*")) { // reserved word or starts with number + return escapeReservedWord(enumName); + } else { + return enumName; + } + } + + @Override + public String toEnumName(CodegenProperty property) { + String enumName = underscore(toModelName(property.name)).toUpperCase(); + + // remove [] for array or map of enum + enumName = enumName.replace("[]", ""); + + if (enumName.matches("\\d.*")) { // starts with number + return "_" + enumName; + } else { + return enumName; + } + } +} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/LumenServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/LumenServerCodegen.java index 5dd35ba1b0f..b8cdadbd07e 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/LumenServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/LumenServerCodegen.java @@ -104,6 +104,15 @@ public Map postProcessOperations(Map objs) { Map objectMap = (Map) objs.get("operations"); @SuppressWarnings("unchecked") List operations = (List) objectMap.get("operation"); + + for (CodegenOperation op : operations) { + op.httpMethod = op.httpMethod.toLowerCase(); + // check to see if the path contains ".", which is not supported by Lumen + // ref: https://github.com/swagger-api/swagger-codegen/issues/6897 + if (op.path != null && op.path.contains(".")) { + throw new IllegalArgumentException("'.' (dot) is not supported by PHP Lumen. Please refer to https://github.com/swagger-api/swagger-codegen/issues/6897 for more info."); + } + } // sort the endpoints in ascending to avoid the route priority issure. // https://github.com/swagger-api/swagger-codegen/issues/2643 diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/NancyFXServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/NancyFXServerCodegen.java index 3d21bed182a..8acdbe5cb97 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/NancyFXServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/NancyFXServerCodegen.java @@ -45,6 +45,8 @@ public class NancyFXServerCodegen extends AbstractCSharpCodegen { private static final String MODEL_NAMESPACE = "Models"; private static final String IMMUTABLE_OPTION = "immutable"; private static final String USE_BASE_PATH = "writeModulePath"; + private static final String PACKAGE_CONTEXT = "packageContext"; + private static final String ASYNC_SERVER = "asyncServer"; private static final Map> propertyToSwaggerTypeMapping = createPropertyToSwaggerTypeMapping(); @@ -56,6 +58,9 @@ public class NancyFXServerCodegen extends AbstractCSharpCodegen { private final Multimap childrenByParent = ArrayListMultimap.create(); private final BiMap modelNameMapping = HashBiMap.create(); + /** If set to true, we will generate c# async endpoints and service interfaces */ + private boolean asyncServer = false; + public NancyFXServerCodegen() { outputFolder = "generated-code" + File.separator + getName(); apiTemplateFiles.put("api.mustache", ".cs"); @@ -76,6 +81,7 @@ public NancyFXServerCodegen() { addOption(SOURCE_FOLDER, SOURCE_FOLDER_DESC, sourceFolder); addOption(INTERFACE_PREFIX, INTERFACE_PREFIX_DESC, interfacePrefix); addOption(OPTIONAL_PROJECT_GUID,OPTIONAL_PROJECT_GUID_DESC, null); + addOption(PACKAGE_CONTEXT, "Optionally overrides the PackageContext which determines the namespace (namespace=packageName.packageContext). If not set, packageContext will default to basePath.", null); // CLI Switches addSwitch(SORT_PARAMS_BY_REQUIRED_FLAG, SORT_PARAMS_BY_REQUIRED_FLAG_DESC, sortParamsByRequiredFlag); @@ -85,6 +91,7 @@ public NancyFXServerCodegen() { addSwitch(RETURN_ICOLLECTION, RETURN_ICOLLECTION_DESC, returnICollection); addSwitch(IMMUTABLE_OPTION, "Enabled by default. If disabled generates model classes with setters", true); addSwitch(USE_BASE_PATH, "Enabled by default. If disabled, module paths will not mirror api base path", true); + addSwitch(ASYNC_SERVER, "Set to true to enable the generation of async routes/endpoints.", this.asyncServer); typeMapping.putAll(nodaTimeTypesMappings()); languageSpecificPrimitives.addAll(nodaTimePrimitiveTypes()); @@ -114,6 +121,7 @@ public void processOpts() { modelPackage = isNullOrEmpty(packageName) ? MODEL_NAMESPACE : packageName + "." + MODEL_NAMESPACE; supportingFiles.add(new SupportingFile("parameters.mustache", sourceFile("Utils"), "Parameters.cs")); + supportingFiles.add(new SupportingFile("localDateConverter.mustache", sourceFile("Utils"), "LocalDateConverter.cs")); supportingFiles.add(new SupportingFile("packages.config.mustache", sourceFolder(), "packages.config")); supportingFiles.add(new SupportingFile("nuspec.mustache", sourceFolder(), packageName + ".nuspec")); @@ -126,6 +134,12 @@ public void processOpts() { setPackageGuid((String) additionalProperties.get(OPTIONAL_PROJECT_GUID)); } + if (additionalProperties.containsKey(ASYNC_SERVER)) { + setAsyncServer(convertPropertyToBooleanAndWriteBack(ASYNC_SERVER)); + } else { + additionalProperties.put(ASYNC_SERVER, this.asyncServer); + } + additionalProperties.put("packageGuid", packageGuid); setupModelTemplate(); @@ -197,7 +211,11 @@ private String sourceFile(final String fileName) { public void setPackageGuid(String packageGuid) { this.packageGuid = packageGuid; } - + + public void setAsyncServer(boolean asyncServer) { + this.asyncServer = asyncServer; + } + @Override public String apiFileFolder() { return outputFolder + File.separator + sourceFolder() + File.separator + API_NAMESPACE; @@ -335,7 +353,8 @@ public String toModelName(final String name) { @Override public void preprocessSwagger(final Swagger swagger) { - additionalProperties.put("packageContext", sanitizeName(swagger.getBasePath())); + final String packageContextOption = (String) additionalProperties.get(PACKAGE_CONTEXT); + additionalProperties.put("packageContext", packageContextOption == null ? sanitizeName(swagger.getBasePath()) : packageContextOption); final Object basePathOption = additionalProperties.get(USE_BASE_PATH); additionalProperties.put("baseContext", basePathOption == null ? swagger.getBasePath() : "/"); } @@ -373,12 +392,12 @@ public boolean apply(Property property) { private static Map nodaTimeTypesMappings() { return ImmutableMap.of( "time", "LocalTime?", - "date", "ZonedDateTime?", + "date", "LocalDate?", "datetime", "ZonedDateTime?"); } private static Set nodaTimePrimitiveTypes() { - return ImmutableSet.of("LocalTime?", "ZonedDateTime?"); + return ImmutableSet.of("LocalTime?", "LocalDate?","ZonedDateTime?"); } private class DependencyInfo { diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/NodeJSServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/NodeJSServerCodegen.java index bd1d988e90a..aebcdea0c58 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/NodeJSServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/NodeJSServerCodegen.java @@ -19,11 +19,12 @@ import java.math.BigDecimal; import java.util.*; import java.util.Map.Entry; +import java.util.regex.Pattern; public class NodeJSServerCodegen extends DefaultCodegen implements CodegenConfig { private static final Logger LOGGER = LoggerFactory.getLogger(NodeJSServerCodegen.class); - + protected String implFolder = "service"; public static final String GOOGLE_CLOUD_FUNCTIONS = "googleCloudFunctions"; public static final String EXPORTED_NAME = "exportedName"; @@ -81,16 +82,19 @@ public NodeJSServerCodegen() { */ additionalProperties.put("apiVersion", apiVersion); additionalProperties.put("serverPort", serverPort); + additionalProperties.put("implFolder", implFolder); + + supportingFiles.add(new SupportingFile("writer.mustache", ("utils").replace(".", "/"), "writer.js")); cliOptions.add(CliOption.newBoolean(GOOGLE_CLOUD_FUNCTIONS, - "When specified, it will generate the code which runs within Google Cloud Functions " - + "instead of standalone Node.JS server. See " - + "https://cloud.google.com/functions/docs/quickstart for the details of how to " - + "deploy the generated code.")); + "When specified, it will generate the code which runs within Google Cloud Functions " + + "instead of standalone Node.JS server. See " + + "https://cloud.google.com/functions/docs/quickstart for the details of how to " + + "deploy the generated code.")); cliOptions.add(new CliOption(EXPORTED_NAME, - "When the generated code will be deployed to Google Cloud Functions, this option can be " - + "used to update the name of the exported function. By default, it refers to the " - + "basePath. This does not affect normal standalone nodejs server code.")); + "When the generated code will be deployed to Google Cloud Functions, this option can be " + + "used to update the name of the exported function. By default, it refers to the " + + "basePath. This does not affect normal standalone nodejs server code.")); } @Override @@ -145,6 +149,23 @@ public String toApiFilename(String name) { return toApiName(name); } + + @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); + } + return result; + } + + private String implFileFolder(String output) { + return outputFolder + "/" + output + "/" + apiPackage().replace('.', '/'); + } + /** * 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 @@ -259,7 +280,7 @@ public void processOpts() { if (additionalProperties.containsKey(GOOGLE_CLOUD_FUNCTIONS)) { setGoogleCloudFunctions( - Boolean.valueOf(additionalProperties.get(GOOGLE_CLOUD_FUNCTIONS).toString())); + Boolean.valueOf(additionalProperties.get(GOOGLE_CLOUD_FUNCTIONS).toString())); } if (additionalProperties.containsKey(EXPORTED_NAME)) { @@ -276,8 +297,8 @@ public void processOpts() { // "controller.js") // ); supportingFiles.add(new SupportingFile("swagger.mustache", - "api", - "swagger.yaml") + "api", + "swagger.yaml") ); if (getGoogleCloudFunctions()) { writeOptional(outputFolder, new SupportingFile("index-gcf.mustache", "", "index.js")); @@ -326,14 +347,14 @@ public void preprocessSwagger(Swagger swagger) { if (!host.endsWith(".cloudfunctions.net")) { LOGGER.warn("Host " + host + " seems not matching with cloudfunctions.net URL."); } - if (!additionalProperties.containsKey(EXPORTED_NAME)) { + if (!additionalProperties.containsKey(EXPORTED_NAME)) { String basePath = swagger.getBasePath(); if (basePath == null || basePath.equals("/")) { LOGGER.warn("Cannot find the exported name properly. Using 'openapi' as the exported name"); basePath = "/openapi"; } additionalProperties.put(EXPORTED_NAME, basePath.substring(1)); - } + } } // need vendor extensions for x-swagger-router-controller @@ -361,7 +382,7 @@ public void preprocessSwagger(Swagger swagger) { } } - @Override + @Override public Map postProcessSupportingFileData(Map objs) { Swagger swagger = (Swagger)objs.get("swagger"); if(swagger != null) { @@ -370,7 +391,7 @@ public Map postProcessSupportingFileData(Map obj module.addSerializer(Double.class, new JsonSerializer() { @Override public void serialize(Double val, JsonGenerator jgen, - SerializerProvider provider) throws IOException, JsonProcessingException { + SerializerProvider provider) throws IOException, JsonProcessingException { jgen.writeNumber(new BigDecimal(val)); } }); diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ObjcClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ObjcClientCodegen.java index 2378489bc06..e13208951d1 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ObjcClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ObjcClientCodegen.java @@ -24,7 +24,7 @@ public class ObjcClientCodegen extends DefaultCodegen implements CodegenConfig { public static final String GIT_REPO_URL = "gitRepoURL"; public static final String DEFAULT_LICENSE = "Proprietary"; public static final String CORE_DATA = "coreData"; - + protected Set foundationClasses = new HashSet(); protected String podName = "SwaggerClient"; protected String podVersion = "1.0.0"; @@ -41,7 +41,7 @@ public class ObjcClientCodegen extends DefaultCodegen implements CodegenConfig { protected String apiFilesPath = "Api/"; protected boolean generateCoreData = false; - + protected Set advancedMapingTypes = new HashSet(); public ObjcClientCodegen() { @@ -255,7 +255,7 @@ public void processOpts() { supportingFiles.add(new SupportingFile("Object-body.mustache", coreFileFolder(), classPrefix + "Object.m")); supportingFiles.add(new SupportingFile("QueryParamCollection-header.mustache", coreFileFolder(), classPrefix + "QueryParamCollection.h")); supportingFiles.add(new SupportingFile("QueryParamCollection-body.mustache", coreFileFolder(), classPrefix + "QueryParamCollection.m")); - supportingFiles.add(new SupportingFile("ApiClient-header.mustache", coreFileFolder(), classPrefix + "ApiClient.h")); + supportingFiles.add(new SupportingFile("ApiClient-header.mustache", coreFileFolder(), classPrefix + "ApiClient.h")); supportingFiles.add(new SupportingFile("ApiClient-body.mustache", coreFileFolder(), classPrefix + "ApiClient.m")); supportingFiles.add(new SupportingFile("JSONRequestSerializer-body.mustache", coreFileFolder(), classPrefix + "JSONRequestSerializer.m")); supportingFiles.add(new SupportingFile("JSONRequestSerializer-header.mustache", coreFileFolder(), classPrefix + "JSONRequestSerializer.h")); @@ -308,6 +308,12 @@ public String getTypeDeclaration(String name) { public String getSwaggerType(Property p) { String swaggerType = super.getSwaggerType(p); String type = null; + + if (swaggerType == null) { + swaggerType = ""; // set swagger type to empty string if null + } + + // TODO avoid using toLowerCase as typeMapping should be case-sensitive if (typeMapping.containsKey(swaggerType.toLowerCase())) { type = typeMapping.get(swaggerType.toLowerCase()); if (languageSpecificPrimitives.contains(type) && !foundationClasses.contains(type)) { @@ -348,7 +354,7 @@ public String getTypeDeclaration(Property p) { Property inner = mp.getAdditionalProperties(); String innerTypeDeclaration = getTypeDeclaration(inner); - + if (innerTypeDeclaration.endsWith("*")) { innerTypeDeclaration = innerTypeDeclaration.substring(0, innerTypeDeclaration.length() - 1); } @@ -464,17 +470,17 @@ public String toModelImport(String name) { public String apiDocFileFolder() { return (outputFolder + "/" + apiDocPath).replace("/", File.separator); } - + @Override public String modelDocFileFolder() { return (outputFolder + "/" + modelDocPath).replace("/", File.separator); } - + @Override public String toModelDocFilename(String name) { return toModelName(name); } - + @Override public String toApiDocFilename(String name) { return toApiName(name); @@ -544,9 +550,9 @@ public String toParamName(String name) { * 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) { + public String escapeReservedWord(String name) { if(this.reservedWordsMappings().containsKey(name)) { return this.reservedWordsMappings().get(name); } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/PhpClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/PhpClientCodegen.java index a9286f58fe8..d0a87381cde 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/PhpClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/PhpClientCodegen.java @@ -117,6 +117,7 @@ public PhpClientCodegen() { typeMapping.put("string", "string"); typeMapping.put("byte", "int"); typeMapping.put("boolean", "bool"); + typeMapping.put("date", "\\DateTime"); typeMapping.put("Date", "\\DateTime"); typeMapping.put("DateTime", "\\DateTime"); typeMapping.put("file", "\\SplFileObject"); @@ -300,12 +301,12 @@ public void processOpts() { // make test path available in mustache template additionalProperties.put("testBasePath", testBasePath); - supportingFiles.add(new SupportingFile("configuration.mustache", toPackagePath(invokerPackage, srcBasePath), "Configuration.php")); - supportingFiles.add(new SupportingFile("ApiClient.mustache", toPackagePath(invokerPackage, srcBasePath), "ApiClient.php")); supportingFiles.add(new SupportingFile("ApiException.mustache", toPackagePath(invokerPackage, srcBasePath), "ApiException.php")); + supportingFiles.add(new SupportingFile("Configuration.mustache", toPackagePath(invokerPackage, srcBasePath), "Configuration.php")); supportingFiles.add(new SupportingFile("ObjectSerializer.mustache", toPackagePath(invokerPackage, srcBasePath), "ObjectSerializer.php")); + supportingFiles.add(new SupportingFile("ModelInterface.mustache", toPackagePath(modelPackage, srcBasePath), "ModelInterface.php")); + supportingFiles.add(new SupportingFile("HeaderSelector.mustache", toPackagePath(invokerPackage, srcBasePath), "HeaderSelector.php")); supportingFiles.add(new SupportingFile("composer.mustache", getPackagePath(), "composer.json")); - supportingFiles.add(new SupportingFile("autoload.mustache", getPackagePath(), "autoload.php")); supportingFiles.add(new SupportingFile("README.mustache", getPackagePath(), "README.md")); supportingFiles.add(new SupportingFile("phpunit.xml.mustache", getPackagePath(), "phpunit.xml.dist")); supportingFiles.add(new SupportingFile(".travis.yml", getPackagePath(), ".travis.yml")); @@ -626,6 +627,8 @@ public void setParameterExampleValue(CodegenParameter p) { example = "2013-10-20T19:20:30+01:00"; } example = "new \\DateTime(\"" + escapeText(example) + "\")"; + } else if ("object".equals(type)) { + example = "new \\stdClass"; } else if (!languageSpecificPrimitives.contains(type)) { // type is a model class, e.g. User example = "new " + getTypeDeclaration(type) + "()"; diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/PistacheServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/PistacheServerCodegen.java index 3d85e81b76f..aa4825fd590 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/PistacheServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/PistacheServerCodegen.java @@ -118,7 +118,7 @@ public void processOpts() { /** * 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 reseved words + * matches the reserved words * * @return the escaped term */ diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/PowerShellClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/PowerShellClientCodegen.java index dcacc549406..13ca320a0b7 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/PowerShellClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/PowerShellClientCodegen.java @@ -153,7 +153,7 @@ public PowerShellClientCodegen() { cliOptions.clear(); cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Client package name (e.g. io.swagger.client).").defaultValue(this.packageName)); cliOptions.add(new CliOption(CodegenConstants.OPTIONAL_PROJECT_GUID, "GUID for PowerShell module (e.g. a27b908d-2a20-467f-bc32-af6f3a654ac5). A random GUID will be generated by default.")); - cliOptions.add(new CliOption("csharpClientPath", "Path to the C# API client generated by Swagger Codegen, e.g. $ScriptDir\\..\\csharp\\SwaggerClient where $ScriptDir is the current directory.").defaultValue(this.csharpClientPath)); + cliOptions.add(new CliOption("csharpClientPath", "Path to the C# API client generated by Swagger Codegen, e.g. $ScriptDir\\..\\csharp\\SwaggerClient where $ScriptDir is the current directory. NOTE: you will need to generate the C# API client separately.").defaultValue(this.csharpClientPath)); } @@ -166,7 +166,7 @@ public String getName() { } public String getHelp() { - return "Generates a PowerShell API client (beta)."; + return "Generates a PowerShell API client (beta). (The dependency C# API client needs to be generated separately."; } public void setPackageName(String packageName) { diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/PythonClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/PythonClientCodegen.java index 1dd62fdf189..0954957f19f 100755 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/PythonClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/PythonClientCodegen.java @@ -21,8 +21,11 @@ import org.apache.commons.lang3.StringUtils; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig { public static final String PACKAGE_URL = "packageUrl"; + public static final String DEFAULT_LIBRARY = "urllib3"; protected String packageName; // e.g. petstore_api protected String packageVersion; @@ -42,6 +45,7 @@ public PythonClientCodegen() { // at the moment importMapping.clear(); + supportsInheritance = true; modelPackage = "models"; apiPackage = "api"; outputFolder = "generated-code" + File.separatorChar + "python"; @@ -63,6 +67,7 @@ public PythonClientCodegen() { languageSpecificPrimitives.add("int"); languageSpecificPrimitives.add("float"); languageSpecificPrimitives.add("list"); + languageSpecificPrimitives.add("dict"); languageSpecificPrimitives.add("bool"); languageSpecificPrimitives.add("str"); languageSpecificPrimitives.add("datetime"); @@ -90,7 +95,7 @@ public PythonClientCodegen() { // map uuid to string for the time being typeMapping.put("UUID", "str"); - // from https://docs.python.org/release/2.5.4/ref/keywords.html + // from https://docs.python.org/3/reference/lexical_analysis.html#keywords setReservedWordsLowerCase( Arrays.asList( // local variable name used in API methods (endpoints) @@ -102,7 +107,7 @@ public PythonClientCodegen() { "and", "del", "from", "not", "while", "as", "elif", "global", "or", "with", "assert", "else", "if", "pass", "yield", "break", "except", "import", "print", "class", "exec", "in", "raise", "continue", "finally", "is", - "return", "def", "for", "lambda", "try", "self", "None")); + "return", "def", "for", "lambda", "try", "self", "nonlocal", "None", "True", "False")); regexModifiers = new HashMap(); regexModifiers.put('i', "IGNORECASE"); @@ -123,6 +128,14 @@ public PythonClientCodegen() { CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG_DESC).defaultValue(Boolean.TRUE.toString())); cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, "hides the timestamp when files were generated") .defaultValue(Boolean.TRUE.toString())); + + supportedLibraries.put("urllib3", "urllib3-based client"); + supportedLibraries.put("asyncio", "Asyncio-based client (python 3.5+)"); + supportedLibraries.put("tornado", "tornado-based client"); + CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "library template (sub-template) to use"); + libraryOption.setDefault(DEFAULT_LIBRARY); + cliOptions.add(libraryOption); + setLibrary(DEFAULT_LIBRARY); } @Override @@ -177,24 +190,16 @@ public void processOpts() { setPackageUrl((String) additionalProperties.get(PACKAGE_URL)); } - String swaggerFolder = packageName; - - modelPackage = swaggerFolder + File.separatorChar + "models"; - apiPackage = swaggerFolder + File.separatorChar + "apis"; - supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); - supportingFiles.add(new SupportingFile("setup.mustache", "", "setup.py")); supportingFiles.add(new SupportingFile("tox.mustache", "", "tox.ini")); supportingFiles.add(new SupportingFile("test-requirements.mustache", "", "test-requirements.txt")); supportingFiles.add(new SupportingFile("requirements.mustache", "", "requirements.txt")); - supportingFiles.add(new SupportingFile("api_client.mustache", swaggerFolder, "api_client.py")); - supportingFiles.add(new SupportingFile("rest.mustache", swaggerFolder, "rest.py")); - supportingFiles.add(new SupportingFile("configuration.mustache", swaggerFolder, "configuration.py")); - supportingFiles.add(new SupportingFile("__init__package.mustache", swaggerFolder, "__init__.py")); - supportingFiles.add(new SupportingFile("__init__model.mustache", modelPackage, "__init__.py")); - supportingFiles.add(new SupportingFile("__init__api.mustache", apiPackage, "__init__.py")); + supportingFiles.add(new SupportingFile("configuration.mustache", packageName, "configuration.py")); + supportingFiles.add(new SupportingFile("__init__package.mustache", packageName, "__init__.py")); + supportingFiles.add(new SupportingFile("__init__model.mustache", packageName + File.separatorChar + modelPackage, "__init__.py")); + supportingFiles.add(new SupportingFile("__init__api.mustache", packageName + File.separatorChar + apiPackage, "__init__.py")); if(Boolean.FALSE.equals(excludeTests)) { supportingFiles.add(new SupportingFile("__init__test.mustache", testFolder, "__init__.py")); @@ -202,12 +207,43 @@ public void processOpts() { supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); supportingFiles.add(new SupportingFile("travis.mustache", "", ".travis.yml")); + supportingFiles.add(new SupportingFile("setup.mustache", "", "setup.py")); + supportingFiles.add(new SupportingFile("api_client.mustache", packageName, "api_client.py")); + + if ("asyncio".equals(getLibrary())) { + supportingFiles.add(new SupportingFile("asyncio/rest.mustache", packageName, "rest.py")); + additionalProperties.put("asyncio", "true"); + } else if ("tornado".equals(getLibrary())) { + supportingFiles.add(new SupportingFile("tornado/rest.mustache", packageName, "rest.py")); + additionalProperties.put("tornado", "true"); + } else { + supportingFiles.add(new SupportingFile("rest.mustache", packageName, "rest.py")); + } + + modelPackage = packageName + "." + modelPackage; + apiPackage = packageName + "." + apiPackage; + } private static String dropDots(String str) { return str.replaceAll("\\.", "_"); } + @Override + public String toModelImport(String name) { + String modelImport; + if (StringUtils.startsWithAny(name,"import", "from")) { + modelImport = name; + } else { + modelImport = "from "; + if (!"".equals(modelPackage())) { + modelImport += modelPackage() + "."; + } + modelImport += toModelFilename(name)+ " import " + name; + } + return modelImport; + } + @Override public Map postProcessModels(Map objs) { // process enum in models @@ -459,7 +495,7 @@ public String toApiFilename(String name) { // replace - with _ e.g. created-at => created_at name = name.replaceAll("-", "_"); - // e.g. PhoneNumberApi.rb => phone_number_api.rb + // e.g. PhoneNumberApi.py => phone_number_api.py return underscore(name) + "_api"; } @@ -649,6 +685,11 @@ public void setParameterExampleValue(CodegenParameter p) { p.example = example; } + @Override + public String sanitizeTag(String tag) { + return sanitizeName(tag); + } + @Override public String escapeQuotationMark(String input) { // remove ' to avoid code injection diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Qt5CPPGenerator.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Qt5CPPGenerator.java index e97ae1a4608..2637409f2fe 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Qt5CPPGenerator.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Qt5CPPGenerator.java @@ -23,7 +23,7 @@ import java.util.Map; import java.util.Set; -public class Qt5CPPGenerator extends DefaultCodegen implements CodegenConfig { +public class Qt5CPPGenerator extends AbstractCppCodegen implements CodegenConfig { public static final String CPP_NAMESPACE = "cppNamespace"; public static final String CPP_NAMESPACE_DESC = "C++ namespace (convention: name::space::for::api)."; @@ -42,6 +42,11 @@ public Qt5CPPGenerator() { // set the output folder here outputFolder = "generated-code/qt5cpp"; + // set modelNamePrefix as default for QT5CPP + if (modelNamePrefix == "") { + modelNamePrefix = PREFIX; + } + /* * Models. You can write model files using the modelTemplateFiles map. * if you want to create one template for file, you can do so here. @@ -78,15 +83,6 @@ public Qt5CPPGenerator() { // CLI options addOption(CPP_NAMESPACE, CPP_NAMESPACE_DESC, this.cppNamespace); - /* - * Reserved words. Override this with reserved words specific to your language - */ - setReservedWordsLowerCase( - Arrays.asList( - "sample1", // replace with static values - "sample2") - ); - /* * Additional Properties. These values can be passed to the templates and * are available in models, apis, and supporting files @@ -225,7 +221,7 @@ public String toModelImport(String name) { /** * 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 reseved words + * those terms here. This logic is only called if a variable matches the reserved words * * @return the escaped term */ @@ -257,7 +253,7 @@ public String apiFileFolder() { @Override public String toModelFilename(String name) { - return PREFIX + initialCaps(name); + return modelNamePrefix + initialCaps(name); } @Override @@ -321,6 +317,9 @@ public String toDefaultValue(Property p) { } else if (p instanceof MapProperty) { MapProperty ap = (MapProperty) p; String inner = getSwaggerType(ap.getAdditionalProperties()); + if (!languageSpecificPrimitives.contains(inner)) { + inner += "*"; + } return "new QMap()"; } else if (p instanceof ArrayProperty) { ArrayProperty ap = (ArrayProperty) p; @@ -372,7 +371,7 @@ public String toModelName(String type) { languageSpecificPrimitives.contains(type)) { return type; } else { - return PREFIX + Character.toUpperCase(type.charAt(0)) + type.substring(1); + return modelNamePrefix + Character.toUpperCase(type.charAt(0)) + type.substring(1); } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RClientCodegen.java new file mode 100644 index 00000000000..5ccb632629d --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RClientCodegen.java @@ -0,0 +1,453 @@ +package io.swagger.codegen.languages; + +import io.swagger.codegen.*; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.MapProperty; +import io.swagger.models.properties.Property; +import io.swagger.models.parameters.Parameter; + +import java.io.File; +import java.util.*; + +import org.apache.commons.lang3.StringUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RClientCodegen extends DefaultCodegen implements CodegenConfig { + static Logger LOGGER = LoggerFactory.getLogger(RClientCodegen.class); + + protected String packageName = "swagger"; + protected String packageVersion = "1.0.0"; + protected String apiDocPath = "docs/"; + protected String modelDocPath = "docs/"; + + public CodegenType getTag() { + return CodegenType.CLIENT; + } + + public String getName() { + return "r"; + } + + public String getHelp() { + return "Generates a R client library (beta)."; + } + + public RClientCodegen() { + super(); + outputFolder = "generated-code/r"; + modelTemplateFiles.put("model.mustache", ".r"); + apiTemplateFiles.put("api.mustache", ".r"); + + modelDocTemplateFiles.put("model_doc.mustache", ".md"); + apiDocTemplateFiles.put("api_doc.mustache", ".md"); + + embeddedTemplateDir = templateDir = "r"; + + setReservedWordsLowerCase( + Arrays.asList( + // reserved words: https://stat.ethz.ch/R-manual/R-devel/library/base/html/Reserved.html + "if", "else", "repeat", "while", "function", "for", "in", + "next", "break", "TRUE", "FALSE", "NULL", "Inf", "NaN", + "NA", "NA_integer_", "NA_real_", "NA_complex_", "NA_character_" + ) + ); + + defaultIncludes = new HashSet( + Arrays.asList( + "map", + "array") + ); + + languageSpecificPrimitives = new HashSet( + Arrays.asList( + "Integer", + "Numeric", + "Character") + ); + + instantiationTypes.clear(); + + typeMapping.clear(); + typeMapping.put("integer", "Integer"); + typeMapping.put("long", "Integer"); + typeMapping.put("number", "Numeric"); + typeMapping.put("float", "Numeric"); + typeMapping.put("double", "Numeric"); + typeMapping.put("boolean", "Character"); + typeMapping.put("string", "Character"); + typeMapping.put("UUID", "Character"); + typeMapping.put("date", "Character"); + typeMapping.put("DateTime", "Character"); + typeMapping.put("password", "Character"); + typeMapping.put("file", "TODO_FILE_MAPPING"); + // map binary to string as a workaround + // the correct solution is to use []byte + typeMapping.put("binary", "Character"); + typeMapping.put("ByteArray", "Character"); + typeMapping.put("object", "TODO_OBJECT_MAPPING"); + + cliOptions.clear(); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "R package name (convention: lowercase).") + .defaultValue("swagger")); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_VERSION, "R package version.") + .defaultValue("1.0.0")); + cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, "hides the timestamp when files were generated") + .defaultValue(Boolean.TRUE.toString())); + + } + + @Override + public void processOpts() { + super.processOpts(); + + // default HIDE_GENERATION_TIMESTAMP to true + if (!additionalProperties.containsKey(CodegenConstants.HIDE_GENERATION_TIMESTAMP)) { + additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, Boolean.TRUE.toString()); + } else { + additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, + Boolean.valueOf(additionalProperties().get(CodegenConstants.HIDE_GENERATION_TIMESTAMP).toString())); + } + + if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { + setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME)); + } else { + setPackageName("swagger"); + } + + 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); + + apiTestTemplateFiles.clear(); // TODO: add api test template + modelTestTemplateFiles.clear(); // TODO: add model test template + + apiDocTemplateFiles.clear(); // TODO: add api doc template + modelDocTemplateFiles.clear(); // TODO: add model doc template + + modelPackage = packageName; + apiPackage = packageName; + + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); + supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); + supportingFiles.add(new SupportingFile("description.mustache", "", "DESCRIPTION")); + supportingFiles.add(new SupportingFile("Rbuildignore.mustache", "", ".Rbuildignore")); + supportingFiles.add(new SupportingFile(".travis.yml", "", ".travis.yml")); + supportingFiles.add(new SupportingFile("response.mustache", "/R", "Response.r")); + supportingFiles.add(new SupportingFile("element.mustache", "/R", "Element.r")); + supportingFiles.add(new SupportingFile("api_client.mustache", "/R", "ApiClient.r")); + } + + @Override + public String escapeReservedWord(String name) + { + // Can't start with an underscore, as our fields need to start with an + // UppercaseLetter so that R treats them as public/visible. + + // Options? + // - MyName + // - AName + // - TheName + // - XName + // - X_Name + // ... or maybe a suffix? + // - Name_ ... think this will work. + if(this.reservedWordsMappings().containsKey(name)) { + return this.reservedWordsMappings().get(name); + } + return camelize(name) + '_'; + } + + @Override + public String apiFileFolder() { + return outputFolder + File.separator + "R" + File.separator; + } + + public String modelFileFolder() { + return outputFolder + File.separator + "R" + File.separator; + } + + @Override + public String toVarName(String name) { + // replace - with _ e.g. created-at => created_at + name = sanitizeName(name.replaceAll("-", "_")); + + // if it's all uppper case, do nothing + if (name.matches("^[A-Z_]*$")) + return name; + + // convert variable name to snake case + // 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) { + return toModelFilename(name); + } + + @Override + public String toModelFilename(String name) { + if (!StringUtils.isEmpty(modelNamePrefix)) { + name = modelNamePrefix + "_" + name; + } + + if (!StringUtils.isEmpty(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 " + camelize("model_" + name)); + name = "model_" + name; // e.g. return => ModelReturn (after camelize) + } + + // model name starts with number + if (name.matches("^\\d.*")) { + LOGGER.warn(name + " (model name starts with number) cannot be used as model name. Renamed to " + camelize("model_" + name)); + name = "model_" + name; // e.g. 200Response => Model200Response (after camelize) + } + + return camelize(name); + } + + @Override + public String toApiFilename(String name) { + // replace - with _ e.g. created-at => created_at + name = name.replaceAll("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + + // e.g. PetApi.r => pet_api.r + return camelize(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 toApiName(String name) { + return camelize(super.toApiName(name)); + } + + @Override + public String getTypeDeclaration(Property p) { + if(p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + Property inner = ap.getItems(); + return getTypeDeclaration(inner); + } else if (p instanceof MapProperty) { + MapProperty mp = (MapProperty) p; + Property inner = mp.getAdditionalProperties(); + return getTypeDeclaration(inner); + } + + // Not using the supertype invocation, because we want to UpperCamelize + // the type. + String swaggerType = getSwaggerType(p); + if (typeMapping.containsKey(swaggerType)) { + return typeMapping.get(swaggerType); + } + + if (typeMapping.containsValue(swaggerType)) { + return swaggerType; + } + + if (languageSpecificPrimitives.contains(swaggerType)) { + return swaggerType; + } + + return toModelName(swaggerType); + } + + @Override + public String getSwaggerType(Property p) { + String swaggerType = super.getSwaggerType(p); + String type = null; + if (typeMapping.containsKey(swaggerType)) { + type = typeMapping.get(swaggerType); + if (languageSpecificPrimitives.contains(type)) + return (type); + } else { + type = swaggerType; + } + return type; + } + + @Override + public String toOperationId(String operationId) { + String sanitizedOperationId = sanitizeName(operationId); + + // method name cannot use reserved keyword, e.g. return + if (isReservedWord(sanitizedOperationId)) { + LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + underscore("call_" + operationId)); + sanitizedOperationId = "call_" + sanitizedOperationId; + } + + return underscore(sanitizedOperationId); + } + + @Override + public Map postProcessModels(Map objs) { + // remove model imports to avoid error + List> imports = (List>) objs.get("imports"); + final String prefix = modelPackage(); + Iterator> iterator = imports.iterator(); + while (iterator.hasNext()) { + String _import = iterator.next().get("import"); + if (_import.startsWith(prefix)) + iterator.remove(); + } + + // recursively add import for mapping one type to multiple imports + List> recursiveImports = (List>) objs.get("imports"); + if (recursiveImports == null) + return objs; + + ListIterator> listIterator = imports.listIterator(); + while (listIterator.hasNext()) { + String _import = listIterator.next().get("import"); + // if the import package happens to be found in the importMapping (key) + // add the corresponding import package to the list + if (importMapping.containsKey(_import)) { + listIterator.add(createMapping("import", importMapping.get(_import))); + } + } + + return postProcessModelsEnum(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("]]", "] ]"); + } + + public Map createMapping(String key, String value){ + Map customImport = new HashMap(); + customImport.put(key, value); + + return customImport; + } + + @Override + public String toEnumValue(String value, String datatype) { + if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { + return value; + } else { + return escapeText(value); + } + } + + @Override + public String toEnumDefaultValue(String value, String datatype) { + return datatype + "_" + value; + } + + @Override + public String toEnumVarName(String name, String datatype) { + if (name.length() == 0) { + return "EMPTY"; + } + + // number + if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { + String varName = name; + varName = varName.replaceAll("-", "MINUS_"); + varName = varName.replaceAll("\\+", "PLUS_"); + varName = varName.replaceAll("\\.", "_DOT_"); + return varName; + } + + // for symbol, e.g. $, # + if (getSymbolName(name) != null) { + return getSymbolName(name).toUpperCase(); + } + + // string + String enumName = sanitizeName(underscore(name).toUpperCase()); + enumName = enumName.replaceFirst("^_", ""); + enumName = enumName.replaceFirst("_$", ""); + + if (isReservedWord(enumName) || enumName.matches("\\d.*")) { // reserved word or starts with number + return escapeReservedWord(enumName); + } else { + return enumName; + } + } + + @Override + public String toEnumName(CodegenProperty property) { + String enumName = underscore(toModelName(property.name)).toUpperCase(); + + // remove [] for array or map of enum + enumName = enumName.replace("[]", ""); + + if (enumName.matches("\\d.*")) { // starts with number + return "_" + enumName; + } else { + return enumName; + } + } +} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Rails5ServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Rails5ServerCodegen.java index 2c9d087927c..0e978ce7d15 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Rails5ServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Rails5ServerCodegen.java @@ -259,7 +259,9 @@ public String toParamName(String name) { public String toModelName(String name) { // model name cannot use reserved keyword, e.g. return if (isReservedWord(name)) { - throw new RuntimeException(name + " (reserved word) cannot be used as a model name"); + String modelName = camelize("Model" + name); + LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to " + modelName); + return modelName; } // camelize the model name @@ -271,7 +273,9 @@ public String toModelName(String name) { public String toModelFilename(String name) { // model name cannot use reserved keyword, e.g. return if (isReservedWord(name)) { - throw new RuntimeException(name + " (reserved word) cannot be used as a model name"); + String filename = underscore("model_" + name); + LOGGER.warn(name + " (reserved word) cannot be used as model filename. Renamed to " + filename); + return filename; } // underscore the model file name @@ -301,7 +305,9 @@ public String toApiName(String name) { public String toOperationId(String operationId) { // method name cannot use reserved keyword, e.g. return if (isReservedWord(operationId)) { - throw new RuntimeException(operationId + " (reserved word) cannot be used as method name"); + String newOperationId = underscore("call_" + operationId); + LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + newOperationId); + return newOperationId; } return underscore(operationId); diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RestbedCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RestbedCodegen.java index 070f4de3ca1..2417fd0d537 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RestbedCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RestbedCodegen.java @@ -99,8 +99,6 @@ public RestbedCodegen() { addOption(DEFAULT_INCLUDE, "The default include statement that should be placed in all headers for including things like the declspec (convention: #include \"Commons.h\" ", this.defaultInclude); - - reservedWords = new HashSet(); supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); @@ -162,7 +160,7 @@ public void processOpts() { /** * 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 reseved words + * matches the reserved words * * @return the escaped term */ diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RustClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RustClientCodegen.java new file mode 100644 index 00000000000..473ad490479 --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RustClientCodegen.java @@ -0,0 +1,482 @@ +package io.swagger.codegen.languages; + +import io.swagger.codegen.*; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.MapProperty; +import io.swagger.models.properties.Property; +import io.swagger.models.parameters.Parameter; + +import java.io.File; +import java.util.*; + +import org.apache.commons.lang3.StringUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RustClientCodegen extends DefaultCodegen implements CodegenConfig { + static Logger LOGGER = LoggerFactory.getLogger(RustClientCodegen.class); + public static final String PACKAGE_NAME = "packageName"; + public static final String PACKAGE_VERSION = "packageVersion"; + + protected String packageName = "swagger"; + protected String packageVersion = "1.0.0"; + protected String apiDocPath = "docs/"; + protected String modelDocPath = "docs/"; + protected String apiFolder = "src/apis"; + protected String modelFolder= "src/models"; + + public CodegenType getTag() { + return CodegenType.CLIENT; + } + + public String getName() { + return "rust"; + } + + public String getHelp() { + return "Generates a Rust client library (beta)."; + } + + public RustClientCodegen() { + super(); + outputFolder = "generated-code/rust"; + modelTemplateFiles.put("model.mustache", ".rs"); + apiTemplateFiles.put("api.mustache", ".rs"); + + modelDocTemplateFiles.put("model_doc.mustache", ".md"); + apiDocTemplateFiles.put("api_doc.mustache", ".md"); + + embeddedTemplateDir = templateDir = "rust"; + + setReservedWordsLowerCase( + Arrays.asList( + "abstract", "alignof", "as", "become", "box", + "break", "const", "continue", "crate", "do", + "else", "enum", "extern", "false", "final", + "fn", "for", "if", "impl", "in", + "let", "loop", "macro", "match", "mod", + "move", "mut", "offsetof", "override", "priv", + "proc", "pub", "pure", "ref", "return", + "Self", "self", "sizeof", "static", "struct", + "super", "trait", "true", "type", "typeof", + "unsafe", "unsized", "use", "virtual", "where", + "while", "yield" + ) + ); + + defaultIncludes = new HashSet( + Arrays.asList( + "map", + "array") + ); + + languageSpecificPrimitives = new HashSet( + Arrays.asList( + "i8", "i16", "i32", "i64", + "u8", "u16", "u32", "u64", + "f32", "f64", + "char", "bool", "String", "Vec", "File") + ); + + instantiationTypes.clear(); + /*instantiationTypes.put("array", "GoArray"); + instantiationTypes.put("map", "GoMap");*/ + + typeMapping.clear(); + typeMapping.put("integer", "i32"); + typeMapping.put("long", "i64"); + typeMapping.put("number", "f32"); + typeMapping.put("float", "f32"); + typeMapping.put("double", "f64"); + typeMapping.put("boolean", "bool"); + typeMapping.put("string", "String"); + typeMapping.put("UUID", "String"); + typeMapping.put("date", "string"); + typeMapping.put("DateTime", "String"); + typeMapping.put("password", "String"); + // TODO(farcaller): map file + typeMapping.put("file", "File"); + typeMapping.put("binary", "Vec"); + typeMapping.put("ByteArray", "String"); + typeMapping.put("object", "Value"); + + // no need for rust + //importMapping = new HashMap(); + + cliOptions.clear(); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Rust package name (convention: lowercase).") + .defaultValue("swagger")); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_VERSION, "Rust package version.") + .defaultValue("1.0.0")); + cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, "hides the timestamp when files were generated") + .defaultValue(Boolean.TRUE.toString())); + + } + + @Override + public void processOpts() { + super.processOpts(); + + // default HIDE_GENERATION_TIMESTAMP to true + if (!additionalProperties.containsKey(CodegenConstants.HIDE_GENERATION_TIMESTAMP)) { + additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, Boolean.TRUE.toString()); + } else { + additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, + Boolean.valueOf(additionalProperties().get(CodegenConstants.HIDE_GENERATION_TIMESTAMP).toString())); + } + + if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { + setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME)); + } + else { + setPackageName("swagger"); + } + + 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); + + modelPackage = packageName; + apiPackage = packageName; + + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); + supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); + supportingFiles.add(new SupportingFile("configuration.mustache", apiFolder, "configuration.rs")); + supportingFiles.add(new SupportingFile(".travis.yml", "", ".travis.yml")); + + supportingFiles.add(new SupportingFile("client.mustache", apiFolder, "client.rs")); + supportingFiles.add(new SupportingFile("api_mod.mustache", apiFolder, "mod.rs")); + supportingFiles.add(new SupportingFile("model_mod.mustache", modelFolder, "mod.rs")); + supportingFiles.add(new SupportingFile("lib.rs", "src", "lib.rs")); + supportingFiles.add(new SupportingFile("Cargo.mustache", "", "Cargo.toml")); + } + + @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); + } + + 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("-", "_")); + + // if it's all uppper case, do nothing + if (name.matches("^[A-Z_]*$")) + return name; + + // 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 camelize(toModelFilename(name)); + } + + @Override + public String toModelFilename(String name) { + if (!StringUtils.isEmpty(modelNamePrefix)) { + name = modelNamePrefix + "_" + name; + } + + if (!StringUtils.isEmpty(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 + 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(String name) { + // replace - with _ e.g. created-at => created_at + name = name.replaceAll("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + + // e.g. PetApi.rs => pet_api.rs + 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(Property p) { + if (p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + Property inner = ap.getItems(); + return "Vec<" + getTypeDeclaration(inner) + ">"; + } + else if (p instanceof MapProperty) { + MapProperty mp = (MapProperty) p; + Property inner = mp.getAdditionalProperties(); + return "::std::collections::HashMap"; + } + + // Not using the supertype invocation, because we want to UpperCamelize + // the type. + String swaggerType = getSwaggerType(p); + if (typeMapping.containsKey(swaggerType)) { + return typeMapping.get(swaggerType); + } + + if (typeMapping.containsValue(swaggerType)) { + return swaggerType; + } + + if (languageSpecificPrimitives.contains(swaggerType)) { + return swaggerType; + } + + // return fully-qualified model name + // ::models::{{classnameFile}}::{{classname}} + return "::models::" + toModelName(swaggerType); + } + + @Override + public String getSwaggerType(Property p) { + String swaggerType = super.getSwaggerType(p); + String type = null; + if(typeMapping.containsKey(swaggerType)) { + type = typeMapping.get(swaggerType); + if(languageSpecificPrimitives.contains(type)) + return (type); + } + else + type = swaggerType; + return type; + } + + @Override + public String toOperationId(String operationId) { + String sanitizedOperationId = sanitizeName(operationId); + + // method name cannot use reserved keyword, e.g. return + if (isReservedWord(sanitizedOperationId)) { + LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + underscore("call_" + operationId)); + sanitizedOperationId = "call_" + sanitizedOperationId; + } + + return underscore(sanitizedOperationId); + } + + @Override + public Map postProcessOperations(Map objs) { + @SuppressWarnings("unchecked") + Map objectMap = (Map) objs.get("operations"); + @SuppressWarnings("unchecked") + List operations = (List) objectMap.get("operation"); + for (CodegenOperation operation : operations) { + // http method verb conversion (e.g. PUT => Put) + operation.httpMethod = camelize(operation.httpMethod.toLowerCase()); + // update return type to conform to rust standard + /* + if (operation.returnType != null) { + if ( operation.returnType.startsWith("Vec") && !languageSpecificPrimitives.contains(operation.returnBaseType)) { + // array of model + String rt = operation.returnType; + int end = rt.lastIndexOf(">"); + if ( end > 0 ) { + operation.vendorExtensions.put("x-returnTypeInMethod", "Vec"); + operation.returnContainer = "List"; + } + } else if (operation.returnType.startsWith("::std::collections::HashMap"); + if ( end > 0 ) { + operation.vendorExtensions.put("x-returnTypeInMethod", "::std::collections::HashMap"); + operation.returnContainer = "Map"; + } + } else if (!languageSpecificPrimitives.contains(operation.returnType)) { + // add super:: to model, e.g. super::pet + operation.vendorExtensions.put("x-returnTypeInMethod", "super::" + operation.returnType); + } else { + // primitive type or array/map of primitive type + operation.vendorExtensions.put("x-returnTypeInMethod", operation.returnType); + } + } + + for (CodegenParameter p : operation.allParams) { + if (p.isListContainer && !languageSpecificPrimitives.contains(p.dataType)) { + // array of model + String rt = p.dataType; + int end = rt.lastIndexOf(">"); + if ( end > 0 ) { + p.dataType = "Vec<" + rt.substring("Vec<".length(), end).trim() + ">"; + } + } else if (p.isMapContainer && !languageSpecificPrimitives.contains(p.dataType)) { + // map of model + String rt = p.dataType; + int end = rt.lastIndexOf(">"); + if ( end > 0 ) { + p.dataType = "::std::collections::HashMap"; + } + } else if (!languageSpecificPrimitives.contains(p.dataType)) { + // add super:: to model, e.g. super::pet + p.dataType = "super::" + p.dataType; + } + }*/ + } + + 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("/*", "/_*"); + } + + + @Override + public String toEnumValue(String value, String datatype) { + if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { + return value; + } else { + return escapeText(value); + } + } + + @Override + public String toEnumDefaultValue(String value, String datatype) { + return datatype + "_" + value; + } + + @Override + public String toEnumVarName(String name, String datatype) { + if (name.length() == 0) { + return "EMPTY"; + } + + // number + if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { + String varName = name; + varName = varName.replaceAll("-", "MINUS_"); + varName = varName.replaceAll("\\+", "PLUS_"); + varName = varName.replaceAll("\\.", "_DOT_"); + return varName; + } + + // for symbol, e.g. $, # + if (getSymbolName(name) != null) { + return getSymbolName(name).toUpperCase(); + } + + // string + String enumName = sanitizeName(underscore(name).toUpperCase()); + enumName = enumName.replaceFirst("^_", ""); + enumName = enumName.replaceFirst("_$", ""); + + if (isReservedWord(enumName) || enumName.matches("\\d.*")) { // reserved word or starts with number + return escapeReservedWord(enumName); + } else { + return enumName; + } + } + + @Override + public String toEnumName(CodegenProperty property) { + String enumName = underscore(toModelName(property.name)).toUpperCase(); + + // remove [] for array or map of enum + enumName = enumName.replace("[]", ""); + + if (enumName.matches("\\d.*")) { // starts with number + return "_" + enumName; + } else { + return enumName; + } + } +} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RustServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RustServerCodegen.java new file mode 100755 index 00000000000..020b78f2dd8 --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RustServerCodegen.java @@ -0,0 +1,963 @@ +package io.swagger.codegen.languages; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import io.swagger.codegen.*; +import io.swagger.models.*; +import io.swagger.models.parameters.BodyParameter; +import io.swagger.models.parameters.Parameter; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.MapProperty; +import io.swagger.models.properties.RefProperty; +import io.swagger.models.properties.*; +import io.swagger.util.Yaml; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.*; +import java.util.Map.Entry; +import org.apache.commons.lang3.StringUtils; + +public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { + + private static final Logger LOGGER = LoggerFactory.getLogger(RustServerCodegen.class); + + private HashMap modelXmlNames = new HashMap(); + + protected String apiVersion = "1.0.0"; + protected int serverPort = 8080; + protected String projectName = "swagger-server"; + protected String apiPath = "rust-server"; + protected String packageName; + protected String packageVersion; + protected String externCrateName; + + public RustServerCodegen() { + super(); + + // set the output folder here + outputFolder = "generated-code/rust-server"; + + /* + * Models. You can write model files using the modelTemplateFiles map. + * if you want to create one template for file, you can do so here. + * for multiple files for model, just put another entry in the `modelTemplateFiles` with + * a different extension + */ + modelTemplateFiles.clear(); + + /* + * Api classes. You can write classes for each Api file with the apiTemplateFiles map. + * as with models, add multiple entries with different extensions for multiple files per + * class + */ + apiTemplateFiles.clear(); + + /* + * Template Location. This is the location which templates will be read from. The generator + * will use the resource stream to attempt to read the templates. + */ + embeddedTemplateDir = templateDir = "rust-server"; + + /* + * Reserved words. Override this with reserved words specific to your language + */ + setReservedWordsLowerCase( + Arrays.asList( + // From https://doc.rust-lang.org/grammar.html#keywords + "abstract", "alignof", "as", "become", "box", "break", "const", + "continue", "crate", "do", "else", "enum", "extern", "false", + "final", "fn", "for", "if", "impl", "in", "let", "loop", "macro", + "match", "mod", "move", "mut", "offsetof", "override", "priv", + "proc", "pub", "pure", "ref", "return", "Self", "self", "sizeof", + "static", "struct", "super", "trait", "true", "type", "typeof", + "unsafe", "unsized", "use", "virtual", "where", "while", "yield" + ) + ); + + defaultIncludes = new HashSet( + Arrays.asList( + "map", + "array") + ); + + languageSpecificPrimitives = new HashSet( + Arrays.asList( + "bool", + "char", + "i8", + "i16", + "i32", + "i64", + "u8", + "u16", + "u32", + "u64", + "isize", + "usize", + "f32", + "f64", + "str") + ); + + instantiationTypes.clear(); + instantiationTypes.put("array", "Vec"); + instantiationTypes.put("map", "Map"); + + typeMapping.clear(); + typeMapping.put("number", "f64"); + typeMapping.put("integer", "i32"); + typeMapping.put("long", "i64"); + typeMapping.put("float", "f32"); + typeMapping.put("double", "f64"); + typeMapping.put("string", "String"); + typeMapping.put("UUID", "uuid::Uuid"); + typeMapping.put("byte", "u8"); + typeMapping.put("ByteArray", "swagger::ByteArray"); + typeMapping.put("binary", "swagger::ByteArray"); + typeMapping.put("boolean", "bool"); + typeMapping.put("date", "chrono::DateTime"); + typeMapping.put("DateTime", "chrono::DateTime"); + typeMapping.put("password", "String"); + typeMapping.put("File", "Box, Error=Error> + Send>"); + typeMapping.put("file", "Box, Error=Error> + Send>"); + typeMapping.put("array", "Vec"); + typeMapping.put("map", "HashMap"); + + importMapping = new HashMap(); + + cliOptions.clear(); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, + "Rust crate name (convention: snake_case).") + .defaultValue("swagger_client")); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_VERSION, + "Rust crate version.") + .defaultValue("1.0.0")); + + /* + * Additional Properties. These values can be passed to the templates and + * are available in models, apis, and supporting files + */ + additionalProperties.put("apiVersion", apiVersion); + additionalProperties.put("serverPort", serverPort); + additionalProperties.put("apiPath", apiPath); + + /* + * 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("swagger.mustache", "api", "swagger.yaml")); + supportingFiles.add(new SupportingFile("Cargo.mustache", "", "Cargo.toml")); + supportingFiles.add(new SupportingFile("cargo-config", ".cargo", "config")); + supportingFiles.add(new SupportingFile("gitignore", "", ".gitignore")); + supportingFiles.add(new SupportingFile("lib.mustache", "src", "lib.rs")); + supportingFiles.add(new SupportingFile("models.mustache", "src", "models.rs")); + supportingFiles.add(new SupportingFile("server.mustache", "src", "server.rs")); + supportingFiles.add(new SupportingFile("client.mustache", "src", "client.rs")); + supportingFiles.add(new SupportingFile("mimetypes.mustache", "src", "mimetypes.rs")); + supportingFiles.add(new SupportingFile("example-server.mustache", "examples", "server.rs")); + supportingFiles.add(new SupportingFile("example-client.mustache", "examples", "client.rs")); + supportingFiles.add(new SupportingFile("example-server_lib.mustache", "examples/server_lib", "mod.rs")); + supportingFiles.add(new SupportingFile("example-ca.pem", "examples", "ca.pem")); + supportingFiles.add(new SupportingFile("example-server-chain.pem", "examples", "server-chain.pem")); + supportingFiles.add(new SupportingFile("example-server-key.pem", "examples", "server-key.pem")); + writeOptional(outputFolder, new SupportingFile("README.mustache", "", "README.md")); + } + + @Override + public void processOpts() { + super.processOpts(); + + if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { + setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME)); + } + else { + setPackageName("swagger_client"); + } + + 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("externCrateName", externCrateName); + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + + // Also set the extern crate name, which has any '-' replace with a '_'. + this.externCrateName = packageName.replace('-', '_'); + } + + public void setPackageVersion(String packageVersion) { + this.packageVersion = packageVersion; + } + + @Override + public String apiPackage() { + return apiPath; + } + + /** + * Configures the type of generator. + * + * @return the CodegenType for this generator + * @see io.swagger.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 -l flag. + * + * @return the friendly name for the generator + */ + @Override + public String getName() { + return "rust-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 Rust client/server library (beta) using the swagger-codegen project."; + } + + @Override + public void preprocessSwagger(Swagger swagger) { + Info info = swagger.getInfo(); + List versionComponents = new ArrayList(Arrays.asList(info.getVersion().split("[.]"))); + if (versionComponents.size() < 1) { + versionComponents.add("1"); + } + while (versionComponents.size() < 3) { + versionComponents.add("0"); + } + info.setVersion(StringUtils.join(versionComponents, ".")); + } + + @Override + public String toApiName(String name) { + if (name.length() == 0) { + return "default"; + } + return underscore(name); + } + + /** + * 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; // add an underscore to the 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); + } + + @Override + public String toModelName(String name) { + // camelize the model name + // phone_number => PhoneNumber + String camelizedName = camelize(toModelFilename(name)); + + // model name cannot use reserved keyword, e.g. return + if (isReservedWord(camelizedName)) { + camelizedName = "Model" + camelizedName; + LOGGER.warn(camelizedName + " (reserved word) cannot be used as model name. Renamed to " + camelizedName); + } + + // model name starts with number + else if (name.matches("^\\d.*")) { + // e.g. 200Response => Model200Response (after camelize) + camelizedName = "Model" + camelizedName; + LOGGER.warn(name + " (model name starts with number) cannot be used as model name. Renamed to " + camelizedName); + } + + return camelizedName; + + } + + @Override + public String toParamName(String name) { + // should be the same as variable name (stolen from RubyClientCodegen) + return toVarName(name); + } + + @Override + public String toVarName(String name) { + String sanitizedName = super.sanitizeName(name); + // for reserved word or word starting with number, append _ + if (isReservedWord(sanitizedName) || sanitizedName.matches("^\\d.*")) { + sanitizedName = escapeReservedWord(sanitizedName); + } + + return underscore(sanitizedName); + } + + @Override + public String toOperationId(String operationId) { + // method name cannot use reserved keyword, e.g. return + if (isReservedWord(operationId)) { + LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + camelize(sanitizeName("call_" + operationId))); + operationId = "call_" + operationId; + } + + return camelize(operationId); + } + + @Override + public String toModelFilename(String name) { + if (!StringUtils.isEmpty(modelNamePrefix)) { + name = modelNamePrefix + "_" + name; + } + + if (!StringUtils.isEmpty(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 " + camelize("model_" + name)); + name = "model_" + name; // e.g. return => ModelReturn (after camelize) + } + + return underscore(name); + } + + @Override + public String toEnumName(CodegenProperty property) { + return sanitizeName(camelize(property.name)) + "Enum"; + } + + @Override + public String toEnumVarName(String value, String datatype) { + String var = null; + if (value.length() == 0) { + var = "EMPTY"; + } + + // for symbol, e.g. $, # + else if (getSymbolName(value) != null) { + var = getSymbolName(value).toUpperCase(); + } + + // number + else if ("Integer".equals(datatype) || "Long".equals(datatype) || + "Float".equals(datatype) || "Double".equals(datatype)) { + String varName = "NUMBER_" + value; + varName = varName.replaceAll("-", "MINUS_"); + varName = varName.replaceAll("\\+", "PLUS_"); + varName = varName.replaceAll("\\.", "_DOT_"); + var = varName; + } + + // string + var = value.replaceAll("\\W+", "_").toUpperCase(); + if (var.matches("\\d.*")) { + var = "_" + var; + } else { + var = sanitizeName(var); + } + return var; + } + + @Override + public String toEnumValue(String value, String datatype) { + if ("Integer".equals(datatype) || "Long".equals(datatype) || + "Float".equals(datatype) || "Double".equals(datatype)) { + return value; + } else { + return "\"" + escapeText(value) + "\""; + } + } + + @Override + public String toApiFilename(String name) { + // replace - with _ e.g. created-at => created_at + name = name.replaceAll("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + + // e.g. PetApi.go => pet_api.go + return underscore(name); + } + + @Override + public String escapeQuotationMark(String input) { + // remove " to avoid code injection + return input.replace("\"", ""); + } + + @Override + public String escapeUnsafeCharacters(String input) { + return input.replace("*/", "*_/").replace("/*", "/_*"); + } + + @Override + public CodegenOperation fromOperation(String path, String httpMethod, Operation operation, Map definitions, Swagger swagger) { + CodegenOperation op = super.fromOperation(path, httpMethod, operation, definitions, swagger); + op.vendorExtensions.put("operation_id", underscore(op.operationId)); + op.vendorExtensions.put("uppercase_operation_id", underscore(op.operationId).toUpperCase()); + op.vendorExtensions.put("path", op.path.replace("{", ":").replace("}", "")); + op.vendorExtensions.put("HttpMethod", Character.toUpperCase(op.httpMethod.charAt(0)) + op.httpMethod.substring(1).toLowerCase()); + op.vendorExtensions.put("httpmethod", op.httpMethod.toLowerCase()); + for (CodegenParameter param : op.allParams) { + String example = null; + + if (param.isString) { + if (param.dataFormat != null && param.dataFormat.equals("byte")) { + param.vendorExtensions.put("formatString", "\\\"{:?}\\\""); + example = "swagger::ByteArray(\"" + ((param.example != null) ? param.example : "") + "\".to_string().into_bytes())"; + } else { + param.vendorExtensions.put("formatString", "\\\"{}\\\""); + example = "\"" + ((param.example != null) ? param.example : "") + "\".to_string()"; + } + } else if (param.isPrimitiveType) { + if ((param.isByteArray) || + (param.isBinary)) { + // Binary primitive types don't implement `Display`. + param.vendorExtensions.put("formatString", "{:?}"); + example = "swagger::ByteArray(Vec::from(\"" + ((param.example != null) ? param.example : "") + "\"))"; + } else { + param.vendorExtensions.put("formatString", "{}"); + example = (param.example != null) ? param.example : ""; + } + } else if (param.isListContainer) { + param.vendorExtensions.put("formatString", "{:?}"); + example = (param.example != null) ? param.example : "&Vec::new()"; + } else if (param.isFile) { + param.vendorExtensions.put("formatString", "{:?}"); + op.vendorExtensions.put("hasFile", true); + additionalProperties.put("apiHasFile", true); + example = "Box::new(stream::once(Ok(b\"hello\".to_vec()))) as Box + Send>"; + } else { + param.vendorExtensions.put("formatString", "{:?}"); + if (param.example != null) { + example = "serde_json::from_str::<" + param.dataType + ">(\"" + param.example + "\").expect(\"Failed to parse JSON example\")"; + } + } + + if (param.required) { + if (example != null) { + param.vendorExtensions.put("example", example); + } else if (param.isListContainer) { + // Use the empty list if we don't have an example + param.vendorExtensions.put("example", "&Vec::new()"); + } + else { + // If we don't have an example that we can provide, we need to disable the client example, as it won't build. + param.vendorExtensions.put("example", "???"); + op.vendorExtensions.put("noClientExample", Boolean.TRUE); + } + } else if ((param.dataFormat != null)&&((param.dataFormat.equals("date-time")) || (param.dataFormat.equals("date")))) { + param.vendorExtensions.put("formatString", "{:?}"); + param.vendorExtensions.put("example", "None"); + } else { + // Not required, so override the format string and example + param.vendorExtensions.put("formatString", "{:?}"); + if (param.isFile) { + // Optional file types are wrapped in a future + param.vendorExtensions.put("example", (example != null) ? "Box::new(future::ok(Some(" + example + "))) as Box + Send>" : "None"); + } else { + param.vendorExtensions.put("example", (example != null) ? "Some(" + example + ")" : "None"); + } + } + } + + List consumes = new ArrayList(); + if (operation.getConsumes() != null) { + if (operation.getConsumes().size() > 0) { + // use consumes defined in the operation + consumes = operation.getConsumes(); + } + } else if (swagger != null && swagger.getConsumes() != null && swagger.getConsumes().size() > 0) { + // use consumes defined globally + consumes = swagger.getConsumes(); + LOGGER.debug("No consumes defined in operation. Using global consumes (" + swagger.getConsumes() + ") for " + op.operationId); + } + + boolean consumesXml = false; + // if "consumes" is defined (per operation or using global definition) + if (consumes != null && !consumes.isEmpty()) { + List> c = new ArrayList>(); + for (String key : consumes) { + Map mediaType = new HashMap(); + String mimeType = processMimeType(key); + + if (mimeType.startsWith("Application/Xml")) { + additionalProperties.put("usesXml", true); + consumesXml = true; + } + + mediaType.put("mediaType", mimeType); + c.add(mediaType); + } + op.consumes = c; + op.hasConsumes = true; + } + + List produces = new ArrayList(); + if (operation.getProduces() != null) { + if (operation.getProduces().size() > 0) { + // use produces defined in the operation + produces = operation.getProduces(); + } + } else if (swagger != null && swagger.getProduces() != null && swagger.getProduces().size() > 0) { + // use produces defined globally + produces = swagger.getProduces(); + LOGGER.debug("No produces defined in operation. Using global produces (" + swagger.getProduces() + ") for " + op.operationId); + } + + boolean producesXml = false; + if (produces != null && !produces.isEmpty()) { + List> c = new ArrayList>(); + for (String key : produces) { + Map mediaType = new HashMap(); + String mimeType = processMimeType(key); + + if (mimeType.startsWith("Application/Xml")) { + additionalProperties.put("usesXml", true); + producesXml = true; + } + + mediaType.put("mediaType", mimeType); + c.add(mediaType); + } + op.produces = c; + op.hasProduces = true; + } + + if (op.bodyParam != null) { + + if (paramHasXmlNamespace(op.bodyParam, definitions)){ + op.bodyParam.vendorExtensions.put("has_namespace", "true"); + } + for (String key : definitions.keySet()) { + op.bodyParam.vendorExtensions.put("model_key", key); + } + + op.bodyParam.vendorExtensions.put("uppercase_operation_id", underscore(op.operationId).toUpperCase()); + if (consumesXml) { + op.bodyParam.vendorExtensions.put("consumesXml", true); + } + + } + for (CodegenParameter param : op.bodyParams) { + + if (paramHasXmlNamespace(param, definitions)){ + param.vendorExtensions.put("has_namespace", "true"); + } + + param.vendorExtensions.put("uppercase_operation_id", underscore(op.operationId).toUpperCase()); + + if (consumesXml) { + param.vendorExtensions.put("consumesXml", true); + } + } + for (CodegenParameter param : op.headerParams) { + // Give header params a name in camel case. CodegenParameters don't have a nameInCamelCase property. + param.vendorExtensions.put("typeName", toModelName(param.baseName)); + } + for (CodegenResponse rsp : op.responses) { + rsp.message = camelize(rsp.message.split("[^A-Za-z ]")[0].replace(" ", "_")); + rsp.vendorExtensions.put("uppercase_operation_id", underscore(op.operationId).toUpperCase()); + rsp.vendorExtensions.put("uppercase_message", underscore(rsp.message).toUpperCase()); + if (rsp.dataType != null) { + rsp.vendorExtensions.put("uppercase_data_type", (rsp.dataType.replace("models::", "")).toUpperCase()); + + if (producesXml) { + rsp.vendorExtensions.put("producesXml", true); + } + + // Check whether we're returning an object with a defined XML namespace. + Object property = rsp.schema; + if ((property != null) && (property instanceof RefProperty)){ + + RefProperty refProperty = (RefProperty) property; + String refName = refProperty.get$ref(); + if (refName.indexOf("#/definitions/") == 0) { + refName = refName.substring("#/definitions/".length()); + } + + Model model = definitions.get(refName); + + if ((model != null) && (model instanceof ModelImpl)) { + Xml xml = ((ModelImpl) model).getXml(); + if ((xml != null) && (xml.getNamespace() != null)){ + rsp.vendorExtensions.put("has_namespace", "true"); + } + } + } + } + for (CodegenProperty header : rsp.headers) { + header.nameInCamelCase = toModelName(header.baseName); + } + } + for (CodegenProperty header : op.responseHeaders) { + header.nameInCamelCase = toModelName(header.baseName); + } + + return op; + } + + @Override + public boolean isDataTypeFile(final String dataType) { + return dataType != null && dataType.equals(typeMapping.get("File").toString()); + } + + @Override + public String getTypeDeclaration(Property p) { + if (p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + Property inner = ap.getItems(); + String innerType = getTypeDeclaration(inner); + StringBuilder typeDeclaration = new StringBuilder(typeMapping.get("array")).append("<"); + if (inner instanceof RefProperty) { + typeDeclaration.append("models::"); + } + typeDeclaration.append(innerType).append(">"); + return typeDeclaration.toString(); + } else if (p instanceof MapProperty) { + MapProperty mp = (MapProperty) p; + Property inner = mp.getAdditionalProperties(); + String innerType = getTypeDeclaration(inner); + StringBuilder typeDeclaration = new StringBuilder(typeMapping.get("map")).append("<").append(typeMapping.get("string")).append(", "); + if (inner instanceof RefProperty) { + typeDeclaration.append("models::"); + } + typeDeclaration.append(innerType).append(">"); + return typeDeclaration.toString(); + } else if (p instanceof RefProperty) { + String datatype; + try { + RefProperty r = (RefProperty) p; + datatype = r.get$ref(); + if (datatype.indexOf("#/definitions/") == 0) { + datatype = toModelName(datatype.substring("#/definitions/".length())); + } + } catch (Exception e) { + LOGGER.warn("Error obtaining the datatype from RefProperty:" + p + ". Datatype default to Object"); + datatype = "Object"; + LOGGER.error(e.getMessage(), e); + } + return datatype; + } else if (p instanceof FileProperty) { + return typeMapping.get("File").toString(); + } + return super.getTypeDeclaration(p); + } + + @Override + public CodegenParameter fromParameter(Parameter param, Set imports) { + CodegenParameter parameter = super.fromParameter(param, imports); + if(param instanceof BodyParameter) { + BodyParameter bp = (BodyParameter) param; + Model model = bp.getSchema(); + if (model instanceof RefModel) { + String name = ((RefModel) model).getSimpleRef(); + name = toModelName(name); + // We need to be able to look up the model in the model definitions later. + parameter.vendorExtensions.put("uppercase_data_type", name.toUpperCase()); + + name = "models::" + getTypeDeclaration(name); + parameter.baseType = name; + parameter.dataType = name; + + String refName = ((RefModel) model).get$ref(); + if (refName.indexOf("#/definitions/") == 0) { + refName = refName.substring("#/definitions/".length()); + } + parameter.vendorExtensions.put("refName", refName); + + } else if (model instanceof ModelImpl) { + parameter.vendorExtensions.put("refName", ((ModelImpl) model).getName()); + } + } + return parameter; + } + + @Override + public CodegenProperty fromProperty(String name, Property p) { + CodegenProperty property = super.fromProperty(name, p); + if (p instanceof RefProperty) { + property.datatype = "models::" + property.datatype; + } + return property; + } + + @Override + public String toInstantiationType(Property p) { + if (p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + Property inner = ap.getItems(); + return instantiationTypes.get("array") + "<" + getSwaggerType(inner) + ">"; + } else if (p instanceof MapProperty) { + MapProperty mp = (MapProperty) p; + Property inner = mp.getAdditionalProperties(); + return instantiationTypes.get("map") + "<" + typeMapping.get("string") + ", " + getSwaggerType(inner) + ">"; + } else { + return null; + } + } + + @Override + public CodegenModel fromModel(String name, Model model) { + return fromModel(name, model, null); + } + + @Override + public CodegenModel fromModel(String name, Model model, Map allDefinitions) { + CodegenModel mdl = super.fromModel(name, model, allDefinitions); + mdl.vendorExtensions.put("upperCaseName", name.toUpperCase()); + if (model instanceof ModelImpl) { + ModelImpl modelImpl = (ModelImpl) model; + mdl.dataType = typeMapping.get(modelImpl.getType()); + } + if (model instanceof ArrayModel) { + ArrayModel am = (ArrayModel) model; + if ((am.getItems() != null) && + (am.getItems().getXml() != null)){ + + // If this model's items require wrapping in xml, squirrel + // away the xml name so we can insert it into the relevant model fields. + String xmlName = am.getItems().getXml().getName(); + if (xmlName != null) { + mdl.vendorExtensions.put("itemXmlName", xmlName); + modelXmlNames.put("models::" + mdl.classname, xmlName); + } + } + mdl.arrayModelType = toModelName(mdl.arrayModelType); + } + + if (mdl.xmlNamespace != null) { + additionalProperties.put("usesXmlNamespaces", true); + } + + return mdl; + } + + @Override + public Map postProcessAllModels(Map objs){ + Map newObjs = super.postProcessAllModels(objs); + + //Index all CodegenModels by model name. + HashMap allModels = new HashMap(); + for (Entry entry : objs.entrySet()) { + String modelName = toModelName(entry.getKey()); + Map inner = (Map) entry.getValue(); + List> models = (List>) inner.get("models"); + for (Map mo : models) { + CodegenModel cm = (CodegenModel) mo.get("model"); + allModels.put(modelName, cm); + } + } + + for (Entry entry : allModels.entrySet()){ + String modelName = entry.getKey(); + CodegenModel model = entry.getValue(); + + for(CodegenProperty prop : model.vars){ + String xmlName = modelXmlNames.get(prop.datatype); + if (xmlName != null){ + prop.vendorExtensions.put("itemXmlName", xmlName); + } + } + } + + return newObjs; + } + + @Override + public Map postProcessSupportingFileData(Map objs) { + Swagger swagger = (Swagger)objs.get("swagger"); + if(swagger != null) { + try { + objs.put("swagger-yaml", Yaml.mapper().writeValueAsString(swagger)); + } catch (JsonProcessingException e) { + LOGGER.error(e.getMessage(), e); + } + } + return super.postProcessSupportingFileData(objs); + } + + @Override + public String toDefaultValue(Property p) { + if (p instanceof StringProperty) { + StringProperty dp = (StringProperty) p; + if (dp.getDefault() != null) { + return "\"" + dp.getDefault() + "\".to_string()"; + } + } else if (p instanceof BooleanProperty) { + BooleanProperty dp = (BooleanProperty) p; + if (dp.getDefault() != null) { + if (dp.getDefault().toString().equalsIgnoreCase("false")) + return "false"; + else + return "true"; + } + } else if (p instanceof DoubleProperty) { + DoubleProperty dp = (DoubleProperty) p; + if (dp.getDefault() != null) { + return dp.getDefault().toString(); + } + } else if (p instanceof FloatProperty) { + FloatProperty dp = (FloatProperty) p; + if (dp.getDefault() != null) { + return dp.getDefault().toString(); + } + } else if (p instanceof IntegerProperty) { + IntegerProperty dp = (IntegerProperty) p; + if (dp.getDefault() != null) { + return dp.getDefault().toString(); + } + } else if (p instanceof LongProperty) { + LongProperty dp = (LongProperty) p; + if (dp.getDefault() != null) { + return dp.getDefault().toString(); + } + } + + return null; + } + + @Override + public void postProcessModelProperty(CodegenModel model, CodegenProperty property) { + super.postProcessModelProperty(model, property); + if(!languageSpecificPrimitives.contains(property.datatype)) { + // If we use a more qualified model name, then only camelize the actual type, not the qualifier. + if(property.datatype.contains(":")) { + int position = property.datatype.lastIndexOf(":"); + property.datatype = property.datatype.substring(0, position) + camelize(property.datatype.substring(position)); + } else { + property.datatype = camelize(property.datatype, false); + } + } + + // Handle custom unsigned integer formats. + if ("integer".equals(property.baseType)) { + if ("uint32".equals(property.dataFormat)) { + property.datatype = "u32"; + } else if ("uint64".equals(property.dataFormat)) { + property.datatype = "u64"; + } + } + + property.name = underscore(property.name); + + if (!property.required) { + property.defaultValue = (property.defaultValue != null) ? "Some(" + property.defaultValue + ")" : "None"; + } + } + + @Override + public Map postProcessModels(Map objs) { + return super.postProcessModelsEnum(objs); + + } + + private boolean paramHasXmlNamespace(CodegenParameter param, Map definitions){ + Object refName = param.vendorExtensions.get("refName"); + + if ((refName != null) && (refName instanceof String)) { + String name = (String) refName; + Model model = definitions.get(name); + + if ((model != null) && (model instanceof ModelImpl)) { + Xml xml = ((ModelImpl) model).getXml(); + if ((xml != null) && (xml.getNamespace() != null)) { + return true; + } + } + } + return false; + } + + private String processMimeType(String mimeType){ + // Transform mime type into a form that the hyper mime! macro can handle. + String result = ""; + + String[] split_attributes = mimeType.split(";"); + String media = split_attributes[0]; + String[] mediaTypes = media.split("/"); + + if (mediaTypes.length == 2) { + + if (mediaTypes[0].equals("*")){ + result += "Star"; + } else { + result += escapeText(escapeQuotationMark(initialCaps(mediaTypes[0]))); + } + + result += "/"; + + if (mediaTypes[1].equals("*")) { + result += "Star"; + } else { + result += escapeText(escapeQuotationMark(initialCaps(mediaTypes[1]))); + } + } else { + LOGGER.error("Failed to parse media type: " + + mimeType + + ", media types should have exactly one /"); + } + + if (split_attributes.length == 2) { + String attributes = ""; + String[] attrs = split_attributes[1].split(","); + + for (String attr : attrs) { + String[] keyValuePair =attr.split("="); + if (keyValuePair.length == 2) { + attributes += "(\"" + + escapeText(escapeQuotationMark(keyValuePair[0].trim())) + + "\")=(\"" + + escapeText(escapeQuotationMark(keyValuePair[1].trim())) + + "\")"; + } else { + LOGGER.error("Failed to parse parameter attributes: " + + split_attributes[1] + + ", attributes must be a comma separated list of 'key=value' pairs"); + } + } + result += "; " + attributes; + } + + return result; + } +} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ScalaClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ScalaClientCodegen.java index 2044b80fc80..14f8f4e182a 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ScalaClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ScalaClientCodegen.java @@ -1,14 +1,11 @@ package io.swagger.codegen.languages; -import io.swagger.codegen.CliOption; -import io.swagger.codegen.CodegenConfig; -import io.swagger.codegen.CodegenConstants; -import io.swagger.codegen.CodegenType; -import io.swagger.codegen.SupportingFile; +import io.swagger.codegen.*; import java.io.File; import java.util.Arrays; import java.util.HashMap; +import java.util.Map; import org.apache.commons.lang3.StringUtils; @@ -20,6 +17,7 @@ public class ScalaClientCodegen extends AbstractScalaCodegen implements CodegenC protected String groupId = "io.swagger"; protected String artifactId = "swagger-scala-client"; protected String artifactVersion = "1.0.0"; + protected String clientName = "AsyncClient"; public ScalaClientCodegen() { super(); @@ -51,10 +49,13 @@ public ScalaClientCodegen() { additionalProperties.put("asyncHttpClient", asyncHttpClient); additionalProperties.put("authScheme", authScheme); additionalProperties.put("authPreemptive", authPreemptive); + additionalProperties.put("clientName", clientName); supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml")); supportingFiles.add(new SupportingFile("apiInvoker.mustache", (sourceFolder + File.separator + invokerPackage).replace(".", java.io.File.separator), "ApiInvoker.scala")); + supportingFiles.add(new SupportingFile("client.mustache", + (sourceFolder + File.separator + invokerPackage).replace(".", java.io.File.separator), clientName + ".scala")); supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); // gradle settings @@ -75,7 +76,7 @@ public ScalaClientCodegen() { importMapping.remove("Set"); importMapping.remove("Map"); - importMapping.put("DateTime", "org.joda.time.DateTime"); + importMapping.put("Date", "java.util.Date"); importMapping.put("ListBuffer", "scala.collection.mutable.ListBuffer"); typeMapping = new HashMap(); @@ -90,7 +91,6 @@ public ScalaClientCodegen() { typeMapping.put("byte", "Byte"); typeMapping.put("short", "Short"); typeMapping.put("char", "Char"); - typeMapping.put("long", "Long"); typeMapping.put("double", "Double"); typeMapping.put("object", "Any"); typeMapping.put("file", "File"); @@ -98,6 +98,10 @@ public ScalaClientCodegen() { // mapped to String as a workaround typeMapping.put("binary", "String"); typeMapping.put("ByteArray", "String"); + typeMapping.put("date-time", "Date"); +// typeMapping.put("date", "Date"); +// typeMapping.put("Date", "Date"); + typeMapping.put("DateTime", "Date"); instantiationTypes.put("array", "ListBuffer"); instantiationTypes.put("map", "HashMap"); @@ -108,7 +112,6 @@ public ScalaClientCodegen() { @Override public void processOpts() { super.processOpts(); - if (additionalProperties.containsKey(CodegenConstants.MODEL_PROPERTY_NAMING)) { setModelPropertyNaming((String) additionalProperties.get(CodegenConstants.MODEL_PROPERTY_NAMING)); } @@ -183,7 +186,7 @@ public String getName() { @Override public String getHelp() { - return "Generates a Scala client library."; + return "Generates a Scala client library (beta)."; } @Override @@ -203,7 +206,7 @@ public String toOperationId(String operationId) { @Override public String toModelName(final String name) { - final String sanitizedName = sanitizeName(modelNamePrefix + name + modelNameSuffix); + final String sanitizedName = sanitizeName(modelNamePrefix + this.stripPackageName(name) + modelNameSuffix); // camelize the model name // phone_number => PhoneNumber @@ -226,6 +229,11 @@ public String toModelName(final String name) { return camelizedName; } + @Override + public String toEnumName(CodegenProperty property) { + return formatIdentifier(stripPackageName(property.baseName), true); + } + @Override public String escapeQuotationMark(String input) { // remove " to avoid code injection diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ScalaLagomServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ScalaLagomServerCodegen.java new file mode 100644 index 00000000000..dcf16f399c0 --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ScalaLagomServerCodegen.java @@ -0,0 +1,280 @@ +package io.swagger.codegen.languages; + +import io.swagger.codegen.*; +import org.apache.commons.lang3.StringUtils; + +import java.util.*; + +public class ScalaLagomServerCodegen extends AbstractScalaCodegen implements CodegenConfig { + + private String authScheme = ""; + private boolean authPreemptive=false; + protected String groupId = "io.swagger"; + protected String artifactId = "scala-lagom-server"; + protected String artifactVersion = "1.0.0"; + + public ScalaLagomServerCodegen() { + super(); + outputFolder = "generated-code/scala-lagom-server"; + modelTemplateFiles.put("model.mustache", ".scala"); + apiTemplateFiles.put("api.mustache", ".scala"); + embeddedTemplateDir = templateDir = "scala-lagom-server"; + apiPackage = "io.swagger.client.api"; + modelPackage = "io.swagger.client.model"; + + setReservedWordsLowerCase( + Arrays.asList( + // local variable names used in API methods (endpoints) + "path", "contentTypes", "contentType", "queryParams", "headerParams", + "formParams", "postBody", "mp", "basePath", "apiInvoker", + + // scala reserved words + "abstract", "case", "catch", "class", "def", "do", "else", "extends", + "false", "final", "finally", "for", "forSome", "if", "implicit", + "import", "lazy", "match", "new", "null", "object", "override", "package", + "private", "protected", "return", "sealed", "super", "this", "throw", + "trait", "try", "true", "type", "val", "var", "while", "with", "yield") + ); + + additionalProperties.put(CodegenConstants.GROUP_ID, groupId); + additionalProperties.put(CodegenConstants.ARTIFACT_ID, artifactId); + additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, artifactVersion); + additionalProperties.put("authScheme", authScheme); + additionalProperties.put("authPreemptive", authPreemptive); + + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); + supportingFiles.add(new SupportingFile("build.sbt.mustache", "", "build.sbt")); + supportingFiles.add(new SupportingFile("build.properties.mustache", "", "project/build.properties")); + supportingFiles.add(new SupportingFile("plugins.sbt.mustache", "", "project/plugins.sbt")); + + importMapping.remove("List"); + importMapping.remove("Set"); + importMapping.remove("Map"); + + importMapping.put("DateTime", "org.joda.time.DateTime"); + importMapping.put("ListBuffer", "scala.collection.mutable.ListBuffer"); + + typeMapping = new HashMap(); + typeMapping.put("Integer", "Int"); + typeMapping.put("enum", "NSString"); + typeMapping.put("array", "Seq"); + typeMapping.put("set", "Set"); + typeMapping.put("boolean", "Boolean"); + typeMapping.put("string", "String"); + typeMapping.put("int", "Int"); + typeMapping.put("long", "Long"); + typeMapping.put("float", "Float"); + typeMapping.put("byte", "Byte"); + typeMapping.put("short", "Short"); + typeMapping.put("char", "Char"); + typeMapping.put("long", "Long"); + typeMapping.put("double", "Double"); + typeMapping.put("object", "Any"); + typeMapping.put("file", "File"); + + //TODO binary should be mapped to byte array + // mapped to String as a workaround + typeMapping.put("binary", "String"); + typeMapping.put("ByteArray", "String"); + + instantiationTypes.put("array", "ListBuffer"); + instantiationTypes.put("map", "HashMap"); + + cliOptions.add(new CliOption(CodegenConstants.MODEL_PROPERTY_NAMING, + CodegenConstants.MODEL_PROPERTY_NAMING_DESC).defaultValue("camelCase")); + } + + @Override + public void processOpts() { + super.processOpts(); + + if (additionalProperties.containsKey(CodegenConstants.MODEL_PROPERTY_NAMING)) { + setModelPropertyNaming( + (String) additionalProperties.get(CodegenConstants.MODEL_PROPERTY_NAMING)); + } + } + + public void setModelPropertyNaming(String naming) { + if ("original".equals(naming) || "camelCase".equals(naming) || + "PascalCase".equals(naming) || "snake_case".equals(naming)) { + this.modelPropertyNaming = naming; + } else { + throw new IllegalArgumentException("Invalid model property naming '" + + naming + "'. Must be 'original', 'camelCase', " + + "'PascalCase' or 'snake_case'"); + } + } + + public String getModelPropertyNaming() { + return this.modelPropertyNaming; + } + + @Override + public String toVarName(String name) { + // sanitize name + name = sanitizeName( + name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + + if ("_".equals(name)) { + name = "_u"; + } + + // if it's all uppper case, do nothing + if (name.matches("^[A-Z_]*$")) { + return name; + } + + name = getNameUsingModelPropertyNaming(name); + + // for reserved word or word starting with number, append _ + if (isReservedWord(name) || name.matches("^\\d.*")) { + name = escapeReservedWord(name); + } + + return name; + } + + @Override + public String toParamName(String name) { + // should be the same as variable name + return toVarName(name); + } + + private String getNameUsingModelPropertyNaming(String name) { + switch (CodegenConstants.MODEL_PROPERTY_NAMING_TYPE.valueOf(getModelPropertyNaming())) { + case original: + return name; + case camelCase: + return camelize(name, true); + case PascalCase: + return camelize(name); + case snake_case: + return underscore(name); + default: + throw new IllegalArgumentException("Invalid model property naming '" + + name + "'. Must be 'original', 'camelCase', " + + "'PascalCase' or 'snake_case'"); + } + + } + + @Override + public CodegenType getTag() { + return CodegenType.SERVER; + } + + @Override + public String getName() { + return "scala-lagom-server"; + } + + @Override + public String getHelp() { + return "Generates a Lagom API (Beta) in scala"; + } + + @Override + public String toOperationId(String operationId) { + // throw exception if method name is empty + if (StringUtils.isEmpty(operationId)) { + throw new RuntimeException("Empty method name (operationId) not allowed"); + } + + // method name cannot use reserved keyword, e.g. return + if (isReservedWord(operationId)) { + throw new RuntimeException(operationId + " (reserved word) cannot be used as method name"); + } + + return camelize(operationId, true); + } + + @Override + public String toModelName(final String name) { + final String sanitizedName = sanitizeName(modelNamePrefix + name + modelNameSuffix); + + // camelize the model name + // phone_number => PhoneNumber + final String camelizedName = camelize(sanitizedName); + + // model name cannot use reserved keyword, e.g. return + if (isReservedWord(camelizedName)) { + final String modelName = "Model" + camelizedName; + LOGGER.warn( + camelizedName + " (reserved word) cannot be used as model name. Renamed to " + modelName); + return modelName; + } + + // model name starts with number + if (name.matches("^\\d.*")) { + final String modelName = + "Model" + camelizedName; // e.g. 200Response => Model200Response (after camelize) + LOGGER.warn( + name + " (model name starts with number) cannot be used as model name. Renamed to " + + modelName); + return modelName; + } + + return camelizedName; + } + + @Override + public String escapeQuotationMark(String input) { + // remove " to avoid code injection + return input.replace("\"", ""); + } + + @Override + public Map postProcessModelsEnum(Map objs) { + objs = super.postProcessModelsEnum(objs); + List models = (List) objs.get("models"); + for (Object _mo : models) { + Map mo = (Map) _mo; + CodegenModel cm = (CodegenModel) mo.get("model"); + + for (CodegenProperty var : cm.vars) { + if (var.isEnum) { + List enumValues = (List) var.allowableValues.get("values"); + + for (final ListIterator i = enumValues.listIterator(); i.hasNext(); ) { + final String element = String.valueOf(i.next()); + i.set(element.replaceAll("^\"|\"$", "")); + } + } + } + } + + //Needed import for Gson based libraries + if (additionalProperties.containsKey("gson")) { + List> imports = (List>) objs.get("imports"); + + for (Object _mo : models) { + Map mo = (Map) _mo; + CodegenModel cm = (CodegenModel) mo.get("model"); + // for enum model + if (Boolean.TRUE.equals(cm.isEnum) && cm.allowableValues != null) { + cm.imports.add(importMapping.get("SerializedName")); + Map item = new HashMap(); + item.put("import", importMapping.get("SerializedName")); + imports.add(item); + } + } + } + + return objs; + } + + @Override + public Map postProcessOperations(Map objs) { + Map operations = (Map) objs.get("operations"); + ArrayList oplist = (ArrayList) operations.get("operation"); + + for (CodegenOperation codegenOperation : oplist) { + String path = codegenOperation.path; + codegenOperation.path = path.replaceAll("\\{", ":").replaceAll("}", ""); + } + return objs; + } + + +} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ScalazClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ScalazClientCodegen.java new file mode 100644 index 00000000000..3a13d269ff0 --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ScalazClientCodegen.java @@ -0,0 +1,250 @@ +package io.swagger.codegen.languages; + +import com.google.common.base.CaseFormat; +import com.samskivert.mustache.Mustache; +import com.samskivert.mustache.Template; + +import io.swagger.codegen.*; + +import io.swagger.models.auth.SecuritySchemeDefinition; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.BooleanProperty; +import io.swagger.models.properties.DateProperty; +import io.swagger.models.properties.DateTimeProperty; +import io.swagger.models.properties.DoubleProperty; +import io.swagger.models.properties.FloatProperty; +import io.swagger.models.properties.IntegerProperty; +import io.swagger.models.properties.LongProperty; +import io.swagger.models.properties.MapProperty; +import io.swagger.models.properties.Property; +import io.swagger.models.properties.StringProperty; + +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.*; + +import org.apache.commons.lang3.StringUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ScalazClientCodegen extends AbstractScalaCodegen implements CodegenConfig { + + public ScalazClientCodegen() { + super(); + outputFolder = "generated-code/scalaz"; + embeddedTemplateDir = templateDir = "scalaz"; + apiPackage = "io.swagger.client.api"; + modelPackage = "io.swagger.client.api"; + + modelTemplateFiles.put("model.mustache", ".scala"); + apiTemplateFiles.put("api.mustache", ".scala"); + + setReservedWordsLowerCase( + Arrays.asList( + // local variable names used in API methods (endpoints) + "path", "contentTypes", "contentType", "queryParams", "headerParams", + "formParams", "postBody", "mp", "basePath", "apiInvoker", + + // scala reserved words + "abstract", "case", "catch", "class", "def", "do", "else", "extends", + "false", "final", "finally", "for", "forSome", "if", "implicit", + "import", "lazy", "match", "new", "null", "object", "override", "package", + "private", "protected", "return", "sealed", "super", "this", "throw", + "trait", "try", "true", "type", "val", "var", "while", "with", "yield") + ); + + additionalProperties.put("apiPackage", apiPackage); + + supportingFiles.add(new SupportingFile("build.sbt.mustache", "", "build.sbt")); + supportingFiles.add(new SupportingFile("dateTimeCodecs.mustache", (sourceFolder + File.separator + apiPackage).replace(".", File.separator), "DateTimeCodecs.scala")); + supportingFiles.add(new SupportingFile("HelperCodecs.mustache", (sourceFolder + File.separator + apiPackage).replace(".", File.separator), "HelperCodecs.scala")); + supportingFiles.add(new SupportingFile("QueryParamTypeclass.mustache", (sourceFolder + File.separator + apiPackage).replace(".", File.separator), "QueryParamTypeclass.scala")); + + importMapping.remove("List"); + importMapping.remove("Set"); + importMapping.remove("Map"); + + importMapping.put("Date", "java.util.Date"); + importMapping.put("ListBuffer", "scala.collection.mutable.ListBuffer"); + + typeMapping = new HashMap(); + typeMapping.put("enum", "NSString"); + typeMapping.put("array", "List"); + typeMapping.put("set", "Set"); + typeMapping.put("boolean", "Boolean"); + typeMapping.put("string", "String"); + typeMapping.put("int", "Int"); + typeMapping.put("long", "Long"); + typeMapping.put("float", "Float"); + typeMapping.put("byte", "Byte"); + typeMapping.put("short", "Short"); + typeMapping.put("char", "Char"); + typeMapping.put("double", "Double"); + typeMapping.put("object", "Any"); + typeMapping.put("file", "File"); + typeMapping.put("number", "BigDecimal"); + typeMapping.put("date-time", "DateTime"); + typeMapping.put("date", "DateTime"); + + + //instantiationTypes.put("array", "ListBuffer"); + instantiationTypes.put("array", "ListBuffer"); + instantiationTypes.put("map", "HashMap"); + + additionalProperties.put("fnEnumEntry", new EnumEntryLambda()); + + cliOptions.add(new CliOption(CodegenConstants.MODEL_PROPERTY_NAMING, CodegenConstants.MODEL_PROPERTY_NAMING_DESC).defaultValue("camelCase")); + } + + @Override + public void processOpts() { + super.processOpts(); + if (additionalProperties.containsKey(CodegenConstants.MODEL_PROPERTY_NAMING)) { + setModelPropertyNaming((String) additionalProperties.get(CodegenConstants.MODEL_PROPERTY_NAMING)); + } + } + + public void setModelPropertyNaming(String naming) { + if ("original".equals(naming) || "camelCase".equals(naming) || + "PascalCase".equals(naming) || "snake_case".equals(naming)) { + this.modelPropertyNaming = naming; + } else { + throw new IllegalArgumentException("Invalid model property naming '" + + naming + "'. Must be 'original', 'camelCase', " + + "'PascalCase' or 'snake_case'"); + } + } + + public String getModelPropertyNaming() { + return this.modelPropertyNaming; + } + @Override + public String toVarName(String name) { + // sanitize name + name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + + if("_".equals(name)) { + name = "_u"; + } + + // if it's all uppper case, do nothing + if (name.matches("^[A-Z_]*$")) { + return name; + } + + name = getNameUsingModelPropertyNaming(name); + + // for reserved word or word starting with number, append _ + if (isReservedWord(name) || name.matches("^\\d.*")) { + name = escapeReservedWord(name); + } + + return name; + } + + @Override + public String toParamName(String name) { + // should be the same as variable name + return toVarName(name); + } + + @Override + public String toEnumName(CodegenProperty property) { + return formatIdentifier(property.baseName, true); + } + + public String getNameUsingModelPropertyNaming(String name) { + switch (CodegenConstants.MODEL_PROPERTY_NAMING_TYPE.valueOf(getModelPropertyNaming())) { + case original: return name; + case camelCase: return camelize(name, true); + case PascalCase: return camelize(name); + case snake_case: return underscore(name); + default: throw new IllegalArgumentException("Invalid model property naming '" + + name + "'. Must be 'original', 'camelCase', " + + "'PascalCase' or 'snake_case'"); + } + + } + + @Override + public CodegenType getTag() { + return CodegenType.CLIENT; + } + + @Override + public String getName() { + return "scalaz"; + } + + @Override + public String getHelp() { + return "Generates a Scalaz client library (beta) that uses http4s"; + } + + @Override + public String toOperationId(String operationId) { + // throw exception if method name is empty + if (StringUtils.isEmpty(operationId)) { + throw new RuntimeException("Empty method name (operationId) not allowed"); + } + + // method name cannot use reserved keyword, e.g. return + if (isReservedWord(operationId)) { + throw new RuntimeException(operationId + " (reserved word) cannot be used as method name"); + } + + return camelize(operationId, true); + } + + @Override + public String toModelName(final String name) { + final String sanitizedName = sanitizeName(modelNamePrefix + this.stripPackageName(name) + modelNameSuffix); + + // camelize the model name + // phone_number => PhoneNumber + final String camelizedName = camelize(sanitizedName); + + // model name cannot use reserved keyword, e.g. return + if (isReservedWord(camelizedName)) { + final String modelName = "Model" + camelizedName; + LOGGER.warn(camelizedName + " (reserved word) cannot be used as model name. Renamed to " + modelName); + return modelName; + } + + // model name starts with number + if (name.matches("^\\d.*")) { + final String modelName = "Model" + camelizedName; // e.g. 200Response => Model200Response (after camelize) + LOGGER.warn(name + " (model name starts with number) cannot be used as model name. Renamed to " + modelName); + return modelName; + } + + return camelizedName; + } + + private static abstract class CustomLambda implements Mustache.Lambda { + @Override + public void execute(Template.Fragment frag, Writer out) throws IOException { + final StringWriter tempWriter = new StringWriter(); + frag.execute(tempWriter); + out.write(formatFragment(tempWriter.toString())); + } + + public abstract String formatFragment(String fragment); + } + + @Override + public String escapeQuotationMark(String input) { + // remove " to avoid code injection + return input.replace("\"", ""); + } + + private class EnumEntryLambda extends CustomLambda { + @Override + public String formatFragment(String fragment) { + return formatIdentifier(fragment, true); + } + } +} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/SilexServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/SilexServerCodegen.java index 088d4bc5b03..dec7c06489d 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/SilexServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/SilexServerCodegen.java @@ -34,13 +34,13 @@ public SilexServerCodegen() { modelPackage = packagePath + "/lib/models"; apiPackage = packagePath + "/lib"; - outputFolder = "generated-code/silex"; + outputFolder = "generated-code/php-silex"; // no model, api files modelTemplateFiles.clear(); apiTemplateFiles.clear(); - embeddedTemplateDir = templateDir = "silex"; + embeddedTemplateDir = templateDir = "php-silex"; setReservedWordsLowerCase( Arrays.asList( @@ -103,12 +103,12 @@ public CodegenType getTag() { @Override public String getName() { - return "silex-PHP"; + return "php-silex"; } @Override public String getHelp() { - return "Generates a Silex server library."; + return "Generates a PHP Silex server library."; } @Override diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/SpringCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/SpringCodegen.java index 0670e1bb097..703555f1153 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/SpringCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/SpringCodegen.java @@ -32,6 +32,7 @@ public class SpringCodegen extends AbstractJavaCodegen public static final String SPRING_MVC_LIBRARY = "spring-mvc"; public static final String SPRING_CLOUD_LIBRARY = "spring-cloud"; public static final String IMPLICIT_HEADERS = "implicitHeaders"; + public static final String SWAGGER_DOCKET_CONFIG = "swaggerDocketConfig"; protected String title = "swagger-petstore"; protected String configPackage = "io.swagger.configuration"; @@ -45,6 +46,7 @@ public class SpringCodegen extends AbstractJavaCodegen protected boolean useTags = false; protected boolean useBeanValidation = true; protected boolean implicitHeaders = false; + protected boolean swaggerDocketConfig = false; protected boolean useOptional = false; public SpringCodegen() { @@ -65,7 +67,7 @@ public SpringCodegen() { cliOptions.add(new CliOption(TITLE, "server title name or client service name")); cliOptions.add(new CliOption(CONFIG_PACKAGE, "configuration package for generated code")); - cliOptions.add(new CliOption(BASE_PACKAGE, "base package for generated code")); + cliOptions.add(new CliOption(BASE_PACKAGE, "base package (invokerPackage) for generated code")); cliOptions.add(CliOption.newBoolean(INTERFACE_ONLY, "Whether to generate only API interface stubs without the server files.")); cliOptions.add(CliOption.newBoolean(DELEGATE_PATTERN, "Whether to generate the server files using the delegate pattern")); cliOptions.add(CliOption.newBoolean(SINGLE_CONTENT_TYPES, "Whether to select only one produces/consumes content-type by operation.")); @@ -75,6 +77,7 @@ public SpringCodegen() { cliOptions.add(CliOption.newBoolean(USE_TAGS, "use tags for creating interface and controller classnames")); cliOptions.add(CliOption.newBoolean(USE_BEANVALIDATION, "Use BeanValidation API annotations")); cliOptions.add(CliOption.newBoolean(IMPLICIT_HEADERS, "Use of @ApiImplicitParams for headers.")); + cliOptions.add(CliOption.newBoolean(SWAGGER_DOCKET_CONFIG, "Generate Spring Swagger Docket configuration class.")); cliOptions.add(CliOption.newBoolean(USE_OPTIONAL, "Use Optional container for optional parameters")); @@ -107,6 +110,26 @@ public String getHelp() { @Override public void processOpts() { + + // Process java8 option before common java ones to change the default dateLibrary to java8. + if (additionalProperties.containsKey(JAVA_8)) { + this.setJava8(Boolean.valueOf(additionalProperties.get(JAVA_8).toString())); + } + if (this.java8) { + additionalProperties.put("javaVersion", "1.8"); + additionalProperties.put("jdk8", "true"); + if (!additionalProperties.containsKey(DATE_LIBRARY)) { + setDateLibrary("java8"); + } + } + + // set invokerPackage as basePackage + if (additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) { + this.setBasePackage((String) additionalProperties.get(CodegenConstants.INVOKER_PACKAGE)); + additionalProperties.put(BASE_PACKAGE, basePackage); + LOGGER.info("Set base package to invoker package (" + basePackage + ")"); + } + super.processOpts(); // clear model and api doc template as this codegen @@ -171,6 +194,10 @@ public void processOpts() { this.setImplicitHeaders(Boolean.valueOf(additionalProperties.get(IMPLICIT_HEADERS).toString())); } + if (additionalProperties.containsKey(SWAGGER_DOCKET_CONFIG)) { + this.setSwaggerDocketConfig(Boolean.valueOf(additionalProperties.get(SWAGGER_DOCKET_CONFIG).toString())); + } + typeMapping.put("file", "Resource"); importMapping.put("Resource", "org.springframework.core.io.Resource"); @@ -178,15 +205,15 @@ public void processOpts() { writePropertyBack(USE_OPTIONAL, useOptional); } - supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml")); - supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); - if (this.interfaceOnly && this.delegatePattern) { throw new IllegalArgumentException( String.format("Can not generate code with `%s` and `%s` both true.", DELEGATE_PATTERN, INTERFACE_ONLY)); } if (!this.interfaceOnly) { + supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml")); + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + if (library.equals(DEFAULT_LIBRARY)) { supportingFiles.add(new SupportingFile("homeController.mustache", (sourceFolder + File.separator + configPackage).replace(".", java.io.File.separator), "HomeController.java")); @@ -219,8 +246,6 @@ public void processOpts() { additionalProperties.put(SINGLE_CONTENT_TYPES, "true"); this.setSingleContentTypes(true); } - additionalProperties.put("useSpringCloudClient", true); - } else { apiTemplateFiles.put("apiController.mustache", "Controller.java"); supportingFiles.add(new SupportingFile("apiException.mustache", @@ -234,8 +259,20 @@ public void processOpts() { supportingFiles.add(new SupportingFile("swaggerDocumentationConfig.mustache", (sourceFolder + File.separator + configPackage).replace(".", java.io.File.separator), "SwaggerDocumentationConfig.java")); } + } else if ( this.swaggerDocketConfig && !library.equals(SPRING_CLOUD_LIBRARY)) { + supportingFiles.add(new SupportingFile("swaggerDocumentationConfig.mustache", + (sourceFolder + File.separator + configPackage).replace(".", java.io.File.separator), "SwaggerDocumentationConfig.java")); } + if ("threetenbp".equals(dateLibrary)) { + supportingFiles.add(new SupportingFile("customInstantDeserializer.mustache", + (sourceFolder + File.separator + configPackage).replace(".", java.io.File.separator), "CustomInstantDeserializer.java")); + if (library.equals(DEFAULT_LIBRARY) || library.equals(SPRING_CLOUD_LIBRARY)) { + supportingFiles.add(new SupportingFile("jacksonConfiguration.mustache", + (sourceFolder + File.separator + configPackage).replace(".", java.io.File.separator), "JacksonConfiguration.java")); + } + } + if (!this.delegatePattern && this.java8) { additionalProperties.put("jdk8-no-delegate", true); } @@ -271,7 +308,7 @@ public void processOpts() { additionalProperties.put(RESPONSE_WRAPPER, "org.springframework.util.concurrent.ListenableFuture"); break; case "DeferredResult": - additionalProperties.put(RESPONSE_WRAPPER, "org.springframework.web.context.request.DeferredResult"); + additionalProperties.put(RESPONSE_WRAPPER, "org.springframework.web.context.request.async.DeferredResult"); break; case "HystrixCommand": additionalProperties.put(RESPONSE_WRAPPER, "com.netflix.hystrix.HystrixCommand"); @@ -576,6 +613,10 @@ public void setImplicitHeaders(boolean implicitHeaders) { this.implicitHeaders = implicitHeaders; } + public void setSwaggerDocketConfig(boolean swaggerDocketConfig) { + this.swaggerDocketConfig = swaggerDocketConfig; + } + @Override public void postProcessModelProperty(CodegenModel model, CodegenProperty property) { super.postProcessModelProperty(model, property); diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Swift3Codegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Swift3Codegen.java index 4e34ec56f7e..22642e00e29 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Swift3Codegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Swift3Codegen.java @@ -27,6 +27,7 @@ public class Swift3Codegen extends DefaultCodegen implements CodegenConfig { public static final String PROJECT_NAME = "projectName"; public static final String RESPONSE_AS = "responseAs"; public static final String UNWRAP_REQUIRED = "unwrapRequired"; + public static final String OBJC_COMPATIBLE = "objcCompatible"; public static final String POD_SOURCE = "podSource"; public static final String POD_AUTHORS = "podAuthors"; public static final String POD_SOCIAL_MEDIA_URL = "podSocialMediaURL"; @@ -45,6 +46,7 @@ public class Swift3Codegen extends DefaultCodegen implements CodegenConfig { protected static final String[] RESPONSE_LIBRARIES = {LIBRARY_PROMISE_KIT, LIBRARY_RX_SWIFT}; protected String projectName = "SwaggerClient"; protected boolean unwrapRequired; + protected boolean objcCompatible = false; protected boolean lenientTypeCast = false; protected boolean swiftUseApiNamespace; protected String[] responseAs = new String[0]; @@ -125,15 +127,14 @@ public Swift3Codegen() { "true", "lazy", "operator", "in", "COLUMN", "left", "private", "return", "FILE", "mutating", "protocol", "switch", "FUNCTION", "none", "public", "where", "LINE", "nonmutating", "static", "while", "optional", "struct", "override", "subscript", "postfix", "typealias", "precedence", "var", "prefix", "Protocol", - "required", "right", "set", "Type", "unowned", "weak") + "required", "right", "set", "Type", "unowned", "weak", "Data") ); typeMapping = new HashMap<>(); typeMapping.put("array", "Array"); typeMapping.put("List", "Array"); typeMapping.put("map", "Dictionary"); - typeMapping.put("date", "Date"); - typeMapping.put("Date", "Date"); + typeMapping.put("date", "ISOFullDate"); typeMapping.put("DateTime", "Date"); typeMapping.put("boolean", "Bool"); typeMapping.put("string", "String"); @@ -159,6 +160,7 @@ public Swift3Codegen() { StringUtils.join(RESPONSE_LIBRARIES, ", ") + " are available.")); cliOptions.add(new CliOption(UNWRAP_REQUIRED, "Treat 'required' properties in response as non-optional " + "(which would crash the app if api returns null as opposed to required option specified in json schema")); + cliOptions.add(new CliOption(OBJC_COMPATIBLE, "Add additional properties and methods for Objective-C compatibility (default: false)")); cliOptions.add(new CliOption(POD_SOURCE, "Source information used for Podspec")); cliOptions.add(new CliOption(CodegenConstants.POD_VERSION, "Version used for Podspec")); cliOptions.add(new CliOption(POD_AUTHORS, "Authors used for Podspec")); @@ -203,6 +205,12 @@ public void processOpts() { } additionalProperties.put(UNWRAP_REQUIRED, unwrapRequired); + // Setup objcCompatible option, which adds additional properties and methods for Objective-C compatibility + if (additionalProperties.containsKey(OBJC_COMPATIBLE)) { + setObjcCompatible(convertPropertyToBooleanAndWriteBack(OBJC_COMPATIBLE)); + } + additionalProperties.put(OBJC_COMPATIBLE, objcCompatible); + // Setup unwrapRequired option, which makes all the properties with "required" non-optional if (additionalProperties.containsKey(RESPONSE_AS)) { Object responseAsObject = additionalProperties.get(RESPONSE_AS); @@ -251,13 +259,13 @@ protected boolean isReservedWord(String word) { } @Override - public String escapeReservedWord(String name) { + public String escapeReservedWord(String name) { if(this.reservedWordsMappings().containsKey(name)) { return this.reservedWordsMappings().get(name); } return "_" + name; // add an underscore to the name } - + @Override public String modelFileFolder() { return outputFolder + File.separator + sourceFolder + modelPackage().replace('.', File.separatorChar); @@ -480,6 +488,10 @@ public void setUnwrapRequired(boolean unwrapRequired) { this.unwrapRequired = unwrapRequired; } + public void setObjcCompatible(boolean objcCompatible) { + this.objcCompatible = objcCompatible; + } + public void setLenientTypeCast(boolean lenientTypeCast) { this.lenientTypeCast = lenientTypeCast; } @@ -580,6 +592,31 @@ public Map postProcessModels(Map objs) { return postProcessModelsEnum(objs); } + @Override + public void postProcessModelProperty(CodegenModel model, CodegenProperty property) { + super.postProcessModelProperty(model, property); + + // The default template code has the following logic for assigning a type as Swift Optional: + // + // {{^unwrapRequired}}?{{/unwrapRequired}}{{#unwrapRequired}}{{^required}}?{{/required}}{{/unwrapRequired}} + // + // which means: + // + // boolean isSwiftOptional = !unwrapRequired || (unwrapRequired && !property.required); + // + // We can drop the check for unwrapRequired in (unwrapRequired && !property.required) + // due to short-circuit evaluation of the || operator. + boolean isSwiftOptional = !unwrapRequired || !property.required; + boolean isSwiftScalarType = property.isInteger || property.isLong || property.isFloat || property.isDouble || property.isBoolean; + if (isSwiftOptional && isSwiftScalarType) { + // Optional scalar types like Int?, Int64?, Float?, Double?, and Bool? + // do not translate to Objective-C. So we want to flag those + // properties in case we want to put special code in the templates + // which provide Objective-C compatibility. + property.vendorExtensions.put("x-swift-optional-scalar", true); + } + } + @Override public String escapeQuotationMark(String input) { // remove " to avoid code injection diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Swift4Codegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Swift4Codegen.java index ed6fc2d123a..b6760ef363e 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Swift4Codegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Swift4Codegen.java @@ -3,7 +3,16 @@ import com.google.common.base.Predicate; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; -import io.swagger.codegen.*; + +import io.swagger.codegen.CliOption; +import io.swagger.codegen.CodegenConfig; +import io.swagger.codegen.CodegenConstants; +import io.swagger.codegen.CodegenModel; +import io.swagger.codegen.CodegenProperty; +import io.swagger.codegen.CodegenType; +import io.swagger.codegen.DefaultCodegen; +import io.swagger.codegen.SupportingFile; + import io.swagger.models.Model; import io.swagger.models.ModelImpl; import io.swagger.models.Operation; @@ -13,20 +22,28 @@ import io.swagger.models.properties.ArrayProperty; import io.swagger.models.properties.MapProperty; import io.swagger.models.properties.Property; + import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.text.WordUtils; -import javax.annotation.Nullable; import java.io.File; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.annotation.Nullable; public class Swift4Codegen extends DefaultCodegen implements CodegenConfig { public static final String PROJECT_NAME = "projectName"; public static final String RESPONSE_AS = "responseAs"; public static final String UNWRAP_REQUIRED = "unwrapRequired"; + public static final String OBJC_COMPATIBLE = "objcCompatible"; public static final String POD_SOURCE = "podSource"; public static final String POD_AUTHORS = "podAuthors"; public static final String POD_SOCIAL_MEDIA_URL = "podSocialMediaURL"; @@ -45,6 +62,7 @@ public class Swift4Codegen extends DefaultCodegen implements CodegenConfig { protected static final String[] RESPONSE_LIBRARIES = {LIBRARY_PROMISE_KIT, LIBRARY_RX_SWIFT}; protected String projectName = "SwaggerClient"; protected boolean unwrapRequired; + protected boolean objcCompatible = false; protected boolean lenientTypeCast = false; protected boolean swiftUseApiNamespace; protected String[] responseAs = new String[0]; @@ -66,15 +84,19 @@ public String getHelp() { } @Override - protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, ModelImpl swaggerModel) { + protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, + ModelImpl swaggerModel) { final Property additionalProperties = swaggerModel.getAdditionalProperties(); - if(additionalProperties != null) { + if (additionalProperties != null) { codegenModel.additionalPropertiesType = getSwaggerType(additionalProperties); } } + /** + * Constructor for the swift4 language codegen module. + */ public Swift4Codegen() { super(); outputFolder = "generated-code" + File.separator + "swift"; @@ -118,14 +140,18 @@ public Swift4Codegen() { "ErrorResponse", "Response", // swift keywords - "Int", "Int32", "Int64", "Int64", "Float", "Double", "Bool", "Void", "String", "Character", "AnyObject", "Any", "Error", "URL", - "class", "Class", "break", "as", "associativity", "deinit", "case", "dynamicType", "convenience", "enum", "continue", - "false", "dynamic", "extension", "default", "is", "didSet", "func", "do", "nil", "final", "import", "else", - "self", "get", "init", "fallthrough", "Self", "infix", "internal", "for", "super", "inout", "let", "if", - "true", "lazy", "operator", "in", "COLUMN", "left", "private", "return", "FILE", "mutating", "protocol", - "switch", "FUNCTION", "none", "public", "where", "LINE", "nonmutating", "static", "while", "optional", - "struct", "override", "subscript", "postfix", "typealias", "precedence", "var", "prefix", "Protocol", - "required", "right", "set", "Type", "unowned", "weak", "Codable", "Encodable", "Decodable") + "Int", "Int32", "Int64", "Int64", "Float", "Double", "Bool", "Void", "String", + "Character", "AnyObject", "Any", "Error", "URL", "class", "Class", "break", + "as", "associativity", "deinit", "case", "dynamicType", "convenience", "enum", + "continue", "false", "dynamic", "extension", "default", "is", "didSet", + "func", "do", "nil", "final", "import", "else", "self", "get", "init", + "fallthrough", "Self", "infix", "internal", "for", "super", "inout", "let", + "if", "true", "lazy", "operator", "in", "COLUMN", "left", "private", "return", + "FILE", "mutating", "protocol", "switch", "FUNCTION", "none", "public", + "where", "LINE", "nonmutating", "static", "while", "optional", "struct", + "override", "subscript", "postfix", "typealias", "precedence", "var", + "prefix", "Protocol", "required", "right", "set", "throw", "Type", "unowned", "weak", + "Data", "Codable", "Encodable", "Decodable") ); typeMapping = new HashMap<>(); @@ -139,10 +165,10 @@ public Swift4Codegen() { typeMapping.put("string", "String"); typeMapping.put("char", "Character"); typeMapping.put("short", "Int"); - typeMapping.put("int", "Int32"); + typeMapping.put("int", "Int"); typeMapping.put("long", "Int64"); - typeMapping.put("integer", "Int32"); - typeMapping.put("Integer", "Int32"); + typeMapping.put("integer", "Int"); + typeMapping.put("Integer", "Int"); typeMapping.put("float", "Float"); typeMapping.put("number", "Double"); typeMapping.put("double", "Double"); @@ -155,10 +181,17 @@ public Swift4Codegen() { importMapping = new HashMap<>(); cliOptions.add(new CliOption(PROJECT_NAME, "Project name in Xcode")); - cliOptions.add(new CliOption(RESPONSE_AS, "Optionally use libraries to manage response. Currently " + - StringUtils.join(RESPONSE_LIBRARIES, ", ") + " are available.")); - cliOptions.add(new CliOption(UNWRAP_REQUIRED, "Treat 'required' properties in response as non-optional " + - "(which would crash the app if api returns null as opposed to required option specified in json schema")); + cliOptions.add(new CliOption(RESPONSE_AS, + "Optionally use libraries to manage response. Currently " + + StringUtils.join(RESPONSE_LIBRARIES, ", ") + + " are available.")); + cliOptions.add(new CliOption(UNWRAP_REQUIRED, + "Treat 'required' properties in response as non-optional " + + "(which would crash the app if api returns null as opposed " + + "to required option specified in json schema")); + cliOptions.add(new CliOption(OBJC_COMPATIBLE, + "Add additional properties and methods for Objective-C " + + "compatibility (default: false)")); cliOptions.add(new CliOption(POD_SOURCE, "Source information used for Podspec")); cliOptions.add(new CliOption(CodegenConstants.POD_VERSION, "Version used for Podspec")); cliOptions.add(new CliOption(POD_AUTHORS, "Authors used for Podspec")); @@ -169,11 +202,17 @@ public Swift4Codegen() { cliOptions.add(new CliOption(POD_SUMMARY, "Summary used for Podspec")); cliOptions.add(new CliOption(POD_DESCRIPTION, "Description used for Podspec")); cliOptions.add(new CliOption(POD_SCREENSHOTS, "Screenshots used for Podspec")); - cliOptions.add(new CliOption(POD_DOCUMENTATION_URL, "Documentation URL used for Podspec")); - cliOptions.add(new CliOption(SWIFT_USE_API_NAMESPACE, "Flag to make all the API classes inner-class of {{projectName}}API")); - cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, "hides the timestamp when files were generated") + cliOptions.add(new CliOption(POD_DOCUMENTATION_URL, + "Documentation URL used for Podspec")); + cliOptions.add(new CliOption(SWIFT_USE_API_NAMESPACE, + "Flag to make all the API classes inner-class " + + "of {{projectName}}API")); + cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, + "hides the timestamp when files were generated") .defaultValue(Boolean.TRUE.toString())); - cliOptions.add(new CliOption(LENIENT_TYPE_CAST, "Accept and cast values for simple types (string->bool, string->int, int->string)") + cliOptions.add(new CliOption(LENIENT_TYPE_CAST, + "Accept and cast values for simple types (string->bool, " + + "string->int, int->string)") .defaultValue(Boolean.FALSE.toString())); } @@ -183,10 +222,13 @@ public void processOpts() { // default HIDE_GENERATION_TIMESTAMP to true if (!additionalProperties.containsKey(CodegenConstants.HIDE_GENERATION_TIMESTAMP)) { - additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, Boolean.TRUE.toString()); - } else { additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, - Boolean.valueOf(additionalProperties().get(CodegenConstants.HIDE_GENERATION_TIMESTAMP).toString())); + Boolean.TRUE.toString()); + } else { + Boolean hide = Boolean.valueOf(additionalProperties() + .get(CodegenConstants.HIDE_GENERATION_TIMESTAMP) + .toString()); + additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, hide); } // Setup project name @@ -197,12 +239,20 @@ public void processOpts() { } sourceFolder = projectName + File.separator + sourceFolder; - // Setup unwrapRequired option, which makes all the properties with "required" non-optional + // Setup unwrapRequired option, which makes all the + // properties with "required" non-optional if (additionalProperties.containsKey(UNWRAP_REQUIRED)) { setUnwrapRequired(convertPropertyToBooleanAndWriteBack(UNWRAP_REQUIRED)); } additionalProperties.put(UNWRAP_REQUIRED, unwrapRequired); + // Setup objcCompatible option, which adds additional properties + // and methods for Objective-C compatibility + if (additionalProperties.containsKey(OBJC_COMPATIBLE)) { + setObjcCompatible(convertPropertyToBooleanAndWriteBack(OBJC_COMPATIBLE)); + } + additionalProperties.put(OBJC_COMPATIBLE, objcCompatible); + // Setup unwrapRequired option, which makes all the properties with "required" non-optional if (additionalProperties.containsKey(RESPONSE_AS)) { Object responseAsObject = additionalProperties.get(RESPONSE_AS); @@ -220,7 +270,8 @@ public void processOpts() { additionalProperties.put("useRxSwift", true); } - // Setup swiftUseApiNamespace option, which makes all the API classes inner-class of {{projectName}}API + // Setup swiftUseApiNamespace option, which makes all the API + // classes inner-class of {{projectName}}API if (additionalProperties.containsKey(SWIFT_USE_API_NAMESPACE)) { setSwiftUseApiNamespace(convertPropertyToBooleanAndWriteBack(SWIFT_USE_API_NAMESPACE)); } @@ -231,20 +282,45 @@ public void processOpts() { setLenientTypeCast(convertPropertyToBooleanAndWriteBack(LENIENT_TYPE_CAST)); - supportingFiles.add(new SupportingFile("Podspec.mustache", "", projectName + ".podspec")); - supportingFiles.add(new SupportingFile("Cartfile.mustache", "", "Cartfile")); - supportingFiles.add(new SupportingFile("APIHelper.mustache", sourceFolder, "APIHelper.swift")); - supportingFiles.add(new SupportingFile("AlamofireImplementations.mustache", sourceFolder, - "AlamofireImplementations.swift")); - supportingFiles.add(new SupportingFile("Configuration.mustache", sourceFolder, "Configuration.swift")); - supportingFiles.add(new SupportingFile("Extensions.mustache", sourceFolder, "Extensions.swift")); - supportingFiles.add(new SupportingFile("Models.mustache", sourceFolder, "Models.swift")); - supportingFiles.add(new SupportingFile("APIs.mustache", sourceFolder, "APIs.swift")); - supportingFiles.add(new SupportingFile("CodableHelper.mustache", sourceFolder, "CodableHelper.swift")); - supportingFiles.add(new SupportingFile("JSONEncodableEncoding.mustache", sourceFolder, "JSONEncodableEncoding.swift")); - supportingFiles.add(new SupportingFile("JSONEncodingHelper.mustache", sourceFolder, "JSONEncodingHelper.swift")); - supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); - supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); + supportingFiles.add(new SupportingFile("Podspec.mustache", + "", + projectName + ".podspec")); + supportingFiles.add(new SupportingFile("Cartfile.mustache", + "", + "Cartfile")); + supportingFiles.add(new SupportingFile("APIHelper.mustache", + sourceFolder, + "APIHelper.swift")); + supportingFiles.add(new SupportingFile("AlamofireImplementations.mustache", + sourceFolder, + "AlamofireImplementations.swift")); + supportingFiles.add(new SupportingFile("Configuration.mustache", + sourceFolder, + "Configuration.swift")); + supportingFiles.add(new SupportingFile("Extensions.mustache", + sourceFolder, + "Extensions.swift")); + supportingFiles.add(new SupportingFile("Models.mustache", + sourceFolder, + "Models.swift")); + supportingFiles.add(new SupportingFile("APIs.mustache", + sourceFolder, + "APIs.swift")); + supportingFiles.add(new SupportingFile("CodableHelper.mustache", + sourceFolder, + "CodableHelper.swift")); + supportingFiles.add(new SupportingFile("JSONEncodableEncoding.mustache", + sourceFolder, + "JSONEncodableEncoding.swift")); + supportingFiles.add(new SupportingFile("JSONEncodingHelper.mustache", + sourceFolder, + "JSONEncodingHelper.swift")); + supportingFiles.add(new SupportingFile("git_push.sh.mustache", + "", + "git_push.sh")); + supportingFiles.add(new SupportingFile("gitignore.mustache", + "", + ".gitignore")); } @@ -255,7 +331,7 @@ protected boolean isReservedWord(String word) { @Override public String escapeReservedWord(String name) { - if(this.reservedWordsMappings().containsKey(name)) { + if (this.reservedWordsMappings().containsKey(name)) { return this.reservedWordsMappings().get(name); } return "_" + name; // add an underscore to the name @@ -263,38 +339,42 @@ public String escapeReservedWord(String name) { @Override public String modelFileFolder() { - return outputFolder + File.separator + sourceFolder + modelPackage().replace('.', File.separatorChar); + return outputFolder + File.separator + sourceFolder + + modelPackage().replace('.', File.separatorChar); } @Override public String apiFileFolder() { - return outputFolder + File.separator + sourceFolder + apiPackage().replace('.', File.separatorChar); + return outputFolder + File.separator + sourceFolder + + apiPackage().replace('.', File.separatorChar); } @Override - public String getTypeDeclaration(Property p) { - if (p instanceof ArrayProperty) { - ArrayProperty ap = (ArrayProperty) p; + public String getTypeDeclaration(Property prop) { + if (prop instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) prop; Property inner = ap.getItems(); return "[" + getTypeDeclaration(inner) + "]"; - } else if (p instanceof MapProperty) { - MapProperty mp = (MapProperty) p; + } else if (prop instanceof MapProperty) { + MapProperty mp = (MapProperty) prop; Property inner = mp.getAdditionalProperties(); return "[String:" + getTypeDeclaration(inner) + "]"; } - return super.getTypeDeclaration(p); + return super.getTypeDeclaration(prop); } @Override - public String getSwaggerType(Property p) { - String swaggerType = super.getSwaggerType(p); + public String getSwaggerType(Property prop) { + String swaggerType = super.getSwaggerType(prop); String type; if (typeMapping.containsKey(swaggerType)) { type = typeMapping.get(swaggerType); - if (languageSpecificPrimitives.contains(type) || defaultIncludes.contains(type)) + if (languageSpecificPrimitives.contains(type) || defaultIncludes.contains(type)) { return type; - } else + } + } else { type = swaggerType; + } return toModelName(type); } @@ -309,14 +389,15 @@ public boolean isDataTypeBinary(final String dataType) { } /** - * Output the proper model name (capitalized) + * Output the proper model name (capitalized). * * @param name the name of the model * @return capitalized model name */ @Override public String toModelName(String name) { - name = sanitizeName(name); // FIXME parameter should not be assigned. Also declare it as "final" + // FIXME parameter should not be assigned. Also declare it as "final" + name = sanitizeName(name); if (!StringUtils.isEmpty(modelNameSuffix)) { // set model suffix name = name + "_" + modelNameSuffix; @@ -333,14 +414,18 @@ public String toModelName(String name) { // model name cannot use reserved keyword, e.g. return if (isReservedWord(name)) { String modelName = "Model" + name; - LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to " + modelName); + LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to " + + modelName); return modelName; } // model name starts with number if (name.matches("^\\d.*")) { - String modelName = "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); + // e.g. 200Response => Model200Response (after camelize) + String modelName = "Model" + name; + LOGGER.warn(name + + " (model name starts with number) cannot be used as model name." + + " Renamed to " + modelName); return modelName; } @@ -348,7 +433,7 @@ public String toModelName(String name) { } /** - * Return the capitalized file name of the model + * Return the capitalized file name of the model. * * @param name the model name * @return the file name of the model @@ -360,19 +445,19 @@ public String toModelFilename(String name) { } @Override - public String toDefaultValue(Property p) { + public String toDefaultValue(Property prop) { // nil return null; } @Override - public String toInstantiationType(Property p) { - if (p instanceof MapProperty) { - MapProperty ap = (MapProperty) p; + public String toInstantiationType(Property prop) { + if (prop instanceof MapProperty) { + MapProperty ap = (MapProperty) prop; String inner = getSwaggerType(ap.getAdditionalProperties()); return inner; - } else if (p instanceof ArrayProperty) { - ArrayProperty ap = (ArrayProperty) p; + } else if (prop instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) prop; String inner = getSwaggerType(ap.getItems()); return "[" + inner + "]"; } @@ -381,8 +466,9 @@ public String toInstantiationType(Property p) { @Override public String toApiName(String name) { - if (name.length() == 0) + if (name.length() == 0) { return "DefaultAPI"; + } return initialCaps(name) + "API"; } @@ -390,7 +476,8 @@ public String toApiName(String name) { public String toOperationId(String operationId) { operationId = camelize(sanitizeName(operationId), true); - // throw exception if method name is empty. This should not happen but keep the check just in case + // Throw exception if method name is empty. + // This should not happen but keep the check just in case if (StringUtils.isEmpty(operationId)) { throw new RuntimeException("Empty method name (operationId) not allowed"); } @@ -398,7 +485,8 @@ public String toOperationId(String operationId) { // method name cannot use reserved keyword, e.g. return if (isReservedWord(operationId)) { String newOperationId = camelize(("call_" + operationId), true); - LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + newOperationId); + LOGGER.warn(operationId + " (reserved word) cannot be used as method name." + + " Renamed to " + newOperationId); return newOperationId; } @@ -455,21 +543,23 @@ public String toParamName(String name) { @Override public CodegenModel fromModel(String name, Model model, Map allDefinitions) { CodegenModel codegenModel = super.fromModel(name, model, allDefinitions); - if(codegenModel.description != null) { + if (codegenModel.description != null) { codegenModel.imports.add("ApiModel"); } if (allDefinitions != null) { - String parentSchema = codegenModel.parentSchema; - - // multilevel inheritance: reconcile properties of all the parents - while (parentSchema != null) { - final Model parentModel = allDefinitions.get(parentSchema); - final CodegenModel parentCodegenModel = super.fromModel(codegenModel.parent, parentModel, allDefinitions); - codegenModel = Swift4Codegen.reconcileProperties(codegenModel, parentCodegenModel); - - // get the next parent - parentSchema = parentCodegenModel.parentSchema; - } + String parentSchema = codegenModel.parentSchema; + + // multilevel inheritance: reconcile properties of all the parents + while (parentSchema != null) { + final Model parentModel = allDefinitions.get(parentSchema); + final CodegenModel parentCodegenModel = super.fromModel(codegenModel.parent, + parentModel, + allDefinitions); + codegenModel = Swift4Codegen.reconcileProperties(codegenModel, parentCodegenModel); + + // get the next parent + parentSchema = parentCodegenModel.parentSchema; + } } return codegenModel; @@ -483,6 +573,10 @@ public void setUnwrapRequired(boolean unwrapRequired) { this.unwrapRequired = unwrapRequired; } + public void setObjcCompatible(boolean objcCompatible) { + this.objcCompatible = objcCompatible; + } + public void setLenientTypeCast(boolean lenientTypeCast) { this.lenientTypeCast = lenientTypeCast; } @@ -539,8 +633,8 @@ public String toEnumVarName(String name, String datatype) { } // Check for numerical conversions - if ("Int".equals(datatype) || "Int32".equals(datatype) || "Int64".equals(datatype) || - "Float".equals(datatype) || "Double".equals(datatype)) { + if ("Int".equals(datatype) || "Int32".equals(datatype) || "Int64".equals(datatype) + || "Float".equals(datatype) || "Double".equals(datatype)) { String varName = "number" + camelize(name); varName = varName.replaceAll("-", "minus"); varName = varName.replaceAll("\\+", "plus"); @@ -555,7 +649,9 @@ public String toEnumVarName(String name, String datatype) { } char[] separators = {'-', '_', ' ', ':', '(', ')'}; - return camelize(WordUtils.capitalizeFully(StringUtils.lowerCase(name), separators).replaceAll("[-_ :\\(\\)]", ""), true); + return camelize(WordUtils.capitalizeFully(StringUtils.lowerCase(name), separators) + .replaceAll("[-_ :\\(\\)]", ""), + true); } @Override @@ -565,11 +661,13 @@ public String toEnumName(CodegenProperty property) { // Ensure that the enum type doesn't match a reserved word or // the variable name doesn't match the generated enum type or the // Swift compiler will generate an error - if (isReservedWord(property.datatypeWithEnum) || toVarName(property.name).equals(property.datatypeWithEnum)) { + if (isReservedWord(property.datatypeWithEnum) + || toVarName(property.name).equals(property.datatypeWithEnum)) { enumName = property.datatypeWithEnum + "Enum"; } - // TODO: toModelName already does something for names starting with number, so this code is probably never called + // TODO: toModelName already does something for names starting with number, + // so this code is probably never called if (enumName.matches("\\d.*")) { // starts with number return "_" + enumName; } else { @@ -583,6 +681,34 @@ public Map postProcessModels(Map objs) { return postProcessModelsEnum(objs); } + @Override + public void postProcessModelProperty(CodegenModel model, CodegenProperty property) { + super.postProcessModelProperty(model, property); + + // The default template code has the following logic for + // assigning a type as Swift Optional: + // + // {{^unwrapRequired}}?{{/unwrapRequired}} + // {{#unwrapRequired}}{{^required}}?{{/required}}{{/unwrapRequired}} + // + // which means: + // + // boolean isSwiftOptional = !unwrapRequired || (unwrapRequired && !property.required); + // + // We can drop the check for unwrapRequired in (unwrapRequired && !property.required) + // due to short-circuit evaluation of the || operator. + boolean isSwiftOptional = !unwrapRequired || !property.required; + boolean isSwiftScalarType = property.isInteger || property.isLong || property.isFloat + || property.isDouble || property.isBoolean; + if (isSwiftOptional && isSwiftScalarType) { + // Optional scalar types like Int?, Int64?, Float?, Double?, and Bool? + // do not translate to Objective-C. So we want to flag those + // properties in case we want to put special code in the templates + // which provide Objective-C compatibility. + property.vendorExtensions.put("x-swift-optional-scalar", true); + } + } + @Override public String escapeQuotationMark(String input) { // remove " to avoid code injection @@ -594,11 +720,13 @@ public String escapeUnsafeCharacters(String input) { return input.replace("*/", "*_/").replace("/*", "/_*"); } - private static CodegenModel reconcileProperties(CodegenModel codegenModel, CodegenModel parentCodegenModel) { + private static CodegenModel reconcileProperties(CodegenModel codegenModel, + CodegenModel parentCodegenModel) { // To support inheritance in this generator, we will analyze // the parent and child models, look for properties that match, and remove // them from the child models and leave them in the parent. - // Because the child models extend the parents, the properties will be available via the parent. + // Because the child models extend the parents, the properties + // will be available via the parent. // Get the properties for the parent and child models final List parentModelCodegenProperties = parentCodegenModel.vars; @@ -610,24 +738,25 @@ private static CodegenModel reconcileProperties(CodegenModel codegenModel, Codeg boolean removedChildProperty = false; for (CodegenProperty parentModelCodegenProperty : parentModelCodegenProperties) { - // Now that we have found a prop in the parent class, - // and search the child class for the same prop. - Iterator iterator = codegenProperties.iterator(); - while (iterator.hasNext()) { - CodegenProperty codegenProperty = iterator.next(); - if (codegenProperty.baseName == parentModelCodegenProperty.baseName) { - // We found a property in the child class that is - // a duplicate of the one in the parent, so remove it. - iterator.remove(); - removedChildProperty = true; - } - } - } - - if(removedChildProperty) { + // Now that we have found a prop in the parent class, + // and search the child class for the same prop. + Iterator iterator = codegenProperties.iterator(); + while (iterator.hasNext()) { + CodegenProperty codegenProperty = iterator.next(); + if (codegenProperty.baseName == parentModelCodegenProperty.baseName) { + // We found a property in the child class that is + // a duplicate of the one in the parent, so remove it. + iterator.remove(); + removedChildProperty = true; + } + } + } + + if (removedChildProperty) { // If we removed an entry from this model's vars, we need to ensure hasMore is updated - int count = 0, numVars = codegenProperties.size(); - for(CodegenProperty codegenProperty : codegenProperties) { + int count = 0; + int numVars = codegenProperties.size(); + for (CodegenProperty codegenProperty : codegenProperties) { count += 1; codegenProperty.hasMore = (count < numVars) ? true : false; } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/SwiftCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/SwiftCodegen.java index cdfcc58a109..902302833de 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/SwiftCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/SwiftCodegen.java @@ -23,6 +23,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +/** + * Swift (2.x) generator is no longer actively maintained. Please use + * 'swift3' or 'swift4' generator instead. + */ + public class SwiftCodegen extends DefaultCodegen implements CodegenConfig { public static final String PROJECT_NAME = "projectName"; public static final String RESPONSE_AS = "responseAs"; @@ -61,7 +66,7 @@ public String getName() { @Override public String getHelp() { - return "Generates a swift client library."; + return "Generates a Swift (2.x) client library. IMPORTANT NOTE: this generator (swfit 2.x) is no longer actively maintained so please use 'swift3' or 'swift4' generator instead."; } public SwiftCodegen() { @@ -112,15 +117,15 @@ public SwiftCodegen() { "true", "lazy", "operator", "in", "COLUMN", "left", "private", "return", "FILE", "mutating", "protocol", "switch", "FUNCTION", "none", "public", "where", "LINE", "nonmutating", "static", "while", "optional", "struct", "override", "subscript", "postfix", "typealias", "precedence", "var", "prefix", "Protocol", - "required", "right", "set", "Type", "unowned", "weak") + "required", "right", "set", "Type", "unowned", "weak", "Data") ); typeMapping = new HashMap(); typeMapping.put("array", "Array"); typeMapping.put("List", "Array"); typeMapping.put("map", "Dictionary"); - typeMapping.put("date", "NSDate"); - typeMapping.put("Date", "NSDate"); + typeMapping.put("date", "ISOFullDate"); + typeMapping.put("Date", "ISOFullDate"); typeMapping.put("DateTime", "NSDate"); typeMapping.put("boolean", "Bool"); typeMapping.put("string", "String"); diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/SymfonyServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/SymfonyServerCodegen.java index 05d4ba7ddd6..8f30fd0f5f0 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/SymfonyServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/SymfonyServerCodegen.java @@ -19,6 +19,7 @@ public class SymfonyServerCodegen extends AbstractPhpCodegen implements CodegenC public static final String BUNDLE_NAME = "bundleName"; public static final String COMPOSER_VENDOR_NAME = "composerVendorName"; public static final String COMPOSER_PROJECT_NAME = "composerProjectName"; + public static final String PHP_LEGACY_SUPPORT = "phpLegacySupport"; public static final Map SYMFONY_EXCEPTIONS; protected String testsPackage; protected String apiTestsPackage; @@ -31,7 +32,12 @@ public class SymfonyServerCodegen extends AbstractPhpCodegen implements CodegenC protected String bundleExtensionName; protected String bundleAlias; protected String controllerDirName = "Controller"; + protected String serviceDirName = "Service"; protected String controllerPackage; + protected String servicePackage; + protected Boolean phpLegacySupport = Boolean.TRUE; + + protected HashSet typeHintable; static { SYMFONY_EXCEPTIONS = new HashMap<>(); @@ -70,40 +76,47 @@ public SymfonyServerCodegen() { modelDocPath = docsBasePath + File.separator + modelDirName; outputFolder = "generated-code" + File.separator + "php"; apiTemplateFiles.put("api_controller.mustache", ".php"); - modelTestTemplateFiles.put("model_test.mustache", ".php"); + modelTestTemplateFiles.put("testing/model_test.mustache", ".php"); + apiTestTemplateFiles = new HashMap(); + apiTestTemplateFiles.put("testing/api_test.mustache", ".php"); embeddedTemplateDir = templateDir = "php-symfony"; setReservedWordsLowerCase( - Arrays.asList( - // local variables used in api methods (endpoints) - "resourcePath", "httpBody", "queryParams", "headerParams", - "formParams", "_header_accept", "_tempBody", - - // PHP reserved words - "__halt_compiler", "abstract", "and", "array", "as", "break", "callable", "case", "catch", "class", "clone", "const", "continue", "declare", "default", "die", "do", "echo", "else", "elseif", "empty", "enddeclare", "endfor", "endforeach", "endif", "endswitch", "endwhile", "eval", "exit", "extends", "final", "for", "foreach", "function", "global", "goto", "if", "implements", "include", "include_once", "instanceof", "insteadof", "interface", "isset", "list", "namespace", "new", "or", "print", "private", "protected", "public", "require", "require_once", "return", "static", "switch", "throw", "trait", "try", "unset", "use", "var", "while", "xor") + Arrays.asList( + // local variables used in api methods (endpoints) + "resourcePath", "httpBody", "queryParams", "headerParams", + "formParams", "_header_accept", "_tempBody", + + // PHP reserved words + "__halt_compiler", "abstract", "and", "array", "as", "break", "callable", "case", "catch", "class", "clone", "const", "continue", "declare", "default", "die", "do", "echo", "else", "elseif", "empty", "enddeclare", "endfor", "endforeach", "endif", "endswitch", "endwhile", "eval", "exit", "extends", "final", "for", "foreach", "function", "global", "goto", "if", "implements", "include", "include_once", "instanceof", "insteadof", "interface", "isset", "list", "namespace", "new", "or", "print", "private", "protected", "public", "require", "require_once", "return", "static", "switch", "throw", "trait", "try", "unset", "use", "var", "while", "xor" + ) ); // ref: http://php.net/manual/en/language.types.intro.php languageSpecificPrimitives = new HashSet( - Arrays.asList( - "bool", - "boolean", - "int", - "integer", - "double", - "float", - "string", - "object", - "DateTime", - "mixed", - "number", - "void", - "byte") + Arrays.asList( + "bool", + "int", + "double", + "float", + "string", + "object", + "mixed", + "number", + "void", + "byte", + "array" + ) ); - instantiationTypes.put("array", "array"); - instantiationTypes.put("map", "map"); + defaultIncludes = new HashSet( + Arrays.asList( + "\\DateTime", + "UploadedFile" + ) + ); + variableNamingConvention = "camelCase"; // provide primitives to mustache template List sortedLanguageSpecificPrimitives= new ArrayList(languageSpecificPrimitives); @@ -123,11 +136,11 @@ public SymfonyServerCodegen() { typeMapping.put("boolean", "bool"); typeMapping.put("Date", "\\DateTime"); typeMapping.put("DateTime", "\\DateTime"); - typeMapping.put("file", "\\SplFileObject"); - typeMapping.put("map", "map"); + typeMapping.put("file", "UploadedFile"); + typeMapping.put("map", "array"); typeMapping.put("array", "array"); typeMapping.put("list", "array"); - typeMapping.put("object", "object"); + typeMapping.put("object", "array"); typeMapping.put("binary", "string"); typeMapping.put("ByteArray", "string"); typeMapping.put("UUID", "string"); @@ -137,6 +150,7 @@ public SymfonyServerCodegen() { cliOptions.add(new CliOption(COMPOSER_PROJECT_NAME, "The project name used in the composer package name. The template uses {{composerVendorName}}/{{composerProjectName}} for the composer package name. e.g. petstore-client. IMPORTANT NOTE (2016/03): composerProjectName will be deprecated and replaced by gitRepoId in the next swagger-codegen release")); cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, "hides the timestamp when files were generated") .defaultValue(Boolean.TRUE.toString())); + cliOptions.add(new CliOption(PHP_LEGACY_SUPPORT, "Should the generated code be compatible with PHP 5.x?").defaultValue(Boolean.TRUE.toString())); } public String getBundleName() { @@ -150,6 +164,9 @@ public void setBundleName(String bundleName) { this.bundleAlias = snakeCase(bundleName).replaceAll("([A-Z]+)", "\\_$1").toLowerCase(); } + public void setPhpLegacySupport(Boolean support) { + this.phpLegacySupport = support; + } public String controllerFileFolder() { return (outputFolder + File.separator + toPackagePath(controllerPackage, srcBasePath)); @@ -218,8 +235,15 @@ public void processOpts() { additionalProperties.put(COMPOSER_VENDOR_NAME, composerVendorName); } + if (additionalProperties.containsKey(PHP_LEGACY_SUPPORT)) { + this.setPhpLegacySupport(Boolean.valueOf((String) additionalProperties.get(PHP_LEGACY_SUPPORT))); + } else { + additionalProperties.put(PHP_LEGACY_SUPPORT, phpLegacySupport); + } + additionalProperties.put("escapedInvokerPackage", invokerPackage.replace("\\", "\\\\")); additionalProperties.put("controllerPackage", controllerPackage); + additionalProperties.put("servicePackage", servicePackage); additionalProperties.put("apiTestsPackage", apiTestsPackage); additionalProperties.put("modelTestsPackage", modelTestsPackage); @@ -253,55 +277,102 @@ public void processOpts() { supportingFiles.add(new SupportingFile("Extension.mustache", dependencyInjectionDir, bundleExtensionName + ".php")); supportingFiles.add(new SupportingFile("ApiPass.mustache", dependencyInjectionDir + File.separator + "Compiler", bundleName + "ApiPass.php")); supportingFiles.add(new SupportingFile("ApiServer.mustache", toPackagePath(apiPackage, srcBasePath), "ApiServer.php")); - supportingFiles.add(new SupportingFile("ModelSerializer.mustache", toPackagePath(modelPackage, srcBasePath), "ModelSerializer.php")); - supportingFiles.add(new SupportingFile("ModelInterface.mustache", toPackagePath(modelPackage, srcBasePath), "ModelInterface.php")); + + // Serialization components + supportingFiles.add(new SupportingFile("serialization/SerializerInterface.mustache", toPackagePath(servicePackage, srcBasePath), "SerializerInterface.php")); + supportingFiles.add(new SupportingFile("serialization/JmsSerializer.mustache", toPackagePath(servicePackage, srcBasePath), "JmsSerializer.php")); + supportingFiles.add(new SupportingFile("serialization/StrictJsonDeserializationVisitor.mustache", toPackagePath(servicePackage, srcBasePath), "StrictJsonDeserializationVisitor.php")); + supportingFiles.add(new SupportingFile("serialization/TypeMismatchException.mustache", toPackagePath(servicePackage, srcBasePath), "TypeMismatchException.php")); + // Validation components + supportingFiles.add(new SupportingFile("validation/ValidatorInterface.mustache", toPackagePath(servicePackage, srcBasePath), "ValidatorInterface.php")); + supportingFiles.add(new SupportingFile("validation/SymfonyValidator.mustache", toPackagePath(servicePackage, srcBasePath), "SymfonyValidator.php")); + + // Testing components + supportingFiles.add(new SupportingFile("testing/phpunit.xml.mustache", getPackagePath(), "phpunit.xml.dist")); + supportingFiles.add(new SupportingFile("testing/pom.xml", getPackagePath(), "pom.xml")); + supportingFiles.add(new SupportingFile("testing/AppKernel.php", toPackagePath(testsPackage, srcBasePath), "AppKernel.php")); + supportingFiles.add(new SupportingFile("testing/test_config.yml", toPackagePath(testsPackage, srcBasePath), "test_config.yml")); + supportingFiles.add(new SupportingFile("routing.mustache", configDir, "routing.yml")); supportingFiles.add(new SupportingFile("services.mustache", configDir, "services.yml")); supportingFiles.add(new SupportingFile("composer.mustache", getPackagePath(), "composer.json")); supportingFiles.add(new SupportingFile("autoload.mustache", getPackagePath(), "autoload.php")); supportingFiles.add(new SupportingFile("README.mustache", getPackagePath(), "README.md")); - supportingFiles.add(new SupportingFile("phpunit.xml.mustache", getPackagePath(), "phpunit.xml.dist")); + supportingFiles.add(new SupportingFile(".travis.yml", getPackagePath(), ".travis.yml")); supportingFiles.add(new SupportingFile(".php_cs", getPackagePath(), ".php_cs")); supportingFiles.add(new SupportingFile("git_push.sh.mustache", getPackagePath(), "git_push.sh")); + + // Type-hintable primitive types + // ref: http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration + if (phpLegacySupport) { + typeHintable = new HashSet( + Arrays.asList( + "array" + ) + ); + } else { + typeHintable = new HashSet( + Arrays.asList( + "array", + "bool", + "float", + "int", + "string" + ) + ); + } } @Override public Map postProcessOperations(Map objs) { objs = super.postProcessOperations(objs); + Map operations = (Map) objs.get("operations"); operations.put("controllerName", toControllerName((String) operations.get("pathPrefix"))); operations.put("symfonyService", toSymfonyService((String) operations.get("pathPrefix"))); HashSet authMethods = new HashSet<>(); - HashSet imports = new HashSet<>(); List operationList = (List) operations.get("operation"); + for (CodegenOperation op : operationList) { + // Loop through all input parameters to determine, whether we have to import something to + // make the input type available. for (CodegenParameter param : op.allParams) { - final String simpleName = extractSimpleName(param.dataType); - param.vendorExtensions.put("x-simpleName", simpleName); - final boolean isScalarType = typeMapping.containsValue(param.dataType); - param.vendorExtensions.put("x-parameterType", isScalarType ? null : simpleName); - if (!isScalarType) { - imports.add(param.dataType); + // Determine if the paramter type is supported as a type hint and make it available + // to the templating engine + String typeHint = getTypeHint(param.dataType); + if (!typeHint.isEmpty()) { + param.vendorExtensions.put("x-parameterType", typeHint); } - } - for (CodegenResponse response : op.responses) { - final String exception = SYMFONY_EXCEPTIONS.get(response.code); - response.vendorExtensions.put("x-symfonyException", exception); - response.vendorExtensions.put("x-symfonyExceptionSimple", extractSimpleName(exception)); + if (param.isContainer) { + param.vendorExtensions.put("x-parameterType", getTypeHint(param.dataType+"[]")); + } + + // Create a variable to display the correct data type in comments for interfaces + param.vendorExtensions.put("x-commentType", param.dataType); + if (param.isContainer) { + param.vendorExtensions.put("x-commentType", param.dataType+"[]"); + } - // Add simple return type to response - if (response.dataType != null) { - final String dataType = extractSimpleName(response.dataType); - response.vendorExtensions.put("x-simpleName", dataType); - imports.add(response.dataType.replaceFirst("\\[\\]$", "")); + // Quote default values for strings + // @todo: The default values for headers, forms and query params are handled + // in DefaultCodegen fromParameter with no real possibility to override + // the functionality. Thus we are handling quoting of string values here + if (param.dataType.equals("string") && param.defaultValue != null && !param.defaultValue.isEmpty()) { + param.defaultValue = "'"+param.defaultValue+"'"; } + } - if (exception != null) { - imports.add(exception); + // 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) { + op.vendorExtensions.put("x-commentType", op.returnType+"[]"); } + } else { + op.vendorExtensions.put("x-commentType", "void"); } // Add operation's authentication methods to whole interface @@ -310,7 +381,6 @@ public Map postProcessOperations(Map objs) { } } - operations.put("imports", new ArrayList<>(imports)); operations.put("authMethods", authMethods); return objs; @@ -323,19 +393,21 @@ public Map postProcessModels(Map objs) { ArrayList modelsArray = (ArrayList) objs.get("models"); Map models = (Map) modelsArray.get(0); CodegenModel model = (CodegenModel) models.get("model"); - HashSet imports = new HashSet<>(); // Simplify model var type for (CodegenProperty var : model.vars) { if (var.datatype != null) { - final String importType = var.datatype.replaceFirst("\\[\\]$", ""); - final String dataType = extractSimpleName(var.datatype); - final boolean isScalarType = typeMapping.containsValue(importType); - var.vendorExtensions.put("x-fullType", var.datatype); - if (!isScalarType) { - var.vendorExtensions.put("x-typeAnnotation", dataType.endsWith("[]") ? "array" : dataType); - imports.add(importType); - var.datatype = dataType; + // Determine if the paramter type is supported as a type hint and make it available + // to the templating engine + String typeHint = getTypeHint(var.datatype); + if (!typeHint.isEmpty()) { + var.vendorExtensions.put("x-parameterType", typeHint); + } + + // Create a variable to display the correct data type in comments for models + var.vendorExtensions.put("x-commentType", var.datatype); + if (var.isContainer) { + var.vendorExtensions.put("x-commentType", var.datatype+"[]"); } if (var.isBoolean) { @@ -344,8 +416,6 @@ public Map postProcessModels(Map objs) { } } - objs.put("useStatements", new ArrayList<>(imports)); - return objs; } @@ -384,6 +454,7 @@ public void setInvokerPackage(String invokerPackage) { apiTestsPackage = testsPackage + "\\" + apiDirName; modelTestsPackage = testsPackage + "\\" + modelDirName; controllerPackage = invokerPackage + "\\" + controllerDirName; + servicePackage = invokerPackage + "\\" + serviceDirName; } @Override @@ -391,19 +462,19 @@ public String getTypeDeclaration(Property p) { if (p instanceof ArrayProperty) { ArrayProperty ap = (ArrayProperty) p; Property inner = ap.getItems(); - return getTypeDeclaration(inner) + "[]"; + return getTypeDeclaration(inner); } if (p instanceof MapProperty) { MapProperty mp = (MapProperty) p; Property inner = mp.getAdditionalProperties(); - return getSwaggerType(p) + "array"; + return getTypeDeclaration(inner); } - + if (p instanceof RefProperty) { return getTypeDeclaration(getPropertyTypeDeclaration(p)); } - + return getPropertyTypeDeclaration(p); } @@ -429,6 +500,41 @@ public String getTypeDeclaration(String name) { return super.getTypeDeclaration(name); } + /** + * Return the fully-qualified "Model" name for import + * + * @param name the name of the "Model" + * @return the fully-qualified "Model" name for import + */ + @Override + public String toModelImport(String name) { + if ("".equals(modelPackage())) { + return name; + } else { + return modelPackage() + "\\" + name; + } + } + + @Override + public String toEnumValue(String value, String datatype) { + if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { + return value; + } else { + return "\"" + escapeText(value) + "\""; + } + } + + /** + * Return the regular expression/JSON schema pattern (http://json-schema.org/latest/json-schema-validation.html#anchor33) + * + * @param pattern the pattern (regular expression) + * @return properly-escaped pattern + */ + @Override + public String toRegularExpression(String pattern) { + return escapeText(pattern); + } + public String toApiName(String name) { if (name.isEmpty()) { return "DefaultApiInterface"; @@ -451,4 +557,34 @@ protected String toSymfonyService(String name) { return prefix + name; } + + protected String getTypeHint(String type) { + // Type hint array types + if (type.endsWith("[]")) { + return "array"; + } + + // Check if the type is a native type that is type hintable in PHP + if (typeHintable.contains(type)) { + return type; + } + + // Default includes are referenced by their fully-qualified class name (including namespace) + if (defaultIncludes.contains(type)) { + return type; + } + + // Model classes are assumed to be imported and we reference them by their class name + if (isModelClass(type)) { + // This parameter is an instance of a model + return extractSimpleName(type); + } + + // PHP does not support type hinting for this parameter data type + return ""; + } + + protected Boolean isModelClass(String type) { + return Boolean.valueOf(type.contains(modelPackage())); + } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TizenClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TizenClientCodegen.java index 3a971b7ee77..e47fbce95e8 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TizenClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TizenClientCodegen.java @@ -25,17 +25,15 @@ import java.util.Map; import java.util.Set; -import org.apache.commons.lang3.StringUtils; public class TizenClientCodegen extends DefaultCodegen implements CodegenConfig { - protected static String PREFIX = "Sami"; - protected Set foundationClasses = new HashSet(); - protected String sourceFolder = "client"; - protected Map namespaces = new HashMap(); + protected static String PREFIX = "ArtikCloud"; + protected String sourceFolder = "src"; + protected String documentationFolder = "doc"; public TizenClientCodegen() { super(); - outputFolder = "generated-code/tizen"; + outputFolder = ""; modelTemplateFiles.put("model-header.mustache", ".h"); modelTemplateFiles.put("model-body.mustache", ".cpp"); apiTemplateFiles.put("api-header.mustache", ".h"); @@ -47,71 +45,72 @@ public TizenClientCodegen() { Arrays.asList( "bool", "int", - "long") + "long long", + "double", + "float") + ); + languageSpecificPrimitives = new HashSet( + Arrays.asList( + "bool", + "int", + "long long", + "double", + "float", + "std::string") ); - languageSpecificPrimitives = new HashSet(); additionalProperties().put("prefix", PREFIX); setReservedWordsLowerCase( - // VERIFY Arrays.asList( - "void", "char", "short", "int", "void", "char", "short", "int", - "long", "float", "double", "signed", "unsigned", "id", "const", - "volatile", "in", "out", "inout", "bycopy", "byref", "oneway", - "self", "super" + "alignas", "alignof", "and", "and_eq", "asm", "atomic_cancel", "atomic_commit", "atomic_noexcept", + "auto", "bitand", "bitor", "bool", "break", "case", "catch", "char", "char16_t", "char32_t", + "class", "compl", "concept", "const", "constexpr", "const_cast", "continue", "decltype", "default", + "delete", "do", "double", "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", + "float", "for", "friend", "goto", "if", "inline", "int", "import", "long", "module", "mutable", + "namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", "private", + "protected", "public", "register", "reinterpret_cast", "requires", "return", "short", "signed", + "sizeof", "static", "static_assert", "static_cast", "struct", "switch", "synchronized", "template", + "this", "thread_local", "throw", "true", "try", "typedef", "typeid", "typename", "union", + "unsigned", "using", "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq" )); super.typeMapping = new HashMap(); - typeMapping.put("Date", "DateTime"); - typeMapping.put("DateTime", "DateTime"); - typeMapping.put("string", "String"); - typeMapping.put("integer", "Integer"); - typeMapping.put("float", "Float"); - typeMapping.put("long", "Long"); - typeMapping.put("boolean", "Boolean"); - typeMapping.put("double", "Double"); - typeMapping.put("array", "IList"); - typeMapping.put("map", "HashMap"); - typeMapping.put("number", "Long"); - typeMapping.put("object", PREFIX + "Object"); - typeMapping.put("UUID", "String"); - //TODO binary should be mapped to byte array - // mapped to String as a workaround - typeMapping.put("binary", "String"); + //typeMapping.put("Date", "DateTime"); + //typeMapping.put("DateTime", "DateTime"); + typeMapping.put("string", "std::string"); + typeMapping.put("integer", "int"); + typeMapping.put("float", "float"); + typeMapping.put("long", "long long"); + typeMapping.put("boolean", "bool"); + typeMapping.put("double", "double"); + typeMapping.put("array", "std::list"); + typeMapping.put("map", "std::map"); + typeMapping.put("number", "long long"); + typeMapping.put("object", "std::string"); + typeMapping.put("binary", "std::string"); + typeMapping.put("password", "std::string"); + //TODO:Maybe use better formats for dateTime? + typeMapping.put("file", "std::string"); + typeMapping.put("DateTime", "std::string"); + typeMapping.put("Date", "std::string"); + typeMapping.put("UUID", "std::string"); importMapping = new HashMap(); - namespaces = new HashMap(); - namespaces.put("DateTime", "Tizen::Base::DateTime"); - namespaces.put("Integer", "Tizen::Base::Integer"); - namespaces.put("Long", "Tizen::Base::Long"); - namespaces.put("Boolean", "Tizen::Base::Boolean"); - namespaces.put("Float", "Tizen::Base::Float"); - namespaces.put("String", "Tizen::Base::String"); - namespaces.put("Double", "Tizen::Base::Double"); - namespaces.put("IList", "Tizen::Base::Collection::IList"); - namespaces.put("HashMap", "Tizen::Base::Collection::HashMap"); - namespaces.put("ArrayList", "Tizen::Base::Collection::ArrayList"); - namespaces.put("JsonNumber", "Tizen::Web::Json"); - namespaces.put("JsonString", "Tizen::Web::Json"); - - foundationClasses = new HashSet( - Arrays.asList( - "String", - "Integer", - "Float") - ); supportingFiles.clear(); - supportingFiles.add(new SupportingFile("modelFactory.mustache", sourceFolder, PREFIX + "ModelFactory.h")); - supportingFiles.add(new SupportingFile("helpers-header.mustache", sourceFolder, PREFIX + "Helpers.h")); - supportingFiles.add(new SupportingFile("helpers-body.mustache", sourceFolder, PREFIX + "Helpers.cpp")); - supportingFiles.add(new SupportingFile("apiclient-header.mustache", sourceFolder, PREFIX + "ApiClient.h")); - supportingFiles.add(new SupportingFile("apiclient-body.mustache", sourceFolder, PREFIX + "ApiClient.cpp")); - supportingFiles.add(new SupportingFile("object.mustache", sourceFolder, PREFIX + "Object.h")); - supportingFiles.add(new SupportingFile("error-header.mustache", sourceFolder, PREFIX + "Error.h")); - supportingFiles.add(new SupportingFile("error-body.mustache", sourceFolder, PREFIX + "Error.cpp")); + supportingFiles.add(new SupportingFile("helpers-header.mustache", sourceFolder, "Helpers.h")); + supportingFiles.add(new SupportingFile("helpers-body.mustache", sourceFolder, "Helpers.cpp")); + supportingFiles.add(new SupportingFile("netclient-header.mustache", sourceFolder, "NetClient.h")); + supportingFiles.add(new SupportingFile("netclient-body.mustache", sourceFolder, "NetClient.cpp")); + supportingFiles.add(new SupportingFile("object.mustache", sourceFolder, "Object.h")); + supportingFiles.add(new SupportingFile("requestinfo.mustache", sourceFolder, "RequestInfo.h")); + supportingFiles.add(new SupportingFile("error-header.mustache", sourceFolder, "Error.h")); + supportingFiles.add(new SupportingFile("error-body.mustache", sourceFolder, "Error.cpp")); + supportingFiles.add(new SupportingFile("Doxyfile.mustache", documentationFolder, "Doxyfile")); + supportingFiles.add(new SupportingFile("generateDocumentation.mustache", documentationFolder, "generateDocumentation.sh")); + supportingFiles.add(new SupportingFile("doc-readme.mustache", documentationFolder, "README.md")); } @Override @@ -142,10 +141,10 @@ public String toInstantiationType(Property p) { @Override public String getTypeDeclaration(String name) { - if (languageSpecificPrimitives.contains(name) && !foundationClasses.contains(name)) { + if (languageSpecificPrimitives.contains(name)) { return name; } else { - return name + "*"; + return name + ""; } } @@ -155,7 +154,7 @@ public String getSwaggerType(Property p) { String type = null; if (typeMapping.containsKey(swaggerType)) { type = typeMapping.get(swaggerType); - if (languageSpecificPrimitives.contains(type) && !foundationClasses.contains(type)) { + if (languageSpecificPrimitives.contains(type)) { return toModelName(type); } } else { @@ -167,10 +166,10 @@ public String getSwaggerType(Property p) { @Override public String getTypeDeclaration(Property p) { String swaggerType = getSwaggerType(p); - if (languageSpecificPrimitives.contains(swaggerType) && !foundationClasses.contains(swaggerType)) { + if (languageSpecificPrimitives.contains(swaggerType)) { return toModelName(swaggerType); } else { - return swaggerType + "*"; + return swaggerType + ""; } } @@ -178,48 +177,48 @@ public String getTypeDeclaration(Property p) { public String toModelName(String type) { if (typeMapping.keySet().contains(type) || typeMapping.values().contains(type) || - foundationClasses.contains(type) || importMapping.values().contains(type) || defaultIncludes.contains(type) || languageSpecificPrimitives.contains(type)) { return type; } else { - return PREFIX + Character.toUpperCase(type.charAt(0)) + type.substring(1); + return Character.toUpperCase(type.charAt(0)) + type.substring(1); } } @Override public String toModelImport(String name) { - if (namespaces.containsKey(name)) { - return "using " + namespaces.get(name) + ";"; + if (name.equals("std::string")) { + return "#include "; + } else if (name.equals("std::map")) { + return "#include "; + } else if (name.equals("std::list")) { + return "#include "; } return "#include \"" + name + ".h\""; } + //Might not be needed @Override public String toDefaultValue(Property p) { if (p instanceof StringProperty) { - return "new String()"; + return "std::string()"; } else if (p instanceof BooleanProperty) { - return "new Boolean(false)"; - } else if (p instanceof DateProperty) { - return "new DateTime()"; - } else if (p instanceof DateTimeProperty) { - return "new DateTime()"; + return "bool(false)"; } else if (p instanceof DoubleProperty) { - return "new Double()"; + return "double(0)"; } else if (p instanceof FloatProperty) { - return "new Float()"; + return "float(0)"; } else if (p instanceof IntegerProperty) { - return "new Integer()"; + return "int(0)"; } else if (p instanceof LongProperty) { - return "new Long()"; + return "long(0)"; } else if (p instanceof DecimalProperty) { - return "new Long()"; + return "long(0)"; } else if (p instanceof MapProperty) { - return "new HashMap()"; + return "new std::map()"; } else if (p instanceof ArrayProperty) { - return "new ArrayList()"; + return "new std::list()"; } // else if (p instanceof RefProperty) { @@ -229,6 +228,7 @@ public String toDefaultValue(Property p) { return "null"; } + @Override public String apiFileFolder() { return outputFolder + File.separator + sourceFolder; @@ -241,24 +241,27 @@ public String modelFileFolder() { @Override public String toModelFilename(String name) { - return PREFIX + initialCaps(name); + return initialCaps(name); } @Override public String toApiName(String name) { - return PREFIX + initialCaps(name) + "Api"; + return initialCaps(name) + "Manager"; } @Override public String toApiFilename(String name) { - return PREFIX + initialCaps(name) + "Api"; + return initialCaps(name) + "Manager"; } @Override public String toVarName(String name) { String paramName = name.replaceAll("[^a-zA-Z0-9_]", ""); - paramName = Character.toUpperCase(paramName.charAt(0)) + paramName.substring(1); - return "p" + paramName; + paramName = Character.toLowerCase(paramName.charAt(0)) + paramName.substring(1); + if (isReservedWord(paramName)) { + return escapeReservedWord(paramName); + } + return "" + paramName; } @Override @@ -272,27 +275,17 @@ public String escapeReservedWord(String name) { @Override public String toOperationId(String operationId) { // throw exception if method name is empty - if (StringUtils.isEmpty(operationId)) { + if (operationId=="") { throw new RuntimeException("Empty method name (operationId) not allowed"); } // method name cannot use reserved keyword, e.g. return$ if (isReservedWord(operationId)) { - throw new RuntimeException(operationId + " (reserved word) cannot be used as method name"); + operationId = escapeReservedWord(operationId); } // add_pet_by_id => addPetById return camelize(operationId, true); } - @Override - public String escapeQuotationMark(String input) { - // remove " to avoid code injection - return input.replace("\"", ""); - } - - @Override - public String escapeUnsafeCharacters(String input) { - return input.replace("*/", "*_/").replace("/*", "/_*"); - } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAngular2ClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAngular2ClientCodegen.java deleted file mode 100644 index 8fe2e2d849b..00000000000 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAngular2ClientCodegen.java +++ /dev/null @@ -1,247 +0,0 @@ -package io.swagger.codegen.languages; - -import java.io.File; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; -import java.util.Map; - -import io.swagger.codegen.CliOption; -import io.swagger.codegen.CodegenModel; -import io.swagger.codegen.CodegenParameter; -import io.swagger.codegen.CodegenOperation; -import io.swagger.codegen.SupportingFile; -import io.swagger.models.ModelImpl; -import io.swagger.models.properties.ArrayProperty; -import io.swagger.models.properties.BooleanProperty; -import io.swagger.models.properties.FileProperty; -import io.swagger.models.properties.MapProperty; -import io.swagger.models.properties.ObjectProperty; -import io.swagger.models.properties.Property; - -public class TypeScriptAngular2ClientCodegen extends AbstractTypeScriptClientCodegen { - private static final SimpleDateFormat SNAPSHOT_SUFFIX_FORMAT = new SimpleDateFormat("yyyyMMddHHmm"); - - public static final String NPM_NAME = "npmName"; - public static final String NPM_VERSION = "npmVersion"; - public static final String NPM_REPOSITORY = "npmRepository"; - public static final String SNAPSHOT = "snapshot"; - public static final String WITH_INTERFACES = "withInterfaces"; - - protected String npmName = null; - protected String npmVersion = "1.0.0"; - protected String npmRepository = null; - - public TypeScriptAngular2ClientCodegen() { - super(); - this.outputFolder = "generated-code/typescript-angular2"; - - embeddedTemplateDir = templateDir = "typescript-angular2"; - modelTemplateFiles.put("model.mustache", ".ts"); - apiTemplateFiles.put("api.mustache", ".ts"); - typeMapping.put("Date","Date"); - apiPackage = "api"; - modelPackage = "model"; - - - this.cliOptions.add(new CliOption(NPM_NAME, "The name under which you want to publish generated npm package")); - this.cliOptions.add(new CliOption(NPM_VERSION, "The version of your npm package")); - this.cliOptions.add(new CliOption(NPM_REPOSITORY, "Use this property to set an url your private npmRepo in the package.json")); - this.cliOptions.add(new CliOption(SNAPSHOT, "When setting this property to true the version will be suffixed with -SNAPSHOT.yyyyMMddHHmm", BooleanProperty.TYPE).defaultValue(Boolean.FALSE.toString())); - this.cliOptions.add(new CliOption(WITH_INTERFACES, "Setting this property to true will generate interfaces next to the default class implementations.", BooleanProperty.TYPE).defaultValue(Boolean.FALSE.toString())); - } - - @Override - protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, ModelImpl swaggerModel) { - codegenModel.additionalPropertiesType = getSwaggerType(swaggerModel.getAdditionalProperties()); - addImport(codegenModel, codegenModel.additionalPropertiesType); - } - - @Override - public String getName() { - return "typescript-angular2"; - } - - @Override - public String getHelp() { - return "Generates a TypeScript Angular2 client library."; - } - - @Override - public void processOpts() { - super.processOpts(); - supportingFiles.add(new SupportingFile("models.mustache", modelPackage().replace('.', File.separatorChar), "models.ts")); - supportingFiles.add(new SupportingFile("apis.mustache", apiPackage().replace('.', File.separatorChar), "api.ts")); - supportingFiles.add(new SupportingFile("index.mustache", getIndexDirectory(), "index.ts")); - supportingFiles.add(new SupportingFile("configuration.mustache", getIndexDirectory(), "configuration.ts")); - supportingFiles.add(new SupportingFile("variables.mustache", getIndexDirectory(), "variables.ts")); - supportingFiles.add(new SupportingFile("gitignore", "", ".gitignore")); - supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); - - if(additionalProperties.containsKey(NPM_NAME)) { - addNpmPackageGeneration(); - } - - if(additionalProperties.containsKey(WITH_INTERFACES)) { - boolean withInterfaces = Boolean.parseBoolean(additionalProperties.get(WITH_INTERFACES).toString()); - if (withInterfaces) { - apiTemplateFiles.put("apiInterface.mustache", "Interface.ts"); - } - } - } - - private void addNpmPackageGeneration() { - if(additionalProperties.containsKey(NPM_NAME)) { - this.setNpmName(additionalProperties.get(NPM_NAME).toString()); - } - - if (additionalProperties.containsKey(NPM_VERSION)) { - this.setNpmVersion(additionalProperties.get(NPM_VERSION).toString()); - } - - if (additionalProperties.containsKey(SNAPSHOT) && Boolean.valueOf(additionalProperties.get(SNAPSHOT).toString())) { - this.setNpmVersion(npmVersion + "-SNAPSHOT." + SNAPSHOT_SUFFIX_FORMAT.format(new Date())); - } - additionalProperties.put(NPM_VERSION, npmVersion); - - if (additionalProperties.containsKey(NPM_REPOSITORY)) { - this.setNpmRepository(additionalProperties.get(NPM_REPOSITORY).toString()); - } - - //Files for building our lib - supportingFiles.add(new SupportingFile("README.mustache", getIndexDirectory(), "README.md")); - supportingFiles.add(new SupportingFile("package.mustache", getIndexDirectory(), "package.json")); - supportingFiles.add(new SupportingFile("typings.mustache", getIndexDirectory(), "typings.json")); - supportingFiles.add(new SupportingFile("tsconfig.mustache", getIndexDirectory(), "tsconfig.json")); - } - - private String getIndexDirectory() { - String indexPackage = modelPackage.substring(0, Math.max(0, modelPackage.lastIndexOf('.'))); - return indexPackage.replace('.', File.separatorChar); - } - - @Override - public String getTypeDeclaration(Property p) { - Property inner; - if(p instanceof ArrayProperty) { - ArrayProperty mp1 = (ArrayProperty)p; - inner = mp1.getItems(); - return this.getSwaggerType(p) + "<" + this.getTypeDeclaration(inner) + ">"; - } else if(p instanceof MapProperty) { - MapProperty mp = (MapProperty)p; - inner = mp.getAdditionalProperties(); - return "{ [key: string]: " + this.getTypeDeclaration(inner) + "; }"; - } else if(p instanceof FileProperty || p instanceof ObjectProperty) { - return "any"; - } else { - return super.getTypeDeclaration(p); - } - } - - @Override - public String getSwaggerType(Property p) { - String swaggerType = super.getSwaggerType(p); - if(isLanguagePrimitive(swaggerType) || isLanguageGenericType(swaggerType)) { - return swaggerType; - } - return addModelPrefix(swaggerType); - } - - private String addModelPrefix(String swaggerType) { - String type = null; - if (typeMapping.containsKey(swaggerType)) { - type = typeMapping.get(swaggerType); - } else { - type = swaggerType; - } - - if (!isLanguagePrimitive(type) && !isLanguageGenericType(type)) { - type = "models." + swaggerType; - } - return type; - } - - private boolean isLanguagePrimitive(String type) { - return languageSpecificPrimitives.contains(type); - } - - private boolean isLanguageGenericType(String type) { - for (String genericType: languageGenericTypes) { - if (type.startsWith(genericType + "<")) { - return true; - } - } - return false; - } - - @Override - public void postProcessParameter(CodegenParameter parameter) { - super.postProcessParameter(parameter); - parameter.dataType = addModelPrefix(parameter.dataType); - } - - @Override - public Map postProcessOperations(Map operations) { - Map objs = (Map) operations.get("operations"); - List ops = (List) objs.get("operation"); - for (CodegenOperation op : ops) { - // Convert httpMethod to Angular's RequestMethod enum - // https://angular.io/docs/ts/latest/api/http/index/RequestMethod-enum.html - switch (op.httpMethod) { - case "GET": - op.httpMethod = "RequestMethod.Get"; - break; - case "POST": - op.httpMethod = "RequestMethod.Post"; - break; - case "PUT": - op.httpMethod = "RequestMethod.Put"; - break; - case "DELETE": - op.httpMethod = "RequestMethod.Delete"; - break; - case "OPTIONS": - op.httpMethod = "RequestMethod.Options"; - break; - case "HEAD": - op.httpMethod = "RequestMethod.Head"; - break; - case "PATCH": - op.httpMethod = "RequestMethod.Patch"; - break; - default: - throw new RuntimeException("Unknown HTTP Method " + op.httpMethod + " not allowed"); - } - - // Convert path to TypeScript template string - op.path = op.path.replaceAll("\\{(.*?)\\}", "\\$\\{$1\\}"); - } - - return operations; - } - - public String getNpmName() { - return npmName; - } - - public void setNpmName(String npmName) { - this.npmName = npmName; - } - - public String getNpmVersion() { - return npmVersion; - } - - public void setNpmVersion(String npmVersion) { - this.npmVersion = npmVersion; - } - - public String getNpmRepository() { - return npmRepository; - } - - public void setNpmRepository(String npmRepository) { - this.npmRepository = npmRepository; - } - -} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAngularClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAngularClientCodegen.java index 8c042f1220b..fefbf143b0c 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAngularClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAngularClientCodegen.java @@ -1,12 +1,70 @@ package io.swagger.codegen.languages; import java.io.File; +import java.lang.StringBuffer; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; -import io.swagger.codegen.SupportingFile; +import io.swagger.codegen.CliOption; +import io.swagger.codegen.CodegenModel; import io.swagger.codegen.CodegenParameter; +import io.swagger.codegen.CodegenOperation; +import io.swagger.codegen.SupportingFile; +import io.swagger.codegen.utils.SemVer; +import io.swagger.models.ModelImpl; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.BooleanProperty; +import io.swagger.models.properties.FileProperty; +import io.swagger.models.properties.MapProperty; +import io.swagger.models.properties.ObjectProperty; import io.swagger.models.properties.Property; public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCodegen { + private static final SimpleDateFormat SNAPSHOT_SUFFIX_FORMAT = new SimpleDateFormat("yyyyMMddHHmm"); + + public static final String NPM_NAME = "npmName"; + public static final String NPM_VERSION = "npmVersion"; + public static final String NPM_REPOSITORY = "npmRepository"; + public static final String SNAPSHOT = "snapshot"; + public static final String WITH_INTERFACES = "withInterfaces"; + public static final String NG_VERSION = "ngVersion"; + + protected String npmName = null; + protected String npmVersion = "1.0.0"; + protected String npmRepository = null; + + public TypeScriptAngularClientCodegen() { + super(); + this.outputFolder = "generated-code/typescript-angular"; + + embeddedTemplateDir = templateDir = "typescript-angular"; + modelTemplateFiles.put("model.mustache", ".ts"); + apiTemplateFiles.put("api.service.mustache", ".ts"); + languageSpecificPrimitives.add("Blob"); + typeMapping.put("file","Blob"); + apiPackage = "api"; + modelPackage = "model"; + + + this.cliOptions.add(new CliOption(NPM_NAME, "The name under which you want to publish generated npm package")); + this.cliOptions.add(new CliOption(NPM_VERSION, "The version of your npm package")); + this.cliOptions.add(new CliOption(NPM_REPOSITORY, "Use this property to set an url your private npmRepo in the package.json")); + this.cliOptions.add(new CliOption(SNAPSHOT, "When setting this property to true the version will be suffixed with -SNAPSHOT.yyyyMMddHHmm", BooleanProperty.TYPE).defaultValue(Boolean.FALSE.toString())); + this.cliOptions.add(new CliOption(WITH_INTERFACES, "Setting this property to true will generate interfaces next to the default class implementations.", BooleanProperty.TYPE).defaultValue(Boolean.FALSE.toString())); + this.cliOptions.add(new CliOption(NG_VERSION, "The version of Angular. Default is '4.3'")); + } + + @Override + protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, ModelImpl swaggerModel) { + codegenModel.additionalPropertiesType = getTypeDeclaration(swaggerModel.getAdditionalProperties()); + addImport(codegenModel, codegenModel.additionalPropertiesType); + } @Override public String getName() { @@ -15,7 +73,7 @@ public String getName() { @Override public String getHelp() { - return "Generates a TypeScript AngularJS client library."; + return "Generates a TypeScript Angular (2.x or 4.x) client library."; } @Override @@ -25,34 +83,62 @@ public void processOpts() { supportingFiles.add(new SupportingFile("apis.mustache", apiPackage().replace('.', File.separatorChar), "api.ts")); supportingFiles.add(new SupportingFile("index.mustache", getIndexDirectory(), "index.ts")); supportingFiles.add(new SupportingFile("api.module.mustache", getIndexDirectory(), "api.module.ts")); - supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); + supportingFiles.add(new SupportingFile("rxjs-operators.mustache", getIndexDirectory(), "rxjs-operators.ts")); + supportingFiles.add(new SupportingFile("configuration.mustache", getIndexDirectory(), "configuration.ts")); + supportingFiles.add(new SupportingFile("variables.mustache", getIndexDirectory(), "variables.ts")); + supportingFiles.add(new SupportingFile("encoder.mustache", getIndexDirectory(), "encoder.ts")); supportingFiles.add(new SupportingFile("gitignore", "", ".gitignore")); + supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); - } - - public TypeScriptAngularClientCodegen() { - super(); - outputFolder = "generated-code/typescript-angular"; - modelTemplateFiles.put("model.mustache", ".ts"); - apiTemplateFiles.put("api.mustache", ".ts"); - embeddedTemplateDir = templateDir = "typescript-angular"; - apiPackage = "api"; - modelPackage = "model"; - } + if(additionalProperties.containsKey(NPM_NAME)) { + addNpmPackageGeneration(); + } - @Override - public String getSwaggerType(Property p) { - String swaggerType = super.getSwaggerType(p); - if(isLanguagePrimitive(swaggerType) || isLanguageGenericType(swaggerType)) { - return swaggerType; + if(additionalProperties.containsKey(WITH_INTERFACES)) { + boolean withInterfaces = Boolean.parseBoolean(additionalProperties.get(WITH_INTERFACES).toString()); + if (withInterfaces) { + apiTemplateFiles.put("apiInterface.mustache", "Interface.ts"); + } + } + + // determine NG version + SemVer ngVersion; + if (additionalProperties.containsKey(NG_VERSION)) { + ngVersion = new SemVer(additionalProperties.get(NG_VERSION).toString()); + } else { + ngVersion = new SemVer("4.3.0"); + LOGGER.info("generating code for Angular {} ...", ngVersion); + LOGGER.info(" (you can select the angular version by setting the additionalProperty ngVersion)"); } - return addModelPrefix(swaggerType); + additionalProperties.put(NG_VERSION, ngVersion); + additionalProperties.put("injectionToken", ngVersion.atLeast("4.0.0") ? "InjectionToken" : "OpaqueToken"); + additionalProperties.put("injectionTokenTyped", ngVersion.atLeast("4.0.0")); + additionalProperties.put("useHttpClient", ngVersion.atLeast("4.3.0")); } - @Override - public void postProcessParameter(CodegenParameter parameter) { - super.postProcessParameter(parameter); - parameter.dataType = addModelPrefix(parameter.dataType); + private void addNpmPackageGeneration() { + if(additionalProperties.containsKey(NPM_NAME)) { + this.setNpmName(additionalProperties.get(NPM_NAME).toString()); + } + + if (additionalProperties.containsKey(NPM_VERSION)) { + this.setNpmVersion(additionalProperties.get(NPM_VERSION).toString()); + } + + if (additionalProperties.containsKey(SNAPSHOT) && Boolean.valueOf(additionalProperties.get(SNAPSHOT).toString())) { + this.setNpmVersion(npmVersion + "-SNAPSHOT." + SNAPSHOT_SUFFIX_FORMAT.format(new Date())); + } + additionalProperties.put(NPM_VERSION, npmVersion); + + if (additionalProperties.containsKey(NPM_REPOSITORY)) { + this.setNpmRepository(additionalProperties.get(NPM_REPOSITORY).toString()); + } + + //Files for building our lib + supportingFiles.add(new SupportingFile("README.mustache", getIndexDirectory(), "README.md")); + supportingFiles.add(new SupportingFile("package.mustache", getIndexDirectory(), "package.json")); + supportingFiles.add(new SupportingFile("typings.mustache", getIndexDirectory(), "typings.json")); + supportingFiles.add(new SupportingFile("tsconfig.mustache", getIndexDirectory(), "tsconfig.json")); } private String getIndexDirectory() { @@ -60,16 +146,44 @@ private String getIndexDirectory() { return indexPackage.replace('.', File.separatorChar); } - private String addModelPrefix(String swaggerType) { - String type = null; - if (typeMapping.containsKey(swaggerType)) { - type = typeMapping.get(swaggerType); + @Override + public boolean isDataTypeFile(final String dataType) { + return dataType != null && dataType.equals("Blob"); + } + + @Override + public String getTypeDeclaration(Property p) { + Property inner; + if(p instanceof ArrayProperty) { + ArrayProperty mp1 = (ArrayProperty)p; + inner = mp1.getItems(); + return this.getSwaggerType(p) + "<" + this.getTypeDeclaration(inner) + ">"; + } else if(p instanceof MapProperty) { + MapProperty mp = (MapProperty)p; + inner = mp.getAdditionalProperties(); + return "{ [key: string]: " + this.getTypeDeclaration(inner) + "; }"; + } else if(p instanceof FileProperty) { + return "Blob"; + } else if(p instanceof ObjectProperty) { + return "any"; } else { - type = swaggerType; + return super.getTypeDeclaration(p); + } + } + + @Override + public String getSwaggerType(Property p) { + String swaggerType = super.getSwaggerType(p); + if(isLanguagePrimitive(swaggerType) || isLanguageGenericType(swaggerType)) { + return swaggerType; } + applyLocalTypeMapping(swaggerType); + return swaggerType; + } - if (!isLanguagePrimitive(type) && !isLanguageGenericType(type)) { - type = "models." + swaggerType; + private String applyLocalTypeMapping(String type) { + if (typeMapping.containsKey(type)) { + type = typeMapping.get(type); } return type; } @@ -86,4 +200,205 @@ private boolean isLanguageGenericType(String type) { } return false; } + + @Override + public void postProcessParameter(CodegenParameter parameter) { + super.postProcessParameter(parameter); + parameter.dataType = applyLocalTypeMapping(parameter.dataType); + } + + @Override + public Map postProcessOperations(Map operations) { + Map objs = (Map) operations.get("operations"); + + // Add filename information for api imports + objs.put("apiFilename", getApiFilenameFromClassname(objs.get("classname").toString())); + + List ops = (List) objs.get("operation"); + for (CodegenOperation op : ops) { + if ((boolean) additionalProperties.get("useHttpClient")) { + op.httpMethod = op.httpMethod.toLowerCase(Locale.ENGLISH); + } else { + // Convert httpMethod to Angular's RequestMethod enum + // https://angular.io/docs/ts/latest/api/http/index/RequestMethod-enum.html + switch (op.httpMethod) { + case "GET": + op.httpMethod = "RequestMethod.Get"; + break; + case "POST": + op.httpMethod = "RequestMethod.Post"; + break; + case "PUT": + op.httpMethod = "RequestMethod.Put"; + break; + case "DELETE": + op.httpMethod = "RequestMethod.Delete"; + break; + case "OPTIONS": + op.httpMethod = "RequestMethod.Options"; + break; + case "HEAD": + op.httpMethod = "RequestMethod.Head"; + break; + case "PATCH": + op.httpMethod = "RequestMethod.Patch"; + break; + default: + throw new RuntimeException("Unknown HTTP Method " + op.httpMethod + " not allowed"); + } + } + + // Prep a string buffer where we're going to set up our new version of the string. + StringBuffer pathBuffer = new StringBuffer(); + + // Set up other variables for tracking the current state of the string. + int insideCurly = 0; + boolean foundUnderscore = false; + + // Iterate through existing string, one character at a time. + for(int i = 0; i < op.path.length(); i++) { + switch(op.path.charAt(i)) { + case '{': + // We entered curly braces, so track that. + insideCurly++; + + // Add the more complicated component instead of just the brace. + pathBuffer.append("${encodeURIComponent(String("); + break; + case '}': + // We exited curly braces, so track that. + insideCurly--; + + // Add the more complicated component instead of just the brace. + pathBuffer.append("))}"); + break; + case '_': + // If we're inside the curly brace, the following character will need to be uppercase. + // Otherwise, just add the character. + if (insideCurly > 0) { + foundUnderscore = true; + } else { + pathBuffer.append(op.path.charAt(i)); + } + break; + default: + // If we previously found an underscore, we need an uppercase letter. + // Otherwise, just add the character. + if (foundUnderscore) { + pathBuffer.append(Character.toUpperCase(op.path.charAt(i))); + foundUnderscore = false; + } else { + pathBuffer.append(op.path.charAt(i)); + } + break; + } + } + + // Overwrite path to TypeScript template string, after applying everything we just did. + op.path = pathBuffer.toString(); + } + + // Add additional filename information for model imports in the services + List> imports = (List>) operations.get("imports"); + for(Map im : imports) { + im.put("filename", im.get("import")); + im.put("classname", getModelnameFromModelFilename(im.get("filename").toString())); + } + + return operations; + } + + @Override + public Map postProcessModels(Map objs) { + Map result = super.postProcessModels(objs); + + // Add additional filename information for imports + List models = (List) postProcessModelsEnum(result).get("models"); + for (Object _mo : models) { + Map mo = (Map) _mo; + CodegenModel cm = (CodegenModel) mo.get("model"); + mo.put("tsImports", toTsImports(cm,cm.imports)); + } + + return result; + } + + private List> toTsImports(CodegenModel cm, Set imports) { + List> tsImports = new ArrayList<>(); + for(String im : imports) { + if(!im.equals(cm.classname)) { + HashMap tsImport = new HashMap<>(); + tsImport.put("classname", im); + tsImport.put("filename", toModelFilename(im)); + tsImports.add(tsImport); + } + } + return tsImports; + } + + @Override + public String toApiName(String name) { + if (name.length() == 0) { + return "DefaultService"; + } + return initialCaps(name) + "Service"; + } + + @Override + public String toApiFilename(String name) { + if (name.length() == 0) { + return "default.service"; + } + return camelize(name, true) + ".service"; + } + + @Override + public String toApiImport(String name) { + return apiPackage() + "/" + toApiFilename(name); + } + + @Override + public String toModelFilename(String name) { + return camelize(toModelName(name), true); + } + + @Override + public String toModelImport(String name) { + return modelPackage() + "/" + toModelFilename(name); + } + + public String getNpmName() { + return npmName; + } + + public void setNpmName(String npmName) { + this.npmName = npmName; + } + + public String getNpmVersion() { + return npmVersion; + } + + public void setNpmVersion(String npmVersion) { + this.npmVersion = npmVersion; + } + + public String getNpmRepository() { + return npmRepository; + } + + public void setNpmRepository(String npmRepository) { + this.npmRepository = npmRepository; + } + + private String getApiFilenameFromClassname(String classname) { + String name = classname.substring(0, classname.length() - "Service".length()); + return toApiFilename(name); + } + + private String getModelnameFromModelFilename(String filename) { + String name = filename.substring((modelPackage() + "/").length()); + return camelize(name); + } + } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAngularJsClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAngularJsClientCodegen.java new file mode 100644 index 00000000000..1e61fab05b9 --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAngularJsClientCodegen.java @@ -0,0 +1,89 @@ +package io.swagger.codegen.languages; + +import java.io.File; + +import io.swagger.codegen.SupportingFile; +import io.swagger.codegen.CodegenParameter; +import io.swagger.models.properties.Property; + +public class TypeScriptAngularJsClientCodegen extends AbstractTypeScriptClientCodegen { + + @Override + public String getName() { + return "typescript-angularjs"; + } + + @Override + public String getHelp() { + return "Generates a TypeScript AngularJS client library."; + } + + @Override + public void processOpts() { + super.processOpts(); + supportingFiles.add(new SupportingFile("models.mustache", modelPackage().replace('.', File.separatorChar), "models.ts")); + supportingFiles.add(new SupportingFile("apis.mustache", apiPackage().replace('.', File.separatorChar), "api.ts")); + supportingFiles.add(new SupportingFile("index.mustache", getIndexDirectory(), "index.ts")); + supportingFiles.add(new SupportingFile("api.module.mustache", getIndexDirectory(), "api.module.ts")); + supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); + supportingFiles.add(new SupportingFile("gitignore", "", ".gitignore")); + + } + + public TypeScriptAngularJsClientCodegen() { + super(); + outputFolder = "generated-code/typescript-angularjs"; + modelTemplateFiles.put("model.mustache", ".ts"); + apiTemplateFiles.put("api.mustache", ".ts"); + embeddedTemplateDir = templateDir = "typescript-angularjs"; + apiPackage = "api"; + modelPackage = "model"; + } + + @Override + public String getSwaggerType(Property p) { + String swaggerType = super.getSwaggerType(p); + if(isLanguagePrimitive(swaggerType) || isLanguageGenericType(swaggerType)) { + return swaggerType; + } + return addModelPrefix(swaggerType); + } + + @Override + public void postProcessParameter(CodegenParameter parameter) { + super.postProcessParameter(parameter); + parameter.dataType = addModelPrefix(parameter.dataType); + } + + private String getIndexDirectory() { + String indexPackage = modelPackage.substring(0, Math.max(0, modelPackage.lastIndexOf('.'))); + return indexPackage.replace('.', File.separatorChar); + } + + private String addModelPrefix(String swaggerType) { + String type = null; + if (typeMapping.containsKey(swaggerType)) { + type = typeMapping.get(swaggerType); + } else { + type = swaggerType; + } + + if (!isLanguagePrimitive(type) && !isLanguageGenericType(type)) { + type = "models." + swaggerType; + } + return type; + } + + private boolean isLanguagePrimitive(String type) { + return languageSpecificPrimitives.contains(type); + } + + private boolean isLanguageGenericType(String type) { + for (String genericType: languageGenericTypes) { + if (type.startsWith(genericType + "<")) { + return true; + } + } + return false; + } +} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAureliaClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAureliaClientCodegen.java new file mode 100644 index 00000000000..2661b4a00ec --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAureliaClientCodegen.java @@ -0,0 +1,131 @@ +package io.swagger.codegen.languages; + +import io.swagger.codegen.*; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; + +public class TypeScriptAureliaClientCodegen extends AbstractTypeScriptClientCodegen { + + public static final String NPM_NAME = "npmName"; + public static final String NPM_VERSION = "npmVersion"; + + protected String npmName = null; + protected String npmVersion = "1.0.0"; + + public TypeScriptAureliaClientCodegen() { + super(); + + apiTemplateFiles.put("api.mustache", ".ts"); + + // clear import mapping (from default generator) as TS does not use it + // at the moment + importMapping.clear(); + + outputFolder = "generated-code/typescript-aurelia"; + embeddedTemplateDir = templateDir = "typescript-aurelia"; + this.cliOptions.add(new CliOption(NPM_NAME, "The name under which you want to publish generated npm package")); + this.cliOptions.add(new CliOption(NPM_VERSION, "The version of your npm package")); + } + + @Override + public void processOpts() { + super.processOpts(); + + if (additionalProperties.containsKey(NPM_NAME)) { + this.setNpmName(additionalProperties.get(NPM_NAME).toString()); + } + + if (additionalProperties.containsKey(NPM_VERSION)) { + this.setNpmVersion(additionalProperties.get(NPM_VERSION).toString()); + } + + // Set supporting files + supportingFiles.add(new SupportingFile("models.mustache", "", "models.ts")); + supportingFiles.add(new SupportingFile("index.ts.mustache", "", "index.ts")); + supportingFiles.add(new SupportingFile("Api.ts.mustache", "", "Api.ts")); + supportingFiles.add(new SupportingFile("AuthStorage.ts.mustache", "", "AuthStorage.ts")); + supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); + supportingFiles.add(new SupportingFile("README.md", "", "README.md")); + supportingFiles.add(new SupportingFile("package.json.mustache", "", "package.json")); + supportingFiles.add(new SupportingFile("tsconfig.json.mustache", "", "tsconfig.json")); + supportingFiles.add(new SupportingFile("tslint.json.mustache", "", "tslint.json")); + supportingFiles.add(new SupportingFile("gitignore", "", ".gitignore")); + } + + @Override + public String getName() { + return "typescript-aurelia"; + } + + @Override + public String getHelp() { + return "Generates a TypeScript client library for the Aurelia framework (beta)."; + } + + public String getNpmName() { + return npmName; + } + + public void setNpmName(String npmName) { + this.npmName = npmName; + } + + public String getNpmVersion() { + return npmVersion; + } + + public void setNpmVersion(String npmVersion) { + this.npmVersion = npmVersion; + } + + @Override + public Map postProcessOperations(Map objs) { + objs = super.postProcessOperations(objs); + + HashSet modelImports = new HashSet<>(); + Map operations = (Map) objs.get("operations"); + List operationList = (List) operations.get("operation"); + for (CodegenOperation op : operationList) { + // Aurelia uses "asGet", "asPost", ... methods; change the method format + op.httpMethod = initialCaps(op.httpMethod.toLowerCase()); + + // Collect models to be imported + for (CodegenParameter param : op.allParams) { + if (!param.isPrimitiveType) { + modelImports.add(param.dataType); + } + } + if (op.returnBaseType != null && !op.returnTypeIsPrimitive) { + modelImports.add(op.returnBaseType); + } + } + + objs.put("modelImports", modelImports); + + return objs; + } + + @Override + public Map postProcessModels(Map objs) { + // process enum in models + List models = (List) postProcessModelsEnum(objs).get("models"); + for (Object _mo : models) { + Map mo = (Map) _mo; + CodegenModel cm = (CodegenModel) mo.get("model"); + cm.imports = new TreeSet(cm.imports); + for (CodegenProperty var : cm.vars) { + // name enum with model name, e.g. StatuEnum => PetStatusEnum + if (Boolean.TRUE.equals(var.isEnum)) { + var.datatypeWithEnum = var.datatypeWithEnum.replace(var.enumName, cm.classname + var.enumName); + var.enumName = cm.classname + var.enumName; + } + } + } + + return objs; + } + +} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptFetchClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptFetchClientCodegen.java index a6449587ac3..1a24e1db7e5 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptFetchClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptFetchClientCodegen.java @@ -4,19 +4,24 @@ import io.swagger.codegen.CodegenModel; import io.swagger.codegen.CodegenProperty; import io.swagger.codegen.SupportingFile; +import io.swagger.models.ModelImpl; +import io.swagger.models.properties.*; import java.io.File; -import java.util.List; -import java.util.Map; -import java.util.TreeSet; +import java.text.SimpleDateFormat; +import java.util.*; public class TypeScriptFetchClientCodegen extends AbstractTypeScriptClientCodegen { + private static final SimpleDateFormat SNAPSHOT_SUFFIX_FORMAT = new SimpleDateFormat("yyyyMMddHHmm"); public static final String NPM_NAME = "npmName"; public static final String NPM_VERSION = "npmVersion"; + public static final String NPM_REPOSITORY = "npmRepository"; + public static final String SNAPSHOT = "snapshot"; protected String npmName = null; protected String npmVersion = "1.0.0"; + protected String npmRepository = null; public TypeScriptFetchClientCodegen() { super(); @@ -26,30 +31,75 @@ public TypeScriptFetchClientCodegen() { importMapping.clear(); outputFolder = "generated-code/typescript-fetch"; - embeddedTemplateDir = templateDir = "TypeScript-Fetch"; + embeddedTemplateDir = templateDir = "typescript-fetch"; + this.cliOptions.add(new CliOption(NPM_NAME, "The name under which you want to publish generated npm package")); this.cliOptions.add(new CliOption(NPM_VERSION, "The version of your npm package")); + this.cliOptions.add(new CliOption(NPM_REPOSITORY, "Use this property to set an url your private npmRepo in the package.json")); + this.cliOptions.add(new CliOption(SNAPSHOT, "When setting this property to true the version will be suffixed with -SNAPSHOT.yyyyMMddHHmm", BooleanProperty.TYPE).defaultValue(Boolean.FALSE.toString())); + } + + @Override + protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, ModelImpl swaggerModel) { + codegenModel.additionalPropertiesType = getTypeDeclaration(swaggerModel.getAdditionalProperties()); + addImport(codegenModel, codegenModel.additionalPropertiesType); } @Override public void processOpts() { super.processOpts(); + supportingFiles.add(new SupportingFile("index.mustache", "", "index.ts")); supportingFiles.add(new SupportingFile("api.mustache", "", "api.ts")); + supportingFiles.add(new SupportingFile("configuration.mustache", "", "configuration.ts")); + supportingFiles.add(new SupportingFile("custom.d.mustache", "", "custom.d.ts")); supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); - supportingFiles.add(new SupportingFile("README.md", "", "README.md")); - supportingFiles.add(new SupportingFile("package.json.mustache", "", "package.json")); - supportingFiles.add(new SupportingFile("typings.json.mustache", "", "typings.json")); - supportingFiles.add(new SupportingFile("tsconfig.json.mustache", "", "tsconfig.json")); - supportingFiles.add(new SupportingFile("tslint.json.mustache", "", "tslint.json")); supportingFiles.add(new SupportingFile("gitignore", "", ".gitignore")); - if(additionalProperties.containsKey(NPM_NAME)) { + if (additionalProperties.containsKey(NPM_NAME)) { + addNpmPackageGeneration(); + } + } + + @Override + public String getTypeDeclaration(Property p) { + Property inner; + if(p instanceof ArrayProperty) { + ArrayProperty mp1 = (ArrayProperty)p; + inner = mp1.getItems(); + return this.getSwaggerType(p) + "<" + this.getTypeDeclaration(inner) + ">"; + } else if(p instanceof MapProperty) { + MapProperty mp = (MapProperty)p; + inner = mp.getAdditionalProperties(); + return "{ [key: string]: " + this.getTypeDeclaration(inner) + "; }"; + } else if(p instanceof FileProperty || p instanceof ObjectProperty) { + return "any"; + } else { + return super.getTypeDeclaration(p); + } + } + + private void addNpmPackageGeneration() { + if (additionalProperties.containsKey(NPM_NAME)) { this.setNpmName(additionalProperties.get(NPM_NAME).toString()); } if (additionalProperties.containsKey(NPM_VERSION)) { this.setNpmVersion(additionalProperties.get(NPM_VERSION).toString()); } + + if (additionalProperties.containsKey(SNAPSHOT) && Boolean.valueOf(additionalProperties.get(SNAPSHOT).toString())) { + this.setNpmVersion(npmVersion + "-SNAPSHOT." + SNAPSHOT_SUFFIX_FORMAT.format(new Date())); + } + additionalProperties.put(NPM_VERSION, npmVersion); + + if (additionalProperties.containsKey(NPM_REPOSITORY)) { + this.setNpmRepository(additionalProperties.get(NPM_REPOSITORY).toString()); + } + + //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("tsconfig.mustache", "", "tsconfig.json")); } @Override @@ -78,24 +128,12 @@ public void setNpmVersion(String npmVersion) { this.npmVersion = npmVersion; } - @Override - public Map postProcessModels(Map objs) { - // process enum in models - List models = (List) postProcessModelsEnum(objs).get("models"); - for (Object _mo : models) { - Map mo = (Map) _mo; - CodegenModel cm = (CodegenModel) mo.get("model"); - cm.imports = new TreeSet(cm.imports); - for (CodegenProperty var : cm.vars) { - // name enum with model name, e.g. StatuEnum => PetStatusEnum - if (Boolean.TRUE.equals(var.isEnum)) { - var.datatypeWithEnum = var.datatypeWithEnum.replace(var.enumName, cm.classname + var.enumName); - var.enumName = cm.classname + var.enumName; - } - } - } + public String getNpmRepository() { + return npmRepository; + } - return objs; + public void setNpmRepository(String npmRepository) { + this.npmRepository = npmRepository; } } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptJqueryClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptJqueryClientCodegen.java index eb319344781..701f764191e 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptJqueryClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptJqueryClientCodegen.java @@ -1,5 +1,9 @@ package io.swagger.codegen.languages; +import io.swagger.codegen.CodegenModel; +import io.swagger.codegen.CodegenParameter; +import io.swagger.models.ModelImpl; +import io.swagger.models.properties.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -9,16 +13,16 @@ import io.swagger.codegen.CliOption; import io.swagger.codegen.SupportingFile; -import io.swagger.models.properties.BooleanProperty; public class TypeScriptJqueryClientCodegen extends AbstractTypeScriptClientCodegen { - private static final Logger LOGGER = LoggerFactory.getLogger(TypeScriptNodeClientCodegen.class); + private static final Logger LOGGER = LoggerFactory.getLogger(TypeScriptJqueryClientCodegen.class); private static final SimpleDateFormat SNAPSHOT_SUFFIX_FORMAT = new SimpleDateFormat("yyyyMMddHHmm"); public static final String NPM_NAME = "npmName"; public static final String NPM_VERSION = "npmVersion"; public static final String NPM_REPOSITORY = "npmRepository"; public static final String SNAPSHOT = "snapshot"; + public static final String JQUERY_ALREADY_IMPORTED = "jqueryAlreadyImported"; protected String npmName = null; protected String npmVersion = "1.0.0"; @@ -26,6 +30,13 @@ public class TypeScriptJqueryClientCodegen extends AbstractTypeScriptClientCodeg public TypeScriptJqueryClientCodegen() { super(); + + modelTemplateFiles.put("model.mustache", ".ts"); + apiTemplateFiles.put("api.mustache", ".ts"); + typeMapping.put("Date", "Date"); + apiPackage = "api"; + modelPackage = "model"; + outputFolder = "generated-code/typescript-jquery"; embeddedTemplateDir = templateDir = "typescript-jquery"; @@ -33,23 +44,75 @@ public TypeScriptJqueryClientCodegen() { this.cliOptions.add(new CliOption(NPM_VERSION, "The version of your npm package")); this.cliOptions.add(new CliOption(NPM_REPOSITORY, "Use this property to set an url your private npmRepo in the package.json")); this.cliOptions.add(new CliOption(SNAPSHOT, "When setting this property to true the version will be suffixed with -SNAPSHOT.yyyyMMddHHmm", BooleanProperty.TYPE).defaultValue(Boolean.FALSE.toString())); + this.cliOptions.add(new CliOption(JQUERY_ALREADY_IMPORTED, "When using this in legacy app using mix of typescript and javascript, this will only declare jquery and not import it", BooleanProperty.TYPE).defaultValue(Boolean.FALSE.toString())); } - @Override public void processOpts() { super.processOpts(); - supportingFiles.add(new SupportingFile("api.mustache", null, "api.ts")); + supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); + supportingFiles.add(new SupportingFile("models.mustache", modelPackage().replace('.', File.separatorChar), "models.ts")); + supportingFiles.add(new SupportingFile("apis.mustache", apiPackage().replace('.', File.separatorChar), "api.ts")); + supportingFiles.add(new SupportingFile("configuration.mustache", getIndexDirectory(), "configuration.ts")); + supportingFiles.add(new SupportingFile("index.mustache", getIndexDirectory(), "index.ts")); + supportingFiles.add(new SupportingFile("variables.mustache", getIndexDirectory(), "variables.ts")); LOGGER.warn("check additionals: " + additionalProperties.get(NPM_NAME)); - if(additionalProperties.containsKey(NPM_NAME)) { + if (additionalProperties.containsKey(NPM_NAME)) { addNpmPackageGeneration(); } } + private String getIndexDirectory() { + String indexPackage = modelPackage.substring(0, Math.max(0, modelPackage.lastIndexOf('.'))); + return indexPackage.replace('.', File.separatorChar); + } + + @Override + public String getSwaggerType(Property p) { + String swaggerType = super.getSwaggerType(p); + if (isLanguagePrimitive(swaggerType) || isLanguageGenericType(swaggerType)) { + return swaggerType; + } + return addModelPrefix(swaggerType); + } + + private String addModelPrefix(String swaggerType) { + String type = null; + if (typeMapping.containsKey(swaggerType)) { + type = typeMapping.get(swaggerType); + } else { + type = swaggerType; + } + + if (!isLanguagePrimitive(type) && !isLanguageGenericType(type)) { + type = "models." + swaggerType; + } + return type; + } + + private boolean isLanguagePrimitive(String type) { + return languageSpecificPrimitives.contains(type); + } + + private boolean isLanguageGenericType(String type) { + for (String genericType : languageGenericTypes) { + if (type.startsWith(genericType + "<")) { + return true; + } + } + return false; + } + + @Override + public void postProcessParameter(CodegenParameter parameter) { + super.postProcessParameter(parameter); + parameter.dataType = addModelPrefix(parameter.dataType); + } + private void addNpmPackageGeneration() { - if(additionalProperties.containsKey(NPM_NAME)) { + if (additionalProperties.containsKey(NPM_NAME)) { this.setNpmName(additionalProperties.get(NPM_NAME).toString()); } @@ -67,6 +130,7 @@ private void addNpmPackageGeneration() { } //Files for building our lib + supportingFiles.add(new SupportingFile("README.mustache", getPackageRootDirectory(), "README.md")); supportingFiles.add(new SupportingFile("package.mustache", getPackageRootDirectory(), "package.json")); supportingFiles.add(new SupportingFile("typings.mustache", getPackageRootDirectory(), "typings.json")); supportingFiles.add(new SupportingFile("tsconfig.mustache", getPackageRootDirectory(), "tsconfig.json")); @@ -77,6 +141,12 @@ private String getPackageRootDirectory() { return indexPackage.replace('.', File.separatorChar); } + @Override + protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, ModelImpl swaggerModel) { + codegenModel.additionalPropertiesType = getSwaggerType(swaggerModel.getAdditionalProperties()); + addImport(codegenModel, codegenModel.additionalPropertiesType); + } + @Override public String getName() { return "typescript-jquery"; diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptNodeClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptNodeClientCodegen.java index 50662a26dd5..1a8e0adbc15 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptNodeClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptNodeClientCodegen.java @@ -77,7 +77,6 @@ private void addNpmPackageGeneration() { //Files for building our lib supportingFiles.add(new SupportingFile("package.mustache", getPackageRootDirectory(), "package.json")); - supportingFiles.add(new SupportingFile("typings.mustache", getPackageRootDirectory(), "typings.json")); supportingFiles.add(new SupportingFile("tsconfig.mustache", getPackageRootDirectory(), "tsconfig.json")); } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ZendExpressivePathHandlerServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ZendExpressivePathHandlerServerCodegen.java index 12328c64d58..91cf0a40ad0 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ZendExpressivePathHandlerServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ZendExpressivePathHandlerServerCodegen.java @@ -55,7 +55,6 @@ public ZendExpressivePathHandlerServerCodegen() { supportingFiles.add(new SupportingFile("Date.php.mustache", packagePath + File.separator + srcBasePath + File.separator + "Strategy", "Date.php")); supportingFiles.add(new SupportingFile("DateTime.php.mustache", packagePath + File.separator + srcBasePath + File.separator + "Strategy", "DateTime.php")); supportingFiles.add(new SupportingFile("Type.php.mustache", packagePath + File.separator + srcBasePath + File.separator + "Validator", "Type.php")); - supportingFiles.add(new SupportingFile("ErrorMiddleware.php.mustache", packagePath + File.separator + srcBasePath, "ErrorMiddleware.php")); additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, "1.0.0"); } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/features/JaxbFeatures.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/features/JaxbFeatures.java deleted file mode 100644 index 9693d6dcd07..00000000000 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/features/JaxbFeatures.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.swagger.codegen.languages.features; - -public interface JaxbFeatures { - public static final String USE_JAXB_ANNOTATIONS = "useJaxbAnnotations"; - - public void setUseJaxbAnnotations(boolean useJaxbAnnotations); -} diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/utils/SemVer.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/utils/SemVer.java new file mode 100644 index 00000000000..ec8b17f31bb --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/utils/SemVer.java @@ -0,0 +1,33 @@ +package io.swagger.codegen.utils; + +public class SemVer implements Comparable { + + public final int major; + public final int minor; + public final int revision; + + public SemVer(String versionString) { + String[] tokens = versionString.split("\\."); + major = Integer.parseInt(tokens[0]); + minor = tokens.length < 2 ? 0 : Integer.parseInt(tokens[1]); + revision = tokens.length < 3 ? 0 : Integer.parseInt(tokens[2]); + } + + @Override + public int compareTo(SemVer o) { + int cmp = major - o.major; + if (cmp != 0) return cmp; + cmp = minor - o.minor; + if (cmp != 0) return cmp; + return revision - o.revision; + } + + public boolean atLeast(String other) { + return compareTo(new SemVer(other)) >= 0; + } + + @Override + public String toString() { + return major + "." + minor + "." + revision; + } +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/Ada/client-body.mustache b/modules/swagger-codegen/src/main/resources/Ada/client-body.mustache new file mode 100644 index 00000000000..0aa184749af --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Ada/client-body.mustache @@ -0,0 +1,50 @@ +{{>licenseInfo}} +with Swagger.Streams; +package body {{package}}.Clients is +{{#apiInfo}} +{{#apis}} +{{#operations}} +{{#operation}} + + -- {{summary}}{{#vendorExtensions.x-has-notes}} + -- {{unescapedNotes}}{{/vendorExtensions.x-has-notes}} + procedure {{operationId}} + (Client : in out Client_Type{{#hasParams}};{{/hasParams}}{{#allParams}} + {{paramName}} : in {{^isFile}}{{^isString}}{{^isPrimitiveType}}{{^isContainer}}{{package}}.Models.{{/isContainer}}{{/isPrimitiveType}}{{/isString}}{{/isFile}}{{dataType}}{{#hasMore}};{{/hasMore}}{{/allParams}}{{#returnType}}; + Result : out {{returnType}}{{/returnType}}) is + URI : Swagger.Clients.URI_Type;{{#hasBodyParam}} + Req : Swagger.Clients.Request_Type;{{/hasBodyParam}}{{#hasFormParams}} + Req : Swagger.Clients.Request_Type;{{/hasFormParams}} + {{#returnType}} + Reply : Swagger.Value_Type; + {{/returnType}} + begin + Client.Set_Accept (({{#hasProduces}}{{#produces}}{{#vendorExtensions.x-has-uniq-produces}}1 => {{/vendorExtensions.x-has-uniq-produces}}Swagger.Clients.{{adaMediaType}}{{#hasMore}}, + {{/hasMore}}{{/produces}}{{/hasProduces}}));{{#hasBodyParam}} + Client.Initialize (Req, ({{#hasConsumes}}{{#consumes}}{{#vendorExtensions.x-has-uniq-consumes}}1 -> {{/vendorExtensions.x-has-uniq-consumes}}Swagger.Clients.{{adaMediaType}}{{#hasMore}}, + {{/hasMore}}{{/consumes}}{{/hasConsumes}}{{^hasConsumes}}1 => Swagger.Clients.APPLICATION_JSON{{/hasConsumes}}));{{#bodyParams}}{{#vendorExtensions.x-is-model-type}} + {{package}}.Models.Serialize (Req.Stream, "{{baseName}}", {{paramName}});{{/vendorExtensions.x-is-model-type}}{{^vendorExtensions.x-is-model-type}}{{#isFile}} + -- TODO: Serialize (Req.Stream, "{{basename}}", {{paramName}});{{/isFile}}{{^isFile}}{{^isLong}} + Req.Stream.Write_Entity ("{{baseName}}", {{paramName}});{{/isLong}}{{#isLong}} + Serialize (Req.Stream, "{{baseName}}", {{paramName}});{{/isLong}}{{/isFile}}{{/vendorExtensions.x-is-model-type}}{{/bodyParams}}{{/hasBodyParam}}{{#hasFormParams}} + Client.Initialize (Req, (1 => Swagger.Clients.APPLICATION_FORM));{{#formParams}}{{#vendorExtensions.x-is-model-type}} + {{package}}.Models.Serialize (Req.Stream, "{{baseName}}", {{paramName}});{{/vendorExtensions.x-is-model-type}}{{^vendorExtensions.x-is-model-type}} + Req.Stream.Write_Entity ("{{baseName}}", {{paramName}});{{/vendorExtensions.x-is-model-type}}{{/formParams}}{{/hasFormParams}} +{{#queryParams}}{{#isQueryParam}}{{^isPrimitiveType}}{{^isString}}{{^isContainer}}{{^isDateTime}} + URI.Add_Param ("{{baseName}}", {{paramName}});{{/isDateTime}}{{/isContainer}}{{/isString}}{{/isPrimitiveType}}{{#isPrimitiveType}}{{^isLong}} + URI.Add_Param ("{{baseName}}", {{paramName}});{{/isLong}}{{/isPrimitiveType}}{{#isLong}} + URI.Add_Param ("{{baseName}}", {{paramName}});{{/isLong}}{{#isContainer}} + URI.Add_Param ("{{baseName}}", {{paramName}});{{/isContainer}}{{#isDateTime}} + URI.Add_Param ("{{baseName}}", {{paramName}});{{/isDateTime}}{{/isQueryParam}}{{/queryParams}} + URI.Set_Path ("{{path}}");{{#pathParams}} + URI.Set_Path_Param ("{{baseName}}", {{^isString}}Swagger.To_String ({{/isString}}{{paramName}}{{^isString}}){{/isString}});{{/pathParams}} + Client.Call (Swagger.Clients.{{httpMethod}}, URI{{#hasBodyParam}}, Req{{/hasBodyParam}}{{#returnType}}, Reply{{/returnType}});{{#returnType}}{{#vendorExtensions.x-codegen-response.isString}} + Swagger.Streams.Deserialize (Reply, "", Result);{{/vendorExtensions.x-codegen-response.isString}}{{^vendorExtensions.x-codegen-response.isString}}{{#returnTypeIsPrimitive}} + Swagger.Streams.Deserialize (Reply, "", Result);{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}} + {{package}}.Models.Deserialize (Reply, "", Result);{{/returnTypeIsPrimitive}}{{/vendorExtensions.x-codegen-response.isString}}{{/returnType}} + end {{operationId}}; +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} +end {{package}}.Clients; diff --git a/modules/swagger-codegen/src/main/resources/Ada/client-spec.mustache b/modules/swagger-codegen/src/main/resources/Ada/client-spec.mustache new file mode 100644 index 00000000000..67ca7d08c22 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Ada/client-spec.mustache @@ -0,0 +1,25 @@ +{{>licenseInfo}} +{{#imports}}with {{import}}; +{{/imports}} +with {{package}}.Models; +with Swagger.Clients; +package {{package}}.Clients is + + type Client_Type is new Swagger.Clients.Client_Type with null record; + +{{#apiInfo}} +{{#apis}} +{{#operations}} +{{#operation}} + -- {{summary}}{{#vendorExtensions.x-has-notes}} + -- {{unescapedNotes}}{{/vendorExtensions.x-has-notes}} + procedure {{operationId}} + (Client : in out Client_Type{{#hasParams}};{{/hasParams}}{{#allParams}} + {{paramName}} : in {{^isFile}}{{^isString}}{{^isPrimitiveType}}{{^isContainer}}{{package}}.Models.{{/isContainer}}{{/isPrimitiveType}}{{/isString}}{{/isFile}}{{dataType}}{{#hasMore}};{{/hasMore}}{{/allParams}}{{#returnType}}; + Result : out {{returnType}}{{/returnType}}); + +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} +end {{package}}.Clients; diff --git a/modules/swagger-codegen/src/main/resources/Ada/gnat-project.mustache b/modules/swagger-codegen/src/main/resources/Ada/gnat-project.mustache new file mode 100644 index 00000000000..f07f3006cc8 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Ada/gnat-project.mustache @@ -0,0 +1,23 @@ +-- {{{appName}}} +-- {{{appDescription}}} +-- OpenAPI spec version: 1.0.0 +-- +-- https://github.com/swagger-api/swagger-codegen.git +-- + -- NOTE: Auto generated by the swagger code generator program. +with "config"; +with "util"; +with "asf"; +project {{{projectName}}} is + + Mains := ("{{{appName}}}-server.adb"); + for Main use Mains; + for Source_Dirs use ("src", "src/client", "src/server"); + for Object_Dir use "./" & Config'Exec_Dir & "/bin"; + + package Binder renames Config.Binder; + package Builder renames Config.Builder; + package Compiler renames Config.Compiler; + package Linker renames Config.Linker; + +end {{{projectName}}}; diff --git a/modules/swagger-codegen/src/main/resources/Ada/licenseInfo.mustache b/modules/swagger-codegen/src/main/resources/Ada/licenseInfo.mustache new file mode 100644 index 00000000000..6c6c4b246e5 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Ada/licenseInfo.mustache @@ -0,0 +1,9 @@ +-- {{{appName}}} +-- {{{appDescription}}} +-- +-- {{#version}}OpenAPI spec version: {{{version}}}{{/version}} +-- {{#infoEmail}}Contact: {{{infoEmail}}}{{/infoEmail}} +-- +-- NOTE: This package is auto generated by the swagger code generator {{{generatorVersion}}}. +-- https://github.com/swagger-api/swagger-codegen.git +-- Do not edit the class manually. \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/Ada/model-body.mustache b/modules/swagger-codegen/src/main/resources/Ada/model-body.mustache new file mode 100644 index 00000000000..120de7b25cd --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Ada/model-body.mustache @@ -0,0 +1,62 @@ +{{>licenseInfo}} + +package body {{package}}.Models is + + use Swagger.Streams; + +{{#orderedModels}} +{{#model}} + + procedure Serialize (Into : in out Swagger.Streams.Output_Stream'Class; + Name : in String; + Value : in {{classname}}) is + begin + Into.Start_Entity (Name);{{#vars}}{{^isPrimitiveType}}{{^isString}}{{^isContainer}}{{^isDateTime}} + Serialize (Into, "{{baseName}}", Value.{{name}});{{/isDateTime}}{{/isContainer}}{{/isString}}{{/isPrimitiveType}}{{#isPrimitiveType}}{{^isLong}} + Into.Write_Entity ("{{baseName}}", Value.{{name}});{{/isLong}}{{/isPrimitiveType}}{{#isLong}} + Serialize (Into, "{{baseName}}", Value.{{name}});{{/isLong}}{{#isString}} + Into.Write_Entity ("{{baseName}}", Value.{{name}});{{/isString}}{{#isContainer}} + Serialize (Into, "{{baseName}}", Value.{{name}});{{/isContainer}}{{#isDateTime}} + Into.Write_Entity ("{{baseName}}", Value.{{name}});{{/isDateTime}}{{/vars}} + Into.End_Entity (Name); + end Serialize; + + procedure Serialize (Into : in out Swagger.Streams.Output_Stream'Class; + Name : in String; + Value : in {{classname}}_Vectors.Vector) is + begin + Into.Start_Array (Name); + for Item of Value loop + Serialize (Into, "", Item); + end loop; + Into.End_Array (Name); + end Serialize; + + procedure Deserialize (From : in Swagger.Value_Type; + Name : in String; + Value : out {{classname}}) is + Object : Swagger.Value_Type; + begin + Swagger.Streams.Deserialize (From, Name, Object);{{#vars}}{{#vendorExtensions.x-is-model-type}} + Deserialize (Object, "{{baseName}}", Value.{{name}});{{/vendorExtensions.x-is-model-type}}{{^vendorExtensions.x-is-model-type}} + Swagger.Streams.Deserialize (Object, "{{baseName}}", Value.{{name}});{{/vendorExtensions.x-is-model-type}}{{/vars}} + end Deserialize; + + procedure Deserialize (From : in Swagger.Value_Type; + Name : in String; + Value : out {{classname}}_Vectors.Vector) is + List : Swagger.Value_Array_Type; + Item : {{classname}}; + begin + Value.Clear; + Swagger.Streams.Deserialize (From, Name, List); + for Data of List loop + Deserialize (Data, "", Item); + Value.Append (Item); + end loop; + end Deserialize; + +{{/model}} +{{/orderedModels}} + +end {{package}}.Models; diff --git a/modules/swagger-codegen/src/main/resources/Ada/model-spec.mustache b/modules/swagger-codegen/src/main/resources/Ada/model-spec.mustache new file mode 100644 index 00000000000..bed1aa4207d --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Ada/model-spec.mustache @@ -0,0 +1,43 @@ +{{>licenseInfo}} +{{#imports}}with {{import}}; +{{/imports}} +with Swagger.Streams; +with Ada.Containers.Vectors; +package {{package}}.Models is + +{{#orderedModels}}{{#model}} + -- ------------------------------ + -- {{title}} + -- {{description}} + -- ------------------------------ + type {{classname}} is + record + {{#vars}} + {{name}} : {{datatype}}; + {{/vars}} + end record; + + package {{classname}}_Vectors is + new Ada.Containers.Vectors (Index_Type => Positive, + Element_Type => {{classname}}); + + procedure Serialize (Into : in out Swagger.Streams.Output_Stream'Class; + Name : in String; + Value : in {{classname}}); + + procedure Serialize (Into : in out Swagger.Streams.Output_Stream'Class; + Name : in String; + Value : in {{classname}}_Vectors.Vector); + + procedure Deserialize (From : in Swagger.Value_Type; + Name : in String; + Value : out {{classname}}); + + procedure Deserialize (From : in Swagger.Value_Type; + Name : in String; + Value : out {{classname}}_Vectors.Vector); + +{{/model}} +{{/orderedModels}} + +end {{package}}.Models; diff --git a/modules/swagger-codegen/src/main/resources/Ada/server-body.mustache b/modules/swagger-codegen/src/main/resources/Ada/server-body.mustache new file mode 100644 index 00000000000..dc1ac8c7528 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Ada/server-body.mustache @@ -0,0 +1,19 @@ +{{>licenseInfo}} +package body {{package}}.Servers is +{{#apiInfo}} +{{#apis}} +{{#operations}} +{{#operation}} + + -- {{summary}} + -- {{notes}} + procedure {{operationId}} ({{#allParams}}{{paramName}} : in {{dataType}}{{#hasMore}}; + {{/hasMore}}{{/allParams}}) is + begin + null; + end {{operationId}}; +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} +end {{package}}.Servers; diff --git a/modules/swagger-codegen/src/main/resources/Ada/server-spec.mustache b/modules/swagger-codegen/src/main/resources/Ada/server-spec.mustache new file mode 100644 index 00000000000..0d887b15d36 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Ada/server-spec.mustache @@ -0,0 +1,20 @@ +{{>licenseInfo}} +{{#imports}}with {{import}}; +{{/imports}} +with {{package}}.Models; +package {{package}}.Servers is +{{#apiInfo}} +{{#apis}} +{{#operations}} +{{#operation}} + + -- {{summary}} + -- {{notes}} + procedure {{operationId}} ({{#allParams}}{{paramName}} : in {{dataType}}{{#hasMore}}; + {{/hasMore}}{{/allParams}}); + +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} +end {{package}}.Servers; diff --git a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/CsharpDotNet2/ApiClient.mustache deleted file mode 100644 index c46667c61f8..00000000000 --- a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/ApiClient.mustache +++ /dev/null @@ -1,296 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Text.RegularExpressions; -using System.IO; -using System.Web; -using System.Linq; -using System.Net; -using System.Text; -using Newtonsoft.Json; -using RestSharp; -using RestSharp.Extensions; - -namespace {{packageName}}.Client -{ - /// - /// API client is mainly responible for making the HTTP call to the API backend. - /// - public class ApiClient - { - private readonly Dictionary _defaultHeaderMap = new Dictionary(); - - /// - /// Initializes a new instance of the class. - /// - /// The base path. - public ApiClient(String basePath="{{{basePath}}}") - { - BasePath = basePath; - RestClient = new RestClient(BasePath); - } - - /// - /// Gets or sets the base path. - /// - /// The base path - public string BasePath { get; set; } - - /// - /// Gets or sets the RestClient. - /// - /// An instance of the RestClient - public RestClient RestClient { get; set; } - - /// - /// Gets the default header. - /// - public Dictionary DefaultHeader - { - get { return _defaultHeaderMap; } - } - - /// - /// Makes the HTTP request (Sync). - /// - /// URL path. - /// HTTP method. - /// Query parameters. - /// HTTP body (POST request). - /// Header parameters. - /// Form parameters. - /// File parameters. - /// Authentication settings. - /// Object - public Object CallApi(String path, RestSharp.Method method, Dictionary queryParams, String postBody, - Dictionary headerParams, Dictionary formParams, - Dictionary fileParams, String[] authSettings) - { - - var request = new RestRequest(path, method); - - UpdateParamsForAuth(queryParams, headerParams, authSettings); - - // add default header, if any - foreach(var defaultHeader in _defaultHeaderMap) - request.AddHeader(defaultHeader.Key, defaultHeader.Value); - - // add header parameter, if any - foreach(var param in headerParams) - request.AddHeader(param.Key, param.Value); - - // add query parameter, if any - foreach(var param in queryParams) - request.AddParameter(param.Key, param.Value, ParameterType.GetOrPost); - - // add form parameter, if any - foreach(var param in formParams) - request.AddParameter(param.Key, param.Value, ParameterType.GetOrPost); - - // add file parameter, if any - foreach(var param in fileParams) - request.AddFile(param.Value.Name, param.Value.Writer, param.Value.FileName, param.Value.ContentType); - - if (postBody != null) // http body (model) parameter - request.AddParameter("application/json", postBody, ParameterType.RequestBody); - - return (Object)RestClient.Execute(request); - - } - - /// - /// Add default header. - /// - /// Header field name. - /// Header field value. - /// - public void AddDefaultHeader(string key, string value) - { - _defaultHeaderMap.Add(key, value); - } - - /// - /// Escape string (url-encoded). - /// - /// String to be escaped. - /// Escaped string. - public string EscapeString(string str) - { - return RestSharp.Contrib.HttpUtility.UrlEncode(str); - } - - /// - /// Create FileParameter based on Stream. - /// - /// Parameter name. - /// Input stream. - /// FileParameter. - public FileParameter ParameterToFile(string name, Stream stream) - { - if (stream is FileStream) - return FileParameter.Create(name, stream.ReadAsBytes(), Path.GetFileName(((FileStream)stream).Name)); - else - return FileParameter.Create(name, stream.ReadAsBytes(), "no_file_name_provided"); - } - - /// - /// If parameter is DateTime, output in a formatted string (default ISO 8601), customizable with Configuration.DateTime. - /// If parameter is a list of string, join the list with ",". - /// Otherwise just return the string. - /// - /// The parameter (header, path, query, form). - /// Formatted string. - public string ParameterToString(object obj) - { - if (obj is DateTime) - // Return a formatted date string - Can be customized with Configuration.DateTimeFormat - // Defaults to an ISO 8601, using the known as a Round-trip date/time pattern ("o") - // https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Anchor_8 - // For example: 2009-06-15T13:45:30.0000000 - return ((DateTime)obj).ToString (Configuration.DateTimeFormat); - else if (obj is List) - return String.Join(",", (obj as List).ToArray()); - else - return Convert.ToString (obj); - } - - /// - /// Deserialize the JSON string into a proper object. - /// - /// HTTP body (e.g. string, JSON). - /// Object type. - /// HTTP headers. - /// Object representation of the JSON string. - public object Deserialize(string content, Type type, IList headers=null) - { - if (type == typeof(Object)) // return an object - { - return content; - } - - if (type == typeof(Stream)) - { - var filePath = String.IsNullOrEmpty(Configuration.TempFolderPath) - ? Path.GetTempPath() - : Configuration.TempFolderPath; - - var fileName = filePath + Guid.NewGuid(); - if (headers != null) - { - var regex = new Regex(@"Content-Disposition:.*filename=['""]?([^'""\s]+)['""]?$"); - var match = regex.Match(headers.ToString()); - if (match.Success) - fileName = filePath + match.Value.Replace("\"", "").Replace("'", ""); - } - File.WriteAllText(fileName, content); - return new FileStream(fileName, FileMode.Open); - - } - - if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object - { - return DateTime.Parse(content, null, System.Globalization.DateTimeStyles.RoundtripKind); - } - - if (type == typeof(String) || type.Name.StartsWith("System.Nullable")) // return primitive type - { - return ConvertType(content, type); - } - - // at this point, it must be a model (json) - try - { - return JsonConvert.DeserializeObject(content, type); - } - catch (IOException e) - { - throw new ApiException(500, e.Message); - } - } - - /// - /// Serialize an object into JSON string. - /// - /// Object. - /// JSON string. - public string Serialize(object obj) - { - try - { - return obj != null ? JsonConvert.SerializeObject(obj) : null; - } - catch (Exception e) - { - throw new ApiException(500, e.Message); - } - } - - /// - /// Get the API key with prefix. - /// - /// API key identifier (authentication scheme). - /// API key with prefix. - public string GetApiKeyWithPrefix (string apiKeyIdentifier) - { - var apiKeyValue = ""; - Configuration.ApiKey.TryGetValue (apiKeyIdentifier, out apiKeyValue); - var apiKeyPrefix = ""; - if (Configuration.ApiKeyPrefix.TryGetValue (apiKeyIdentifier, out apiKeyPrefix)) - return apiKeyPrefix + " " + apiKeyValue; - else - return apiKeyValue; - } - - /// - /// Update parameters based on authentication. - /// - /// Query parameters. - /// Header parameters. - /// Authentication settings. - public void UpdateParamsForAuth(Dictionary queryParams, Dictionary headerParams, string[] authSettings) - { - if (authSettings == null || authSettings.Length == 0) - return; - - foreach (string auth in authSettings) - { - // determine which one to use - switch(auth) - { - {{#authMethods}} - case "{{name}}": - {{#isApiKey}}{{#isKeyInHeader}}headerParams["{{keyParamName}}"] = GetApiKeyWithPrefix("{{keyParamName}}");{{/isKeyInHeader}}{{#isKeyInQuery}}queryParams["{{keyParamName}}"] = GetApiKeyWithPrefix("{{keyParamName}}");{{/isKeyInQuery}}{{/isApiKey}}{{#isBasic}}headerParams["Authorization"] = "Basic " + Base64Encode(Configuration.Username + ":" + Configuration.Password);{{/isBasic}} - {{#isOAuth}}//TODO support oauth{{/isOAuth}} - break; - {{/authMethods}} - default: - //TODO show warning about security definition not found - break; - } - } - } - - /// - /// Encode string in base64 format. - /// - /// String to be encoded. - /// Encoded string. - public static string Base64Encode(string text) - { - var textByte = System.Text.Encoding.UTF8.GetBytes(text); - return System.Convert.ToBase64String(textByte); - } - - /// - /// Dynamically cast the object into target type. - /// Ref: http://stackoverflow.com/questions/4925718/c-dynamic-runtime-cast - /// - /// Object to be casted - /// Target type - /// Casted object - public static Object ConvertType(Object source, Type dest) { - return Convert.ChangeType(source, dest); - } - - } -} diff --git a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/Configuration.mustache b/modules/swagger-codegen/src/main/resources/CsharpDotNet2/Configuration.mustache deleted file mode 100644 index 56f4e617556..00000000000 --- a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/Configuration.mustache +++ /dev/null @@ -1,132 +0,0 @@ -using System; -using System.Reflection; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace {{packageName}}.Client -{ - /// - /// Represents a set of configuration settings - /// - public class Configuration - { - - /// - /// Version of the package. - /// - /// Version of the package. - public const string Version = "{{packageVersion}}"; - - /// - /// Gets or sets the default API client for making HTTP calls. - /// - /// The API client. - public static ApiClient DefaultApiClient = new ApiClient(); - - /// - /// Gets or sets the username (HTTP basic authentication). - /// - /// The username. - public static String Username { get; set; } - - /// - /// Gets or sets the password (HTTP basic authentication). - /// - /// The password. - public static String Password { get; set; } - - /// - /// Gets or sets the API key based on the authentication name. - /// - /// The API key. - public static Dictionary ApiKey = new Dictionary(); - - /// - /// Gets or sets the prefix (e.g. Token) of the API key based on the authentication name. - /// - /// The prefix of the API key. - public static Dictionary ApiKeyPrefix = new Dictionary(); - - private static string _tempFolderPath = Path.GetTempPath(); - - /// - /// Gets or sets the temporary folder path to store the files downloaded from the server. - /// - /// Folder path. - public static String TempFolderPath - { - get { return _tempFolderPath; } - - set - { - if (String.IsNullOrEmpty(value)) - { - _tempFolderPath = value; - return; - } - - // create the directory if it does not exist - if (!Directory.Exists(value)) - Directory.CreateDirectory(value); - - // check if the path contains directory separator at the end - if (value[value.Length - 1] == Path.DirectorySeparatorChar) - _tempFolderPath = value; - else - _tempFolderPath = value + Path.DirectorySeparatorChar; - } - } - - private const string ISO8601_DATETIME_FORMAT = "o"; - - private static string _dateTimeFormat = ISO8601_DATETIME_FORMAT; - - /// - /// Gets or sets the the date time format used when serializing in the ApiClient - /// By default, it's set to ISO 8601 - "o", for others see: - /// https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx - /// and https://msdn.microsoft.com/en-us/library/8kb3ddd4(v=vs.110).aspx - /// No validation is done to ensure that the string you're providing is valid - /// - /// The DateTimeFormat string - public static String DateTimeFormat - { - get - { - return _dateTimeFormat; - } - set - { - if (string.IsNullOrEmpty(value)) - { - // Never allow a blank or null string, go back to the default - _dateTimeFormat = ISO8601_DATETIME_FORMAT; - return; - } - - // Caution, no validation when you choose date time format other than ISO 8601 - // Take a look at the above links - _dateTimeFormat = value; - } - } - - /// - /// Returns a string with essential information for debugging. - /// - public static String ToDebugReport() - { - String report = "C# SDK ({{packageName}}) Debug Report:\n"; - report += " OS: " + Environment.OSVersion + "\n"; - report += " .NET Framework Version: " + Assembly - .GetExecutingAssembly() - .GetReferencedAssemblies() - .Where(x => x.Name == "System.Core").First().Version.ToString() + "\n"; - report += " Version of the API: {{version}}\n"; - report += " SDK Package Version: {{packageVersion}}\n"; - - return report; - } - } -} diff --git a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/README.mustache b/modules/swagger-codegen/src/main/resources/CsharpDotNet2/README.mustache deleted file mode 100644 index 89ed69c1df8..00000000000 --- a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/README.mustache +++ /dev/null @@ -1,153 +0,0 @@ -# {{packageName}} - the C# library for the {{appName}} - -{{#appDescription}} -{{{appDescription}}} -{{/appDescription}} - -This C# SDK is automatically generated by the [Swagger Codegen](https://github.com/swagger-api/swagger-codegen) project: - -- API version: {{appVersion}} -- SDK version: {{packageVersion}} -{{^hideGenerationTimestamp}} -- Build date: {{generatedDate}} -{{/hideGenerationTimestamp}} -- Build package: {{generatorClass}} -{{#infoUrl}} - For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) -{{/infoUrl}} - - -## Frameworks supported -{{^supportUWP}} -- .NET 2.0 -{{/supportUWP}} -{{#supportUWP}} -- UWP -{{/supportUWP}} - - -## Dependencies -- Mono compiler -- Newtonsoft.Json.7.0.1 -- RestSharp.Net2.1.1.11 - -Note: NuGet is downloaded by the mono compilation script and packages are installed with it. No dependency DLLs are bundled with this generator - - -## Installation -Run the following command to generate the DLL -- [Mac/Linux] `/bin/sh compile-mono.sh` -- [Windows] TODO - -Then include the DLL (under the `bin` folder) in the C# project, and use the namespaces: -```csharp -using {{packageName}}.{{apiPackage}}; -using {{packageName}}.Client; -using {{packageName}}.{{modelPackage}}; -``` - -## Getting Started - -```csharp -using System; -using System.Diagnostics; -using {{apiPackage}}; -using {{packageName}}.Client; -using {{modelPackage}}; - -namespace Example -{ - public class {{operationId}}Example - { - public void main() - { - {{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#operation}}{{#-first}}{{#hasAuthMethods}}{{#authMethods}}{{#isBasic}} - // Configure HTTP basic authorization: {{{name}}} - Configuration.Default.Username = "YOUR_USERNAME"; - Configuration.Default.Password = "YOUR_PASSWORD";{{/isBasic}}{{#isApiKey}} - // Configure API key authorization: {{{name}}} - Configuration.Default.ApiKey.Add("{{{keyParamName}}}", "YOUR_API_KEY"); - // Uncomment below to setup prefix (e.g. Bearer) for API key, if needed - // Configuration.Default.ApiKeyPrefix.Add("{{{keyParamName}}}", "Bearer");{{/isApiKey}}{{#isOAuth}} - // Configure OAuth2 access token for authorization: {{{name}}} - Configuration.Default.AccessToken = "YOUR_ACCESS_TOKEN";{{/isOAuth}}{{/authMethods}} - {{/hasAuthMethods}} - - var apiInstance = new {{classname}}(); - {{#allParams}} - {{#isPrimitiveType}} - var {{paramName}} = {{{example}}}; // {{{dataType}}} | {{{description}}}{{^required}} (optional) {{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} - {{/isPrimitiveType}} - {{^isPrimitiveType}} - var {{paramName}} = new {{{dataType}}}(); // {{{dataType}}} | {{{description}}}{{^required}} (optional) {{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} - {{/isPrimitiveType}} - {{/allParams}} - - try - { - {{#summary}} - // {{{.}}} - {{/summary}} - {{#returnType}}{{{.}}} result = {{/returnType}}apiInstance.{{{operationId}}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{#returnType}} - Debug.WriteLine(result);{{/returnType}} - } - catch (Exception e) - { - Debug.Print("Exception when calling {{classname}}.{{operationId}}: " + e.Message ); - } - } - } -}{{/-first}}{{/operation}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}} -``` - - -## Documentation for API Endpoints - -All URIs are relative to *{{{basePath}}}* - -Class | Method | HTTP request | Description ------------- | ------------- | ------------- | ------------- -{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{{summary}}}{{/summary}} -{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} - - -## Documentation for Models - -{{#modelPackage}} -{{#models}}{{#model}} - [{{{modelPackage}}}.{{{classname}}}]({{modelDocPath}}{{{classname}}}.md) -{{/model}}{{/models}} -{{/modelPackage}} -{{^modelPackage}} -No model defined in this package -{{/modelPackage}} - - -## Documentation for Authorization - -{{^authMethods}} -All endpoints do not require authorization. -{{/authMethods}} -{{#authMethods}} -{{#last}} -Authentication schemes defined for the API: -{{/last}} -{{/authMethods}} -{{#authMethods}} - -### {{name}} - -{{#isApiKey}}- **Type**: API key -- **API key parameter name**: {{keyParamName}} -- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}} -{{/isApiKey}} -{{#isBasic}}- **Type**: HTTP basic authentication -{{/isBasic}} -{{#isOAuth}}- **Type**: OAuth -- **Flow**: {{flow}} -- **Authorization URL**: {{authorizationUrl}} -- **Scopes**: {{^scopes}}N/A{{/scopes}} -{{#scopes}} - {{scope}}: {{description}} -{{/scopes}} -{{/isOAuth}} - -{{/authMethods}} diff --git a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/api.mustache b/modules/swagger-codegen/src/main/resources/CsharpDotNet2/api.mustache deleted file mode 100644 index fc7583b182c..00000000000 --- a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/api.mustache +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.Collections.Generic; -using RestSharp; -using {{packageName}}.Client; -{{#hasImport}}using {{packageName}}.Model; -{{/hasImport}} - -namespace {{packageName}}.Api -{ - {{#operations}} - /// - /// Represents a collection of functions to interact with the API endpoints - /// - public interface I{{classname}} - { - {{#operation}} - /// - /// {{summary}} {{notes}} - /// - {{#allParams}}/// {{description}} - {{/allParams}}/// {{#returnType}}{{returnType}}{{/returnType}} - {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{nickname}} ({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); - {{/operation}} - } - - /// - /// Represents a collection of functions to interact with the API endpoints - /// - public class {{classname}} : I{{classname}} - { - /// - /// Initializes a new instance of the class. - /// - /// an instance of ApiClient (optional) - /// - public {{classname}}(ApiClient apiClient = null) - { - if (apiClient == null) // use the default one in Configuration - this.ApiClient = Configuration.DefaultApiClient; - else - this.ApiClient = apiClient; - } - - /// - /// Initializes a new instance of the class. - /// - /// - public {{classname}}(String basePath) - { - this.ApiClient = new ApiClient(basePath); - } - - /// - /// Sets the base path of the API client. - /// - /// The base path - /// The base path - public void SetBasePath(String basePath) - { - this.ApiClient.BasePath = basePath; - } - - /// - /// Gets the base path of the API client. - /// - /// The base path - /// The base path - public String GetBasePath(String basePath) - { - return this.ApiClient.BasePath; - } - - /// - /// Gets or sets the API client. - /// - /// An instance of the ApiClient - public ApiClient ApiClient {get; set;} - - {{#operation}} - /// - /// {{summary}} {{notes}} - /// - {{#allParams}}/// {{description}} - {{/allParams}}/// {{#returnType}}{{returnType}}{{/returnType}} - public {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{nickname}} ({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) - { - {{#allParams}}{{#required}} - // verify the required parameter '{{paramName}}' is set - if ({{paramName}} == null) throw new ApiException(400, "Missing required parameter '{{paramName}}' when calling {{nickname}}"); - {{/required}}{{/allParams}} - - var path = "{{{path}}}"; - path = path.Replace("{format}", "json"); - {{#pathParams}}path = path.Replace("{" + "{{baseName}}" + "}", ApiClient.ParameterToString({{{paramName}}})); - {{/pathParams}} - - var queryParams = new Dictionary(); - var headerParams = new Dictionary(); - var formParams = new Dictionary(); - var fileParams = new Dictionary(); - String postBody = null; - - {{#queryParams}} if ({{paramName}} != null) queryParams.Add("{{baseName}}", ApiClient.ParameterToString({{paramName}})); // query parameter - {{/queryParams}} - {{#headerParams}} if ({{paramName}} != null) headerParams.Add("{{baseName}}", ApiClient.ParameterToString({{paramName}})); // header parameter - {{/headerParams}} - {{#formParams}}if ({{paramName}} != null) {{#isFile}}fileParams.Add("{{baseName}}", ApiClient.ParameterToFile("{{baseName}}", {{paramName}}));{{/isFile}}{{^isFile}}formParams.Add("{{baseName}}", ApiClient.ParameterToString({{paramName}})); // form parameter{{/isFile}} - {{/formParams}} - {{#bodyParam}}postBody = ApiClient.Serialize({{paramName}}); // http body (model) parameter - {{/bodyParam}} - - // authentication setting, if any - String[] authSettings = new String[] { {{#authMethods}}"{{name}}"{{#hasMore}}, {{/hasMore}}{{/authMethods}} }; - - // make the HTTP request - IRestResponse response = (IRestResponse) ApiClient.CallApi(path, Method.{{httpMethod}}, queryParams, postBody, headerParams, formParams, fileParams, authSettings); - - if (((int)response.StatusCode) >= 400) - throw new ApiException ((int)response.StatusCode, "Error calling {{nickname}}: " + response.Content, response.Content); - else if (((int)response.StatusCode) == 0) - throw new ApiException ((int)response.StatusCode, "Error calling {{nickname}}: " + response.ErrorMessage, response.ErrorMessage); - - {{#returnType}}return ({{{returnType}}}) ApiClient.Deserialize(response.Content, typeof({{{returnType}}}), response.Headers);{{/returnType}}{{^returnType}}return;{{/returnType}} - } - - {{/operation}} - } - {{/operations}} -} diff --git a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/api_doc.mustache b/modules/swagger-codegen/src/main/resources/CsharpDotNet2/api_doc.mustache deleted file mode 100644 index c8b447c3a44..00000000000 --- a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/api_doc.mustache +++ /dev/null @@ -1,97 +0,0 @@ -# {{packageName}}.{{apiPackage}}.{{classname}}{{#description}} -{{description}}{{/description}} - -All URIs are relative to *{{{basePath}}}* - -Method | HTTP request | Description -------------- | ------------- | ------------- -{{#operations}}{{#operation}}[**{{operationId}}**]({{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} -{{/operation}}{{/operations}} - -{{#operations}} -{{#operation}} - -# **{{{operationId}}}** -> {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}} ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) - -{{{summary}}}{{#notes}} - -{{{notes}}}{{/notes}} - -### Example -```csharp -using System; -using System.Diagnostics; -using {{packageName}}.Api; -using {{packageName}}.Client; -using {{modelPackage}}; - -namespace Example -{ - public class {{operationId}}Example - { - public void main() - { - {{#hasAuthMethods}}{{#authMethods}}{{#isBasic}} - // Configure HTTP basic authorization: {{{name}}} - Configuration.Default.Username = "YOUR_USERNAME"; - Configuration.Default.Password = "YOUR_PASSWORD";{{/isBasic}}{{#isApiKey}} - // Configure API key authorization: {{{name}}} - Configuration.Default.ApiKey.Add("{{{keyParamName}}}", "YOUR_API_KEY"); - // Uncomment below to setup prefix (e.g. Bearer) for API key, if needed - // Configuration.Default.ApiKeyPrefix.Add("{{{keyParamName}}}", "Bearer");{{/isApiKey}}{{#isOAuth}} - // Configure OAuth2 access token for authorization: {{{name}}} - Configuration.Default.AccessToken = "YOUR_ACCESS_TOKEN";{{/isOAuth}}{{/authMethods}} - {{/hasAuthMethods}} - - var apiInstance = new {{classname}}(); - {{#allParams}} - {{#isPrimitiveType}} - var {{paramName}} = {{{example}}}; // {{{dataType}}} | {{{description}}}{{^required}} (optional) {{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} - {{/isPrimitiveType}} - {{^isPrimitiveType}} - var {{paramName}} = new {{{dataType}}}(); // {{{dataType}}} | {{{description}}}{{^required}} (optional) {{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} - {{/isPrimitiveType}} - {{/allParams}} - - try - { - {{#summary}} - // {{{.}}} - {{/summary}} - {{#returnType}}{{returnType}} result = {{/returnType}}apiInstance.{{{operationId}}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{#returnType}} - Debug.WriteLine(result);{{/returnType}} - } - catch (Exception e) - { - Debug.Print("Exception when calling {{classname}}.{{operationId}}: " + e.Message ); - } - } - } -} -``` - -### Parameters -{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}} -Name | Type | Description | Notes -------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}} -{{#allParams}} **{{paramName}}** | {{#isFile}}**{{{dataType}}}**{{/isFile}}{{#isPrimitiveType}}**{{{dataType}}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}[**{{{dataType}}}**]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} | {{^required}}[optional] {{/required}}{{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}} -{{/allParams}} - -### Return type - -{{#returnType}}{{#returnTypeIsPrimitive}}**{{{returnType}}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{{returnType}}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}void (empty response body){{/returnType}} - -### Authorization - -{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{{name}}}](../README.md#{{{name}}}){{^-last}}, {{/-last}}{{/authMethods}} - -### HTTP request headers - - - **Content-Type**: {{#consumes}}{{{mediaType}}}{{#hasMore}}, {{/hasMore}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} - - **Accept**: {{#produces}}{{{mediaType}}}{{#hasMore}}, {{/hasMore}}{{/produces}}{{^produces}}Not defined{{/produces}} - -[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) - -{{/operation}} -{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/model.mustache b/modules/swagger-codegen/src/main/resources/CsharpDotNet2/model.mustache deleted file mode 100644 index 3e6e32320e7..00000000000 --- a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/model.mustache +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Text; -using System.Collections; -using System.Collections.Generic; -using System.Runtime.Serialization; -using Newtonsoft.Json; - -{{#models}} -{{#model}} -namespace {{packageName}}.Model { - - /// - /// {{description}} - /// - [DataContract] - public class {{classname}}{{#parent}} : {{{parent}}}{{/parent}} { - {{#vars}} - /// - /// {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{{description}}}{{/description}} - /// {{#description}} - /// {{{description}}}{{/description}} - [DataMember(Name="{{baseName}}", EmitDefaultValue=false)] - [JsonProperty(PropertyName = "{{baseName}}")] - public {{{datatype}}} {{name}} { get; set; } - - {{/vars}} - - /// - /// Get the string presentation of the object - /// - /// String presentation of the object - public override string ToString() { - var sb = new StringBuilder(); - sb.Append("class {{classname}} {\n"); - {{#vars}} - sb.Append(" {{name}}: ").Append({{name}}).Append("\n"); - {{/vars}} - sb.Append("}\n"); - return sb.ToString(); - } - - /// - /// Get the JSON string presentation of the object - /// - /// JSON string presentation of the object - public {{#parent}} new {{/parent}}string ToJson() { - return JsonConvert.SerializeObject(this, Formatting.Indented); - } - -} -{{/model}} -{{/models}} -} diff --git a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/model_doc.mustache b/modules/swagger-codegen/src/main/resources/CsharpDotNet2/model_doc.mustache deleted file mode 100644 index aff3e7e0d1e..00000000000 --- a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/model_doc.mustache +++ /dev/null @@ -1,14 +0,0 @@ -{{#models}} -{{#model}} -# {{{packageName}}}.Model.{{{classname}}} -## Properties - -Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- -{{#vars}}**{{name}}** | {{#isPrimitiveType}}**{{datatype}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{datatype}}**]({{complexType}}.md){{/isPrimitiveType}} | {{description}} | {{^required}}[optional] {{/required}}{{#readOnly}}[readonly] {{/readOnly}}{{#defaultValue}}[default to {{{.}}}]{{/defaultValue}} -{{/vars}} - -[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) - -{{/model}} -{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/Eiffel/README.mustache b/modules/swagger-codegen/src/main/resources/Eiffel/README.mustache index b0e787e8fd9..9143997024d 100644 --- a/modules/swagger-codegen/src/main/resources/Eiffel/README.mustache +++ b/modules/swagger-codegen/src/main/resources/Eiffel/README.mustache @@ -29,12 +29,12 @@ All URIs are relative to *{{basePath}}* Class | Method | HTTP request | Description ------------ | ------------- | ------------- | ------------- -{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}/{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} {{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} ## Documentation For Models -{{#models}}{{#model}} - [{{{classname}}}]({{modelDocPath}}{{{classname}}}.md) +{{#models}}{{#model}} - [{{{classname}}}]({{modelDocPath}}/{{{classname}}}.md) {{/model}}{{/models}} ## Documentation For Authorization diff --git a/modules/swagger-codegen/src/main/resources/Eiffel/api.mustache b/modules/swagger-codegen/src/main/resources/Eiffel/api.mustache index 3cae5b2c6e5..53a5edc0698 100644 --- a/modules/swagger-codegen/src/main/resources/Eiffel/api.mustache +++ b/modules/swagger-codegen/src/main/resources/Eiffel/api.mustache @@ -13,7 +13,7 @@ feature -- API Access {{#operation}} - {{operationId}} {{#hasParams}}({{#allParams}}{{paramName}}: {{#required}}{{{dataType}}}{{/required}}{{^required}}detachable {{{dataType}}}{{/required}}{{#hasMore}}; {{/hasMore}}{{/allParams}}){{/hasParams}}{{#returnType}}: detachable {{{returnType}}}{{/returnType}}{{^returnType}}{{/returnType}} + {{operationId}} {{#hasParams}}({{#allParams}}{{paramName}}: {{#required}}{{{dataType}}}{{/required}}{{^required}}{{#isPrimitiveType}}{{{dataType}}}{{/isPrimitiveType}}{{^isPrimitiveType}}detachable {{{dataType}}}{{/isPrimitiveType}}{{/required}}{{#hasMore}}; {{/hasMore}}{{/allParams}}){{/hasParams}}{{#returnType}}: detachable {{{returnType}}}{{/returnType}}{{^returnType}}{{/returnType}} -- {{summary}} -- {{notes}} -- {{#allParams}} @@ -21,6 +21,17 @@ feature -- API Access -- {{/allParams}} -- {{#returnType}} -- Result {{returnType}}{{/returnType}} + require + {{#allParams}} + {{#hasValidation}} + {{#maximum}} + {{{paramName}}}_is_less_or_equal_than: {{{paramName}}} <= {{{maximum}}} + {{/maximum}} + {{#minimum}} + {{{paramName}}}_is_greater_or_equal_than: {{{paramName}}} >= {{{minimum}}} + {{/minimum}} + {{/hasValidation}} + {{/allParams}} local l_path: STRING l_request: API_CLIENT_REQUEST @@ -36,6 +47,7 @@ feature -- API Access {{#queryParams}} l_request.fill_query_params(api_client.parameter_to_tuple("{{#collectionFormat}}{{{collectionFormat}}}{{/collectionFormat}}", "{{baseName}}", {{paramName}})); {{/queryParams}} + {{#headerParams}} if attached {{paramName}} as l_{{paramName}} then l_request.add_header(l_{{paramName}}.out,"{{baseName}}"); diff --git a/modules/swagger-codegen/src/main/resources/Eiffel/api_client.mustache b/modules/swagger-codegen/src/main/resources/Eiffel/api_client.mustache index 1e8845b6f51..9f7ae9ccc10 100644 --- a/modules/swagger-codegen/src/main/resources/Eiffel/api_client.mustache +++ b/modules/swagger-codegen/src/main/resources/Eiffel/api_client.mustache @@ -1,4 +1,4 @@ -{{>noteInfo}} +{{>noteinfo}} class API_CLIENT @@ -123,7 +123,7 @@ feature -- Helper: OAuth Authentication feature -- Query Parameter Helpers - parameter_to_tuple (a_collection_format, a_name: STRING; a_value: ANY): LIST [TUPLE [name: STRING; value: STRING]] + parameter_to_tuple (a_collection_format, a_name: STRING; a_value: detachable ANY): LIST [TUPLE [name: STRING; value: STRING]] -- A list of tuples with name and valule. -- collectionFormat collection format (e.g. csv, tsv) -- name Name @@ -135,7 +135,7 @@ feature -- Query Parameter Helpers l_delimiter: STRING l_value: STRING do - if attached {LIST [STRING_32]} a_value as a_list then + if attached {LIST [ANY]} a_value as a_list then -- Collection if a_list.is_empty then -- Return an empty list @@ -150,7 +150,7 @@ feature -- Query Parameter Helpers end if l_format.is_case_insensitive_equal ("multi") then across a_list as ic loop - Result.force ([a_name, ic.item.as_string_8]) + Result.force ([a_name, parameter_to_string (ic.item)]) end else if l_format.is_case_insensitive_equal ("csv") then @@ -166,7 +166,7 @@ feature -- Query Parameter Helpers end across a_list as ic from create l_value.make_empty loop - l_value.append (ic.item) + l_value.append (parameter_to_string (ic.item)) l_value.append (l_delimiter) end l_value.remove_tail (1) @@ -175,7 +175,71 @@ feature -- Query Parameter Helpers end else create {ARRAYED_LIST [TUPLE [name: STRING; value: STRING]]} Result.make (1) - Result.force ([a_name,a_value.out]) + if attached a_value then + Result.force ([a_name,a_value.out]) + else + Result.force ([a_name,""]) + end + + end + end + + + parameter_to_string (a_param: detachable ANY): STRING + -- return the string representation of the givien object `a_param'. + do + if a_param = Void then + Result := "" + else + if attached {BOOLEAN} a_param as bool then + Result := bool.out + elseif attached {NUMERIC} a_param as num then + if attached {INTEGER_64} num as i64 then + Result := i64.out + elseif attached {INTEGER_32} num as i32 then + Result := i32.out + elseif attached {INTEGER_16} num as i16 then + Result := i16.out + elseif attached {INTEGER_8} num as i8 then + Result := i8.out + elseif attached {NATURAL_64} num as n64 then + Result := n64.out + elseif attached {NATURAL_32} num as n32 then + Result := n32.out + elseif attached {NATURAL_16} num as n16 then + Result := n16.out + elseif attached {NATURAL_8} num as n8 then + Result := n8.out + elseif attached {REAL_64} num as r64 then + Result := r64.out + elseif attached {REAL_32} num as r32 then + Result := r32.out + else + check is_basic_numeric_type: False end + end + Result := num.out + elseif attached {CHARACTER_8} a_param as ch8 then + Result := ch8.out + elseif attached {CHARACTER_32} a_param as ch32 then + Result := ch32.out + elseif attached {POINTER} a_param as ptr then + Result := ptr.to_integer_32.out + elseif attached {DATE} a_param as date then + --TODO improve to support + -- date string As defined by full-date - RFC3339 + Result := date.debug_output + elseif attached {DATE_TIME} a_param as date_time then + -- TODO improve to support + -- dateTime string date-time As defined by date-time - RFC3339 + Result := date_time.date.debug_output + elseif attached {STRING_32} a_param as str_32 then + Result := str_32 + elseif attached {STRING_8} a_param as str_8 then + Result := str_8 + else + -- Unsupported Object type. + Result := "" + end end end diff --git a/modules/swagger-codegen/src/main/resources/Eiffel/ecf.mustache b/modules/swagger-codegen/src/main/resources/Eiffel/ecf.mustache index 53926e66537..73341130a9c 100644 --- a/modules/swagger-codegen/src/main/resources/Eiffel/ecf.mustache +++ b/modules/swagger-codegen/src/main/resources/Eiffel/ecf.mustache @@ -19,6 +19,7 @@ + diff --git a/modules/swagger-codegen/src/main/resources/Eiffel/framework/auth/authentication.mustache b/modules/swagger-codegen/src/main/resources/Eiffel/framework/auth/authentication.mustache index 19c0b046779..8e274812fb5 100644 --- a/modules/swagger-codegen/src/main/resources/Eiffel/framework/auth/authentication.mustache +++ b/modules/swagger-codegen/src/main/resources/Eiffel/framework/auth/authentication.mustache @@ -1,4 +1,4 @@ -{{>noteInfo}} +{{>noteinfo}} deferred class AUTHENTICATION diff --git a/modules/swagger-codegen/src/main/resources/Eiffel/model.mustache b/modules/swagger-codegen/src/main/resources/Eiffel/model.mustache index 388fc7c9cc2..aa5e906c448 100644 --- a/modules/swagger-codegen/src/main/resources/Eiffel/model.mustache +++ b/modules/swagger-codegen/src/main/resources/Eiffel/model.mustache @@ -1,7 +1,3 @@ -{{>noteinfo}} -{{#models}} -{{#model}} - class {{classname}} inherit @@ -18,7 +14,12 @@ inherit {{#parent}} {{{parent}}} rename - out as out_{{{parentSchema}}} + out as out_{{{parentSchema}}}, + is_equal as is_equal_{{{parentSchema}}}, + copy as copy_{{{parentSchema}}} + select + is_equal_{{{parentSchema}}}, + copy_{{{parentSchema}}} end {{/parent}} @@ -73,7 +74,7 @@ feature -- Change Element {{#isListContainer}} if attached {{name}} as l_{{name}} then across l_{{name}} as ic loop - Result.append ("%N") + Result.append ("%N {{name}}:") Result.append (ic.item.out) Result.append ("%N") end @@ -81,6 +82,7 @@ feature -- Change Element {{/isListContainer}} {{#isMapContainer}} if attached {{name}} as l_{{name}} then + Result.append ("%N{{name}}:") across l_{{name}} as ic loop Result.append ("%N") Result.append ("key:") @@ -94,7 +96,7 @@ feature -- Change Element {{/isMapContainer}} {{^isContainer}} if attached {{name}} as l_{{name}} then - Result.append ("%N") + Result.append ("%N{{name}}:") Result.append (l_{{name}}.out) Result.append ("%N") end @@ -103,5 +105,3 @@ feature -- Change Element {{/vars}} end end -{{/model}} -{{/models}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/Eiffel/model_enum.mustache b/modules/swagger-codegen/src/main/resources/Eiffel/model_enum.mustache new file mode 100644 index 00000000000..7ec1a444db7 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Eiffel/model_enum.mustache @@ -0,0 +1,36 @@ +class {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} + +feature -- Access + + value: detachable {{dataType}} + -- enumerated value. + note + option: stable + attribute + end + +feature -- Enum + + {{#allowableValues}} + {{#enumVars}} + {{{name}}}: {{classname}} + once + create Result + Result.set_value ({{{value}}}){{^-last}}{{/-last}}{{#-last}}{{/-last}} + end + + {{/enumVars}} + {{/allowableValues}} + +feature -- Element Change + + set_value (a_val: like value) + -- Set `value' with `a_value'. + do + value := a_val + ensure + value_set: value = a_val + end + + +end diff --git a/modules/swagger-codegen/src/main/resources/Eiffel/model_generic.mustache b/modules/swagger-codegen/src/main/resources/Eiffel/model_generic.mustache new file mode 100644 index 00000000000..1a83370f8d2 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Eiffel/model_generic.mustache @@ -0,0 +1,7 @@ +{{>noteinfo}} +{{#models}} +{{#model}} +{{#isEnum}}{{>model_enum}}{{/isEnum}}{{^isEnum}}{{>model}}{{/isEnum}} +{{/model}} +{{/models}} + diff --git a/modules/swagger-codegen/src/main/resources/Eiffel/test/ecf_test.mustache b/modules/swagger-codegen/src/main/resources/Eiffel/test/ecf_test.mustache index b5f9b4a8240..347dc9f6311 100644 --- a/modules/swagger-codegen/src/main/resources/Eiffel/test/ecf_test.mustache +++ b/modules/swagger-codegen/src/main/resources/Eiffel/test/ecf_test.mustache @@ -16,6 +16,8 @@ + + diff --git a/modules/swagger-codegen/src/main/resources/Eiffel/travis.mustache b/modules/swagger-codegen/src/main/resources/Eiffel/travis.mustache new file mode 100644 index 00000000000..6cb4545088a --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Eiffel/travis.mustache @@ -0,0 +1,20 @@ +language: eiffel +before_script: + - export current_dir=$(pwd) + - echo current_dir + - cd .. + - wget https://ftp.eiffel.com/pub/beta/nightly/Eiffel_17.11_gpl_100608-linux-x86-64.tar.bz2 + - tar -xvf Eiffel_17.11_gpl_100608-linux-x86-64.tar.bz2 + - export ISE_EIFFEL=$PWD/Eiffel_17.11 + - export ISE_PLATFORM=linux-x86-64 + - export PATH=$PATH:$ISE_EIFFEL/studio/spec/$ISE_PLATFORM/bin + - export PATH=$PATH:$ISE_EIFFEL/tools/spec/$ISE_PLATFORM/bin + - cd $current_dir + +# safelist +branches: + only: + - master + +script: compile_all -ecb -melt -list_failures -clean -options dotnet=false + diff --git a/modules/swagger-codegen/src/main/resources/Java/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Java/ApiClient.mustache index 9fe8d556135..718a7e57129 100644 --- a/modules/swagger-codegen/src/main/resources/Java/ApiClient.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/ApiClient.mustache @@ -1,14 +1,21 @@ {{>licenseInfo}} package {{invokerPackage}}; +{{#threetenbp}} +import org.threeten.bp.*; + +{{/threetenbp}} import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.*; +{{#joda}} +import com.fasterxml.jackson.datatype.joda.JodaModule; +{{/joda}} {{#java8}} -import com.fasterxml.jackson.datatype.jsr310.*; -{{/java8}} -{{^java8}} -import com.fasterxml.jackson.datatype.joda.*; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; {{/java8}} +{{#threetenbp}} +import com.fasterxml.jackson.datatype.threetenbp.ThreeTenModule; +{{/threetenbp}} import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; import com.sun.jersey.api.client.Client; @@ -72,12 +79,19 @@ public class ApiClient { objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); objectMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING); + {{#joda}} + objectMapper.registerModule(new JodaModule()); + {{/joda}} {{#java8}} objectMapper.registerModule(new JavaTimeModule()); {{/java8}} - {{^java8}} - objectMapper.registerModule(new JodaModule()); - {{/java8}} + {{#threetenbp}} + ThreeTenModule module = new ThreeTenModule(); + module.addDeserializer(Instant.class, CustomInstantDeserializer.INSTANT); + module.addDeserializer(OffsetDateTime.class, CustomInstantDeserializer.OFFSET_DATE_TIME); + module.addDeserializer(ZonedDateTime.class, CustomInstantDeserializer.ZONED_DATE_TIME); + objectMapper.registerModule(module); + {{/threetenbp}} objectMapper.setDateFormat(ApiClient.buildDefaultDateFormat()); dateFormat = ApiClient.buildDefaultDateFormat(); @@ -393,62 +407,71 @@ public class ApiClient { } } - /* - * Format to {@code Pair} objects. - * @param collectionFormat Collection format - * @param name Name - * @param value Value - * @return List of pair + /** + * Formats the specified query parameter to a list containing a single {@code Pair} object. + * + * Note that {@code value} must not be a collection. + * + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list containing a single {@code Pair} object. */ - public List parameterToPairs(String collectionFormat, String name, Object value){ + public List parameterToPair(String name, Object value) { List params = new ArrayList(); // preconditions - if (name == null || name.isEmpty() || value == null) return params; + if (name == null || name.isEmpty() || value == null || value instanceof Collection) return params; - Collection valueCollection; - if (value instanceof Collection) { - valueCollection = (Collection) value; - } else { - params.add(new Pair(name, parameterToString(value))); - return params; - } + params.add(new Pair(name, parameterToString(value))); + return params; + } + + /** + * Formats the specified collection query parameters to a list of {@code Pair} objects. + * + * Note that the values of each of the returned Pair objects are percent-encoded. + * + * @param collectionFormat The collection format of the parameter. + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list of {@code Pair} objects. + */ + public List parameterToPairs(String collectionFormat, String name, Collection value) { + List params = new ArrayList(); - if (valueCollection.isEmpty()){ + // preconditions + if (name == null || name.isEmpty() || value == null) { return params; } - // get the collection format - String format = (collectionFormat == null || collectionFormat.isEmpty() ? "csv" : collectionFormat); // default: csv - // create the params based on the collection format - if ("multi".equals(format)) { - for (Object item : valueCollection) { - params.add(new Pair(name, parameterToString(item))); + if ("multi".equals(collectionFormat)) { + for (Object item : value) { + params.add(new Pair(name, escapeString(parameterToString(item)))); } - return params; } + // collectionFormat is assumed to be "csv" by default String delimiter = ","; - if ("csv".equals(format)) { - delimiter = ","; - } else if ("ssv".equals(format)) { - delimiter = " "; - } else if ("tsv".equals(format)) { - delimiter = "\t"; - } else if ("pipes".equals(format)) { - delimiter = "|"; + // escape all delimiters except commas, which are URI reserved + // characters + if ("ssv".equals(collectionFormat)) { + delimiter = escapeString(" "); + } else if ("tsv".equals(collectionFormat)) { + delimiter = escapeString("\t"); + } else if ("pipes".equals(collectionFormat)) { + delimiter = escapeString("|"); } StringBuilder sb = new StringBuilder() ; - for (Object item : valueCollection) { + for (Object item : value) { sb.append(delimiter); - sb.append(parameterToString(item)); + sb.append(escapeString(parameterToString(item))); } - params.add(new Pair(name, sb.substring(1))); + params.add(new Pair(name, sb.substring(delimiter.length()))); return params; } @@ -465,7 +488,7 @@ public class ApiClient { */ public boolean isJsonMime(String mime) { String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"; - return mime != null && (mime.matches(jsonMime) || mime.equalsIgnoreCase("application/json-patch+json")); + return mime != null && (mime.matches(jsonMime) || mime.equals("*/*")); } /** @@ -496,10 +519,10 @@ public class ApiClient { * * @param contentTypes The Content-Type array to select from * @return The Content-Type header to use. If the given array is empty, - * JSON will be used. + * or matches "any", JSON will be used. */ public String selectHeaderContentType(String[] contentTypes) { - if (contentTypes.length == 0) { + if (contentTypes.length == 0 || contentTypes[0].equals("*/*")) { return "application/json"; } for (String contentType : contentTypes) { @@ -564,9 +587,10 @@ public class ApiClient { * * @param path The sub path * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters * @return The full URL */ - private String buildUrl(String path, List queryParams) { + private String buildUrl(String path, List queryParams, List collectionQueryParams) { final StringBuilder url = new StringBuilder(); url.append(basePath).append(path); @@ -587,17 +611,34 @@ public class ApiClient { } } + if (collectionQueryParams != null && !collectionQueryParams.isEmpty()) { + String prefix = url.toString().contains("?") ? "&" : "?"; + for (Pair param : collectionQueryParams) { + if (param.getValue() != null) { + if (prefix != null) { + url.append(prefix); + prefix = null; + } else { + url.append("&"); + } + String value = parameterToString(param.getValue()); + // collection query parameter value already escaped as part of parameterToPairs + url.append(escapeString(param.getName())).append("=").append(value); + } + } + } + return url.toString(); } - private ClientResponse getAPIResponse(String path, String method, List queryParams, Object body, Map headerParams, Map formParams, String accept, String contentType, String[] authNames) throws ApiException { + private ClientResponse getAPIResponse(String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map formParams, String accept, String contentType, String[] authNames) throws ApiException { if (body != null && !formParams.isEmpty()) { throw new ApiException(500, "Cannot have body and form params"); } updateParamsForAuth(authNames, queryParams, headerParams); - final String url = buildUrl(path, queryParams); + final String url = buildUrl(path, queryParams, collectionQueryParams); Builder builder; if (accept == null) { builder = httpClient.resource(url).getRequestBuilder(); @@ -626,8 +667,9 @@ public class ApiClient { response = builder.type(contentType).delete(ClientResponse.class, serialize(body, contentType, formParams)); } else if ("PATCH".equals(method)) { response = builder.type(contentType).header("X-HTTP-Method-Override", "PATCH").post(ClientResponse.class, serialize(body, contentType, formParams)); - } - else { + } else if ("HEAD".equals(method)) { + response = builder.head(); + } else { throw new ApiException(500, "unknown method type " + method); } return response; @@ -640,6 +682,7 @@ public class ApiClient { * @param path The sub-path of the HTTP URL * @param method The request method, one of "GET", "POST", "PUT", and "DELETE" * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters * @param body The request body object - if it is not binary, otherwise null * @param headerParams The header parameters * @param formParams The form parameters @@ -650,9 +693,9 @@ public class ApiClient { * @return The response body in type of string * @throws ApiException API exception */ - public T invokeAPI(String path, String method, List queryParams, Object body, Map headerParams, Map formParams, String accept, String contentType, String[] authNames, GenericType returnType) throws ApiException { + public T invokeAPI(String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map formParams, String accept, String contentType, String[] authNames, GenericType returnType) throws ApiException { - ClientResponse response = getAPIResponse(path, method, queryParams, body, headerParams, formParams, accept, contentType, authNames); + ClientResponse response = getAPIResponse(path, method, queryParams, collectionQueryParams, body, headerParams, formParams, accept, contentType, authNames); statusCode = response.getStatusInfo().getStatusCode(); responseHeaders = response.getHeaders(); diff --git a/modules/swagger-codegen/src/main/resources/Java/CustomInstantDeserializer.mustache b/modules/swagger-codegen/src/main/resources/Java/CustomInstantDeserializer.mustache new file mode 100644 index 00000000000..dbf4d30e117 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/CustomInstantDeserializer.mustache @@ -0,0 +1,232 @@ +package {{invokerPackage}}; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonTokenId; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.datatype.threetenbp.DateTimeUtils; +import com.fasterxml.jackson.datatype.threetenbp.DecimalUtils; +import com.fasterxml.jackson.datatype.threetenbp.deser.ThreeTenDateTimeDeserializerBase; +import com.fasterxml.jackson.datatype.threetenbp.function.BiFunction; +import com.fasterxml.jackson.datatype.threetenbp.function.Function; +import org.threeten.bp.DateTimeException; +import org.threeten.bp.Instant; +import org.threeten.bp.OffsetDateTime; +import org.threeten.bp.ZoneId; +import org.threeten.bp.ZonedDateTime; +import org.threeten.bp.format.DateTimeFormatter; +import org.threeten.bp.temporal.Temporal; +import org.threeten.bp.temporal.TemporalAccessor; + +import java.io.IOException; +import java.math.BigDecimal; + +/** + * Deserializer for ThreeTen temporal {@link Instant}s, {@link OffsetDateTime}, and {@link ZonedDateTime}s. + * Adapted from the jackson threetenbp InstantDeserializer to add support for deserializing rfc822 format. + * + * @author Nick Williams + */ +public class CustomInstantDeserializer + extends ThreeTenDateTimeDeserializerBase { + private static final long serialVersionUID = 1L; + + public static final CustomInstantDeserializer INSTANT = new CustomInstantDeserializer( + Instant.class, DateTimeFormatter.ISO_INSTANT, + new Function() { + @Override + public Instant apply(TemporalAccessor temporalAccessor) { + return Instant.from(temporalAccessor); + } + }, + new Function() { + @Override + public Instant apply(FromIntegerArguments a) { + return Instant.ofEpochMilli(a.value); + } + }, + new Function() { + @Override + public Instant apply(FromDecimalArguments a) { + return Instant.ofEpochSecond(a.integer, a.fraction); + } + }, + null + ); + + public static final CustomInstantDeserializer OFFSET_DATE_TIME = new CustomInstantDeserializer( + OffsetDateTime.class, DateTimeFormatter.ISO_OFFSET_DATE_TIME, + new Function() { + @Override + public OffsetDateTime apply(TemporalAccessor temporalAccessor) { + return OffsetDateTime.from(temporalAccessor); + } + }, + new Function() { + @Override + public OffsetDateTime apply(FromIntegerArguments a) { + return OffsetDateTime.ofInstant(Instant.ofEpochMilli(a.value), a.zoneId); + } + }, + new Function() { + @Override + public OffsetDateTime apply(FromDecimalArguments a) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(a.integer, a.fraction), a.zoneId); + } + }, + new BiFunction() { + @Override + public OffsetDateTime apply(OffsetDateTime d, ZoneId z) { + return d.withOffsetSameInstant(z.getRules().getOffset(d.toLocalDateTime())); + } + } + ); + + public static final CustomInstantDeserializer ZONED_DATE_TIME = new CustomInstantDeserializer( + ZonedDateTime.class, DateTimeFormatter.ISO_ZONED_DATE_TIME, + new Function() { + @Override + public ZonedDateTime apply(TemporalAccessor temporalAccessor) { + return ZonedDateTime.from(temporalAccessor); + } + }, + new Function() { + @Override + public ZonedDateTime apply(FromIntegerArguments a) { + return ZonedDateTime.ofInstant(Instant.ofEpochMilli(a.value), a.zoneId); + } + }, + new Function() { + @Override + public ZonedDateTime apply(FromDecimalArguments a) { + return ZonedDateTime.ofInstant(Instant.ofEpochSecond(a.integer, a.fraction), a.zoneId); + } + }, + new BiFunction() { + @Override + public ZonedDateTime apply(ZonedDateTime zonedDateTime, ZoneId zoneId) { + return zonedDateTime.withZoneSameInstant(zoneId); + } + } + ); + + protected final Function fromMilliseconds; + + protected final Function fromNanoseconds; + + protected final Function parsedToValue; + + protected final BiFunction adjust; + + protected CustomInstantDeserializer(Class supportedType, + DateTimeFormatter parser, + Function parsedToValue, + Function fromMilliseconds, + Function fromNanoseconds, + BiFunction adjust) { + super(supportedType, parser); + this.parsedToValue = parsedToValue; + this.fromMilliseconds = fromMilliseconds; + this.fromNanoseconds = fromNanoseconds; + this.adjust = adjust == null ? new BiFunction() { + @Override + public T apply(T t, ZoneId zoneId) { + return t; + } + } : adjust; + } + + @SuppressWarnings("unchecked") + protected CustomInstantDeserializer(CustomInstantDeserializer base, DateTimeFormatter f) { + super((Class) base.handledType(), f); + parsedToValue = base.parsedToValue; + fromMilliseconds = base.fromMilliseconds; + fromNanoseconds = base.fromNanoseconds; + adjust = base.adjust; + } + + @Override + protected JsonDeserializer withDateFormat(DateTimeFormatter dtf) { + if (dtf == _formatter) { + return this; + } + return new CustomInstantDeserializer(this, dtf); + } + + @Override + public T deserialize(JsonParser parser, DeserializationContext context) throws IOException { + //NOTE: Timestamps contain no timezone info, and are always in configured TZ. Only + //string values have to be adjusted to the configured TZ. + switch (parser.getCurrentTokenId()) { + case JsonTokenId.ID_NUMBER_FLOAT: { + BigDecimal value = parser.getDecimalValue(); + long seconds = value.longValue(); + int nanoseconds = DecimalUtils.extractNanosecondDecimal(value, seconds); + return fromNanoseconds.apply(new FromDecimalArguments( + seconds, nanoseconds, getZone(context))); + } + + case JsonTokenId.ID_NUMBER_INT: { + long timestamp = parser.getLongValue(); + if (context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS)) { + return this.fromNanoseconds.apply(new FromDecimalArguments( + timestamp, 0, this.getZone(context) + )); + } + return this.fromMilliseconds.apply(new FromIntegerArguments( + timestamp, this.getZone(context) + )); + } + + case JsonTokenId.ID_STRING: { + String string = parser.getText().trim(); + if (string.length() == 0) { + return null; + } + if (string.endsWith("+0000")) { + string = string.substring(0, string.length() - 5) + "Z"; + } + T value; + try { + TemporalAccessor acc = _formatter.parse(string); + value = parsedToValue.apply(acc); + if (context.isEnabled(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)) { + return adjust.apply(value, this.getZone(context)); + } + } catch (DateTimeException e) { + throw _peelDTE(e); + } + return value; + } + } + throw context.mappingException("Expected type float, integer, or string."); + } + + private ZoneId getZone(DeserializationContext context) { + // Instants are always in UTC, so don't waste compute cycles + return (_valueClass == Instant.class) ? null : DateTimeUtils.timeZoneToZoneId(context.getTimeZone()); + } + + private static class FromIntegerArguments { + public final long value; + public final ZoneId zoneId; + + private FromIntegerArguments(long value, ZoneId zoneId) { + this.value = value; + this.zoneId = zoneId; + } + } + + private static class FromDecimalArguments { + public final long integer; + public final int fraction; + public final ZoneId zoneId; + + private FromDecimalArguments(long integer, int fraction, ZoneId zoneId) { + this.integer = integer; + this.fraction = fraction; + this.zoneId = zoneId; + } + } +} diff --git a/modules/swagger-codegen/src/main/resources/Java/api.mustache b/modules/swagger-codegen/src/main/resources/Java/api.mustache index 6903f4dec7c..542ccbba287 100644 --- a/modules/swagger-codegen/src/main/resources/Java/api.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/api.mustache @@ -52,7 +52,17 @@ public class {{classname}} { * @return {{returnType}} {{/returnType}} * @throws ApiException if fails to make API call + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} */ + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} public {{#returnType}}{{{returnType}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException { Object {{localVariablePrefix}}localVarPostBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}; {{#allParams}}{{#required}} @@ -67,11 +77,12 @@ public class {{classname}} { // query params {{javaUtilPrefix}}List {{localVariablePrefix}}localVarQueryParams = new {{javaUtilPrefix}}ArrayList(); + {{javaUtilPrefix}}List {{localVariablePrefix}}localVarCollectionQueryParams = new {{javaUtilPrefix}}ArrayList(); {{javaUtilPrefix}}Map {{localVariablePrefix}}localVarHeaderParams = new {{javaUtilPrefix}}HashMap(); {{javaUtilPrefix}}Map {{localVariablePrefix}}localVarFormParams = new {{javaUtilPrefix}}HashMap(); {{#queryParams}} - {{localVariablePrefix}}localVarQueryParams.addAll({{localVariablePrefix}}apiClient.parameterToPairs("{{#collectionFormat}}{{{collectionFormat}}}{{/collectionFormat}}", "{{baseName}}", {{paramName}})); + {{localVariablePrefix}}{{#collectionFormat}}localVarCollectionQueryParams.addAll({{localVariablePrefix}}apiClient.parameterToPairs("{{{collectionFormat}}}", {{/collectionFormat}}{{^collectionFormat}}localVarQueryParams.addAll({{localVariablePrefix}}apiClient.parameterToPair({{/collectionFormat}}"{{baseName}}", {{paramName}})); {{/queryParams}} {{#headerParams}}if ({{paramName}} != null) @@ -96,9 +107,9 @@ public class {{classname}} { {{#returnType}} GenericType<{{{returnType}}}> {{localVariablePrefix}}localVarReturnType = new GenericType<{{{returnType}}}>() {}; - return {{localVariablePrefix}}apiClient.invokeAPI({{localVariablePrefix}}localVarPath, "{{httpMethod}}", {{localVariablePrefix}}localVarQueryParams, {{localVariablePrefix}}localVarPostBody, {{localVariablePrefix}}localVarHeaderParams, {{localVariablePrefix}}localVarFormParams, {{localVariablePrefix}}localVarAccept, {{localVariablePrefix}}localVarContentType, {{localVariablePrefix}}localVarAuthNames, {{localVariablePrefix}}localVarReturnType); + return {{localVariablePrefix}}apiClient.invokeAPI({{localVariablePrefix}}localVarPath, "{{httpMethod}}", {{localVariablePrefix}}localVarQueryParams, {{localVariablePrefix}}localVarCollectionQueryParams, {{localVariablePrefix}}localVarPostBody, {{localVariablePrefix}}localVarHeaderParams, {{localVariablePrefix}}localVarFormParams, {{localVariablePrefix}}localVarAccept, {{localVariablePrefix}}localVarContentType, {{localVariablePrefix}}localVarAuthNames, {{localVariablePrefix}}localVarReturnType); {{/returnType}}{{^returnType}} - {{localVariablePrefix}}apiClient.invokeAPI({{localVariablePrefix}}localVarPath, "{{httpMethod}}", {{localVariablePrefix}}localVarQueryParams, {{localVariablePrefix}}localVarPostBody, {{localVariablePrefix}}localVarHeaderParams, {{localVariablePrefix}}localVarFormParams, {{localVariablePrefix}}localVarAccept, {{localVariablePrefix}}localVarContentType, {{localVariablePrefix}}localVarAuthNames, null); + {{localVariablePrefix}}apiClient.invokeAPI({{localVariablePrefix}}localVarPath, "{{httpMethod}}", {{localVariablePrefix}}localVarQueryParams, {{localVariablePrefix}}localVarCollectionQueryParams, {{localVariablePrefix}}localVarPostBody, {{localVariablePrefix}}localVarHeaderParams, {{localVariablePrefix}}localVarFormParams, {{localVariablePrefix}}localVarAccept, {{localVariablePrefix}}localVarContentType, {{localVariablePrefix}}localVarAuthNames, null); {{/returnType}} } {{/operation}} diff --git a/modules/swagger-codegen/src/main/resources/Java/build.gradle.mustache b/modules/swagger-codegen/src/main/resources/Java/build.gradle.mustache index 5f60c01b36e..6c05fd5a36e 100644 --- a/modules/swagger-codegen/src/main/resources/Java/build.gradle.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/build.gradle.mustache @@ -107,7 +107,7 @@ if(hasProperty('target') && target == 'android') { ext { swagger_annotations_version = "1.5.15" - jackson_version = "2.8.9" + jackson_version = "{{^threetenbp}}2.8.9{{/threetenbp}}{{#threetenbp}}2.6.4{{/threetenbp}}" jersey_version = "1.19.4" jodatime_version = "2.9.9" junit_version = "4.12" @@ -121,12 +121,16 @@ dependencies { compile "com.fasterxml.jackson.core:jackson-annotations:$jackson_version" compile "com.fasterxml.jackson.core:jackson-databind:$jackson_version" compile "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:$jackson_version" + {{#joda}} + compile "com.fasterxml.jackson.datatype:jackson-datatype-joda:$jackson_version", + {{/joda}} {{#java8}} - compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version" + compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version", {{/java8}} + {{#threetenbp}} + compile "com.github.joschi.jackson:jackson-datatype-threetenbp:$jackson_version", + {{/threetenbp}} {{^java8}} - compile "com.fasterxml.jackson.datatype:jackson-datatype-joda:$jackson_version" - compile "joda-time:joda-time:$jodatime_version" compile "com.brsanthu:migbase64:2.2" {{/java8}} testCompile "junit:junit:$junit_version" diff --git a/modules/swagger-codegen/src/main/resources/Java/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/Java/git_push.sh.mustache index e153ce23ecf..a2d75234837 100755 --- a/modules/swagger-codegen/src/main/resources/Java/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/feign/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/feign/ApiClient.mustache index e9cb564a63b..fa1dea42e8a 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/feign/ApiClient.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/feign/ApiClient.mustache @@ -5,16 +5,22 @@ import java.util.Map; import org.apache.oltu.oauth2.client.request.OAuthClientRequest.AuthenticationRequestBuilder; import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder; +{{#threetenbp}} +import org.threeten.bp.*; +{{/threetenbp}} import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; -{{^java8}} +{{#joda}} import com.fasterxml.jackson.datatype.joda.JodaModule; -{{/java8}} +{{/joda}} {{#java8}} import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; {{/java8}} +{{#threetenbp}} +import com.fasterxml.jackson.datatype.threetenbp.ThreeTenModule; +{{/threetenbp}} import feign.Feign; import feign.RequestInterceptor; @@ -149,12 +155,19 @@ public class ApiClient { objectMapper.disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE); objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); objectMapper.setDateFormat(new RFC3339DateFormat()); - {{^java8}} + {{#joda}} objectMapper.registerModule(new JodaModule()); - {{/java8}} + {{/joda}} {{#java8}} objectMapper.registerModule(new JavaTimeModule()); {{/java8}} + {{#threetenbp}} + ThreeTenModule module = new ThreeTenModule(); + module.addDeserializer(Instant.class, CustomInstantDeserializer.INSTANT); + module.addDeserializer(OffsetDateTime.class, CustomInstantDeserializer.OFFSET_DATE_TIME); + module.addDeserializer(ZonedDateTime.class, CustomInstantDeserializer.ZONED_DATE_TIME); + objectMapper.registerModule(module); + {{/threetenbp}} return objectMapper; } diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/feign/api.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/feign/api.mustache index 67a772a8772..03eb7dd40cb 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/feign/api.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/feign/api.mustache @@ -30,11 +30,15 @@ public interface {{classname}} extends ApiClient.Api { {{#returnType}} * @return {{returnType}} {{/returnType}} +{{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation +{{/externalDocs}} */ @RequestLine("{{httpMethod}} {{{path}}}{{#hasQueryParams}}?{{/hasQueryParams}}{{#queryParams}}{{baseName}}={{=<% %>=}}{<%paramName%>}<%={{ }}=%>{{#hasMore}}&{{/hasMore}}{{/queryParams}}") @Headers({ - "Content-Type: {{vendorExtensions.x-contentType}}", - "Accept: {{vendorExtensions.x-accepts}}",{{#headerParams}} +{{#vendorExtensions.x-contentType}} "Content-Type: {{vendorExtensions.x-contentType}}", +{{/vendorExtensions.x-contentType}} "Accept: {{vendorExtensions.x-accepts}}",{{#headerParams}} "{{baseName}}: {{=<% %>=}}{<%paramName%>}<%={{ }}=%>"{{#hasMore}}, {{/hasMore}}{{/headerParams}} }) @@ -64,11 +68,15 @@ public interface {{classname}} extends ApiClient.Api { {{#returnType}} * @return {{returnType}} {{/returnType}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} */ @RequestLine("{{httpMethod}} {{{path}}}?{{#queryParams}}{{baseName}}={{=<% %>=}}{<%paramName%>}<%={{ }}=%>{{#hasMore}}&{{/hasMore}}{{/queryParams}}") @Headers({ - "Content-Type: {{vendorExtensions.x-contentType}}", - "Accept: {{vendorExtensions.x-accepts}}",{{#headerParams}} +{{#vendorExtensions.x-contentType}} "Content-Type: {{vendorExtensions.x-contentType}}", +{{/vendorExtensions.x-contentType}} "Accept: {{vendorExtensions.x-accepts}}",{{#headerParams}} "{{baseName}}: {{=<% %>=}}{<%paramName%>}<%={{ }}=%>"{{#hasMore}}, {{/hasMore}}{{/headerParams}} }) diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/feign/build.gradle.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/feign/build.gradle.mustache index 51ac6819fd4..0074c0e38a2 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/feign/build.gradle.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/feign/build.gradle.mustache @@ -102,6 +102,9 @@ if(hasProperty('target') && target == 'android') { ext { swagger_annotations_version = "1.5.9" jackson_version = "2.8.7" + {{#threetenbp}} + threepane_version = "2.6.4" + {{/threetenbp}} feign_version = "9.4.0" feign_form_version = "2.1.0" junit_version = "4.12" @@ -110,14 +113,22 @@ ext { dependencies { compile "io.swagger:swagger-annotations:$swagger_annotations_version" - compile "com.netflix.feign:feign-core:$feign_version" - compile "com.netflix.feign:feign-jackson:$feign_version" - compile "com.netflix.feign:feign-slf4j:$feign_version" + compile "io.github.openfeign:feign-core:$feign_version" + compile "io.github.openfeign:feign-jackson:$feign_version" + compile "io.github.openfeign:feign-slf4j:$feign_version" compile "io.github.openfeign.form:feign-form:$feign_form_version" compile "com.fasterxml.jackson.core:jackson-core:$jackson_version" compile "com.fasterxml.jackson.core:jackson-annotations:$jackson_version" compile "com.fasterxml.jackson.core:jackson-databind:$jackson_version" - compile "com.fasterxml.jackson.datatype:jackson-datatype-{{^java8}}joda{{/java8}}{{#java8}}jsr310{{/java8}}:$jackson_version" + {{#joda}} + compile "com.fasterxml.jackson.datatype:jackson-datatype-joda:$jackson_version" + {{/joda}} + {{#java8}} + compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version" + {{/java8}} + {{#threetenbp}} + compile "com.github.joschi.jackson:jackson-datatype-threetenbp:$threepane_version" + {{/threetenbp}} compile "org.apache.oltu.oauth2:org.apache.oltu.oauth2.client:$oltu_version" compile "com.brsanthu:migbase64:2.2" testCompile "junit:junit:$junit_version" diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/feign/build.sbt.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/feign/build.sbt.mustache index c2d771961a0..d745ef7e86d 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/feign/build.sbt.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/feign/build.sbt.mustache @@ -10,9 +10,9 @@ lazy val root = (project in file(".")). resolvers += Resolver.mavenLocal, libraryDependencies ++= Seq( "io.swagger" % "swagger-annotations" % "1.5.9" % "compile", - "com.netflix.feign" % "feign-core" % "9.4.0" % "compile", - "com.netflix.feign" % "feign-jackson" % "9.4.0" % "compile", - "com.netflix.feign" % "feign-slf4j" % "9.4.0" % "compile", + "io.github.openfeign" % "feign-core" % "9.4.0" % "compile", + "io.github.openfeign" % "feign-jackson" % "9.4.0" % "compile", + "io.github.openfeign" % "feign-slf4j" % "9.4.0" % "compile", "io.github.openfeign.form" % "feign-form" % "2.1.0" % "compile", "com.fasterxml.jackson.core" % "jackson-core" % "2.8.7" % "compile", "com.fasterxml.jackson.core" % "jackson-annotations" % "2.8.7" % "compile", diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/feign/pom.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/feign/pom.mustache index 20d8f9b8886..49965181d7b 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/feign/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/feign/pom.mustache @@ -1,268 +1,307 @@ - 4.0.0 - {{groupId}} - {{artifactId}} - jar - {{artifactId}} - {{artifactVersion}} - {{artifactUrl}} - {{artifactDescription}} - - {{scmConnection}} - {{scmDeveloperConnection}} - {{scmUrl}} - - - 2.2.0 - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + {{artifactUrl}} + {{artifactDescription}} + + {{scmConnection}} + {{scmDeveloperConnection}} + {{scmUrl}} + - - - {{licenseName}} - {{licenseUrl}} - repo - - + + + {{licenseName}} + {{licenseUrl}} + repo + + - - - {{developerName}} - {{developerEmail}} - {{developerOrganization}} - {{developerOrganizationUrl}} - - + + + {{developerName}} + {{developerEmail}} + {{developerOrganization}} + {{developerOrganizationUrl}} + + - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.12 - - - - loggerPath - conf/log4j.properties - - - -Xms512m -Xmx1500m - methods - pertest - - - - maven-dependency-plugin - - - package - - copy-dependencies - - - ${project.build.directory}/lib - - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 2.2 - - - - jar - test-jar - - - - - - + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + pertest + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + - - org.codehaus.mojo - build-helper-maven-plugin - - - add_sources - generate-sources - - add-source - - - - src/main/java - - - - - add_test_sources - generate-test-sources - - add-test-source - - - - src/test/java - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.4 - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - + + + org.apache.maven.plugins + maven-jar-plugin + 2.2 + + + + jar + test-jar + + + + + + - - - sign-artifacts - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - - sign-artifacts - verify - - sign - - - - + + org.codehaus.mojo + build-helper-maven-plugin + + + add_sources + generate-sources + + add-source + + + + + src/main/java + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + + src/test/java + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + - - - + + + + + sign-artifacts + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + + - - - io.swagger - swagger-annotations - ${swagger-core-version} - + + + io.swagger + swagger-annotations + ${swagger-core-version} + - - - io.github.openfeign - feign-core - ${feign-version} - - - io.github.openfeign - feign-jackson - ${feign-version} - - - io.github.openfeign - feign-slf4j - ${feign-version} - - - io.github.openfeign.form - feign-form - ${feign-form-version} - + + + io.github.openfeign + feign-core + ${feign-version} + + + io.github.openfeign + feign-jackson + ${feign-version} + + + io.github.openfeign + feign-slf4j + ${feign-version} + + + io.github.openfeign.form + feign-form + ${feign-form-version} + - - - com.fasterxml.jackson.core - jackson-core - ${jackson-version} - - - com.fasterxml.jackson.core - jackson-annotations - ${jackson-version} - - - com.fasterxml.jackson.core - jackson-databind - ${jackson-version} - - {{#withXml}} + + + com.fasterxml.jackson.core + jackson-core + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-version} + + {{#withXml}} - - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - ${jackson-version} - + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + ${jackson-version} + - {{/withXml}} - - com.fasterxml.jackson.datatype - jackson-datatype-{{^java8}}joda{{/java8}}{{#java8}}jsr310{{/java8}} - ${jackson-version} - - - org.apache.oltu.oauth2 - org.apache.oltu.oauth2.client - ${oltu-version} - + {{/withXml}} + {{#joda}} + + com.fasterxml.jackson.datatype + jackson-datatype-joda + ${jackson-version} + + {{/joda}} + {{#java8}} + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson-version} + + {{/java8}} + {{#threetenbp}} + + com.github.joschi.jackson + jackson-datatype-threetenbp + ${jackson-threetenbp-version} + + {{/threetenbp}} + + org.apache.oltu.oauth2 + org.apache.oltu.oauth2.client + ${oltu-version} + - - - junit - junit - ${junit-version} - test - - - com.squareup.okhttp3 - mockwebserver - 3.6.0 - test - - - org.assertj - assertj-core - 1.7.1 - test - - - - {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} - ${java.version} - ${java.version} - 1.5.15 - 9.4.0 - 2.1.0 - 2.8.9 - 4.12 - 1.0.0 - 1.0.1 - + + + junit + junit + ${junit-version} + test + + + com.squareup.okhttp3 + mockwebserver + 3.6.0 + test + + + org.assertj + assertj-core + 1.7.1 + test + + + + UTF-8 + {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} + ${java.version} + ${java.version} + 1.5.15 + 9.4.0 + 2.1.0 + 2.8.9 + {{#threetenbp}} + 2.6.4 + {{/threetenbp}} + 4.12 + 1.0.0 + 1.0.1 + diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/ApiClient.mustache new file mode 100644 index 00000000000..fa6e7a601fe --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/ApiClient.mustache @@ -0,0 +1,109 @@ +package {{invokerPackage}}; + +import {{apiPackage}}.*; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +{{#joda}} +import com.fasterxml.jackson.datatype.joda.JodaModule; +{{/joda}} +{{#java8}} +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +{{/java8}} +{{#threetenbp}} +import com.fasterxml.jackson.datatype.threetenbp.ThreeTenModule; +{{/threetenbp}} +{{#threetenbp}} +import org.threeten.bp.*; +{{/threetenbp}} +import com.google.api.client.googleapis.util.Utils; +import com.google.api.client.http.AbstractHttpContent; +import com.google.api.client.http.HttpRequestFactory; +import com.google.api.client.http.HttpRequestInitializer; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.json.Json; + +import java.io.IOException; +import java.io.OutputStream; + +{{>generatedAnnotation}} +public class ApiClient { + private final String basePath; + private final HttpRequestFactory httpRequestFactory; + private final ObjectMapper objectMapper; + + private static final String defaultBasePath = "{{basePath}}"; + + // A reasonable default object mapper. Client can pass in a chosen ObjectMapper anyway, this is just for reasonable defaults. + private static ObjectMapper createDefaultObjectMapper() { + ObjectMapper objectMapper = new ObjectMapper() + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .setDateFormat(new RFC3339DateFormat()); + {{#joda}} + objectMapper.registerModule(new JodaModule()); + {{/joda}} + {{#java8}} + objectMapper.registerModule(new JavaTimeModule()); + {{/java8}} + {{#threetenbp}} + ThreeTenModule module = new ThreeTenModule(); + module.addDeserializer(Instant.class, CustomInstantDeserializer.INSTANT); + module.addDeserializer(OffsetDateTime.class, CustomInstantDeserializer.OFFSET_DATE_TIME); + module.addDeserializer(ZonedDateTime.class, CustomInstantDeserializer.ZONED_DATE_TIME); + objectMapper.registerModule(module); + {{/threetenbp}} + return objectMapper; + } + + public ApiClient() { + this(null, null, null, null); + } + + public ApiClient( + String basePath, + HttpTransport httpTransport, + HttpRequestInitializer initializer, + ObjectMapper objectMapper + ) { + this.basePath = basePath == null ? defaultBasePath : ( + basePath.endsWith("/") ? basePath.substring(0, basePath.length() - 1) : basePath + ); + this.httpRequestFactory = (httpTransport == null ? Utils.getDefaultTransport() : httpTransport).createRequestFactory(initializer); + this.objectMapper = (objectMapper == null ? createDefaultObjectMapper() : objectMapper); + } + + public HttpRequestFactory getHttpRequestFactory() { + return httpRequestFactory; + } + + public String getBasePath() { + return basePath; + } + + public ObjectMapper getObjectMapper() { + return objectMapper; + } + + public class JacksonJsonHttpContent extends AbstractHttpContent { + /* A POJO that can be serialized with a com.fasterxml Jackson ObjectMapper */ + private final Object data; + + public JacksonJsonHttpContent(Object data) { + super(Json.MEDIA_TYPE); + this.data = data; + } + + @Override + public void writeTo(OutputStream out) throws IOException { + objectMapper.writeValue(out, data); + } + } + + // Builder pattern to get API instances for this client. + {{#apiInfo}}{{#apis}} + public {{classname}} {{classVarName}}Api() { + return new {{classname}}(this); + } + {{/apis}}{{/apiInfo}} +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/api.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/api.mustache new file mode 100644 index 00000000000..87119b9c771 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/api.mustache @@ -0,0 +1,144 @@ +package {{package}}; + +import {{invokerPackage}}.ApiClient; + +{{#imports}}import {{import}}; +{{/imports}} + +import com.fasterxml.jackson.core.type.TypeReference; +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpContent; +import com.google.api.client.http.HttpMethods; +import com.google.api.client.http.HttpResponse; + +import javax.ws.rs.core.UriBuilder; +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.List; +import java.util.ArrayList; + +{{>generatedAnnotation}} +{{#operations}} +public class {{classname}} { + private ApiClient {{localVariablePrefix}}apiClient; + + public {{classname}}() { + this(new ApiClient()); + } + + public {{classname}}(ApiClient apiClient) { + this.{{localVariablePrefix}}apiClient = apiClient; + } + + public ApiClient getApiClient() { + return {{localVariablePrefix}}apiClient; + } + + public void setApiClient(ApiClient apiClient) { + this.{{localVariablePrefix}}apiClient = apiClient; + } + + {{#operation}} + /**{{#summary}} + * {{summary}}{{/summary}}{{#notes}} + * {{notes}}{{/notes}}{{#responses}} + *

{{code}}{{#message}} - {{message}}{{/message}}{{/responses}}{{#allParams}} + * @param {{paramName}} {{description}}{{^description}}The {{paramName}} parameter{{/description}}{{/allParams}}{{#returnType}} + * @return {{returnType}}{{/returnType}} + * @throws IOException if an error occurs while attempting to invoke the API{{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} + + **/ + public {{#returnType}}{{{returnType}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws IOException { + {{#returnType}}HttpResponse response = {{/returnType}}{{operationId}}ForHttpResponse({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{#returnType}} + TypeReference typeRef = new TypeReference<{{{returnType}}}>() {}; + return apiClient.getObjectMapper().readValue(response.getContent(), typeRef);{{/returnType}} + } + + /**{{#summary}} + * {{summary}}{{/summary}}{{#notes}} + * {{notes}}{{/notes}}{{#responses}} + *

{{code}}{{#message}} - {{message}}{{/message}}{{/responses}}{{#requiredParams}} + * @param {{paramName}} {{description}}{{^description}}The {{paramName}} parameter{{/description}}{{/requiredParams}} + * @param params Map of query params. A collection will be interpreted as passing in multiple instances of the same query param.{{#returnType}} + * @return {{returnType}}{{/returnType}} + * @throws IOException if an error occurs while attempting to invoke the API{{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} + + **/ + public {{#returnType}}{{{returnType}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#bodyParam}}{{^required}}{{{dataType}}} {{paramName}}, {{/required}}{{/bodyParam}}{{#requiredParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/requiredParams}}{{#hasRequiredParams}}, {{/hasRequiredParams}}Map params) throws IOException { + {{#returnType}}HttpResponse response = {{/returnType}}{{operationId}}ForHttpResponse({{#bodyParam}}{{^required}}{{paramName}}, {{/required}}{{/bodyParam}}{{#requiredParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/requiredParams}}{{#hasRequiredParams}}, {{/hasRequiredParams}}params);{{#returnType}} + TypeReference typeRef = new TypeReference<{{{returnType}}}>() {}; + return apiClient.getObjectMapper().readValue(response.getContent(), typeRef);{{/returnType}} + } + + public HttpResponse {{operationId}}ForHttpResponse({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws IOException { + {{#allParams}}{{#required}}// verify the required parameter '{{paramName}}' is set + if ({{paramName}} == null) { + throw new IllegalArgumentException("Missing the required parameter '{{paramName}}' when calling {{operationId}}"); + }{{/required}}{{/allParams}} + {{#hasPathParams}} + // create a map of path variables + final Map uriVariables = new HashMap();{{#pathParams}} + uriVariables.put("{{baseName}}", {{{paramName}}});{{/pathParams}} + {{/hasPathParams}} + UriBuilder uriBuilder = UriBuilder.fromUri(apiClient.getBasePath() + "{{{path}}}");{{#hasQueryParams}} + {{#queryParams}} + if ({{paramName}} != null) { + uriBuilder = uriBuilder.queryParam("{{baseName}}", {{paramName}}); + }{{/queryParams}}{{/hasQueryParams}} + + String url = uriBuilder{{#hasPathParams}}.buildFromMap(uriVariables).toString();{{/hasPathParams}}{{^hasPathParams}}.build().toString();{{/hasPathParams}} + GenericUrl genericUrl = new GenericUrl(url); + + HttpContent content = {{#bodyParam}}{{paramName}} == null ? null : apiClient.new JacksonJsonHttpContent({{paramName}}){{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}; + return apiClient.getHttpRequestFactory().buildRequest(HttpMethods.{{httpMethod}}, genericUrl, content).execute(); + } + + public HttpResponse {{operationId}}ForHttpResponse({{#bodyParam}}{{^required}}{{{dataType}}} {{paramName}}, {{/required}}{{/bodyParam}}{{#requiredParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/requiredParams}}{{#hasRequiredParams}}, {{/hasRequiredParams}}Map params) throws IOException { + {{#allParams}}{{#required}}// verify the required parameter '{{paramName}}' is set + if ({{paramName}} == null) { + throw new IllegalArgumentException("Missing the required parameter '{{paramName}}' when calling {{operationId}}"); + }{{/required}}{{/allParams}} + {{#hasPathParams}} + // create a map of path variables + final Map uriVariables = new HashMap();{{#pathParams}} + uriVariables.put("{{baseName}}", {{{paramName}}});{{/pathParams}} + {{/hasPathParams}} + UriBuilder uriBuilder = UriBuilder.fromUri(apiClient.getBasePath() + "{{{path}}}"); + + // Copy the params argument if present, to allow passing in immutable maps + Map allParams = params == null ? new HashMap() : new HashMap(params);{{#queryParams}}{{#required}} + // Add the required query param '{{paramName}}' to the map of query params + allParams.put("{{paramName}}", {{paramName}});{{/required}}{{/queryParams}} + + for (Map.Entry entry: allParams.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + + if (key != null && value != null) { + if (value instanceof Collection) { + uriBuilder = uriBuilder.queryParam(key, ((Collection) value).toArray()); + } else { + uriBuilder = uriBuilder.queryParam(key, value); + } + } + } + + String url = uriBuilder{{#hasPathParams}}.buildFromMap(uriVariables).toString();{{/hasPathParams}}{{^hasPathParams}}.build().toString();{{/hasPathParams}} + GenericUrl genericUrl = new GenericUrl(url); + + HttpContent content = {{#bodyParam}}{{paramName}} == null ? null : apiClient.new JacksonJsonHttpContent({{paramName}}){{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}; + return apiClient.getHttpRequestFactory().buildRequest(HttpMethods.{{httpMethod}}, genericUrl, content).execute(); + } + + + {{/operation}} +} +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/api_test.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/api_test.mustache new file mode 100644 index 00000000000..365106fbedf --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/api_test.mustache @@ -0,0 +1,45 @@ +{{>licenseInfo}} + +package {{package}}; + +{{#imports}}import {{import}}; +{{/imports}} +import org.junit.Test; +import org.junit.Ignore; + +import java.io.IOException; +{{^fullJavaUtil}} +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +{{/fullJavaUtil}} + +/** + * API tests for {{classname}} + */ +@Ignore +public class {{classname}}Test { + + private final {{classname}} api = new {{classname}}(); + + {{#operations}}{{#operation}} + /** + * {{summary}} + * + * {{notes}} + * + * @throws IOException + * if the Api call fails + */ + @Test + public void {{operationId}}Test() throws IOException { + {{#allParams}} + {{{dataType}}} {{paramName}} = null; + {{/allParams}} + {{#returnType}}{{{returnType}}} response = {{/returnType}}api.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + + // TODO: test validations + } + {{/operation}}{{/operations}} +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/build.gradle.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/build.gradle.mustache new file mode 100644 index 00000000000..ca34bacc3f5 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/build.gradle.mustache @@ -0,0 +1,142 @@ +apply plugin: 'idea' +apply plugin: 'eclipse' + +group = '{{groupId}}' +version = '{{artifactVersion}}' + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.5.+' + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' + } +} + +repositories { + jcenter() +} + + +if(hasProperty('target') && target == 'android') { + + apply plugin: 'com.android.library' + apply plugin: 'com.github.dcendents.android-maven' + + android { + compileSdkVersion 23 + buildToolsVersion '23.0.2' + defaultConfig { + minSdkVersion 14 + targetSdkVersion 22 + } + compileOptions { + {{#java8}} + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + {{/java8}} + {{^java8}} + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + {{/java8}} + } + + // Rename the aar correctly + libraryVariants.all { variant -> + variant.outputs.each { output -> + def outputFile = output.outputFile + if (outputFile != null && outputFile.name.endsWith('.aar')) { + def fileName = "${project.name}-${variant.baseName}-${version}.aar" + output.outputFile = new File(outputFile.parent, fileName) + } + } + } + + dependencies { + provided 'javax.annotation:jsr250-api:1.0' + } + } + + afterEvaluate { + android.libraryVariants.all { variant -> + def task = project.tasks.create "jar${variant.name.capitalize()}", Jar + task.description = "Create jar artifact for ${variant.name}" + task.dependsOn variant.javaCompile + task.from variant.javaCompile.destinationDir + task.destinationDir = project.file("${project.buildDir}/outputs/jar") + task.archiveName = "${project.name}-${variant.baseName}-${version}.jar" + artifacts.add('archives', task); + } + } + + task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' + } + + artifacts { + archives sourcesJar + } + +} else { + + apply plugin: 'java' + apply plugin: 'maven' + + {{#java8}} + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + {{/java8}} + {{^java8}} + sourceCompatibility = JavaVersion.VERSION_1_7 + targetCompatibility = JavaVersion.VERSION_1_7 + {{/java8}} + + install { + repositories.mavenInstaller { + pom.artifactId = '{{artifactId}}' + } + } + + task execute(type:JavaExec) { + main = System.getProperty('mainClass') + classpath = sourceSets.main.runtimeClasspath + } +} + +ext { + swagger_annotations_version = "1.5.15" + jackson_version = "2.8.9" + google_api_client_version = "1.23.0" + jersey_common_version = "2.25.1" + jodatime_version = "2.9.9" + junit_version = "4.12" + {{#threetenbp}} + jackson_threeten_version = "2.6.4" + {{/threetenbp}} +} + +dependencies { + compile "io.swagger:swagger-annotations:$swagger_annotations_version" + compile "com.google.api-client:google-api-client:${google_api_client_version}" + compile "org.glassfish.jersey.core:jersey-common:${jersey_common_version}" + compile "com.fasterxml.jackson.core:jackson-core:$jackson_version" + compile "com.fasterxml.jackson.core:jackson-annotations:$jackson_version" + compile "com.fasterxml.jackson.core:jackson-databind:$jackson_version" + compile "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:$jackson_version" + {{#java8}} + compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version" + {{/java8}} + {{#joda}} + compile "com.fasterxml.jackson.datatype:jackson-datatype-joda:$jackson_version" + compile "joda-time:joda-time:$jodatime_version" + {{/joda}} + {{#threetenbp}} + compile "com.github.joschi.jackson:jackson-datatype-threetenbp:$jackson_threeten_version" + {{/threetenbp}} + {{#withXml}} + compile "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:$jackson_version" + {{/withXml}} + testCompile "junit:junit:$junit_version" +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/build.sbt.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/build.sbt.mustache new file mode 100644 index 00000000000..8567f06e714 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/build.sbt.mustache @@ -0,0 +1,33 @@ +lazy val root = (project in file(".")). + settings( + organization := "{{groupId}}", + name := "{{artifactId}}", + version := "{{artifactVersion}}", + scalaVersion := "2.11.4", + scalacOptions ++= Seq("-feature"), + javacOptions in compile ++= Seq("-Xlint:deprecation"), + publishArtifact in (Compile, packageDoc) := false, + resolvers += Resolver.mavenLocal, + libraryDependencies ++= Seq( + "io.swagger" % "swagger-annotations" % "1.5.15", + "com.google.api-client" % "google-api-client" % "1.23.0", + "org.glassfish.jersey.core" % "jersey-common" % "2.25.1", + "com.fasterxml.jackson.core" % "jackson-core" % "2.8.9" % "compile", + "com.fasterxml.jackson.core" % "jackson-annotations" % "2.8.9" % "compile", + "com.fasterxml.jackson.core" % "jackson-databind" % "2.8.9" % "compile", + {{#withXml}} + "com.fasterxml.jackson.dataformat" % "jackson-dataformat-xml" % "2.8.9" % "compile", + {{/withXml}} + {{#joda}} + "com.fasterxml.jackson.datatype" % "jackson-datatype-joda" % "2.8.9" % "compile", + {{/joda}} + {{#java8}} + "com.fasterxml.jackson.datatype" % "jackson-datatype-jsr310" % "2.8.9" % "compile", + {{/java8}} + {{#threetenbp}} + "com.github.joschi.jackson" % "jackson-datatype-threetenbp" % "2.6.4" % "compile", + {{/threetenbp}} + "junit" % "junit" % "4.12" % "test", + "com.novocode" % "junit-interface" % "0.10" % "test" + ) + ) diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/pom.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/pom.mustache new file mode 100644 index 00000000000..9a8c6202d93 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client/pom.mustache @@ -0,0 +1,299 @@ + + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + {{artifactUrl}} + {{artifactDescription}} + + {{scmConnection}} + {{scmDeveloperConnection}} + {{scmUrl}} + + + + + {{licenseName}} + {{licenseUrl}} + repo + + + + + + {{developerName}} + {{developerEmail}} + {{developerOrganization}} + {{developerOrganizationUrl}} + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + pertest + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.2 + + + + jar + test-jar + + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.10 + + + add_sources + generate-sources + + add-source + + + + + src/main/java + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + + src/test/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + {{#java8}} + + 1.8 + 1.8 + {{/java8}} + {{^java8}} + + 1.7 + 1.7 + {{/java8}} + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + + + + + sign-artifacts + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + + + + + + io.swagger + swagger-annotations + ${swagger-annotations-version} + + + + com.google.api-client + google-api-client + ${google-api-client-version} + + + + org.glassfish.jersey.core + jersey-common + ${jersey-common-version} + + + + com.fasterxml.jackson.core + jackson-core + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-version} + + {{#withXml}} + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + ${jackson-version} + + {{/withXml}} + {{#java8}} + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson-version} + + {{/java8}} + {{#joda}} + + com.fasterxml.jackson.datatype + jackson-datatype-joda + ${jackson-version} + + + joda-time + joda-time + ${jodatime-version} + + {{/joda}} + {{#threetenbp}} + + com.github.joschi.jackson + jackson-datatype-threetenbp + ${jackson-threetenbp-version} + + {{/threetenbp}} + + + + junit + junit + ${junit-version} + test + + + + UTF-8 + 1.5.15 + 1.23.0 + 2.25.1 + 2.8.9 + {{#joda}} + 2.9.9 + {{/joda}} + {{#threetenbp}} + 2.6.4 + {{/threetenbp}} + 1.0.0 + 4.12 + + diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/ApiClient.mustache index 53fdcb9895d..261402281f8 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/ApiClient.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/ApiClient.mustache @@ -13,8 +13,9 @@ import javax.ws.rs.core.Response.Status; import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.client.ClientProperties; -import org.glassfish.jersey.filter.LoggingFilter; +import org.glassfish.jersey.client.HttpUrlConnectorProvider; import org.glassfish.jersey.jackson.JacksonFeature; +import org.glassfish.jersey.logging.LoggingFeature; import org.glassfish.jersey.media.multipart.FormDataBodyPart; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.MultiPart; @@ -56,21 +57,22 @@ import {{invokerPackage}}.auth.OAuth; {{>generatedAnnotation}} public class ApiClient { - private Map defaultHeaderMap = new HashMap(); - private String basePath = "{{{basePath}}}"; - private boolean debugging = false; - private int connectionTimeout = 0; + protected Map defaultHeaderMap = new HashMap(); + protected String basePath = "{{{basePath}}}"; + protected boolean debugging = false; + protected int connectionTimeout = 0; + private int readTimeout = 0; - private Client httpClient; - private JSON json; - private String tempFolderPath = null; + protected Client httpClient; + protected JSON json; + protected String tempFolderPath = null; - private Map authentications; + protected Map authentications; - private int statusCode; - private Map> responseHeaders; + protected int statusCode; + protected Map> responseHeaders; - private DateFormat dateFormat; + protected DateFormat dateFormat; public ApiClient() { json = new JSON(); @@ -305,6 +307,27 @@ public class ApiClient { return this; } + /** + * read timeout (in milliseconds). + * @return Read timeout + */ + public int getReadTimeout() { + return readTimeout; + } + + /** + * Set the read timeout (in milliseconds). + * A value of 0 means no timeout, otherwise values must be between 1 and + * {@link Integer#MAX_VALUE}. + * @param readTimeout Read timeout in milliseconds + * @return API client + */ + public ApiClient setReadTimeout(int readTimeout) { + this.readTimeout = readTimeout; + httpClient.property(ClientProperties.READ_TIMEOUT, readTimeout); + return this; + } + /** * Get the date format used to parse/format date parameters. * @return Date format @@ -438,12 +461,13 @@ public class ApiClient { * application/json; charset=UTF8 * APPLICATION/JSON * application/vnd.company+json + * "* / *" is also default to JSON * @param mime MIME * @return True if the MIME type is JSON */ public boolean isJsonMime(String mime) { String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"; - return mime != null && (mime.matches(jsonMime) || mime.equalsIgnoreCase("application/json-patch+json")); + return mime != null && (mime.matches(jsonMime) || mime.equals("*/*")); } /** @@ -566,8 +590,6 @@ public class ApiClient { List contentTypes = response.getHeaders().get("Content-Type"); if (contentTypes != null && !contentTypes.isEmpty()) contentType = String.valueOf(contentTypes.get(0)); - if (contentType == null) - throw new ApiException(500, "missing Content-Type in response"); return response.readEntity(returnType); } @@ -634,7 +656,7 @@ public class ApiClient { * * @param Type * @param path The sub-path of the HTTP URL - * @param method The request method, one of "GET", "POST", "PUT", and "DELETE" + * @param method The request method, one of "GET", "POST", "PUT", "HEAD" and "DELETE" * @param queryParams The query parameters * @param body The request body object * @param headerParams The header parameters @@ -694,7 +716,9 @@ public class ApiClient { } else if ("DELETE".equals(method)) { response = invocationBuilder.delete(); } else if ("PATCH".equals(method)) { - response = invocationBuilder.header("X-HTTP-Method-Override", "PATCH").post(entity); + response = invocationBuilder.method("PATCH", entity); + } else if ("HEAD".equals(method)) { + response = invocationBuilder.head(); } else { throw new ApiException(500, "unknown method type " + method); } @@ -740,18 +764,27 @@ public class ApiClient { * @param debugging Debug setting * @return Client */ - private Client buildHttpClient(boolean debugging) { + protected Client buildHttpClient(boolean debugging) { final ClientConfig clientConfig = new ClientConfig(); clientConfig.register(MultiPartFeature.class); clientConfig.register(json); clientConfig.register(JacksonFeature.class); + clientConfig.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true); if (debugging) { - clientConfig.register(new LoggingFilter(java.util.logging.Logger.getLogger(LoggingFilter.class.getName()), true)); + clientConfig.register(new LoggingFeature(java.util.logging.Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME), java.util.logging.Level.INFO, LoggingFeature.Verbosity.PAYLOAD_ANY, 1024*50 /* Log payloads up to 50K */)); + clientConfig.property(LoggingFeature.LOGGING_FEATURE_VERBOSITY, LoggingFeature.Verbosity.PAYLOAD_ANY); + // Set logger to ALL + java.util.logging.Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME).setLevel(java.util.logging.Level.ALL); } + performAdditionalClientConfiguration(clientConfig); return ClientBuilder.newClient(clientConfig); } - private Map> buildResponseHeaders(Response response) { + protected void performAdditionalClientConfiguration(ClientConfig clientConfig) { + // No-op extension point + } + + protected Map> buildResponseHeaders(Response response) { Map> responseHeaders = new HashMap>(); for (Entry> entry: response.getHeaders().entrySet()) { List values = entry.getValue(); @@ -769,7 +802,7 @@ public class ApiClient { * * @param authNames The authentications to apply */ - private void updateParamsForAuth(String[] authNames, List queryParams, Map headerParams) { + protected void updateParamsForAuth(String[] authNames, List queryParams, Map headerParams) { for (String authName : authNames) { Authentication auth = authentications.get(authName); if (auth == null) throw new RuntimeException("Authentication undefined: " + authName); diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/JSON.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/JSON.mustache index 9d5ec1b07ab..911391a6baf 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/JSON.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/JSON.mustache @@ -1,13 +1,19 @@ package {{invokerPackage}}; +{{#threetenbp}} +import org.threeten.bp.*; +{{/threetenbp}} import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.*; {{#java8}} -import com.fasterxml.jackson.datatype.jsr310.*; -{{/java8}} -{{^java8}} -import com.fasterxml.jackson.datatype.joda.*; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; {{/java8}} +{{#joda}} +import com.fasterxml.jackson.datatype.joda.JodaModule; +{{/joda}} +{{#threetenbp}} +import com.fasterxml.jackson.datatype.threetenbp.ThreeTenModule; +{{/threetenbp}} import java.text.DateFormat; @@ -29,9 +35,16 @@ public class JSON implements ContextResolver { {{#java8}} mapper.registerModule(new JavaTimeModule()); {{/java8}} - {{^java8}} + {{#joda}} mapper.registerModule(new JodaModule()); - {{/java8}} + {{/joda}} + {{#threetenbp}} + ThreeTenModule module = new ThreeTenModule(); + module.addDeserializer(Instant.class, CustomInstantDeserializer.INSTANT); + module.addDeserializer(OffsetDateTime.class, CustomInstantDeserializer.OFFSET_DATE_TIME); + module.addDeserializer(ZonedDateTime.class, CustomInstantDeserializer.ZONED_DATE_TIME); + mapper.registerModule(module); + {{/threetenbp}} } /** diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/api.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/api.mustache index 63df83ee24b..dd84a7dbd31 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/api.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/api.mustache @@ -49,7 +49,17 @@ public class {{classname}} { * @return {{returnType}} {{/returnType}} * @throws ApiException if fails to make API call + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} */ + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} public {{#returnType}}{{{returnType}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException { Object {{localVariablePrefix}}localVarPostBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}; {{#allParams}}{{#required}} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/build.gradle.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/build.gradle.mustache index 42db9488d15..8394d1253e4 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/build.gradle.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/build.gradle.mustache @@ -108,9 +108,6 @@ ext { swagger_annotations_version = "1.5.15" jackson_version = "2.8.9" jersey_version = "2.25.1" - {{^java8}} - jodatime_version = "2.9.9" - {{/java8}} {{#supportJava6}} commons_io_version=2.5 commons_lang3_version=3.6 @@ -126,17 +123,21 @@ dependencies { compile "com.fasterxml.jackson.core:jackson-core:$jackson_version" compile "com.fasterxml.jackson.core:jackson-annotations:$jackson_version" compile "com.fasterxml.jackson.core:jackson-databind:$jackson_version" + {{#joda}} + compile "com.fasterxml.jackson.datatype:jackson-datatype-joda:$jackson_version", + {{/joda}} {{#java8}} - compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version" - {{/java8}} - {{^java8}} - compile "com.fasterxml.jackson.datatype:jackson-datatype-joda:$jackson_version" - compile "joda-time:joda-time:$jodatime_version" - compile "com.brsanthu:migbase64:2.2" + compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version", {{/java8}} {{#supportJava6}} compile "commons-io:commons-io:$commons_io_version" compile "org.apache.commons:commons-lang3:$commons_lang3_version" {{/supportJava6}} + {{#threetenbp}} + compile "com.github.joschi.jackson:jackson-datatype-threetenbp:$jackson_version", + {{/threetenbp}} + {{^java8}} + compile "com.brsanthu:migbase64:2.2" + {{/java8}} testCompile "junit:junit:$junit_version" } diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/build.sbt.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/build.sbt.mustache index 22d69f8ba66..ba6f9e36e5e 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/build.sbt.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/build.sbt.mustache @@ -13,17 +13,21 @@ lazy val root = (project in file(".")). "org.glassfish.jersey.core" % "jersey-client" % "2.25.1", "org.glassfish.jersey.media" % "jersey-media-multipart" % "2.25.1", "org.glassfish.jersey.media" % "jersey-media-json-jackson" % "2.25.1", - "com.fasterxml.jackson.core" % "jackson-core" % "2.8.9", - "com.fasterxml.jackson.core" % "jackson-annotations" % "2.8.9", - "com.fasterxml.jackson.core" % "jackson-databind" % "2.8.9", + "com.fasterxml.jackson.core" % "jackson-core" % "{{^threetenbp}}2.8.9{{/threetenbp}}{{#threetenbp}}2.6.4{{/threetenbp}}" % "compile", + "com.fasterxml.jackson.core" % "jackson-annotations" % "{{^threetenbp}}2.8.9{{/threetenbp}}{{#threetenbp}}2.6.4{{/threetenbp}}" % "compile", + "com.fasterxml.jackson.core" % "jackson-databind" % "{{^threetenbp}}2.8.9{{/threetenbp}}{{#threetenbp}}2.6.4{{/threetenbp}}" % "compile", + {{#joda}} + "com.fasterxml.jackson.datatype" % "jackson-datatype-joda" % "2.8.9" % "compile", + {{/joda}} {{#java8}} - "com.fasterxml.jackson.datatype" % "jackson-datatype-jsr310" % "2.8.9", - {{/java8}} - {{^java8}} - "com.fasterxml.jackson.datatype" % "jackson-datatype-joda" % "2.8.9", - "joda-time" % "joda-time" % "2.9.9", + "com.fasterxml.jackson.datatype" % "jackson-datatype-jsr310" % "2.8.9" % "compile", {{/java8}} + {{#threetenbp}} + "com.github.joschi.jackson" % "jackson-datatype-threetenbp" % "2.6.4" % "compile", + {{/threetenbp}} + {{^java8}} "com.brsanthu" % "migbase64" % "2.2", + {{/java8}} {{#supportJava6}} "org.apache.commons" % "commons-lang3" % "3.6", "commons-io" % "commons-io" % "2.5", diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/pom.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/pom.mustache index b3c95a2f2b6..02677980e09 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/jersey2/pom.mustache @@ -1,298 +1,329 @@ - 4.0.0 - {{groupId}} - {{artifactId}} - jar - {{artifactId}} - {{artifactVersion}} - {{artifactUrl}} - {{artifactDescription}} - - {{scmConnection}} - {{scmDeveloperConnection}} - {{scmUrl}} - - - 2.2.0 - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + {{artifactUrl}} + {{artifactDescription}} + + {{scmConnection}} + {{scmDeveloperConnection}} + {{scmUrl}} + - - - {{licenseName}} - {{licenseUrl}} - repo - - + + + {{licenseName}} + {{licenseUrl}} + repo + + - - - {{developerName}} - {{developerEmail}} - {{developerOrganization}} - {{developerOrganizationUrl}} - - + + + {{developerName}} + {{developerEmail}} + {{developerOrganization}} + {{developerOrganizationUrl}} + + - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.12 - - - - loggerPath - conf/log4j.properties - - - -Xms512m -Xmx1500m - methods - pertest - - - - maven-dependency-plugin - - - package - - copy-dependencies - - - ${project.build.directory}/lib - - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 2.6 - - - - jar - test-jar - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - add_sources - generate-sources - - add-source - - - - src/main/java - - - - - add_test_sources - generate-test-sources - - add-test-source - - - - src/test/java - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.6.1 - - {{#java8}} - 1.8 - 1.8 - {{/java8}} - {{^java8}} - 1.7 - 1.7 - {{/java8}} - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.4 - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - - - - - sign-artifacts - + - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - - sign-artifacts - verify - - sign - - - - - - - - - - - - io.swagger - swagger-annotations - ${swagger-core-version} - + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + pertest + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + - - - org.glassfish.jersey.core - jersey-client - ${jersey-version} - - - org.glassfish.jersey.media - jersey-media-multipart - ${jersey-version} - - - org.glassfish.jersey.media - jersey-media-json-jackson - ${jersey-version} - + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + + jar + test-jar + + + + + + - - - com.fasterxml.jackson.core - jackson-core - ${jackson-version} - - - com.fasterxml.jackson.core - jackson-annotations - ${jackson-version} - - - com.fasterxml.jackson.core - jackson-databind - ${jackson-version} - - {{#withXml}} + + org.codehaus.mojo + build-helper-maven-plugin + + + add_sources + generate-sources + + add-source + + + + + src/main/java + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + + src/test/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + {{#java8}} + + 1.8 + 1.8 + {{/java8}} + {{^java8}} + + 1.7 + 1.7 + {{/java8}} + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + - - - org.glassfish.jersey.media - jersey-media-jaxb - ${jersey-version} - + + + sign-artifacts + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + + - {{/withXml}} - {{#java8}} - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - ${jackson-version} - - {{/java8}} - {{^java8}} - - com.fasterxml.jackson.datatype - jackson-datatype-joda - ${jackson-version} - - - joda-time - joda-time - ${jodatime-version} - + + + io.swagger + swagger-annotations + ${swagger-core-version} + - - - com.brsanthu - migbase64 - 2.2 - - {{/java8}} + + + org.glassfish.jersey.core + jersey-client + ${jersey-version} + + + org.glassfish.jersey.media + jersey-media-multipart + ${jersey-version} + + + org.glassfish.jersey.media + jersey-media-json-jackson + ${jersey-version} + - {{#supportJava6}} - - org.apache.commons - commons-lang3 - ${commons_lang3_version} - + + + com.fasterxml.jackson.core + jackson-core + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-version} + + {{#withXml}} - - commons-io - commons-io - ${commons_io_version} - - {{/supportJava6}} + + + org.glassfish.jersey.media + jersey-media-jaxb + ${jersey-version} + - - - junit - junit - ${junit-version} - test - - - - 1.5.15 - 2.25.1 - 2.8.9 - {{^java8}} - 2.9.9 - {{/java8}} - {{#supportJava6}} - 2.5 - 3.6 - {{/supportJava6}} - 1.0.0 - 4.12 - + {{/withXml}} + {{#joda}} + + com.fasterxml.jackson.datatype + jackson-datatype-joda + ${jackson-version} + + {{/joda}} + {{#java8}} + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson-version} + + {{/java8}} + {{#threetenbp}} + + com.github.joschi.jackson + jackson-datatype-threetenbp + ${jackson-version} + + {{/threetenbp}} + {{^java8}} + + + com.brsanthu + migbase64 + 2.2 + + {{/java8}} + {{#supportJava6}} + + org.apache.commons + commons-lang3 + ${commons_lang3_version} + + + commons-io + commons-io + ${commons_io_version} + + {{/supportJava6}} + {{#useBeanValidation}} + + + javax.validation + validation-api + 1.1.0.Final + provided + + {{/useBeanValidation}} + + + junit + junit + ${junit-version} + test + + + + UTF-8 + 1.5.15 + {{^supportJava6}} + 2.25.1 + {{/supportJava6}} + {{#supportJava6}} + 2.6 + 2.5 + 3.6 + {{/supportJava6}} + {{^threetenbp}}2.7.5{{/threetenbp}}{{#threetenbp}}2.6.4{{/threetenbp}} + 1.0.0 + 4.12 + diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache index b3cb0221ac6..0ae00335726 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache @@ -2,43 +2,31 @@ package {{invokerPackage}}; -import com.squareup.okhttp.Call; -import com.squareup.okhttp.Callback; -import com.squareup.okhttp.OkHttpClient; -import com.squareup.okhttp.Request; -import com.squareup.okhttp.Response; -import com.squareup.okhttp.RequestBody; -import com.squareup.okhttp.FormEncodingBuilder; -import com.squareup.okhttp.MultipartBuilder; -import com.squareup.okhttp.MediaType; -import com.squareup.okhttp.Headers; +import com.squareup.okhttp.*; import com.squareup.okhttp.internal.http.HttpMethod; import com.squareup.okhttp.logging.HttpLoggingInterceptor; import com.squareup.okhttp.logging.HttpLoggingInterceptor.Level; - -import java.lang.reflect.Type; - -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Map.Entry; -import java.util.HashMap; -import java.util.List; -import java.util.ArrayList; -import java.util.Date; -import java.util.TimeZone; -import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import java.net.URLEncoder; -import java.net.URLConnection; - +import okio.BufferedSink; +import okio.Okio; +{{#joda}} +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormatter; +{{/joda}} +{{#threetenbp}} +import org.threeten.bp.LocalDate; +import org.threeten.bp.OffsetDateTime; +import org.threeten.bp.format.DateTimeFormatter; +{{/threetenbp}} + +import javax.net.ssl.*; import java.io.File; -import java.io.InputStream; import java.io.IOException; +import java.io.InputStream; import java.io.UnsupportedEncodingException; - +import java.lang.reflect.Type; +import java.net.URLConnection; +import java.net.URLEncoder; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.SecureRandom; @@ -46,22 +34,17 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; - import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.text.ParseException; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; - -import okio.BufferedSink; -import okio.Okio; +{{#java8}} +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +{{/java8}} +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import {{invokerPackage}}.auth.Authentication; import {{invokerPackage}}.auth.HttpBasicAuth; @@ -69,40 +52,8 @@ import {{invokerPackage}}.auth.ApiKeyAuth; import {{invokerPackage}}.auth.OAuth; public class ApiClient { - public static final double JAVA_VERSION; - public static final boolean IS_ANDROID; - public static final int ANDROID_SDK_VERSION; - - static { - JAVA_VERSION = Double.parseDouble(System.getProperty("java.specification.version")); - boolean isAndroid; - try { - Class.forName("android.app.Activity"); - isAndroid = true; - } catch (ClassNotFoundException e) { - isAndroid = false; - } - IS_ANDROID = isAndroid; - int sdkVersion = 0; - if (IS_ANDROID) { - try { - sdkVersion = Class.forName("android.os.Build$VERSION").getField("SDK_INT").getInt(null); - } catch (Exception e) { - try { - sdkVersion = Integer.parseInt((String) Class.forName("android.os.Build$VERSION").getField("SDK").get(null)); - } catch (Exception e2) { } - } - } - ANDROID_SDK_VERSION = sdkVersion; - } - - /** - * The datetime format to be used when lenientDatetimeFormat is enabled. - */ - public static final String LENIENT_DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; private String basePath = "{{{basePath}}}"; - private boolean lenientOnJson = false; private boolean debugging = false; private Map defaultHeaderMap = new HashMap(); private String tempFolderPath = null; @@ -136,20 +87,7 @@ public class ApiClient { verifyingSsl = true; - json = new JSON(this); - - /* - * Use RFC3339 format for date and datetime. - * See http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14 - */ - this.dateFormat = new SimpleDateFormat("yyyy-MM-dd"); - // Always use UTC as the default time zone when dealing with date (without time). - this.dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - initDatetimeFormat(); - - // Be lenient on datetime formats when parsing datetime from string. - // See parseDatetime. - this.lenientDatetimeFormat = true; + json = new JSON(); // Set default User-Agent. setUserAgent("{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{{artifactVersion}}}/java{{/httpUserAgent}}"); @@ -290,138 +228,42 @@ public class ApiClient { } public ApiClient setDateFormat(DateFormat dateFormat) { - this.dateFormat = dateFormat; - this.dateLength = this.dateFormat.format(new Date()).length(); + this.json.setDateFormat(dateFormat); return this; } - public DateFormat getDatetimeFormat() { - return datetimeFormat; - } - - public ApiClient setDatetimeFormat(DateFormat datetimeFormat) { - this.datetimeFormat = datetimeFormat; + public ApiClient setSqlDateFormat(DateFormat dateFormat) { + this.json.setSqlDateFormat(dateFormat); return this; } - /** - * Whether to allow various ISO 8601 datetime formats when parsing a datetime string. - * @see #parseDatetime(String) - * @return True if lenientDatetimeFormat flag is set to true - */ - public boolean isLenientDatetimeFormat() { - return lenientDatetimeFormat; - } - - public ApiClient setLenientDatetimeFormat(boolean lenientDatetimeFormat) { - this.lenientDatetimeFormat = lenientDatetimeFormat; + {{#joda}} + public ApiClient setDateTimeFormat(DateTimeFormatter dateFormat) { + this.json.setDateTimeFormat(dateFormat); return this; } - /** - * Parse the given date string into Date object. - * The default dateFormat supports these ISO 8601 date formats: - * 2015-08-16 - * 2015-8-16 - * @param str String to be parsed - * @return Date - */ - public Date parseDate(String str) { - if (str == null) - return null; - try { - return dateFormat.parse(str); - } catch (ParseException e) { - throw new RuntimeException(e); - } - } - - /** - * Parse the given datetime string into Date object. - * When lenientDatetimeFormat is enabled, the following ISO 8601 datetime formats are supported: - * 2015-08-16T08:20:05Z - * 2015-8-16T8:20:05Z - * 2015-08-16T08:20:05+00:00 - * 2015-08-16T08:20:05+0000 - * 2015-08-16T08:20:05.376Z - * 2015-08-16T08:20:05.376+00:00 - * 2015-08-16T08:20:05.376+00 - * Note: The 3-digit milli-seconds is optional. Time zone is required and can be in one of - * these formats: - * Z (same with +0000) - * +08:00 (same with +0800) - * -02 (same with -0200) - * -0200 - * @see ISO 8601 - * @param str Date time string to be parsed - * @return Date representation of the string - */ - public Date parseDatetime(String str) { - if (str == null) - return null; - - DateFormat format; - if (lenientDatetimeFormat) { - /* - * When lenientDatetimeFormat is enabled, normalize the date string - * into LENIENT_DATETIME_FORMAT to support various formats - * defined by ISO 8601. - */ - // normalize time zone - // trailing "Z": 2015-08-16T08:20:05Z => 2015-08-16T08:20:05+0000 - str = str.replaceAll("[zZ]\\z", "+0000"); - // remove colon in time zone: 2015-08-16T08:20:05+00:00 => 2015-08-16T08:20:05+0000 - str = str.replaceAll("([+-]\\d{2}):(\\d{2})\\z", "$1$2"); - // expand time zone: 2015-08-16T08:20:05+00 => 2015-08-16T08:20:05+0000 - str = str.replaceAll("([+-]\\d{2})\\z", "$100"); - // add milliseconds when missing - // 2015-08-16T08:20:05+0000 => 2015-08-16T08:20:05.000+0000 - str = str.replaceAll("(:\\d{1,2})([+-]\\d{4})\\z", "$1.000$2"); - format = new SimpleDateFormat(LENIENT_DATETIME_FORMAT); - } else { - format = this.datetimeFormat; - } - - try { - return format.parse(str); - } catch (ParseException e) { - throw new RuntimeException(e); - } + public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) { + this.json.setLocalDateFormat(dateFormat); + return this; } - /* - * Parse date or date time in string format into Date object. - * - * @param str Date time string to be parsed - * @return Date representation of the string - */ - public Date parseDateOrDatetime(String str) { - if (str == null) - return null; - else if (str.length() <= dateLength) - return parseDate(str); - else - return parseDatetime(str); + {{/joda}} + {{#jsr310}} + public ApiClient setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { + this.json.setOffsetDateTimeFormat(dateFormat); + return this; } - /** - * Format the given Date object into string (Date format). - * - * @param date Date object - * @return Formatted date in string representation - */ - public String formatDate(Date date) { - return dateFormat.format(date); + public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) { + this.json.setLocalDateFormat(dateFormat); + return this; } - /** - * Format the given Date object into string (Datetime format). - * - * @param date Date object - * @return Formatted datetime in string representation - */ - public String formatDatetime(Date date) { - return datetimeFormat.format(date); + {{/jsr310}} + public ApiClient setLenientOnJson(boolean lenientOnJson) { + this.json.setLenientOnJson(lenientOnJson); + return this; } /** @@ -541,26 +383,6 @@ public class ApiClient { return this; } - /** - * @see setLenient - * - * @return True if lenientOnJson is enabled, false otherwise. - */ - public boolean isLenientOnJson() { - return lenientOnJson; - } - - /** - * Set LenientOnJson - * - * @param lenient True to enable lenientOnJson - * @return ApiClient - */ - public ApiClient setLenientOnJson(boolean lenient) { - this.lenientOnJson = lenient; - return this; - } - /** * Check that whether debugging is enabled for this API client. * @@ -604,7 +426,7 @@ public class ApiClient { } /** - * Set the tempoaray folder path (for downloading files) + * Set the temporary folder path (for downloading files) * * @param tempFolderPath Temporary folder path * @return ApiClient @@ -626,6 +448,7 @@ public class ApiClient { /** * Sets the connect timeout (in milliseconds). * A value of 0 means no timeout, otherwise values must be between 1 and + * {@link Integer#MAX_VALUE}. * * @param connectionTimeout connection timeout in milliseconds * @return Api client @@ -635,6 +458,50 @@ public class ApiClient { return this; } + /** + * Get read timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getReadTimeout() { + return httpClient.getReadTimeout(); + } + + /** + * Sets the read timeout (in milliseconds). + * A value of 0 means no timeout, otherwise values must be between 1 and + * {@link Integer#MAX_VALUE}. + * + * @param readTimeout read timeout in milliseconds + * @return Api client + */ + public ApiClient setReadTimeout(int readTimeout) { + httpClient.setReadTimeout(readTimeout, TimeUnit.MILLISECONDS); + return this; + } + + /** + * Get write timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getWriteTimeout() { + return httpClient.getWriteTimeout(); + } + + /** + * Sets the write timeout (in milliseconds). + * A value of 0 means no timeout, otherwise values must be between 1 and + * {@link Integer#MAX_VALUE}. + * + * @param writeTimeout connection timeout in milliseconds + * @return Api client + */ + public ApiClient setWriteTimeout(int writeTimeout) { + httpClient.setWriteTimeout(writeTimeout, TimeUnit.MILLISECONDS); + return this; + } + /** * Format the given parameter object into string. * @@ -644,8 +511,10 @@ public class ApiClient { public String parameterToString(Object param) { if (param == null) { return ""; - } else if (param instanceof Date) { - return formatDatetime((Date) param); + } else if (param instanceof Date {{#joda}}|| param instanceof DateTime || param instanceof LocalDate{{/joda}}{{#jsr310}}|| param instanceof OffsetDateTime || param instanceof LocalDate{{/jsr310}}) { + //Serialize to json string and remove the " enclosing characters + String jsonStr = json.serialize(param); + return jsonStr.substring(1, jsonStr.length() - 1); } else if (param instanceof Collection) { StringBuilder b = new StringBuilder(); for (Object o : (Collection)param) { @@ -661,62 +530,70 @@ public class ApiClient { } /** - * Format to {@code Pair} objects. + * Formats the specified query parameter to a list containing a single {@code Pair} object. * - * @param collectionFormat collection format (e.g. csv, tsv) - * @param name Name - * @param value Value - * @return A list of Pair objects + * Note that {@code value} must not be a collection. + * + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list containing a single {@code Pair} object. */ - public List parameterToPairs(String collectionFormat, String name, Object value){ + public List parameterToPair(String name, Object value) { List params = new ArrayList(); // preconditions - if (name == null || name.isEmpty() || value == null) return params; + if (name == null || name.isEmpty() || value == null || value instanceof Collection) return params; - Collection valueCollection = null; - if (value instanceof Collection) { - valueCollection = (Collection) value; - } else { - params.add(new Pair(name, parameterToString(value))); - return params; - } + params.add(new Pair(name, parameterToString(value))); + return params; + } - if (valueCollection.isEmpty()){ + /** + * Formats the specified collection query parameters to a list of {@code Pair} objects. + * + * Note that the values of each of the returned Pair objects are percent-encoded. + * + * @param collectionFormat The collection format of the parameter. + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list of {@code Pair} objects. + */ + public List parameterToPairs(String collectionFormat, String name, Collection value) { + List params = new ArrayList(); + + // preconditions + if (name == null || name.isEmpty() || value == null || value.isEmpty()) { return params; } - // get the collection format - collectionFormat = (collectionFormat == null || collectionFormat.isEmpty() ? "csv" : collectionFormat); // default: csv - // create the params based on the collection format - if (collectionFormat.equals("multi")) { - for (Object item : valueCollection) { - params.add(new Pair(name, parameterToString(item))); + if ("multi".equals(collectionFormat)) { + for (Object item : value) { + params.add(new Pair(name, escapeString(parameterToString(item)))); } - return params; } + // collectionFormat is assumed to be "csv" by default String delimiter = ","; - if (collectionFormat.equals("csv")) { - delimiter = ","; - } else if (collectionFormat.equals("ssv")) { - delimiter = " "; - } else if (collectionFormat.equals("tsv")) { - delimiter = "\t"; - } else if (collectionFormat.equals("pipes")) { - delimiter = "|"; + // escape all delimiters except commas, which are URI reserved + // characters + if ("ssv".equals(collectionFormat)) { + delimiter = escapeString(" "); + } else if ("tsv".equals(collectionFormat)) { + delimiter = escapeString("\t"); + } else if ("pipes".equals(collectionFormat)) { + delimiter = escapeString("|"); } StringBuilder sb = new StringBuilder() ; - for (Object item : valueCollection) { + for (Object item : value) { sb.append(delimiter); - sb.append(parameterToString(item)); + sb.append(escapeString(parameterToString(item))); } - params.add(new Pair(name, sb.substring(1))); + params.add(new Pair(name, sb.substring(delimiter.length()))); return params; } @@ -739,12 +616,13 @@ public class ApiClient { * application/json; charset=UTF8 * APPLICATION/JSON * application/vnd.company+json + * "* / *" is also default to JSON * @param mime MIME (Multipurpose Internet Mail Extensions) * @return True if the given MIME is JSON, false otherwise. */ public boolean isJsonMime(String mime) { String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"; - return mime != null && (mime.matches(jsonMime) || mime.equalsIgnoreCase("application/json-patch+json")); + return mime != null && (mime.matches(jsonMime) || mime.equals("*/*")); } /** @@ -775,11 +653,11 @@ public class ApiClient { * * @param contentTypes The Content-Type array to select from * @return The Content-Type header to use. If the given array is empty, - * JSON will be used. + * or matches "any", JSON will be used. */ public String selectHeaderContentType(String[] contentTypes) { - if (contentTypes.length == 0) { - return "application/json"; + if (contentTypes.length == 0 || contentTypes[0].equals("*/*")) { + return "application/json"; } for (String contentType : contentTypes) { if (isJsonMime(contentType)) { @@ -1076,6 +954,7 @@ public class ApiClient { * @param path The sub-path of the HTTP URL * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters * @param body The request body object * @param headerParams The header parameters * @param formParams The form parameters @@ -1084,8 +963,8 @@ public class ApiClient { * @return The HTTP call * @throws ApiException If fail to serialize the request body object */ - public Call buildCall(String path, String method, List queryParams, Object body, Map headerParams, Map formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { - Request request = buildRequest(path, method, queryParams, body, headerParams, formParams, authNames, progressRequestListener); + public Call buildCall(String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + Request request = buildRequest(path, method, queryParams, collectionQueryParams, body, headerParams, formParams, authNames, progressRequestListener); return httpClient.newCall(request); } @@ -1096,6 +975,7 @@ public class ApiClient { * @param path The sub-path of the HTTP URL * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters * @param body The request body object * @param headerParams The header parameters * @param formParams The form parameters @@ -1104,10 +984,10 @@ public class ApiClient { * @return The HTTP request * @throws ApiException If fail to serialize the request body object */ - public Request buildRequest(String path, String method, List queryParams, Object body, Map headerParams, Map formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + public Request buildRequest(String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { updateParamsForAuth(authNames, queryParams, headerParams); - final String url = buildUrl(path, queryParams); + final String url = buildUrl(path, queryParams, collectionQueryParams); final Request.Builder reqBuilder = new Request.Builder().url(url); processHeaderParams(headerParams, reqBuilder); @@ -1153,9 +1033,10 @@ public class ApiClient { * * @param path The sub path * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters * @return The full URL */ - public String buildUrl(String path, List queryParams) { + public String buildUrl(String path, List queryParams, List collectionQueryParams) { final StringBuilder url = new StringBuilder(); url.append(basePath).append(path); @@ -1176,6 +1057,23 @@ public class ApiClient { } } + if (collectionQueryParams != null && !collectionQueryParams.isEmpty()) { + String prefix = url.toString().contains("?") ? "&" : "?"; + for (Pair param : collectionQueryParams) { + if (param.getValue() != null) { + if (prefix != null) { + url.append(prefix); + prefix = null; + } else { + url.append("&"); + } + String value = parameterToString(param.getValue()); + // collection query parameter value already escaped as part of parameterToPairs + url.append(escapeString(param.getName())).append("=").append(value); + } + } + } + return url.toString(); } @@ -1263,31 +1161,6 @@ public class ApiClient { } } - /** - * Initialize datetime format according to the current environment, e.g. Java 1.7 and Android. - */ - private void initDatetimeFormat() { - String formatWithTimeZone = null; - if (IS_ANDROID) { - if (ANDROID_SDK_VERSION >= 18) { - // The time zone format "ZZZZZ" is available since Android 4.3 (SDK version 18) - formatWithTimeZone = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"; - } - } else if (JAVA_VERSION >= 1.7) { - // The time zone format "XXX" is available since Java 1.7 - formatWithTimeZone = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"; - } - if (formatWithTimeZone != null) { - this.datetimeFormat = new SimpleDateFormat(formatWithTimeZone); - // NOTE: Use the system's default time zone (mainly for datetime formatting). - } else { - // Use a common format that works across all systems. - this.datetimeFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - // Always use the UTC time zone as we are using a constant trailing "Z" here. - this.datetimeFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - } - } - /** * Apply SSL related settings to httpClient according to the current values of * verifyingSsl and sslCaCert. diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/JSON.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/JSON.mustache index 10a377ebb5a..34ae1795cf8 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/JSON.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/JSON.mustache @@ -4,55 +4,107 @@ package {{invokerPackage}}; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonNull; import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; import com.google.gson.TypeAdapter; +import com.google.gson.internal.bind.util.ISO8601Utils; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; - -import java.io.IOException; -import java.io.StringReader; -import java.lang.reflect.Type; -import java.util.Date; - -{{^java8}} +import com.google.gson.JsonElement; +import io.gsonfire.GsonFireBuilder; +import io.gsonfire.TypeSelector; +{{#joda}} import org.joda.time.DateTime; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.DateTimeFormatterBuilder; import org.joda.time.format.ISODateTimeFormat; -{{/java8}} +{{/joda}} +{{#threetenbp}} +import org.threeten.bp.LocalDate; +import org.threeten.bp.OffsetDateTime; +import org.threeten.bp.format.DateTimeFormatter; +{{/threetenbp}} + +import {{modelPackage}}.*; + +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Type; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.ParsePosition; {{#java8}} import java.time.LocalDate; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; {{/java8}} +import java.util.Date; +import java.util.Map; +import java.util.HashMap; public class JSON { - private ApiClient apiClient; private Gson gson; + private boolean isLenientOnJson = false; + private DateTypeAdapter dateTypeAdapter = new DateTypeAdapter(); + private SqlDateTypeAdapter sqlDateTypeAdapter = new SqlDateTypeAdapter(); + {{#joda}} + private DateTimeTypeAdapter dateTimeTypeAdapter = new DateTimeTypeAdapter(); + private LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter(); + {{/joda}} + {{#jsr310}} + private OffsetDateTimeTypeAdapter offsetDateTimeTypeAdapter = new OffsetDateTimeTypeAdapter(); + private LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter(); + {{/jsr310}} - /** - * JSON constructor. - * - * @param apiClient An instance of ApiClient - */ - public JSON(ApiClient apiClient) { - this.apiClient = apiClient; - gson = new GsonBuilder() - .registerTypeAdapter(Date.class, new DateAdapter(apiClient)) - {{^java8}} - .registerTypeAdapter(DateTime.class, new DateTimeTypeAdapter()) - {{/java8}} - {{#java8}} - .registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeTypeAdapter()) - {{/java8}} - .registerTypeAdapter(LocalDate.class, new LocalDateTypeAdapter()) + public static GsonBuilder createGson() { + GsonFireBuilder fireBuilder = new GsonFireBuilder() + {{#parent}} + .registerTypeSelector({{classname}}.class, new TypeSelector() { + @Override + public Class getClassForElement(JsonElement readElement) { + Map classByDiscriminatorValue = new HashMap(); + {{#children}} + classByDiscriminatorValue.put("{{name}}".toUpperCase(), {{classname}}.class); + {{/children}} + classByDiscriminatorValue.put("{{classname}}".toUpperCase(), {{classname}}.class); + return getClassByDiscriminator( + classByDiscriminatorValue, + getDiscriminatorValue(readElement, "{{discriminator}}")); + } + }) + {{/parent}} + ; + return fireBuilder.createGsonBuilder(); + } + + private static String getDiscriminatorValue(JsonElement readElement, String discriminatorField) { + JsonElement element = readElement.getAsJsonObject().get(discriminatorField); + if(null == element) { + throw new IllegalArgumentException("missing discriminator field: <" + discriminatorField + ">"); + } + return element.getAsString(); + } + + private static Class getClassByDiscriminator(Map classByDiscriminatorValue, String discriminatorValue) { + Class clazz = (Class) classByDiscriminatorValue.get(discriminatorValue.toUpperCase()); + if(null == clazz) { + throw new IllegalArgumentException("cannot determine model class of name: <" + discriminatorValue + ">"); + } + return clazz; + } + + public JSON() { + gson = createGson() + .registerTypeAdapter(Date.class, dateTypeAdapter) + .registerTypeAdapter(java.sql.Date.class, sqlDateTypeAdapter) + {{#joda}} + .registerTypeAdapter(DateTime.class, dateTimeTypeAdapter) + .registerTypeAdapter(LocalDate.class, localDateTypeAdapter) + {{/joda}} + {{#jsr310}} + .registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter) + .registerTypeAdapter(LocalDate.class, localDateTypeAdapter) + {{/jsr310}} .create(); } @@ -69,9 +121,16 @@ public class JSON { * Set Gson. * * @param gson Gson + * @return JSON */ - public void setGson(Gson gson) { + public JSON setGson(Gson gson) { this.gson = gson; + return this; + } + + public JSON setLenientOnJson(boolean lenientOnJson) { + isLenientOnJson = lenientOnJson; + return this; } /** @@ -87,15 +146,15 @@ public class JSON { /** * Deserialize the given JSON string to Java object. * - * @param Type - * @param body The JSON string + * @param Type + * @param body The JSON string * @param returnType The type to deserialize into * @return The deserialized Java object */ @SuppressWarnings("unchecked") public T deserialize(String body, Type returnType) { try { - if (apiClient.isLenientOnJson()) { + if (isLenientOnJson) { JsonReader jsonReader = new JsonReader(new StringReader(body)); // see https://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/stream/JsonReader.html#setLenient(boolean) jsonReader.setLenient(true); @@ -105,187 +164,327 @@ public class JSON { } } catch (JsonParseException e) { // Fallback processing when failed to parse JSON form response body: - // return the response body string directly for the String return type; - // parse response body into date or datetime for the Date return type. + // return the response body string directly for the String return type; if (returnType.equals(String.class)) return (T) body; - else if (returnType.equals(Date.class)) - return (T) apiClient.parseDateOrDatetime(body); - else throw(e); + else throw (e); } } -} - -class DateAdapter implements JsonSerializer, JsonDeserializer { - private final ApiClient apiClient; + {{#joda}} /** - * Constructor for DateAdapter - * - * @param apiClient Api client + * Gson TypeAdapter for Joda DateTime type */ - public DateAdapter(ApiClient apiClient) { - super(); - this.apiClient = apiClient; + public static class DateTimeTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public DateTimeTypeAdapter() { + this(new DateTimeFormatterBuilder() + .append(ISODateTimeFormat.dateTime().getPrinter(), ISODateTimeFormat.dateOptionalTimeParser().getParser()) + .toFormatter()); + } + + public DateTimeTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, DateTime date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.print(date)); + } + } + + @Override + public DateTime read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + return formatter.parseDateTime(date); + } + } } /** - * Serialize - * - * @param src Date - * @param typeOfSrc Type - * @param context Json Serialization Context - * @return Json Element + * Gson TypeAdapter for Joda LocalDate type */ - @Override - public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) { - if (src == null) { - return JsonNull.INSTANCE; - } else { - return new JsonPrimitive(apiClient.formatDatetime(src)); + public class LocalDateTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public LocalDateTypeAdapter() { + this(ISODateTimeFormat.date()); } + + public LocalDateTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, LocalDate date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.print(date)); + } + } + + @Override + public LocalDate read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + return formatter.parseLocalDate(date); + } + } + } + + public JSON setDateTimeFormat(DateTimeFormatter dateFormat) { + dateTimeTypeAdapter.setFormat(dateFormat); + return this; + } + + public JSON setLocalDateFormat(DateTimeFormatter dateFormat) { + localDateTypeAdapter.setFormat(dateFormat); + return this; } + {{/joda}} + {{#jsr310}} /** - * Deserialize - * - * @param json Json element - * @param date Type - * @param context Json Serialization Context - * @return Date - * @throws JsonParseException if fail to parse + * Gson TypeAdapter for JSR310 OffsetDateTime type */ - @Override - public Date deserialize(JsonElement json, Type date, JsonDeserializationContext context) throws JsonParseException { - String str = json.getAsJsonPrimitive().getAsString(); - try { - return apiClient.parseDateOrDatetime(str); - } catch (RuntimeException e) { - throw new JsonParseException(e); + public static class OffsetDateTimeTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public OffsetDateTimeTypeAdapter() { + this(DateTimeFormatter.ISO_OFFSET_DATE_TIME); } - } -} -{{^java8}} -/** - * Gson TypeAdapter for Joda DateTime type - */ -class DateTimeTypeAdapter extends TypeAdapter { + public OffsetDateTimeTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } - private final DateTimeFormatter parseFormatter = ISODateTimeFormat.dateOptionalTimeParser(); - private final DateTimeFormatter printFormatter = ISODateTimeFormat.dateTime(); + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } - @Override - public void write(JsonWriter out, DateTime date) throws IOException { - if (date == null) { - out.nullValue(); - } else { - out.value(printFormatter.print(date)); + @Override + public void write(JsonWriter out, OffsetDateTime date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.format(date)); + } } - } - @Override - public DateTime read(JsonReader in) throws IOException { - switch (in.peek()) { - case NULL: - in.nextNull(); - return null; - default: - String date = in.nextString(); - return parseFormatter.parseDateTime(date); + @Override + public OffsetDateTime read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + if (date.endsWith("+0000")) { + date = date.substring(0, date.length()-5) + "Z"; + } + return OffsetDateTime.parse(date, formatter); + } } } -} -/** - * Gson TypeAdapter for Joda LocalDate type - */ -class LocalDateTypeAdapter extends TypeAdapter { + /** + * Gson TypeAdapter for JSR310 LocalDate type + */ + public class LocalDateTypeAdapter extends TypeAdapter { - private final DateTimeFormatter formatter = ISODateTimeFormat.date(); + private DateTimeFormatter formatter; - @Override - public void write(JsonWriter out, LocalDate date) throws IOException { - if (date == null) { - out.nullValue(); - } else { - out.value(formatter.print(date)); + public LocalDateTypeAdapter() { + this(DateTimeFormatter.ISO_LOCAL_DATE); } - } - @Override - public LocalDate read(JsonReader in) throws IOException { - switch (in.peek()) { - case NULL: - in.nextNull(); - return null; - default: - String date = in.nextString(); - return formatter.parseLocalDate(date); + public LocalDateTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; } - } -} -{{/java8}} -{{#java8}} -/** - * Gson TypeAdapter for jsr310 OffsetDateTime type - */ -class OffsetDateTimeTypeAdapter extends TypeAdapter { - private final DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME; + @Override + public void write(JsonWriter out, LocalDate date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.format(date)); + } + } - @Override - public void write(JsonWriter out, OffsetDateTime date) throws IOException { - if (date == null) { - out.nullValue(); - } else { - out.value(formatter.format(date)); + @Override + public LocalDate read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + return LocalDate.parse(date, formatter); + } } } - @Override - public OffsetDateTime read(JsonReader in) throws IOException { - switch (in.peek()) { - case NULL: - in.nextNull(); - return null; - default: - String date = in.nextString(); - if (date.endsWith("+0000")) { - date = date.substring(0, date.length()-5) + "Z"; + public JSON setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { + offsetDateTimeTypeAdapter.setFormat(dateFormat); + return this; + } + + public JSON setLocalDateFormat(DateTimeFormatter dateFormat) { + localDateTypeAdapter.setFormat(dateFormat); + return this; + } + + {{/jsr310}} + /** + * Gson TypeAdapter for java.sql.Date type + * If the dateFormat is null, a simple "yyyy-MM-dd" format will be used + * (more efficient than SimpleDateFormat). + */ + public static class SqlDateTypeAdapter extends TypeAdapter { + + private DateFormat dateFormat; + + public SqlDateTypeAdapter() { + } + + public SqlDateTypeAdapter(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + public void setFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + @Override + public void write(JsonWriter out, java.sql.Date date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + String value; + if (dateFormat != null) { + value = dateFormat.format(date); + } else { + value = date.toString(); } + out.value(value); + } + } - return OffsetDateTime.parse(date, formatter); + @Override + public java.sql.Date read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + try { + if (dateFormat != null) { + return new java.sql.Date(dateFormat.parse(date).getTime()); + } + return new java.sql.Date(ISO8601Utils.parse(date, new ParsePosition(0)).getTime()); + } catch (ParseException e) { + throw new JsonParseException(e); + } + } } } -} -/** - * Gson TypeAdapter for jsr310 LocalDate type - */ -class LocalDateTypeAdapter extends TypeAdapter { + /** + * Gson TypeAdapter for java.util.Date type + * If the dateFormat is null, ISO8601Utils will be used. + */ + public static class DateTypeAdapter extends TypeAdapter { - private final DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE; + private DateFormat dateFormat; - @Override - public void write(JsonWriter out, LocalDate date) throws IOException { - if (date == null) { - out.nullValue(); - } else { - out.value(formatter.format(date)); + public DateTypeAdapter() { + } + + public DateTypeAdapter(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + public void setFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + @Override + public void write(JsonWriter out, Date date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + String value; + if (dateFormat != null) { + value = dateFormat.format(date); + } else { + value = ISO8601Utils.format(date, true); + } + out.value(value); + } } - } - @Override - public LocalDate read(JsonReader in) throws IOException { - switch (in.peek()) { - case NULL: - in.nextNull(); - return null; - default: - String date = in.nextString(); - return LocalDate.parse(date, formatter); + @Override + public Date read(JsonReader in) throws IOException { + try { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + try { + if (dateFormat != null) { + return dateFormat.parse(date); + } + return ISO8601Utils.parse(date, new ParsePosition(0)); + } catch (ParseException e) { + throw new JsonParseException(e); + } + } + } catch (IllegalArgumentException e) { + throw new JsonParseException(e); + } } } + + public JSON setDateFormat(DateFormat dateFormat) { + dateTypeAdapter.setFormat(dateFormat); + return this; + } + + public JSON setSqlDateFormat(DateFormat dateFormat) { + sqlDateTypeAdapter.setFormat(dateFormat); + return this; + } + } -{{/java8}} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/api.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/api.mustache index 49f9ea0a6a6..ffc68bc7b5a 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/api.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/api.mustache @@ -70,17 +70,28 @@ public class {{classname}} { * @param progressRequestListener Progress request listener * @return Call to execute * @throws ApiException If fail to serialize the request body object + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} */ + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} public com.squareup.okhttp.Call {{operationId}}Call({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { Object {{localVariablePrefix}}localVarPostBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}; - + // create path and map variables String {{localVariablePrefix}}localVarPath = "{{{path}}}"{{#pathParams}} .replaceAll("\\{" + "{{baseName}}" + "\\}", {{localVariablePrefix}}apiClient.escapeString({{{paramName}}}.toString())){{/pathParams}}; - {{javaUtilPrefix}}List {{localVariablePrefix}}localVarQueryParams = new {{javaUtilPrefix}}ArrayList();{{#queryParams}} + {{javaUtilPrefix}}List {{localVariablePrefix}}localVarQueryParams = new {{javaUtilPrefix}}ArrayList(); + {{javaUtilPrefix}}List {{localVariablePrefix}}localVarCollectionQueryParams = new {{javaUtilPrefix}}ArrayList();{{#queryParams}} if ({{paramName}} != null) - {{localVariablePrefix}}localVarQueryParams.addAll({{localVariablePrefix}}apiClient.parameterToPairs("{{#collectionFormat}}{{{collectionFormat}}}{{/collectionFormat}}", "{{baseName}}", {{paramName}}));{{/queryParams}} + {{localVariablePrefix}}{{#collectionFormat}}localVarCollectionQueryParams.addAll({{localVariablePrefix}}apiClient.parameterToPairs("{{{collectionFormat}}}", {{/collectionFormat}}{{^collectionFormat}}localVarQueryParams.addAll({{localVariablePrefix}}apiClient.parameterToPair({{/collectionFormat}}"{{baseName}}", {{paramName}}));{{/queryParams}} {{javaUtilPrefix}}Map {{localVariablePrefix}}localVarHeaderParams = new {{javaUtilPrefix}}HashMap();{{#headerParams}} if ({{paramName}} != null) @@ -115,9 +126,12 @@ public class {{classname}} { } String[] {{localVariablePrefix}}localVarAuthNames = new String[] { {{#authMethods}}"{{name}}"{{#hasMore}}, {{/hasMore}}{{/authMethods}} }; - return {{localVariablePrefix}}apiClient.buildCall({{localVariablePrefix}}localVarPath, "{{httpMethod}}", {{localVariablePrefix}}localVarQueryParams, {{localVariablePrefix}}localVarPostBody, {{localVariablePrefix}}localVarHeaderParams, {{localVariablePrefix}}localVarFormParams, {{localVariablePrefix}}localVarAuthNames, progressRequestListener); + return {{localVariablePrefix}}apiClient.buildCall({{localVariablePrefix}}localVarPath, "{{httpMethod}}", {{localVariablePrefix}}localVarQueryParams, {{localVariablePrefix}}localVarCollectionQueryParams, {{localVariablePrefix}}localVarPostBody, {{localVariablePrefix}}localVarHeaderParams, {{localVariablePrefix}}localVarFormParams, {{localVariablePrefix}}localVarAuthNames, progressRequestListener); } - + + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} @SuppressWarnings("rawtypes") private com.squareup.okhttp.Call {{operationId}}ValidateBeforeCall({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { {{^performBeanValidation}} @@ -127,7 +141,7 @@ public class {{classname}} { throw new ApiException("Missing the required parameter '{{paramName}}' when calling {{operationId}}(Async)"); } {{/required}}{{/allParams}} - + com.squareup.okhttp.Call {{localVariablePrefix}}call = {{operationId}}Call({{#allParams}}{{paramName}}, {{/allParams}}progressListener, progressRequestListener); return {{localVariablePrefix}}call; @@ -145,7 +159,7 @@ public class {{classname}} { if (violations.size() == 0) { com.squareup.okhttp.Call {{localVariablePrefix}}call = {{operationId}}Call({{#allParams}}{{paramName}}, {{/allParams}}progressListener, progressRequestListener); return {{localVariablePrefix}}call; - + } else { throw new BeanValidationException((Set) violations); } @@ -156,12 +170,8 @@ public class {{classname}} { e.printStackTrace(); throw new ApiException(e.getMessage()); } - + {{/performBeanValidation}} - - - - } /** @@ -170,7 +180,17 @@ public class {{classname}} { * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}}{{#returnType}} * @return {{returnType}}{{/returnType}} * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} */ + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} public {{#returnType}}{{{returnType}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException { {{#returnType}}ApiResponse<{{{returnType}}}> {{localVariablePrefix}}resp = {{/returnType}}{{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{#returnType}} return {{localVariablePrefix}}resp.getData();{{/returnType}} @@ -182,7 +202,17 @@ public class {{classname}} { * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}} * @return ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Void{{/returnType}}> * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} */ + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} public ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{operationId}}WithHttpInfo({{#allParams}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException { com.squareup.okhttp.Call {{localVariablePrefix}}call = {{operationId}}ValidateBeforeCall({{#allParams}}{{paramName}}, {{/allParams}}null, null); {{#returnType}}Type {{localVariablePrefix}}localVarReturnType = new TypeToken<{{{returnType}}}>(){}.getType(); @@ -196,7 +226,17 @@ public class {{classname}} { * @param callback The callback to be executed when the API call finishes * @return The request call * @throws ApiException If fail to process the API call, e.g. serializing the request body object + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} */ + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} public com.squareup.okhttp.Call {{operationId}}Async({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ApiCallback<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{localVariablePrefix}}callback) throws ApiException { ProgressResponseBody.ProgressListener progressListener = null; @@ -225,4 +265,4 @@ public class {{classname}} { } {{/operation}} } -{{/operations}} +{{/operations}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/build.gradle.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/build.gradle.mustache index 0d169cf9c96..918ba8a540a 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/build.gradle.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/build.gradle.mustache @@ -104,8 +104,12 @@ dependencies { compile 'com.squareup.okhttp:okhttp:2.7.5' compile 'com.squareup.okhttp:logging-interceptor:2.7.5' compile 'com.google.code.gson:gson:2.8.1' - {{^java8}} + compile 'io.gsonfire:gson-fire:1.8.0' + {{#joda}} compile 'joda-time:joda-time:2.9.9' - {{/java8}} + {{/joda}} + {{#threetenbp}} + compile 'org.threeten:threetenbp:1.3.5' + {{/threetenbp}} testCompile 'junit:junit:4.12' } diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/build.sbt.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/build.sbt.mustache index c2eef2ede15..09adadc3334 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/build.sbt.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/build.sbt.mustache @@ -13,9 +13,13 @@ lazy val root = (project in file(".")). "com.squareup.okhttp" % "okhttp" % "2.7.5", "com.squareup.okhttp" % "logging-interceptor" % "2.7.5", "com.google.code.gson" % "gson" % "2.8.1", - {{^java8}} + {{#joda}} "joda-time" % "joda-time" % "2.9.9" % "compile", - {{/java8}} + {{/joda}} + {{#threetenbp}} + "org.threeten" % "threetenbp" % "1.3.5" % "compile", + {{/threetenbp}} + "io.gsonfire" % "gson-fire" % "1.8.0" % "compile", "junit" % "junit" % "4.12" % "test", "com.novocode" % "junit-interface" % "0.10" % "test" ) diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/pom.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/pom.mustache index e25c5882a06..97324975b63 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/pom.mustache @@ -1,250 +1,287 @@ - 4.0.0 - {{groupId}} - {{artifactId}} - jar - {{artifactId}} - {{artifactVersion}} - {{artifactUrl}} - {{artifactDescription}} - - {{scmConnection}} - {{scmDeveloperConnection}} - {{scmUrl}} - - - 2.2.0 - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + {{artifactUrl}} + {{artifactDescription}} + + {{scmConnection}} + {{scmDeveloperConnection}} + {{scmUrl}} + - - - {{licenseName}} - {{licenseUrl}} - repo - - + + + {{licenseName}} + {{licenseUrl}} + repo + + - - - {{developerName}} - {{developerEmail}} - {{developerOrganization}} - {{developerOrganizationUrl}} - - + + + {{developerName}} + {{developerEmail}} + {{developerOrganization}} + {{developerOrganizationUrl}} + + - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.12 - - - - loggerPath - conf/log4j.properties - - - -Xms512m -Xmx1500m - methods - pertest - - - - maven-dependency-plugin - - - package - - copy-dependencies - - - ${project.build.directory}/lib - - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 2.2 - - - - jar - test-jar - - - - - - + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + pertest + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + - - org.codehaus.mojo - build-helper-maven-plugin - 1.10 - - - add_sources - generate-sources - - add-source - - - - src/main/java - - - - - add_test_sources - generate-test-sources - - add-test-source - - - - src/test/java - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.4 - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - + + + org.apache.maven.plugins + maven-jar-plugin + 2.2 + + + + jar + test-jar + + + + + + - - - sign-artifacts - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - - sign-artifacts - verify - - sign - - - - + + org.codehaus.mojo + build-helper-maven-plugin + 1.10 + + + add_sources + generate-sources + + add-source + + + + + src/main/java + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + + src/test/java + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + - - - + + + + + sign-artifacts + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + + - - - io.swagger - swagger-annotations - ${swagger-core-version} - - - com.squareup.okhttp - okhttp - ${okhttp-version} - - - com.squareup.okhttp - logging-interceptor - ${okhttp-version} - - - com.google.code.gson - gson - ${gson-version} - - {{^java8}} - - joda-time - joda-time - ${jodatime-version} - - {{/java8}} - {{#useBeanValidation}} - - - javax.validation - validation-api - 1.1.0.Final - provided - - {{/useBeanValidation}} - {{#performBeanValidation}} - - - org.hibernate - hibernate-validator - 5.4.1.Final - - - javax.el - el-api - 2.2 - - {{/performBeanValidation}} - {{#parcelableModel}} - - - com.google.android - android - 4.1.1.4 - provided - - {{/parcelableModel}} - - - junit - junit - ${junit-version} - test - - - - {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} - ${java.version} - ${java.version} - 1.5.15 - 2.7.5 - 2.8.1 - 2.9.9 - 1.0.0 - 4.12 - UTF-8 - + + + io.swagger + swagger-annotations + ${swagger-core-version} + + + com.squareup.okhttp + okhttp + ${okhttp-version} + + + com.squareup.okhttp + logging-interceptor + ${okhttp-version} + + + com.google.code.gson + gson + ${gson-version} + + + io.gsonfire + gson-fire + ${gson-fire-version} + + {{#joda}} + + joda-time + joda-time + ${jodatime-version} + + {{/joda}} + {{#threetenbp}} + + org.threeten + threetenbp + ${threetenbp-version} + + {{/threetenbp}} + {{#useBeanValidation}} + + + javax.validation + validation-api + 1.1.0.Final + provided + + {{/useBeanValidation}} + {{#performBeanValidation}} + + + org.hibernate + hibernate-validator + 5.4.1.Final + + + javax.el + el-api + 2.2 + + {{/performBeanValidation}} + {{#parcelableModel}} + + + com.google.android + android + 4.1.1.4 + provided + + {{/parcelableModel}} + + + junit + junit + ${junit-version} + test + + + + {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} + ${java.version} + ${java.version} + 1.8.0 + 1.5.15 + 2.7.5 + 2.8.1 + {{#joda}} + 2.9.9 + {{/joda}} + {{#threetenbp}} + 1.3.5 + {{/threetenbp}} + 1.0.0 + 4.12 + UTF-8 + diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/resteasy/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/resteasy/ApiClient.mustache index 9ca31c6073b..24953959b98 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/resteasy/ApiClient.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/resteasy/ApiClient.mustache @@ -377,9 +377,13 @@ public class ApiClient { * application/json * application/json; charset=UTF8 * APPLICATION/JSON + * application/vnd.company+json + * @param mime MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. */ public boolean isJsonMime(String mime) { - return mime != null && mime.matches("(?i)application\\/json(;.*)?"); + String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"; + return mime != null && (mime.matches(jsonMime) || mime.equals("*/*")); } /** @@ -557,7 +561,7 @@ public class ApiClient { * Invoke API by sending HTTP request with the given options. * * @param path The sub-path of the HTTP URL - * @param method The request method, one of "GET", "POST", "PUT", and "DELETE" + * @param method The request method, one of "GET", "POST", "PUT", "HEAD" and "DELETE" * @param queryParams The query parameters * @param body The request body object * @param headerParams The header parameters @@ -615,6 +619,8 @@ public class ApiClient { response = invocationBuilder.delete(); } else if ("PATCH".equals(method)) { response = invocationBuilder.header("X-HTTP-Method-Override", "PATCH").post(entity); + } else if ("HEAD".equals(method)) { + response = invocationBuilder.head(); } else { throw new ApiException(500, "unknown method type " + method); } diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/resteasy/api.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/resteasy/api.mustache index 26cb9d7d946..f3fd01ed61b 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/resteasy/api.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/resteasy/api.mustache @@ -45,7 +45,17 @@ public class {{classname}} { * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}}{{#returnType}} * @return {{{returnType}}}{{/returnType}} * @throws ApiException if fails to make API call + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} */ + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} public {{#returnType}}{{{returnType}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException { Object {{localVariablePrefix}}localVarPostBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}; {{#allParams}}{{#required}} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/resteasy/pom.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/resteasy/pom.mustache index 057d3484d83..d06d96919c6 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/resteasy/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/resteasy/pom.mustache @@ -1,230 +1,262 @@ - 4.0.0 - {{groupId}} - {{artifactId}} - jar - {{artifactId}} - {{artifactVersion}} - - scm:git:git@github.com:swagger-api/swagger-mustache.git - scm:git:git@github.com:swagger-api/swagger-codegen.git - https://github.com/swagger-api/swagger-codegen - - - 2.2.0 - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + + scm:git:git@github.com:swagger-api/swagger-mustache.git + scm:git:git@github.com:swagger-api/swagger-codegen.git + https://github.com/swagger-api/swagger-codegen + - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.12 - - - - loggerPath - conf/log4j.properties - - - -Xms512m -Xmx1500m - methods - pertest - - - - maven-dependency-plugin - - - package - - copy-dependencies - - - ${project.build.directory}/lib - - - - + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + pertest + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + - - - org.apache.maven.plugins - maven-jar-plugin - 2.6 - - - - jar - test-jar - - - - - - + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + + jar + test-jar + + + + + + - - org.codehaus.mojo - build-helper-maven-plugin - - - add_sources - generate-sources - - add-source - - - - src/main/java - - - - - add_test_sources - generate-test-sources - - add-test-source - - - - src/test/java - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.5.1 - - {{#java8}} - 1.8 - 1.8 - {{/java8}} - {{^java8}} - 1.7 - 1.7 - {{/java8}} - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.4 - - - - - - io.swagger - swagger-annotations - ${swagger-core-version} - - - - org.jboss.resteasy - resteasy-client - ${resteasy-version} - - - org.jboss.resteasy - resteasy-multipart-provider - ${resteasy-version} - - - - com.fasterxml.jackson.core - jackson-core - ${jackson-version} - - - com.fasterxml.jackson.core - jackson-annotations - ${jackson-version} - - - com.fasterxml.jackson.core - jackson-databind - ${jackson-version} - - {{#withXml}} + + org.codehaus.mojo + build-helper-maven-plugin + + + add_sources + generate-sources + + add-source + + + + + src/main/java + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + + src/test/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5.1 + + {{#java8}} + + 1.8 + 1.8 + {{/java8}} + {{^java8}} + + 1.7 + 1.7 + {{/java8}} + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + + + + io.swagger + swagger-annotations + ${swagger-core-version} + + + + org.jboss.resteasy + resteasy-client + ${resteasy-version} + + + org.jboss.resteasy + resteasy-multipart-provider + ${resteasy-version} + + + + com.fasterxml.jackson.core + jackson-core + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-version} + + {{#withXml}} - - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - ${jackson-version} - + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + ${jackson-version} + - {{/withXml}} - {{#java8}} - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - ${jackson-version} - - {{/java8}} - {{^java8}} - - com.fasterxml.jackson.datatype - jackson-datatype-joda - ${jackson-version} - - - joda-time - joda-time - ${jodatime-version} - + {{/withXml}} + {{#java8}} + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson-version} + + {{/java8}} + {{^java8}} + + com.fasterxml.jackson.datatype + jackson-datatype-joda + ${jackson-version} + + + joda-time + joda-time + ${jodatime-version} + - - - com.brsanthu - migbase64 - 2.2 - - {{/java8}} + + + com.brsanthu + migbase64 + 2.2 + + {{/java8}} - {{#supportJava6}} - - org.apache.commons - commons-lang3 - ${commons_lang3_version} - + {{#supportJava6}} + + org.apache.commons + commons-lang3 + ${commons_lang3_version} + - - commons-io - commons-io - ${commons_io_version} - - {{/supportJava6}} - - org.jboss.resteasy - resteasy-jackson-provider - 3.1.3.Final - - - - junit - junit - ${junit-version} - test - - - - 1.5.15 - 3.1.3.Final - 2.8.9 - {{^java8}} - 2.9.9 - {{/java8}} - {{#supportJava6}} - 2.5 - 3.6 - {{/supportJava6}} - 1.0.0 - 4.12 - + + commons-io + commons-io + ${commons_io_version} + + {{/supportJava6}} + + org.jboss.resteasy + resteasy-jackson2-provider + 3.1.3.Final + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson-version} + + + com.github.joschi.jackson + jackson-datatype-threetenbp + ${jackson-version} + + + + junit + junit + ${junit-version} + test + + + + UTF-8 + 1.5.15 + 3.1.3.Final + 2.6.4 + {{^java8}} + 2.9.9 + {{/java8}} + {{#supportJava6}} + 2.5 + 3.6 + {{/supportJava6}} + 1.0.0 + 4.12 + diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/resttemplate/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/resttemplate/ApiClient.mustache index f3c9b9dde25..54f2b14573b 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/resttemplate/ApiClient.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/resttemplate/ApiClient.mustache @@ -33,6 +33,13 @@ import org.springframework.util.StringUtils; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; +{{#threetenbp}} +import org.threeten.bp.*; +import com.fasterxml.jackson.datatype.threetenbp.ThreeTenModule; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter; +import com.fasterxml.jackson.databind.ObjectMapper; +{{/threetenbp}} import java.io.BufferedReader; import java.io.IOException; @@ -261,6 +268,9 @@ public class ApiClient { * @return ApiClient this client */ public ApiClient addDefaultHeader(String name, String value) { + if (defaultHeaders.containsKey(name)) { + defaultHeaders.remove(name); + } defaultHeaders.add(name, value); return this; } @@ -312,6 +322,14 @@ public class ApiClient { */ public ApiClient setDateFormat(DateFormat dateFormat) { this.dateFormat = dateFormat; + {{#threetenbp}} + for(HttpMessageConverter converter:restTemplate.getMessageConverters()){ + if(converter instanceof AbstractJackson2HttpMessageConverter){ + ObjectMapper mapper = ((AbstractJackson2HttpMessageConverter)converter).getObjectMapper(); + mapper.setDateFormat(dateFormat); + } + } + {{/threetenbp}} return this; } @@ -409,6 +427,11 @@ public class ApiClient { * @return boolean true if the MediaType represents JSON, false otherwise */ public boolean isJsonMime(String mediaType) { + // "* / *" is default to JSON + if ("*/*".equals(mediaType)) { + return true; + } + try { return isJsonMime(MediaType.parseMediaType(mediaType)); } catch (InvalidMediaTypeException e) { @@ -567,6 +590,18 @@ public class ApiClient { RestTemplate restTemplate = new RestTemplate(messageConverters); {{/withXml}}{{^withXml}}RestTemplate restTemplate = new RestTemplate();{{/withXml}} + {{#threetenbp}} + for(HttpMessageConverter converter:restTemplate.getMessageConverters()){ + if(converter instanceof AbstractJackson2HttpMessageConverter){ + ObjectMapper mapper = ((AbstractJackson2HttpMessageConverter)converter).getObjectMapper(); + ThreeTenModule module = new ThreeTenModule(); + module.addDeserializer(Instant.class, CustomInstantDeserializer.INSTANT); + module.addDeserializer(OffsetDateTime.class, CustomInstantDeserializer.OFFSET_DATE_TIME); + module.addDeserializer(ZonedDateTime.class, CustomInstantDeserializer.ZONED_DATE_TIME); + mapper.registerModule(module); + } + } + {{/threetenbp}} // This allows us to read the response more than once - Necessary for debugging. restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(restTemplate.getRequestFactory())); return restTemplate; @@ -640,4 +675,4 @@ public class ApiClient { return builder.toString(); } } -} \ No newline at end of file +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/resttemplate/api.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/resttemplate/api.mustache index 0bcd442edff..f48180f4381 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/resttemplate/api.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/resttemplate/api.mustache @@ -55,6 +55,10 @@ public class {{classname}} { {{/responses}}{{#allParams}} * @param {{paramName}} {{description}}{{^description}}The {{paramName}} parameter{{/description}} {{/allParams}}{{#returnType}} * @return {{returnType}} {{/returnType}} * @throws RestClientException if an error occurs while attempting to invoke the API +{{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation +{{/externalDocs}} */ public {{#returnType}}{{{returnType}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws RestClientException { Object {{localVariablePrefix}}postBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}; @@ -85,11 +89,11 @@ public class {{classname}} { {{/hasMore}}{{/formParams}}{{/hasFormParams}} final String[] {{localVariablePrefix}}accepts = { {{#hasProduces}} - {{#produces}}"{{mediaType}}"{{#hasMore}}, {{/hasMore}}{{/produces}} + {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} {{/hasProduces}}}; final List {{localVariablePrefix}}accept = {{localVariablePrefix}}apiClient.selectHeaderAccept({{localVariablePrefix}}accepts); final String[] {{localVariablePrefix}}contentTypes = { {{#hasConsumes}} - {{#consumes}}"{{mediaType}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} + {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} {{/hasConsumes}}}; final MediaType {{localVariablePrefix}}contentType = {{localVariablePrefix}}apiClient.selectHeaderContentType({{localVariablePrefix}}contentTypes); diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/resttemplate/build.gradle.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/resttemplate/build.gradle.mustache index 4738272ebf3..122d8171699 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/resttemplate/build.gradle.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/resttemplate/build.gradle.mustache @@ -111,6 +111,9 @@ ext { spring_web_version = "4.3.9.RELEASE" jodatime_version = "2.9.9" junit_version = "4.12" + {{#threetenbp}} + jackson_threeten_version = "2.6.4" + {{/threetenbp}} } dependencies { @@ -123,10 +126,13 @@ dependencies { {{#java8}} compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version" {{/java8}} - {{^java8}} + {{#joda}} compile "com.fasterxml.jackson.datatype:jackson-datatype-joda:$jackson_version" compile "joda-time:joda-time:$jodatime_version" - {{/java8}} + {{/joda}} + {{#threetenbp}} + compile "com.github.joschi.jackson:jackson-datatype-threetenbp:$jackson_threeten_version" + {{/threetenbp}} {{#withXml}} compile "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:$jackson_version" {{/withXml}} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/resttemplate/pom.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/resttemplate/pom.mustache index a18dc167761..746953f6dd9 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/resttemplate/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/resttemplate/pom.mustache @@ -1,270 +1,301 @@ - 4.0.0 - {{groupId}} - {{artifactId}} - jar - {{artifactId}} - {{artifactVersion}} - {{artifactUrl}} - {{artifactDescription}} - - {{scmConnection}} - {{scmDeveloperConnection}} - {{scmUrl}} - - - 2.2.0 - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + {{artifactUrl}} + {{artifactDescription}} + + {{scmConnection}} + {{scmDeveloperConnection}} + {{scmUrl}} + - - - {{licenseName}} - {{licenseUrl}} - repo - - + + + {{licenseName}} + {{licenseUrl}} + repo + + - - - {{developerName}} - {{developerEmail}} - {{developerOrganization}} - {{developerOrganizationUrl}} - - + + + {{developerName}} + {{developerEmail}} + {{developerOrganization}} + {{developerOrganizationUrl}} + + - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.12 - - - - loggerPath - conf/log4j.properties - - - -Xms512m -Xmx1500m - methods - pertest - - - - maven-dependency-plugin - - - package - - copy-dependencies - - - ${project.build.directory}/lib - - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 2.2 - - - - jar - test-jar - - - - - - + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + pertest + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + - - org.codehaus.mojo - build-helper-maven-plugin - 1.10 - - - add_sources - generate-sources - - add-source - - - - src/main/java - - - - - add_test_sources - generate-test-sources - - add-test-source - - - - src/test/java - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.6.1 - - {{#java8}} - 1.8 - 1.8 - {{/java8}} - {{^java8}} - 1.7 - 1.7 - {{/java8}} - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.4 - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - + + + org.apache.maven.plugins + maven-jar-plugin + 2.2 + + + + jar + test-jar + + + + + + - - - sign-artifacts - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - - sign-artifacts - verify - - sign - - - - + + org.codehaus.mojo + build-helper-maven-plugin + 1.10 + + + add_sources + generate-sources + + add-source + + + + + src/main/java + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + + src/test/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + {{#java8}} + + 1.8 + 1.8 + {{/java8}} + {{^java8}} + + 1.7 + 1.7 + {{/java8}} + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + - - - + + + + + sign-artifacts + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + + + + + + io.swagger + swagger-annotations + ${swagger-annotations-version} + - - - io.swagger - swagger-annotations - ${swagger-annotations-version} - + + + org.springframework + spring-web + ${spring-web-version} + - - - org.springframework - spring-web - ${spring-web-version} - - - - - com.fasterxml.jackson.core - jackson-core - ${jackson-version} - - - com.fasterxml.jackson.core - jackson-annotations - ${jackson-version} - - - com.fasterxml.jackson.core - jackson-databind - ${jackson-version} - - - com.fasterxml.jackson.jaxrs - jackson-jaxrs-json-provider - ${jackson-version} - - {{#withXml}} + + + com.fasterxml.jackson.core + jackson-core + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-version} + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + ${jackson-version} + + {{#withXml}} - - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - ${jackson-version} - + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + ${jackson-version} + - {{/withXml}} - {{#java8}} - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - ${jackson-version} - - {{/java8}} - {{^java8}} - - com.fasterxml.jackson.datatype - jackson-datatype-joda - ${jackson-version} - - - joda-time - joda-time - ${jodatime-version} - - {{/java8}} + {{/withXml}} + {{#java8}} + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson-version} + + {{/java8}} + {{#joda}} + + com.fasterxml.jackson.datatype + jackson-datatype-joda + ${jackson-version} + + + joda-time + joda-time + ${jodatime-version} + + {{/joda}} + {{#threetenbp}} + + com.github.joschi.jackson + jackson-datatype-threetenbp + ${jackson-threetenbp-version} + + {{/threetenbp}} - - - junit - junit - ${junit-version} - test - - - - UTF-8 - 1.5.15 - 4.3.9.RELEASE - 2.8.9 - {{^java8}} - 2.9.9 - {{/java8}} - 1.0.0 - 4.12 - + + + junit + junit + ${junit-version} + test + + + + UTF-8 + 1.5.15 + 4.3.9.RELEASE + 2.8.9 + {{#joda}} + 2.9.9 + {{/joda}} + {{#threetenbp}} + 2.6.4 + {{/threetenbp}} + 1.0.0 + 4.12 + diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit/api.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit/api.mustache index 5ff72917614..f94389c12a1 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit/api.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit/api.mustache @@ -29,6 +29,10 @@ public interface {{classname}} { {{#returnType}} * @return {{returnType}} {{/returnType}} +{{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation +{{/externalDocs}} */ {{#formParams}}{{#-first}} {{#isMultipart}}@retrofit.http.Multipart{{/isMultipart}}{{^isMultipart}}@retrofit.http.FormUrlEncoded{{/isMultipart}}{{/-first}}{{/formParams}} @@ -44,6 +48,10 @@ public interface {{classname}} { * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} {{/allParams}} * @param cb callback method +{{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation +{{/externalDocs}} */ {{#formParams}}{{#-first}} {{#isMultipart}}@retrofit.http.Multipart{{/isMultipart}}{{^isMultipart}}@retrofit.http.FormUrlEncoded{{/isMultipart}}{{/-first}}{{/formParams}} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit/auth/OAuth.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit/auth/OAuth.mustache index d0c62ff03ae..a88378f3e2e 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit/auth/OAuth.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit/auth/OAuth.mustache @@ -73,6 +73,10 @@ public class OAuth implements Interceptor { public Response intercept(Chain chain) throws IOException { + return retryingIntercept(chain, true); + } + + private Response retryingIntercept(Chain chain, boolean updateTokenAndRetryOnAuthorizationFailure) throws IOException { Request request = chain.request(); // If the request already have an authorization (eg. Basic auth), do nothing @@ -107,11 +111,10 @@ public class OAuth implements Interceptor { //Execute the request Response response = chain.proceed(rb.build()); - // 401 most likely indicates that access token has expired. - // Time to refresh and resend the request - if ( response != null && (response.code() == HTTP_UNAUTHORIZED | response.code() == HTTP_FORBIDDEN) ) { + // 401/403 most likely indicates that access token has expired. Unless it happens two times in a row. + if ( response != null && (response.code() == HTTP_UNAUTHORIZED || response.code() == HTTP_FORBIDDEN) && updateTokenAndRetryOnAuthorizationFailure ) { if (updateAccessToken(requestAccessToken)) { - return intercept( chain ); + return retryingIntercept( chain, false ); } } return response; diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit/pom.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit/pom.mustache index 672d4b754ca..cbb4e298fb5 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit/pom.mustache @@ -1,233 +1,253 @@ - 4.0.0 - {{groupId}} - {{artifactId}} - jar - {{artifactId}} - {{artifactVersion}} - {{artifactUrl}} - {{artifactDescription}} - - {{scmConnection}} - {{scmDeveloperConnection}} - {{scmUrl}} - - - 2.2.0 - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + {{artifactUrl}} + {{artifactDescription}} + + {{scmConnection}} + {{scmDeveloperConnection}} + {{scmUrl}} + - - - {{licenseName}} - {{licenseUrl}} - repo - - + + + {{licenseName}} + {{licenseUrl}} + repo + + - - - {{developerName}} - {{developerEmail}} - {{developerOrganization}} - {{developerOrganizationUrl}} - - + + + {{developerName}} + {{developerEmail}} + {{developerOrganization}} + {{developerOrganizationUrl}} + + - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.12 - - - - loggerPath - conf/log4j.properties - - - -Xms512m -Xmx1500m - methods - pertest - - - - maven-dependency-plugin - - - package - - copy-dependencies - - - ${project.build.directory}/lib - - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 2.2 - - - - jar - test-jar - - - - - - + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + pertest + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + - - org.codehaus.mojo - build-helper-maven-plugin - - - add_sources - generate-sources - - add-source - - - - src/main/java - - - - - add_test_sources - generate-test-sources - - add-test-source - - - - src/test/java - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.6.1 - - {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} - {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.4 - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - + + + org.apache.maven.plugins + maven-jar-plugin + 2.2 + + + + jar + test-jar + + + + + + - - - sign-artifacts - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - - sign-artifacts - verify - - sign - - - - + + org.codehaus.mojo + build-helper-maven-plugin + + + add_sources + generate-sources + + add-source + + + + + src/main/java + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + + src/test/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} + {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + - - - + + + + + sign-artifacts + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + + - - - io.swagger - swagger-annotations - ${swagger-core-version} - - - com.squareup.retrofit - retrofit - ${retrofit-version} - - - org.apache.oltu.oauth2 - org.apache.oltu.oauth2.client - ${oltu-version} - - - com.squareup.okhttp - okhttp - ${okhttp-version} - - - joda-time - joda-time - ${jodatime-version} - + + + io.swagger + swagger-annotations + ${swagger-core-version} + + + com.squareup.retrofit + retrofit + ${retrofit-version} + + + org.apache.oltu.oauth2 + org.apache.oltu.oauth2.client + ${oltu-version} + + + com.squareup.okhttp + okhttp + ${okhttp-version} + + + joda-time + joda-time + ${jodatime-version} + - {{#parcelableModel}} - - - com.google.android - android - 4.1.1.4 - provided - - {{/parcelableModel}} + {{#parcelableModel}} + + + com.google.android + android + 4.1.1.4 + provided + + {{/parcelableModel}} - - - junit - junit - ${junit-version} - test - - - - 1.5.15 - 1.9.0 - 2.7.5 - 2.9.9 - 1.0.1 - 1.0.0 - 4.12 - + + + junit + junit + ${junit-version} + test + + + + UTF-8 + 1.5.15 + 1.9.0 + 2.7.5 + 2.9.9 + 1.0.1 + 1.0.0 + 4.12 + diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/ApiClient.mustache index a0daf87c9cb..66d3056e540 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/ApiClient.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/ApiClient.mustache @@ -1,58 +1,53 @@ package {{invokerPackage}}; -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.util.Date; -import java.util.LinkedHashMap; -import java.util.Map; - +import com.google.gson.Gson; +import com.google.gson.JsonParseException; +import com.google.gson.JsonElement; +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.RequestBody; +import okhttp3.ResponseBody; import org.apache.oltu.oauth2.client.request.OAuthClientRequest.AuthenticationRequestBuilder; import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder; -{{^java8}} -import org.joda.time.DateTime; -import org.joda.time.LocalDate; +{{#joda}} import org.joda.time.format.DateTimeFormatter; -import org.joda.time.format.ISODateTimeFormat; -{{/java8}} -{{#java8}} -import java.time.LocalDate; -import java.time.OffsetDateTime; -import java.time.format.DateTimeFormatter; -{{/java8}} +{{/joda}} +{{#threetenbp}} +import org.threeten.bp.format.DateTimeFormatter; +{{/threetenbp}} import retrofit2.Converter; import retrofit2.Retrofit; {{#useRxJava}} import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; {{/useRxJava}} {{#useRxJava2}} -import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; {{/useRxJava2}} import retrofit2.converter.gson.GsonConverterFactory; import retrofit2.converter.scalars.ScalarsConverterFactory; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonParseException; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import okhttp3.Interceptor; -import okhttp3.OkHttpClient; -import okhttp3.RequestBody; -import okhttp3.ResponseBody; - import {{invokerPackage}}.auth.HttpBasicAuth; import {{invokerPackage}}.auth.ApiKeyAuth; import {{invokerPackage}}.auth.OAuth; import {{invokerPackage}}.auth.OAuth.AccessTokenListener; import {{invokerPackage}}.auth.OAuthFlow; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.text.DateFormat; +{{#java8}} +import java.time.format.DateTimeFormatter; +{{/java8}} +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.HashMap; + public class ApiClient { private Map apiAuthorizations; private OkHttpClient.Builder okBuilder; private Retrofit.Builder adapterBuilder; + private JSON json; public ApiClient() { apiAuthorizations = new LinkedHashMap(); @@ -65,18 +60,19 @@ public class ApiClient { {{#hasAuthMethods}} Interceptor auth; {{#authMethods}}if ("{{name}}".equals(authName)) { - {{#isBasic}} + {{#isBasic}} auth = new HttpBasicAuth(); - {{/isBasic}} - {{#isApiKey}} + {{/isBasic}} + {{#isApiKey}} auth = new ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}"); - {{/isApiKey}} - {{#isOAuth}} + {{/isApiKey}} + {{#isOAuth}} auth = new OAuth(OAuthFlow.{{flow}}, "{{authorizationUrl}}", "{{tokenUrl}}", "{{#scopes}}{{scope}}{{#hasMore}}, {{/hasMore}}{{/scopes}}"); - {{/isOAuth}} + {{/isOAuth}} } else {{/authMethods}}{ throw new RuntimeException("auth name \"" + authName + "\" not found in available auth names"); } + addAuthorization(authName, auth); {{/hasAuthMethods}} {{^hasAuthMethods}} @@ -132,21 +128,11 @@ public class ApiClient { } public void createDefaultAdapter() { - Gson gson = new GsonBuilder() - .setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ") - {{^java8}} - .registerTypeAdapter(DateTime.class, new DateTimeTypeAdapter()) - {{/java8}} - {{#java8}} - .registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeTypeAdapter()) - {{/java8}} - .registerTypeAdapter(LocalDate.class, new LocalDateTypeAdapter()) - .create(); - + json = new JSON(); okBuilder = new OkHttpClient.Builder(); String baseUrl = "{{{basePath}}}"; - if(!baseUrl.endsWith("/")) + if (!baseUrl.endsWith("/")) baseUrl = baseUrl + "/"; adapterBuilder = new Retrofit @@ -154,12 +140,11 @@ public class ApiClient { .baseUrl(baseUrl) {{#useRxJava}} .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) - {{/useRxJava}} - {{#useRxJava2}} + {{/useRxJava}}{{#useRxJava2}} .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) {{/useRxJava2}} .addConverterFactory(ScalarsConverterFactory.create()) - .addConverterFactory(GsonCustomConverterFactory.create(gson)); + .addConverterFactory(GsonCustomConverterFactory.create(json.getGson())); } public S createService(Class serviceClass) { @@ -167,41 +152,79 @@ public class ApiClient { .client(okBuilder.build()) .build() .create(serviceClass); + } + + public ApiClient setDateFormat(DateFormat dateFormat) { + this.json.setDateFormat(dateFormat); + return this; + } + + public ApiClient setSqlDateFormat(DateFormat dateFormat) { + this.json.setSqlDateFormat(dateFormat); + return this; + } + + {{#joda}} + public ApiClient setDateTimeFormat(DateTimeFormatter dateFormat) { + this.json.setDateTimeFormat(dateFormat); + return this; + } + + public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) { + this.json.setLocalDateFormat(dateFormat); + return this; + } + + {{/joda}} + {{#jsr310}} + public ApiClient setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { + this.json.setOffsetDateTimeFormat(dateFormat); + return this; + } + public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) { + this.json.setLocalDateFormat(dateFormat); + return this; } + {{/jsr310}} + /** * Helper method to configure the first api key found * @param apiKey API key + * @return ApiClient */ - private void setApiKey(String apiKey) { + public ApiClient setApiKey(String apiKey) { for(Interceptor apiAuthorization : apiAuthorizations.values()) { if (apiAuthorization instanceof ApiKeyAuth) { ApiKeyAuth keyAuth = (ApiKeyAuth) apiAuthorization; keyAuth.setApiKey(apiKey); - return; + return this; } } + return this; } /** * Helper method to configure the username/password for basic auth or password oauth * @param username Username * @param password Password + * @return ApiClient */ - private void setCredentials(String username, String password) { + public ApiClient setCredentials(String username, String password) { for(Interceptor apiAuthorization : apiAuthorizations.values()) { if (apiAuthorization instanceof HttpBasicAuth) { HttpBasicAuth basicAuth = (HttpBasicAuth) apiAuthorization; basicAuth.setCredentials(username, password); - return; + return this; } if (apiAuthorization instanceof OAuth) { OAuth oauth = (OAuth) apiAuthorization; oauth.getTokenRequestBuilder().setUsername(username).setPassword(password); - return; + return this; } } + return this; } /** @@ -235,15 +258,17 @@ public class ApiClient { /** * Helper method to pre-set the oauth access token of the first oauth found in the apiAuthorizations (there should be only one) * @param accessToken Access token + * @return ApiClient */ - public void setAccessToken(String accessToken) { + public ApiClient setAccessToken(String accessToken) { for(Interceptor apiAuthorization : apiAuthorizations.values()) { if (apiAuthorization instanceof OAuth) { OAuth oauth = (OAuth) apiAuthorization; oauth.setAccessToken(accessToken); - return; + return this; } } + return this; } /** @@ -251,8 +276,9 @@ public class ApiClient { * @param clientId Client ID * @param clientSecret Client secret * @param redirectURI Redirect URI + * @return ApiClient */ - public void configureAuthorizationFlow(String clientId, String clientSecret, String redirectURI) { + public ApiClient configureAuthorizationFlow(String clientId, String clientSecret, String redirectURI) { for(Interceptor apiAuthorization : apiAuthorizations.values()) { if (apiAuthorization instanceof OAuth) { OAuth oauth = (OAuth) apiAuthorization; @@ -263,52 +289,59 @@ public class ApiClient { oauth.getAuthenticationRequestBuilder() .setClientId(clientId) .setRedirectURI(redirectURI); - return; + return this; } } + return this; } /** * Configures a listener which is notified when a new access token is received. * @param accessTokenListener Access token listener + * @return ApiClient */ - public void registerAccessTokenListener(AccessTokenListener accessTokenListener) { + public ApiClient registerAccessTokenListener(AccessTokenListener accessTokenListener) { for(Interceptor apiAuthorization : apiAuthorizations.values()) { if (apiAuthorization instanceof OAuth) { OAuth oauth = (OAuth) apiAuthorization; oauth.registerAccessTokenListener(accessTokenListener); - return; + return this; } } + return this; } /** * Adds an authorization to be used by the client * @param authName Authentication name * @param authorization Authorization interceptor + * @return ApiClient */ - public void addAuthorization(String authName, Interceptor authorization) { + public ApiClient addAuthorization(String authName, Interceptor authorization) { if (apiAuthorizations.containsKey(authName)) { throw new RuntimeException("auth name \"" + authName + "\" already in api authorizations"); } apiAuthorizations.put(authName, authorization); okBuilder.addInterceptor(authorization); + return this; } public Map getApiAuthorizations() { return apiAuthorizations; } - public void setApiAuthorizations(Map apiAuthorizations) { + public ApiClient setApiAuthorizations(Map apiAuthorizations) { this.apiAuthorizations = apiAuthorizations; + return this; } public Retrofit.Builder getAdapterBuilder() { return adapterBuilder; } - public void setAdapterBuilder(Retrofit.Builder adapterBuilder) { + public ApiClient setAdapterBuilder(Retrofit.Builder adapterBuilder) { this.adapterBuilder = adapterBuilder; + return this; } public OkHttpClient.Builder getOkBuilder() { @@ -337,7 +370,6 @@ public class ApiClient { * expected type is String, then just return the body string. */ class GsonResponseBodyConverterToString implements Converter { - private final Gson gson; private final Type type; @@ -357,8 +389,8 @@ class GsonResponseBodyConverterToString implements Converter } } -class GsonCustomConverterFactory extends Converter.Factory { - +class GsonCustomConverterFactory extends Converter.Factory +{ private final Gson gson; private final GsonConverterFactory gsonConverterFactory; @@ -386,126 +418,3 @@ class GsonCustomConverterFactory extends Converter.Factory { return gsonConverterFactory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, retrofit); } } - -{{^java8}} -/** - * Gson TypeAdapter for Joda DateTime type - */ -class DateTimeTypeAdapter extends TypeAdapter { - - private final DateTimeFormatter parseFormatter = ISODateTimeFormat.dateOptionalTimeParser(); - private final DateTimeFormatter printFormatter = ISODateTimeFormat.dateTime(); - - @Override - public void write(JsonWriter out, DateTime date) throws IOException { - if (date == null) { - out.nullValue(); - } else { - out.value(printFormatter.print(date)); - } - } - - @Override - public DateTime read(JsonReader in) throws IOException { - switch (in.peek()) { - case NULL: - in.nextNull(); - return null; - default: - String date = in.nextString(); - return parseFormatter.parseDateTime(date); - } - } -} - -/** - * Gson TypeAdapter for Joda LocalDate type - */ -class LocalDateTypeAdapter extends TypeAdapter { - - private final DateTimeFormatter formatter = ISODateTimeFormat.date(); - - @Override - public void write(JsonWriter out, LocalDate date) throws IOException { - if (date == null) { - out.nullValue(); - } else { - out.value(formatter.print(date)); - } - } - - @Override - public LocalDate read(JsonReader in) throws IOException { - switch (in.peek()) { - case NULL: - in.nextNull(); - return null; - default: - String date = in.nextString(); - return formatter.parseLocalDate(date); - } - } -} -{{/java8}} -{{#java8}} -/** - * Gson TypeAdapter for jsr310 OffsetDateTime type - */ -class OffsetDateTimeTypeAdapter extends TypeAdapter { - - private final DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME; - - @Override - public void write(JsonWriter out, OffsetDateTime date) throws IOException { - if (date == null) { - out.nullValue(); - } else { - out.value(formatter.format(date)); - } - } - - @Override - public OffsetDateTime read(JsonReader in) throws IOException { - switch (in.peek()) { - case NULL: - in.nextNull(); - return null; - default: - String date = in.nextString(); - if (date.endsWith("+0000")) { - date = date.substring(0, date.length()-5) + "Z"; - } - return OffsetDateTime.parse(date, formatter); - } - } -} - -/** - * Gson TypeAdapter for jsr310 LocalDate type - */ -class LocalDateTypeAdapter extends TypeAdapter { - - private final DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE; - - @Override - public void write(JsonWriter out, LocalDate date) throws IOException { - if (date == null) { - out.nullValue(); - } else { - out.value(formatter.format(date)); - } - } - - @Override - public LocalDate read(JsonReader in) throws IOException { - switch (in.peek()) { - case NULL: - in.nextNull(); - return null; - default: - String date = in.nextString(); - return LocalDate.parse(date, formatter); - } - } -} -{{/java8}} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/JSON.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/JSON.mustache new file mode 100644 index 00000000000..abab9159d79 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/JSON.mustache @@ -0,0 +1,446 @@ +{{>licenseInfo}} + +package {{invokerPackage}}; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.internal.bind.util.ISO8601Utils; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import com.google.gson.JsonElement; +import io.gsonfire.GsonFireBuilder; +import io.gsonfire.TypeSelector; +{{#joda}} +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.DateTimeFormatterBuilder; +import org.joda.time.format.ISODateTimeFormat; +{{/joda}} +{{#threetenbp}} +import org.threeten.bp.LocalDate; +import org.threeten.bp.OffsetDateTime; +import org.threeten.bp.format.DateTimeFormatter; +{{/threetenbp}} + +import {{modelPackage}}.*; + +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Type; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.ParsePosition; +{{#java8}} +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +{{/java8}} +import java.util.Date; +import java.util.Map; +import java.util.HashMap; + +public class JSON { + private Gson gson; + private DateTypeAdapter dateTypeAdapter = new DateTypeAdapter(); + private SqlDateTypeAdapter sqlDateTypeAdapter = new SqlDateTypeAdapter(); + {{#joda}} + private DateTimeTypeAdapter dateTimeTypeAdapter = new DateTimeTypeAdapter(); + private LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter(); + {{/joda}} + {{#jsr310}} + private OffsetDateTimeTypeAdapter offsetDateTimeTypeAdapter = new OffsetDateTimeTypeAdapter(); + private LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter(); + {{/jsr310}} + + public static GsonBuilder createGson() { + GsonFireBuilder fireBuilder = new GsonFireBuilder() + {{#parent}} + .registerTypeSelector({{classname}}.class, new TypeSelector() { + @Override + public Class getClassForElement(JsonElement readElement) { + Map classByDiscriminatorValue = new HashMap(); + {{#children}} + classByDiscriminatorValue.put("{{name}}".toUpperCase(), {{classname}}.class); + {{/children}} + classByDiscriminatorValue.put("{{classname}}".toUpperCase(), {{classname}}.class); + return getClassByDiscriminator( + classByDiscriminatorValue, + getDiscriminatorValue(readElement, "{{discriminator}}")); + } + }) + {{/parent}} + ; + return fireBuilder.createGsonBuilder(); + } + + private static String getDiscriminatorValue(JsonElement readElement, String discriminatorField) { + JsonElement element = readElement.getAsJsonObject().get(discriminatorField); + if(null == element) { + throw new IllegalArgumentException("missing discriminator field: <" + discriminatorField + ">"); + } + return element.getAsString(); + } + + private static Class getClassByDiscriminator(Map classByDiscriminatorValue, String discriminatorValue) { + Class clazz = (Class) classByDiscriminatorValue.get(discriminatorValue.toUpperCase()); + if(null == clazz) { + throw new IllegalArgumentException("cannot determine model class of name: <" + discriminatorValue + ">"); + } + return clazz; + } + + public JSON() { + gson = createGson() + .registerTypeAdapter(Date.class, dateTypeAdapter) + .registerTypeAdapter(java.sql.Date.class, sqlDateTypeAdapter) + {{#joda}} + .registerTypeAdapter(DateTime.class, dateTimeTypeAdapter) + .registerTypeAdapter(LocalDate.class, localDateTypeAdapter) + {{/joda}} + {{#jsr310}} + .registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter) + .registerTypeAdapter(LocalDate.class, localDateTypeAdapter) + {{/jsr310}} + .create(); + } + + /** + * Get Gson. + * + * @return Gson + */ + public Gson getGson() { + return gson; + } + + /** + * Set Gson. + * + * @param gson Gson + * @return JSON + */ + public JSON setGson(Gson gson) { + this.gson = gson; + return this; + } + + {{#joda}} + /** + * Gson TypeAdapter for Joda DateTime type + */ + public static class DateTimeTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public DateTimeTypeAdapter() { + this(new DateTimeFormatterBuilder() + .append(ISODateTimeFormat.dateTime().getPrinter(), ISODateTimeFormat.dateOptionalTimeParser().getParser()) + .toFormatter()); + } + + public DateTimeTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, DateTime date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.print(date)); + } + } + + @Override + public DateTime read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + return formatter.parseDateTime(date); + } + } + } + + /** + * Gson TypeAdapter for Joda LocalDate type + */ + public class LocalDateTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public LocalDateTypeAdapter() { + this(ISODateTimeFormat.date()); + } + + public LocalDateTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, LocalDate date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.print(date)); + } + } + + @Override + public LocalDate read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + return formatter.parseLocalDate(date); + } + } + } + + public JSON setDateTimeFormat(DateTimeFormatter dateFormat) { + dateTimeTypeAdapter.setFormat(dateFormat); + return this; + } + + public JSON setLocalDateFormat(DateTimeFormatter dateFormat) { + localDateTypeAdapter.setFormat(dateFormat); + return this; + } + + {{/joda}} + {{#jsr310}} + /** + * Gson TypeAdapter for JSR310 OffsetDateTime type + */ + public static class OffsetDateTimeTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public OffsetDateTimeTypeAdapter() { + this(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } + + public OffsetDateTimeTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, OffsetDateTime date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.format(date)); + } + } + + @Override + public OffsetDateTime read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + if (date.endsWith("+0000")) { + date = date.substring(0, date.length()-5) + "Z"; + } + return OffsetDateTime.parse(date, formatter); + } + } + } + + /** + * Gson TypeAdapter for JSR310 LocalDate type + */ + public class LocalDateTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public LocalDateTypeAdapter() { + this(DateTimeFormatter.ISO_LOCAL_DATE); + } + + public LocalDateTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, LocalDate date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.format(date)); + } + } + + @Override + public LocalDate read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + return LocalDate.parse(date, formatter); + } + } + } + + public JSON setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { + offsetDateTimeTypeAdapter.setFormat(dateFormat); + return this; + } + + public JSON setLocalDateFormat(DateTimeFormatter dateFormat) { + localDateTypeAdapter.setFormat(dateFormat); + return this; + } + + {{/jsr310}} + /** + * Gson TypeAdapter for java.sql.Date type + * If the dateFormat is null, a simple "yyyy-MM-dd" format will be used + * (more efficient than SimpleDateFormat). + */ + public static class SqlDateTypeAdapter extends TypeAdapter { + + private DateFormat dateFormat; + + public SqlDateTypeAdapter() { + } + + public SqlDateTypeAdapter(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + public void setFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + @Override + public void write(JsonWriter out, java.sql.Date date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + String value; + if (dateFormat != null) { + value = dateFormat.format(date); + } else { + value = date.toString(); + } + out.value(value); + } + } + + @Override + public java.sql.Date read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + try { + if (dateFormat != null) { + return new java.sql.Date(dateFormat.parse(date).getTime()); + } + return new java.sql.Date(ISO8601Utils.parse(date, new ParsePosition(0)).getTime()); + } catch (ParseException e) { + throw new JsonParseException(e); + } + } + } + } + + /** + * Gson TypeAdapter for java.util.Date type + * If the dateFormat is null, ISO8601Utils will be used. + */ + public static class DateTypeAdapter extends TypeAdapter { + + private DateFormat dateFormat; + + public DateTypeAdapter() { + } + + public DateTypeAdapter(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + public void setFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + @Override + public void write(JsonWriter out, Date date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + String value; + if (dateFormat != null) { + value = dateFormat.format(date); + } else { + value = ISO8601Utils.format(date, true); + } + out.value(value); + } + } + + @Override + public Date read(JsonReader in) throws IOException { + try { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + try { + if (dateFormat != null) { + return dateFormat.parse(date); + } + return ISO8601Utils.parse(date, new ParsePosition(0)); + } catch (ParseException e) { + throw new JsonParseException(e); + } + } + } catch (IllegalArgumentException e) { + throw new JsonParseException(e); + } + } + } + + public JSON setDateFormat(DateFormat dateFormat) { + dateTypeAdapter.setFormat(dateFormat); + return this; + } + + public JSON setSqlDateFormat(DateFormat dateFormat) { + sqlDateTypeAdapter.setFormat(dateFormat); + return this; + } + +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/api.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/api.mustache index 98d511179fb..d19c634e29d 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/api.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/api.mustache @@ -2,12 +2,19 @@ package {{package}}; import {{invokerPackage}}.CollectionFormats.*; -{{#useRxJava}}import rx.Observable;{{/useRxJava}} -{{#useRxJava2}}import io.reactivex.Observable;{{/useRxJava2}} -{{#doNotUseRx}}import retrofit2.Call;{{/doNotUseRx}} +{{#useRxJava}} +import rx.Observable; +{{/useRxJava}} +{{#useRxJava2}} +import io.reactivex.Observable; +{{/useRxJava2}} +{{#doNotUseRx}} +import retrofit2.Call; +{{/doNotUseRx}} import retrofit2.http.*; import okhttp3.RequestBody; +import okhttp3.ResponseBody; {{#imports}}import {{import}}; {{/imports}} @@ -19,11 +26,6 @@ import java.util.List; import java.util.Map; {{/fullJavaUtil}} -{{#usePlay24WS}} -import play.libs.F; -import retrofit2.Response; -{{/usePlay24WS}} - {{#operations}} public interface {{classname}} { {{#operation}} @@ -34,6 +36,10 @@ public interface {{classname}} { * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} {{/allParams}} * @return Call<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Object{{/returnType}}> +{{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation +{{/externalDocs}} */ {{#formParams}} {{#-first}} @@ -44,13 +50,13 @@ public interface {{classname}} { {{#prioritizedContentTypes}} {{#-first}} @Headers({ - "Content-Type:{{mediaType}}" + "Content-Type:{{{mediaType}}}" }) {{/-first}} {{/prioritizedContentTypes}} {{/formParams}} @{{httpMethod}}("{{{path}}}") - {{^usePlay24WS}}{{^doNotUseRx}}Observable{{/doNotUseRx}}{{#doNotUseRx}}Call{{/doNotUseRx}}{{/usePlay24WS}}{{#usePlay24WS}}F.Promise{{#usePlay24WS}}>{{/usePlay24WS}} {{operationId}}({{^allParams}});{{/allParams}} + {{^doNotUseRx}}Observable{{/doNotUseRx}}{{#doNotUseRx}}Call{{/doNotUseRx}}<{{#isResponseFile}}ResponseBody{{/isResponseFile}}{{^isResponseFile}}{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Object{{/returnType}}{{/isResponseFile}}> {{operationId}}({{^allParams}});{{/allParams}} {{#allParams}}{{>libraries/retrofit2/queryParams}}{{>libraries/retrofit2/pathParams}}{{>libraries/retrofit2/headerParams}}{{>libraries/retrofit2/bodyParams}}{{>libraries/retrofit2/formParams}}{{#hasMore}}, {{/hasMore}}{{^hasMore}} );{{/hasMore}}{{/allParams}} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/api_test.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/api_test.mustache index a34cfe0e12b..0a85ef5095c 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/api_test.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/api_test.mustache @@ -25,7 +25,8 @@ public class {{classname}}Test { api = new ApiClient().createService({{classname}}.class); } - {{#operations}}{{#operation}} + {{#operations}} + {{#operation}} /** * {{summary}} * @@ -40,5 +41,6 @@ public class {{classname}}Test { // TODO: test validations } - {{/operation}}{{/operations}} + {{/operation}} + {{/operations}} } diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/auth/OAuth.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/auth/OAuth.mustache index bb070d4840b..e3b3727cd83 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/auth/OAuth.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/auth/OAuth.mustache @@ -73,6 +73,10 @@ public class OAuth implements Interceptor { public Response intercept(Chain chain) throws IOException { + return retryingIntercept(chain, true); + } + + private Response retryingIntercept(Chain chain, boolean updateTokenAndRetryOnAuthorizationFailure) throws IOException { Request request = chain.request(); // If the request already have an authorization (eg. Basic auth), do nothing @@ -107,11 +111,10 @@ public class OAuth implements Interceptor { //Execute the request Response response = chain.proceed(rb.build()); - // 401 most likely indicates that access token has expired. - // Time to refresh and resend the request - if ( response != null && (response.code() == HTTP_UNAUTHORIZED | response.code() == HTTP_FORBIDDEN) ) { + // 401/403 most likely indicates that access token has expired. Unless it happens two times in a row. + if ( response != null && (response.code() == HTTP_UNAUTHORIZED || response.code() == HTTP_FORBIDDEN) && updateTokenAndRetryOnAuthorizationFailure ) { if (updateAccessToken(requestAccessToken)) { - return intercept( chain ); + return retryingIntercept( chain, false ); } } return response; diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/build.gradle.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/build.gradle.mustache index 9e0ad8a572c..bc92925ac00 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/build.gradle.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/build.gradle.mustache @@ -101,14 +101,17 @@ if(hasProperty('target') && target == 'android') { ext { oltu_version = "1.0.1" - {{^usePlay24WS}} retrofit_version = "2.3.0" - {{/usePlay24WS}} - {{#usePlay24WS}} - retrofit_version = "2.1.0" - jackson_version = "2.8.9" + {{#usePlayWS}} + {{#play24}} + jackson_version = "2.6.6" play_version = "2.4.11" - {{/usePlay24WS}} + {{/play24}} + {{#play25}} + jackson_version = "2.7.8" + play_version = "2.5.14" + {{/play25}} + {{/usePlayWS}} swagger_annotations_version = "1.5.15" junit_version = "4.12" {{#useRxJava}} @@ -117,9 +120,13 @@ ext { {{#useRxJava2}} rx_java_version = "2.1.1" {{/useRxJava2}} - {{^java8}} + {{#joda}} jodatime_version = "2.9.9" - {{/java8}} + {{/joda}} + {{#threetenbp}} + threetenbp_version = "1.3.5" + {{/threetenbp}} + json_fire_version = "1.8.0" } dependencies { @@ -131,21 +138,25 @@ dependencies { compile "io.reactivex:rxjava:$rx_java_version" {{/useRxJava}} {{#useRxJava2}} - compile "com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0" + compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' compile "io.reactivex.rxjava2:rxjava:$rx_java_version" {{/useRxJava2}} compile "io.swagger:swagger-annotations:$swagger_annotations_version" compile "org.apache.oltu.oauth2:org.apache.oltu.oauth2.client:$oltu_version" - {{^java8}} + compile "io.gsonfire:gson-fire:$json_fire_version" + {{#joda}} compile "joda-time:joda-time:$jodatime_version" - {{/java8}} - {{#usePlay24WS}} + {{/joda}} + {{#threetenbp}} + compile "org.threeten:threetenbp:$threetenbp_version" + {{/threetenbp}} + {{#usePlayWS}} compile "com.typesafe.play:play-java-ws_2.11:$play_version" compile "com.squareup.retrofit2:converter-jackson:$retrofit_version" compile "com.fasterxml.jackson.core:jackson-core:$jackson_version" compile "com.fasterxml.jackson.core:jackson-annotations:$jackson_version" compile "com.fasterxml.jackson.datatype:jackson-datatype-{{^java8}}joda{{/java8}}{{#java8}}jsr310{{/java8}}:$jackson_version" - {{/usePlay24WS}} + {{/usePlayWS}} testCompile "junit:junit:$junit_version" } diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/build.sbt.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/build.sbt.mustache index 2146acd4971..5d43e1619d3 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/build.sbt.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/build.sbt.mustache @@ -9,34 +9,43 @@ lazy val root = (project in file(".")). publishArtifact in (Compile, packageDoc) := false, resolvers += Resolver.mavenLocal, libraryDependencies ++= Seq( - {{^usePlay24WS}} "com.squareup.retrofit2" % "retrofit" % "2.3.0" % "compile", "com.squareup.retrofit2" % "converter-scalars" % "2.3.0" % "compile", + {{^usePlayWS}} "com.squareup.retrofit2" % "converter-gson" % "2.3.0" % "compile", - {{/usePlay24WS}} - {{#usePlay24WS}} + {{/usePlayWS}} + {{#usePlayWS}} + {{#play24}} "com.typesafe.play" % "play-java-ws_2.11" % "2.4.11" % "compile", - "com.squareup.retrofit2" % "retrofit" % "2.1.0" % "compile", - "com.squareup.retrofit2" % "converter-scalars" % "2.1.0" % "compile", - "com.squareup.retrofit2" % "converter-gson" % "2.1.0" % "compile", - "com.squareup.retrofit2" % "converter-jackson" % "2.1.0" % "compile", - "com.fasterxml.jackson.core" % "jackson-core" % "2.8.9" % "compile", - "com.fasterxml.jackson.core" % "jackson-annotations" % "2.8.9" % "compile", - "com.fasterxml.jackson.core" % "jackson-databind" % "2.8.9" % "compile", - {{/usePlay24WS}} + "com.fasterxml.jackson.core" % "jackson-core" % "2.6.6" % "compile", + "com.fasterxml.jackson.core" % "jackson-annotations" % "2.6.6" % "compile", + "com.fasterxml.jackson.core" % "jackson-databind" % "2.6.6" % "compile", + {{/play24}} + {{#play25}} + "com.typesafe.play" % "play-java-ws_2.11" % "2.5.15" % "compile", + "com.fasterxml.jackson.core" % "jackson-core" % "2.7.8" % "compile", + "com.fasterxml.jackson.core" % "jackson-annotations" % "2.7.8" % "compile", + "com.fasterxml.jackson.core" % "jackson-databind" % "2.7.8" % "compile", + {{/play25}} + "com.squareup.retrofit2" % "converter-jackson" % "2.3.0" % "compile", + {{/usePlayWS}} {{#useRxJava}} - "com.squareup.retrofit2" % "adapter-rxjava" % "{{^usePlay24WS}}2.2.0{{/usePlay24WS}}{{#usePlay24WS}}2.1.0{{/usePlay24WS}}" % "compile", + "com.squareup.retrofit2" % "adapter-rxjava" % "2.3.0" % "compile", "io.reactivex" % "rxjava" % "1.3.0" % "compile", {{/useRxJava}} {{#useRxJava2}} - "com.jakewharton.retrofit" % "retrofit2-rxjava2-adapter" % "1.0.0" % "compile", + "com.squareup.retrofit2" % "adapter-rxjava2" % "2.3.0" % "compile", "io.reactivex.rxjava2" % "rxjava" % "2.1.1" % "compile", {{/useRxJava2}} "io.swagger" % "swagger-annotations" % "1.5.15" % "compile", "org.apache.oltu.oauth2" % "org.apache.oltu.oauth2.client" % "1.0.1" % "compile", - {{^java8}} + {{#joda}} "joda-time" % "joda-time" % "2.9.9" % "compile", - {{/java8}} + {{/joda}} + {{#threetenbp}} + "org.threeten" % "threetenbp" % "1.3.5" % "compile", + {{/threetenbp}} + "io.gsonfire" % "gson-fire" % "1.8.0" % "compile", "junit" % "junit" % "4.12" % "test", "com.novocode" % "junit-interface" % "0.11" % "test" ) diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/formParams.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/formParams.mustache index 2e237383ee3..06c13e9ea33 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/formParams.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/formParams.mustache @@ -1 +1 @@ -{{#isFormParam}}{{#notFile}}{{#isMultipart}}@retrofit2.http.Part{{/isMultipart}}{{^isMultipart}}@retrofit2.http.Field{{/isMultipart}}("{{baseName}}") {{{dataType}}} {{paramName}}{{/notFile}}{{#isFile}}{{#isMultipart}}@retrofit2.http.Part{{/isMultipart}}{{^isMultipart}}@retrofit2.http.Field{{/isMultipart}}("{{baseName}}\"; filename=\"{{baseName}}") RequestBody {{paramName}}{{/isFile}}{{/isFormParam}} \ No newline at end of file +{{#isFormParam}}{{#notFile}}{{#isMultipart}}@retrofit2.http.Part{{/isMultipart}}{{^isMultipart}}@retrofit2.http.Field{{/isMultipart}}("{{baseName}}") {{{dataType}}} {{paramName}}{{/notFile}}{{#isFile}}{{#isMultipart}}@retrofit2.http.Part{{/isMultipart}}{{^isMultipart}}@retrofit2.http.Field{{/isMultipart}}{{#usePlayWS}} okhttp3.MultipartBody.Part {{/usePlayWS}}{{^usePlayWS}}("{{baseName}}\"; filename=\"{{baseName}}") RequestBody {{/usePlayWS}}{{paramName}}{{/isFile}}{{/isFormParam}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/formParams.mustache.save b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/formParams.mustache.save index a203e99a223..84c4704432a 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/formParams.mustache.save +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/formParams.mustache.save @@ -1 +1 @@ -{{#isFormParam}}{{#notFile}}{{#isMultipart}}retrofit.http@retrofit2.http.Part{{/isMultipart}}{{^isMultipart}}@Field{{/isMultipart}}("{{baseName}}") {{{dataType}}} {{paramName}}{{/notFile}}{{#isFile}}{{#isMultipart}}@Part{{/isMultipart}}{{^isMultipart}}@Field{{/isMultipart}}("{{baseName}}\"; filename=\"{{baseName}}") RequestBody {{paramName}}{{/isFile}}{{/isFormParam}} \ No newline at end of file +{{#isFormParam}}{{#notFile}}{{#isMultipart}}retrofit.http@retrofit2.http.Part{{/isMultipart}}{{^isMultipart}}@Field{{/isMultipart}}("{{baseName}}") {{{dataType}}} {{paramName}}{{/notFile}}{{#isFile}}{{#isMultipart}}@Part{{/isMultipart}}{{^isMultipart}}@Field{{/isMultipart}} okhttp3.MultipartBody.Part {{paramName}}{{/isFile}}{{/isFormParam}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play24/auth/ApiKeyAuth.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play-common/auth/ApiKeyAuth.mustache similarity index 100% rename from modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play24/auth/ApiKeyAuth.mustache rename to modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play-common/auth/ApiKeyAuth.mustache diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play24/Play24CallAdapterFactory.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play24/Play24CallAdapterFactory.mustache index 89fca2613b9..aa4391a9f1d 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play24/Play24CallAdapterFactory.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play24/Play24CallAdapterFactory.mustache @@ -27,34 +27,39 @@ public class Play24CallAdapterFactory extends CallAdapter.Factory { return createAdapter((ParameterizedType) returnType); } - private Type getTypeParam(ParameterizedType type) { - Type[] types = type.getActualTypeArguments(); + private CallAdapter> createAdapter(ParameterizedType returnType) { + Type[] types = returnType.getActualTypeArguments(); if (types.length != 1) { throw new IllegalStateException("Must be exactly one type parameter"); } - Type paramType = types[0]; - if (paramType instanceof WildcardType) { - return ((WildcardType) paramType).getUpperBounds()[0]; + Type resultType = types[0]; + Class rawTypeParam = getRawType(resultType); + + boolean includeResponse = false; + if (rawTypeParam == Response.class) { + if (!(resultType instanceof ParameterizedType)) { + throw new IllegalStateException("Response must be parameterized" + + " as Response"); + } + resultType = ((ParameterizedType) resultType).getActualTypeArguments()[0]; + includeResponse = true; } - return paramType; - } - - private CallAdapter> createAdapter(ParameterizedType returnType) { - Type parameterType = getTypeParam(returnType); - return new ValueAdapter(parameterType); + return new ValueAdapter(resultType, includeResponse); } - + /** - * Adpater that coverts values returned by API interface into Play promises + * Adpater that coverts values returned by API interface into CompletionStage */ - static final class ValueAdapter implements CallAdapter> { + private static final class ValueAdapter implements CallAdapter> { private final Type responseType; + private final boolean includeResponse; - ValueAdapter(Type responseType) { + ValueAdapter(Type responseType, boolean includeResponse) { this.responseType = responseType; + this.includeResponse = includeResponse; } @Override @@ -71,9 +76,13 @@ public class Play24CallAdapterFactory extends CallAdapter.Factory { @Override public void onResponse(Call call, Response response) { if (response.isSuccessful()) { - promise.success(response.body()); + if (includeResponse) { + promise.success((R) response); + } else { + promise.success(response.body()); + } } else { - promise.failure(new Exception(response.errorBody().toString())); + promise.failure(new HttpException(response)); } } @@ -87,4 +96,5 @@ public class Play24CallAdapterFactory extends CallAdapter.Factory { return promise; } } + } diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play24/Play24CallFactory.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play24/Play24CallFactory.mustache index 62df91f7983..5572ac47619 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play24/Play24CallFactory.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play24/Play24CallFactory.mustache @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; /** * Creates {@link Call} instances that invoke underlying {@link WSClient} @@ -120,7 +121,7 @@ public class Play24CallFactory implements okhttp3.Call.Factory { responseCallback.onFailure(call, new IOException(throwable)); } } - + }); } @@ -163,18 +164,21 @@ public class Play24CallFactory implements okhttp3.Call.Factory { @Override public MediaType contentType() { - return MediaType.parse(r.getHeader("Content-Type")); + return Optional.ofNullable(r.getHeader("Content-Type")) + .map(MediaType::parse) + .orElse(null); } @Override public long contentLength() { - return r.getBody().getBytes().length; + return r.asByteArray().length; } @Override public BufferedSource source() { - return new Buffer().write(r.getBody().getBytes()); + return new Buffer().write(r.asByteArray()); } + }); for (Map.Entry> entry : r.getAllHeaders().entrySet()) { @@ -196,7 +200,7 @@ public class Play24CallFactory implements okhttp3.Call.Factory { public void cancel() { throw new UnsupportedOperationException("Not supported"); } - + @Override public PlayWSCall clone() { throw new UnsupportedOperationException("Not supported"); diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play24/api.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play24/api.mustache new file mode 100644 index 00000000000..6ffa06c5385 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play24/api.mustache @@ -0,0 +1,57 @@ +package {{package}}; + +import {{invokerPackage}}.CollectionFormats.*; + +{{#useRxJava}}import rx.Observable;{{/useRxJava}} +{{#useRxJava2}}import io.reactivex.Observable;{{/useRxJava2}} +{{#doNotUseRx}}import retrofit2.Call;{{/doNotUseRx}} +import retrofit2.http.*; + +import okhttp3.RequestBody; + +{{#imports}}import {{import}}; +{{/imports}} + +{{^fullJavaUtil}} +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +{{/fullJavaUtil}} + +import play.libs.F; +import retrofit2.Response; + +{{#operations}} +public interface {{classname}} { + {{#operation}} + /** + * {{summary}} + * {{notes}} +{{#allParams}} + * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} +{{/allParams}} + * @return Call<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Object{{/returnType}}> + */ + {{#formParams}} + {{#-first}} + {{#isMultipart}}@retrofit2.http.Multipart{{/isMultipart}}{{^isMultipart}}@retrofit2.http.FormUrlEncoded{{/isMultipart}} + {{/-first}} + {{/formParams}} + {{^formParams}} + {{#prioritizedContentTypes}} + {{#-first}} + @Headers({ + "Content-Type:{{{mediaType}}}" + }) + {{/-first}} + {{/prioritizedContentTypes}} + {{/formParams}} + @{{httpMethod}}("{{{path}}}") + F.Promise> {{operationId}}({{^allParams}});{{/allParams}} + {{#allParams}}{{>libraries/retrofit2/queryParams}}{{>libraries/retrofit2/pathParams}}{{>libraries/retrofit2/headerParams}}{{>libraries/retrofit2/bodyParams}}{{>libraries/retrofit2/formParams}}{{#hasMore}}, {{/hasMore}}{{^hasMore}} + );{{/hasMore}}{{/allParams}} + + {{/operation}} +} +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play25/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play25/ApiClient.mustache new file mode 100644 index 00000000000..163afd670c1 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play25/ApiClient.mustache @@ -0,0 +1,136 @@ +package {{invokerPackage}}; + +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.*; + +import retrofit2.Retrofit; +import retrofit2.converter.scalars.ScalarsConverterFactory; +import retrofit2.converter.jackson.JacksonConverterFactory; + +import play.libs.Json; +import play.libs.ws.WSClient; + +import {{invokerPackage}}.Play25CallAdapterFactory; +import {{invokerPackage}}.Play25CallFactory; + +import okhttp3.Interceptor; +import {{invokerPackage}}.auth.ApiKeyAuth; +import {{invokerPackage}}.auth.Authentication; + +/** + * API client + */ +public class ApiClient { + + /** Underlying HTTP-client */ + private WSClient wsClient; + + /** Supported auths */ + private Map authentications; + + /** API base path */ + private String basePath = "{{{basePath}}}"; + + public ApiClient(WSClient wsClient) { + this(); + this.wsClient = wsClient; + } + + public ApiClient() { + // Setup authentications (key: authentication name, value: authentication). + authentications = new HashMap<>();{{#authMethods}}{{#isBasic}} + // authentications.put("{{name}}", new HttpBasicAuth());{{/isBasic}}{{#isApiKey}} + authentications.put("{{name}}", new ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}"));{{/isApiKey}}{{#isOAuth}} + // authentications.put("{{name}}", new OAuth());{{/isOAuth}}{{/authMethods}} + // Prevent the authentications from being modified. + authentications = Collections.unmodifiableMap(authentications); + + } + + /** + * Creates a retrofit2 client for given API interface + */ + public S createService(Class serviceClass) { + if(!basePath.endsWith("/")) { + basePath = basePath + "/"; + } + + Map extraHeaders = new HashMap<>(); + List extraQueryParams = new ArrayList<>(); + + for (String authName : authentications.keySet()) { + Authentication auth = authentications.get(authName); + if (auth == null) throw new RuntimeException("Authentication undefined: " + authName); + + auth.applyToParams(extraQueryParams, extraHeaders); + } + + return new Retrofit.Builder() + .baseUrl(basePath) + .addConverterFactory(ScalarsConverterFactory.create()) + .addConverterFactory(JacksonConverterFactory.create(Json.mapper())) + .callFactory(new Play25CallFactory(wsClient, extraHeaders, extraQueryParams)) + .addCallAdapterFactory(new Play25CallAdapterFactory()) + .build() + .create(serviceClass); + } + + /** + * Helper method to set API base path + */ + public ApiClient setBasePath(String basePath) { + this.basePath = basePath; + return this; + } + + /** + * Get authentications (key: authentication name, value: authentication). + */ + public Map getAuthentications() { + return authentications; + } + + /** + * Get authentication for the given name. + * + * @param authName The authentication name + * @return The authentication, null if not found + */ + public Authentication getAuthentication(String authName) { + return authentications.get(authName); + } + + /** + * Helper method to set API key value for the first API key authentication. + */ + public ApiClient setApiKey(String apiKey) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKey(apiKey); + return this; + } + } + + throw new RuntimeException("No API key authentication configured!"); + } + + /** + * Helper method to set API key prefix for the first API key authentication. + */ + public ApiClient setApiKeyPrefix(String apiKeyPrefix) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); + return this; + } + } + + throw new RuntimeException("No API key authentication configured!"); + } + + +} + + diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play25/Play25CallAdapterFactory.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play25/Play25CallAdapterFactory.mustache new file mode 100644 index 00000000000..e72ee4aa7ab --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play25/Play25CallAdapterFactory.mustache @@ -0,0 +1,117 @@ +package {{invokerPackage}}; + +import java.util.concurrent.CompletionStage; +import retrofit2.*; + +import java.lang.annotation.Annotation; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.function.Function; + +/** + * Creates {@link CallAdapter} instances that convert {@link Call} into {@link java.util.concurrent.CompletionStage} + */ +public class Play25CallAdapterFactory extends CallAdapter.Factory { + + private Function exceptionConverter = Function.identity(); + + public Play25CallAdapterFactory() { + } + + public Play25CallAdapterFactory( + Function exceptionConverter) { + this.exceptionConverter = exceptionConverter; + } + + @Override + public CallAdapter get(Type returnType, Annotation[] annotations, Retrofit retrofit) { + if (!(returnType instanceof ParameterizedType)) { + return null; + } + + ParameterizedType type = (ParameterizedType) returnType; + if (type.getRawType() != CompletionStage.class) { + return null; + } + + return createAdapter((ParameterizedType) returnType); + } + + private CallAdapter> createAdapter(ParameterizedType returnType) { + // Get CompletionStage type argument + Type[] types = returnType.getActualTypeArguments(); + if (types.length != 1) { + throw new IllegalStateException("Must be exactly one type parameter"); + } + + Type resultType = types[0]; + Class rawTypeParam = getRawType(resultType); + + boolean includeResponse = false; + if (rawTypeParam == Response.class) { + if (!(resultType instanceof ParameterizedType)) { + throw new IllegalStateException("Response must be parameterized" + + " as Response"); + } + resultType = ((ParameterizedType) resultType).getActualTypeArguments()[0]; + includeResponse = true; + } + + return new ValueAdapter(resultType, includeResponse, exceptionConverter); + } + + /** + * Adpater that coverts values returned by API interface into CompletionStage + */ + private static final class ValueAdapter implements CallAdapter> { + + private final Type responseType; + private final boolean includeResponse; + private Function exceptionConverter; + + ValueAdapter(Type responseType, boolean includeResponse, + Function exceptionConverter) { + this.responseType = responseType; + this.includeResponse = includeResponse; + this.exceptionConverter = exceptionConverter; + } + + @Override + public Type responseType() { + return responseType; + } + + @Override + public CompletionStage adapt(final Call call) { + final CompletableFuture promise = new CompletableFuture(); + + call.enqueue(new Callback() { + + @Override + public void onResponse(Call call, Response response) { + if (response.isSuccessful()) { + if (includeResponse) { + promise.complete((R) response); + } else { + promise.complete(response.body()); + } + } else { + promise.completeExceptionally(exceptionConverter.apply(new HttpException(response))); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + promise.completeExceptionally(t); + } + + }); + + return promise; + } + } +} + diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play25/Play25CallFactory.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play25/Play25CallFactory.mustache new file mode 100644 index 00000000000..93df7a2718d --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play25/Play25CallFactory.mustache @@ -0,0 +1,228 @@ +package {{invokerPackage}}; + +import okhttp3.*; +import okio.Buffer; +import okio.BufferedSource; +import play.libs.ws.WSClient; +import play.libs.ws.WSRequest; +import play.libs.ws.WSResponse; +import play.libs.ws.WSRequestFilter; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletionStage; + +/** + * Creates {@link Call} instances that invoke underlying {@link WSClient} + */ +public class Play25CallFactory implements okhttp3.Call.Factory { + + /** PlayWS http client */ + private final WSClient wsClient; + + /** Extra headers to add to request */ + private Map extraHeaders = new HashMap<>(); + + /** Extra query parameters to add to request */ + private List extraQueryParams = new ArrayList<>(); + + /** Filters (interceptors) */ + private List filters = new ArrayList<>(); + + public Play25CallFactory(WSClient wsClient) { + this.wsClient = wsClient; + } + + public Play25CallFactory(WSClient wsClient, List filters) { + this.wsClient = wsClient; + this.filters.addAll(filters); + } + + public Play25CallFactory(WSClient wsClient, Map extraHeaders, + List extraQueryParams) { + this.wsClient = wsClient; + + this.extraHeaders.putAll(extraHeaders); + this.extraQueryParams.addAll(extraQueryParams); + } + + @Override + public Call newCall(Request request) { + // add extra headers + Request.Builder rb = request.newBuilder(); + for (Map.Entry header : this.extraHeaders.entrySet()) { + rb.addHeader(header.getKey(), header.getValue()); + } + + // add extra query params + if (!this.extraQueryParams.isEmpty()) { + String newQuery = request.url().uri().getQuery(); + for (Pair queryParam : this.extraQueryParams) { + String param = String.format("%s=%s", queryParam.getName(), queryParam.getValue()); + if (newQuery == null) { + newQuery = param; + } else { + newQuery += "&" + param; + } + } + + URI newUri; + try { + newUri = new URI(request.url().uri().getScheme(), request.url().uri().getAuthority(), + request.url().uri().getPath(), newQuery, request.url().uri().getFragment()); + rb.url(newUri.toURL()); + } catch (MalformedURLException | URISyntaxException e) { + throw new RuntimeException("Error while updating an url", e); + } + } + + return new PlayWSCall(wsClient, this.filters, rb.build()); + } + + /** + * Call implementation that delegates to Play WS Client + */ + static class PlayWSCall implements Call { + + private final WSClient wsClient; + private WSRequest wsRequest; + private List filters; + + private final Request request; + + public PlayWSCall(WSClient wsClient, List filters, Request request) { + this.wsClient = wsClient; + this.request = request; + this.filters = filters; + } + + @Override + public Request request() { + return request; + } + + @Override + public void enqueue(final okhttp3.Callback responseCallback) { + final Call call = this; + final CompletionStage promise = executeAsync(); + + promise.whenCompleteAsync((v, t) -> { + if (t != null) { + if (t instanceof IOException) { + responseCallback.onFailure(call, (IOException) t); + } else { + responseCallback.onFailure(call, new IOException(t)); + } + } else { + try { + responseCallback.onResponse(call, PlayWSCall.this.toWSResponse(v)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }, play.libs.concurrent.HttpExecution.defaultContext()); + } + + CompletionStage executeAsync() { + try { + wsRequest = wsClient.url(request.url().uri().toString()); + addHeaders(wsRequest); + if (request.body() != null) { + addBody(wsRequest); + } + filters.stream().forEach(f -> wsRequest.withRequestFilter(f)); + + return wsRequest.execute(request.method()); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + private void addHeaders(WSRequest wsRequest) { + for(Map.Entry> entry : request.headers().toMultimap().entrySet()) { + List values = entry.getValue(); + for (String value : values) { + wsRequest.setHeader(entry.getKey(), value); + } + } + } + + private void addBody(WSRequest wsRequest) throws IOException { + Buffer buffer = new Buffer(); + request.body().writeTo(buffer); + wsRequest.setBody(buffer.inputStream()); + + MediaType mediaType = request.body().contentType(); + if (mediaType != null) { + wsRequest.setContentType(mediaType.toString()); + } + } + + private Response toWSResponse(final WSResponse r) { + final Response.Builder builder = new Response.Builder(); + builder.request(request) + .code(r.getStatus()) + .body(new ResponseBody() { + + @Override + public MediaType contentType() { + return Optional.ofNullable(r.getHeader("Content-Type")) + .map(MediaType::parse) + .orElse(null); + } + + @Override + public long contentLength() { + return r.asByteArray().length; + } + + @Override + public BufferedSource source() { + return new Buffer().write(r.asByteArray()); + } + + }); + + for (Map.Entry> entry : r.getAllHeaders().entrySet()) { + for (String value : entry.getValue()) { + builder.addHeader(entry.getKey(), value); + } + } + + builder.protocol(Protocol.HTTP_1_1); + return builder.build(); + } + + @Override + public Response execute() throws IOException { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public void cancel() { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public PlayWSCall clone() { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public boolean isExecuted() { + return false; + } + + @Override + public boolean isCanceled() { + return false; + } + } +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play25/api.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play25/api.mustache new file mode 100644 index 00000000000..b16f1779b9a --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/play25/api.mustache @@ -0,0 +1,57 @@ +package {{package}}; + +import {{invokerPackage}}.CollectionFormats.*; + +{{#useRxJava}}import rx.Observable;{{/useRxJava}} +{{#useRxJava2}}import io.reactivex.Observable;{{/useRxJava2}} +{{#doNotUseRx}}import retrofit2.Call;{{/doNotUseRx}} +import retrofit2.http.*; + +import okhttp3.RequestBody; + +{{#imports}}import {{import}}; +{{/imports}} + +{{^fullJavaUtil}} +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +{{/fullJavaUtil}} + +import java.util.concurrent.*; +import retrofit2.Response; + +{{#operations}} +public interface {{classname}} { + {{#operation}} + /** + * {{summary}} + * {{notes}} +{{#allParams}} + * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} +{{/allParams}} + * @return Call<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Object{{/returnType}}> + */ + {{#formParams}} + {{#-first}} + {{#isMultipart}}@retrofit2.http.Multipart{{/isMultipart}}{{^isMultipart}}@retrofit2.http.FormUrlEncoded{{/isMultipart}} + {{/-first}} + {{/formParams}} + {{^formParams}} + {{#prioritizedContentTypes}} + {{#-first}} + @Headers({ + "Content-Type:{{{mediaType}}}" + }) + {{/-first}} + {{/prioritizedContentTypes}} + {{/formParams}} + @{{httpMethod}}("{{{path}}}") + CompletionStage> {{operationId}}({{^allParams}});{{/allParams}} + {{#allParams}}{{>libraries/retrofit2/queryParams}}{{>libraries/retrofit2/pathParams}}{{>libraries/retrofit2/headerParams}}{{>libraries/retrofit2/bodyParams}}{{>libraries/retrofit2/formParams}}{{#hasMore}}, {{/hasMore}}{{^hasMore}} + );{{/hasMore}}{{/allParams}} + + {{/operation}} +} +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/pom.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/pom.mustache index d9933716813..b506ac89517 100644 --- a/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/pom.mustache @@ -1,314 +1,355 @@ - 4.0.0 - {{groupId}} - {{artifactId}} - jar - {{artifactId}} - {{artifactVersion}} - {{artifactUrl}} - {{artifactDescription}} - - {{scmConnection}} - {{scmDeveloperConnection}} - {{scmUrl}} - - - 2.2.0 - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + {{artifactUrl}} + {{artifactDescription}} + + {{scmConnection}} + {{scmDeveloperConnection}} + {{scmUrl}} + - - - {{licenseName}} - {{licenseUrl}} - repo - - + + + {{licenseName}} + {{licenseUrl}} + repo + + - - - {{developerName}} - {{developerEmail}} - {{developerOrganization}} - {{developerOrganizationUrl}} - - + + + {{developerName}} + {{developerEmail}} + {{developerOrganization}} + {{developerOrganizationUrl}} + + - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.12 - - - - loggerPath - conf/log4j.properties - - - -Xms512m -Xmx1500m - methods - pertest - - - - maven-dependency-plugin - - - package - - copy-dependencies - - - ${project.build.directory}/lib - - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 2.2 - - - - jar - test-jar - - - - - - + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + pertest + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + - - org.codehaus.mojo - build-helper-maven-plugin - 1.10 - - - add_sources - generate-sources - - add-source - - - - src/main/java - - - - - add_test_sources - generate-test-sources - - add-test-source - - - - src/test/java - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.4 - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - + + + org.apache.maven.plugins + maven-jar-plugin + 2.2 + + + + jar + test-jar + + + + + + - - - sign-artifacts - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - - sign-artifacts - verify - - sign - - - - + + org.codehaus.mojo + build-helper-maven-plugin + 1.10 + + + add_sources + generate-sources + + add-source + + + + + src/main/java + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + + src/test/java + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + - - - + + + + + sign-artifacts + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + + - - - io.swagger - swagger-annotations - ${swagger-core-version} - - - com.squareup.retrofit2 - converter-gson - ${retrofit-version} - - - com.squareup.retrofit2 - retrofit - ${retrofit-version} - - - com.squareup.retrofit2 - converter-scalars - ${retrofit-version} - - - org.apache.oltu.oauth2 - org.apache.oltu.oauth2.client - ${oltu-version} - - {{^java8}} - - joda-time - joda-time - ${jodatime-version} - - {{/java8}} - {{#useRxJava}} - - io.reactivex - rxjava - ${rxjava-version} - - - com.squareup.retrofit2 - adapter-rxjava - ${retrofit-version} - - {{/useRxJava}} - {{#useRxJava2}} - - io.reactivex.rxjava2 - rxjava - ${rxjava-version} - - - com.jakewharton.retrofit - retrofit2-rxjava2-adapter - 1.0.0 - - {{/useRxJava2}} + + + io.swagger + swagger-annotations + ${swagger-core-version} + + + com.squareup.retrofit2 + converter-gson + ${retrofit-version} + + + com.squareup.retrofit2 + retrofit + ${retrofit-version} + + + com.squareup.retrofit2 + converter-scalars + ${retrofit-version} + + + org.apache.oltu.oauth2 + org.apache.oltu.oauth2.client + ${oltu-version} + + + io.gsonfire + gson-fire + ${gson-fire-version} + + {{#joda}} + + joda-time + joda-time + ${jodatime-version} + + {{/joda}} + {{#threetenbp}} + + org.threeten + threetenbp + ${threetenbp-version} + + {{/threetenbp}} + {{#useRxJava}} + + io.reactivex + rxjava + ${rxjava-version} + + + com.squareup.retrofit2 + adapter-rxjava + ${retrofit-version} + + {{/useRxJava}} + {{#useRxJava2}} + + io.reactivex.rxjava2 + rxjava + ${rxjava-version} + + + com.squareup.retrofit2 + adapter-rxjava2 + ${retrofit-version} + + {{/useRxJava2}} - {{#usePlay24WS}} - - - com.squareup.retrofit2 - converter-jackson - ${retrofit-version} - - - com.fasterxml.jackson.core - jackson-core - ${jackson-version} - - - com.fasterxml.jackson.core - jackson-annotations - ${jackson-version} - - - com.fasterxml.jackson.core - jackson-databind - ${jackson-version} - - - com.fasterxml.jackson.datatype - jackson-datatype-{{^java8}}joda{{/java8}}{{#java8}}jsr310{{/java8}} - ${jackson-version} - - {{#withXml}} + {{#usePlayWS}} + + + com.squareup.retrofit2 + converter-jackson + ${retrofit-version} + + + com.fasterxml.jackson.core + jackson-core + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-version} + + + com.fasterxml.jackson.datatype + jackson-datatype-{{^java8}}joda{{/java8}}{{#java8}}jsr310{{/java8}} + ${jackson-version} + + {{#withXml}} - - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - ${jackson-version} - + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + ${jackson-version} + - {{/withXml}} - - com.typesafe.play - play-java-ws_2.11 - ${play-version} - - {{/usePlay24WS}} + {{/withXml}} + + com.typesafe.play + play-java-ws_2.11 + ${play-version} + + {{/usePlayWS}} - {{#parcelableModel}} - - - com.google.android - android - 4.1.1.4 - provided - - {{/parcelableModel}} + {{#parcelableModel}} + + + com.google.android + android + 4.1.1.4 + provided + + {{/parcelableModel}} - - - junit - junit - ${junit-version} - test - - - - UTF-8 - {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} - ${java.version} - ${java.version} - 1.5.15 - {{#usePlay24WS}} - 2.8.9 - 2.4.11 - {{/usePlay24WS}} - 2.3.0 - {{#useRxJava}} - 1.3.0 - {{/useRxJava}} - {{#useRxJava2}} - 2.1.1 - {{/useRxJava2}} - {{^java8}} - 2.9.9 - {{/java8}} - 1.0.1 - 4.12 - + + + junit + junit + ${junit-version} + test + + + + UTF-8 + {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} + ${java.version} + ${java.version} + 1.8.0 + 1.5.15 + {{#usePlayWS}} + {{#play24}} + 2.6.6 + 2.4.11 + {{/play24}} + {{#play25}} + 2.7.8 + 2.5.15 + {{/play25}} + {{/usePlayWS}} + 2.3.0 + {{#useRxJava}} + 1.3.0 + {{/useRxJava}} + {{#useRxJava2}} + 2.1.1 + {{/useRxJava2}} + {{#joda}} + 2.9.9 + {{/joda}} + {{#threetenbp}} + 1.3.5 + {{/threetenbp}} + 1.0.1 + 4.12 + diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/ApiClient.mustache new file mode 100644 index 00000000000..1de56ec9ca9 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/ApiClient.mustache @@ -0,0 +1,600 @@ +package {{invokerPackage}}; + +import {{invokerPackage}}.auth.Authentication; +import {{invokerPackage}}.auth.HttpBasicAuth; +import {{invokerPackage}}.auth.ApiKeyAuth; +import {{invokerPackage}}.auth.OAuth; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import io.vertx.core.*; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.file.AsyncFile; +import io.vertx.core.file.FileSystem; +import io.vertx.core.file.OpenOptions; +import io.vertx.core.http.HttpHeaders; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.json.Json; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.client.HttpRequest; +import io.vertx.ext.web.client.HttpResponse; +import io.vertx.ext.web.client.WebClient; +import io.vertx.ext.web.client.WebClientOptions; + +import java.text.DateFormat; +import java.util.*; +import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.util.stream.Collectors.toMap; + +{{>generatedAnnotation}} +public class ApiClient { + + private static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?"); + private static final OpenOptions FILE_DOWNLOAD_OPTIONS = new OpenOptions().setCreate(true).setTruncateExisting(true); + + private final Vertx vertx; + private final JsonObject config; + private final String identifier; + + private MultiMap defaultHeaders = MultiMap.caseInsensitiveMultiMap(); + private Map authentications; + private String basePath = "{{{basePath}}}"; + private DateFormat dateFormat; + private ObjectMapper objectMapper; + private String downloadsDir = ""; + + public ApiClient(Vertx vertx, JsonObject config) { + Objects.requireNonNull(vertx, "Vertx must not be null"); + Objects.requireNonNull(config, "Config must not be null"); + + this.vertx = vertx; + + // Use RFC3339 format for date and datetime. + // See http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14 + this.dateFormat = new RFC3339DateFormat(); + + // Use UTC as the default time zone. + this.dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + + // Build object mapper + this.objectMapper = new ObjectMapper(); + this.objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + this.objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false); + this.objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + this.objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); + this.objectMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING); + this.objectMapper.registerModule(new JavaTimeModule()); + this.objectMapper.setDateFormat(dateFormat); + + // Setup authentications (key: authentication name, value: authentication). + this.authentications = new HashMap<>();{{#authMethods}}{{#isBasic}} + authentications.put("{{name}}", new HttpBasicAuth());{{/isBasic}}{{#isApiKey}} + authentications.put("{{name}}", new ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}"));{{/isApiKey}}{{#isOAuth}} + authentications.put("{{name}}", new OAuth());{{/isOAuth}}{{/authMethods}} + // Prevent the authentications from being modified. + this.authentications = Collections.unmodifiableMap(authentications); + + // Configurations + this.basePath = config.getString("basePath", this.basePath); + this.downloadsDir = config.getString("downloadsDir", this.downloadsDir); + this.config = config; + this.identifier = UUID.randomUUID().toString(); + } + + public Vertx getVertx() { + return vertx; + } + + public ObjectMapper getObjectMapper() { + return objectMapper; + } + + public ApiClient setObjectMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + return this; + } + + public synchronized WebClient getWebClient() { + String webClientIdentifier = "web-client-" + identifier; + WebClient webClient = Vertx.currentContext().get(webClientIdentifier); + if (webClient == null) { + webClient = buildWebClient(vertx, config); + Vertx.currentContext().put(webClientIdentifier, webClient); + } + return webClient; + } + + public String getBasePath() { + return basePath; + } + + public ApiClient setBasePath(String basePath) { + this.basePath = basePath; + return this; + } + + public String getDownloadsDir() { + return downloadsDir; + } + + public ApiClient setDownloadsDir(String downloadsDir) { + this.downloadsDir = downloadsDir; + return this; + } + + public MultiMap getDefaultHeaders() { + return defaultHeaders; + } + + public ApiClient addDefaultHeader(String key, String value) { + defaultHeaders.add(key, value); + return this; + } + + /** + * Get authentications (key: authentication name, value: authentication). + * + * @return Map of authentication object + */ + public Map getAuthentications() { + return authentications; + } + + /** + * Get authentication for the given name. + * + * @param authName The authentication name + * @return The authentication, null if not found + */ + public Authentication getAuthentication(String authName) { + return authentications.get(authName); + } + + /** + * Helper method to set username for the first HTTP basic authentication. + * + * @param username Username + */ + public ApiClient setUsername(String username) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setUsername(username); + return this; + } + } + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + /** + * Helper method to set password for the first HTTP basic authentication. + * + * @param password Password + */ + public ApiClient setPassword(String password) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setPassword(password); + return this; + } + } + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + /** + * Helper method to set API key value for the first API key authentication. + * + * @param apiKey API key + */ + public ApiClient setApiKey(String apiKey) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKey(apiKey); + return this; + } + } + throw new RuntimeException("No API key authentication configured!"); + } + + /** + * Helper method to set API key prefix for the first API key authentication. + * + * @param apiKeyPrefix API key prefix + */ + public ApiClient setApiKeyPrefix(String apiKeyPrefix) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); + return this; + } + } + throw new RuntimeException("No API key authentication configured!"); + } + + /** + * Helper method to set access token for the first OAuth2 authentication. + * + * @param accessToken Access token + */ + public ApiClient setAccessToken(String accessToken) { + for (Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).setAccessToken(accessToken); + return this; + } + } + throw new RuntimeException("No OAuth2 authentication configured!"); + } + + /** + * Format the given Date object into string. + * + * @param date Date + * @return Date in string format + */ + public String formatDate(Date date) { + return dateFormat.format(date); + } + + /** + * Format the given parameter object into string. + * + * @param param Object + * @return Object in string format + */ + public String parameterToString(Object param) { + if (param == null) { + return ""; + } else if (param instanceof Date) { + return formatDate((Date) param); + } else if (param instanceof Collection) { + StringBuilder b = new StringBuilder(); + for (Object o : (Collection) param) { + if (b.length() > 0) { + b.append(','); + } + b.append(String.valueOf(o)); + } + return b.toString(); + } else { + return String.valueOf(param); + } + } + + /* + * Format to {@code Pair} objects. + * @param collectionFormat Collection format + * @param name Name + * @param value Value + * @return List of pairs + */ + public List parameterToPairs(String collectionFormat, String name, Object value) { + List params = new ArrayList(); + + // preconditions + if (name == null || name.isEmpty() || value == null) return params; + + Collection valueCollection; + if (value instanceof Collection) { + valueCollection = (Collection) value; + } else { + params.add(new Pair(name, parameterToString(value))); + return params; + } + + if (valueCollection.isEmpty()) { + return params; + } + + // get the collection format (default: csv) + String format = (collectionFormat == null || collectionFormat.isEmpty() ? "csv" : collectionFormat); + + // create the params based on the collection format + if ("multi".equals(format)) { + for (Object item : valueCollection) { + params.add(new Pair(name, parameterToString(item))); + } + return params; + } + + String delimiter = ","; + if ("csv".equals(format)) { + delimiter = ","; + } else if ("ssv".equals(format)) { + delimiter = " "; + } else if ("tsv".equals(format)) { + delimiter = "\t"; + } else if ("pipes".equals(format)) { + delimiter = "|"; + } + + StringBuilder sb = new StringBuilder(); + for (Object item : valueCollection) { + sb.append(delimiter); + sb.append(parameterToString(item)); + } + + params.add(new Pair(name, sb.substring(1))); + + return params; + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * + * @param mime MIME + * @return True if the MIME type is JSON + */ + private boolean isJsonMime(String mime) { + String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"; + return mime != null && (mime.matches(jsonMime) || mime.equalsIgnoreCase("application/json-patch+json")); + } + + /** + * Select the Accept header's value from the given accepts array: + * if JSON exists in the given array, use it; + * otherwise use all of them (joining into a string) + * + * @param accepts The accepts array to select from + * @return The Accept header to use. If the given array is empty, + * null will be returned (not to set the Accept header explicitly). + */ + protected String selectHeaderAccept(String[] accepts) { + if (accepts.length == 0) { + return null; + } + for (String accept : accepts) { + if (isJsonMime(accept)) { + return accept; + } + } + return StringUtil.join(accepts, ","); + } + + /** + * Select the Content-Type header's value from the given array: + * if JSON exists in the given array, use it; + * otherwise use the first one of the array. + * + * @param contentTypes The Content-Type array to select from + * @return The Content-Type header to use. If the given array is empty, + * JSON will be used. + */ + protected String selectHeaderContentType(String[] contentTypes) { + if (contentTypes.length == 0) { + return "application/json"; + } + for (String contentType : contentTypes) { + if (isJsonMime(contentType)) { + return contentType; + } + } + return contentTypes[0]; + } + + public void sendBody(HttpRequest request, + Handler>> responseHandler, + Object body) { + if (body instanceof byte[]) { + Buffer buffer = Buffer.buffer((byte[]) body); + request.sendBuffer(buffer, responseHandler); + } else if (body instanceof AsyncFile) { + AsyncFile file = (AsyncFile) body; + request.sendStream(file, responseHandler); + } else { + request.sendJson(body, responseHandler); + } + } + + /** + * Invoke API by sending HTTP request with the given options. + * + * @param Type + * @param path The sub-path of the HTTP URL + * @param method The request method, one of "GET", "POST", "PUT", "HEAD" and "DELETE" + * @param queryParams The query parameters + * @param body The request body object + * @param headerParams The header parameters + * @param formParams The form parameters + * @param accepts The request's Accept headers + * @param contentTypes The request's Content-Type headers + * @param authNames The authentications to apply + * @param returnType The return type into which to deserialize the response + * @param resultHandler The asynchronous response handler + */ + public void invokeAPI(String path, String method, List queryParams, Object body, MultiMap headerParams, + Map formParams, String[] accepts, String[] contentTypes, String[] authNames, + TypeReference returnType, Handler> resultHandler) { + + updateParamsForAuth(authNames, queryParams, headerParams); + + if (accepts != null) { + headerParams.add(HttpHeaders.ACCEPT, selectHeaderAccept(accepts)); + } + + if (contentTypes != null) { + headerParams.add(HttpHeaders.CONTENT_TYPE, selectHeaderContentType(contentTypes)); + } + + HttpMethod httpMethod = HttpMethod.valueOf(method); + HttpRequest request = getWebClient().requestAbs(httpMethod, basePath + path); + + if (httpMethod == HttpMethod.PATCH) { + request.putHeader("X-HTTP-Method-Override", "PATCH"); + } + + queryParams.forEach(entry -> { + if (entry.getValue() != null) { + request.addQueryParam(entry.getName(), entry.getValue()); + } + }); + + headerParams.forEach(entry -> { + if (entry.getValue() != null) { + request.putHeader(entry.getKey(), entry.getValue()); + } + }); + + defaultHeaders.forEach(entry -> { + if (entry.getValue() != null) { + request.putHeader(entry.getKey(), entry.getValue()); + } + }); + + Handler>> responseHandler = buildResponseHandler(returnType, resultHandler); + if (body != null) { + sendBody(request, responseHandler, body); + } else if (formParams != null && !formParams.isEmpty()) { + Map formMap = formParams.entrySet().stream().collect(toMap(Map.Entry::getKey, entry -> parameterToString(entry.getValue()))); + MultiMap form = MultiMap.caseInsensitiveMultiMap().addAll(formMap); + request.sendForm(form, responseHandler); + } else { + request.send(responseHandler); + } + } + + /** + * Sanitize filename by removing path. + * e.g. ../../sun.gif becomes sun.gif + * + * @param filename The filename to be sanitized + * @return The sanitized filename + */ + protected String sanitizeFilename(String filename) { + return filename.replaceAll(".*[/\\\\]", ""); + } + + /** + * Create a filename from the given headers. + * When the headers have no "Content-Disposition" information, a random UUID name is generated. + * + * @param headers The HTTP response headers + * @return The filename + */ + protected String generateFilename(MultiMap headers) { + String filename = UUID.randomUUID().toString(); + String contentDisposition = headers.get("Content-Disposition"); + if (contentDisposition != null && !contentDisposition.isEmpty()) { + Matcher matcher = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition); + if (matcher.find()) { + filename = sanitizeFilename(matcher.group(1)); + } + } + return filename; + } + + /** + * File Download handling. + * + * @param response The HTTP response + * @param handler The response handler + */ + protected void handleFileDownload(HttpResponse response, Handler> handler) { + FileSystem fs = getVertx().fileSystem(); + + String filename = generateFilename(response.headers()); + Consumer fileHandler = directory -> { + fs.open(directory + filename, FILE_DOWNLOAD_OPTIONS, asyncFileResult -> { + if (asyncFileResult.succeeded()) { + AsyncFile asyncFile = asyncFileResult.result(); + asyncFile.write(response.bodyAsBuffer()); + //noinspection unchecked + handler.handle(Future.succeededFuture((T) asyncFile)); + } else { + handler.handle(ApiException.fail(asyncFileResult.cause())); + } + }); + }; + + String dir = getDownloadsDir(); + if (dir != null && !dir.isEmpty()) { + fs.mkdirs(dir, mkdirResult -> { + String sanitizedFolder = dir.endsWith("/") ? dir : dir + "/"; + fileHandler.accept(sanitizedFolder); + }); + } else { + fileHandler.accept(""); + } + } + + /** + * Build a response handler for the HttpResponse. + * + * @param returnType The return type + * @param handler The response handler + * @return The HTTP response handler + */ + protected Handler>> buildResponseHandler(TypeReference returnType, + Handler> handler) { + return response -> { + AsyncResult result; + if (response.succeeded()) { + HttpResponse httpResponse = response.result(); + if (httpResponse.statusCode() / 100 == 2) { + if (httpResponse.statusCode() == 204 || returnType == null) { + result = Future.succeededFuture(null); + } else { + T resultContent; + if ("byte[]".equals(returnType.getType().toString())) { + resultContent = (T) httpResponse.body().getBytes(); + } else if (AsyncFile.class.equals(returnType.getType())) { + handleFileDownload(httpResponse, handler); + return; + } else { + resultContent = Json.decodeValue(httpResponse.body(), returnType); + } + result = Future.succeededFuture(resultContent); + } + } else { + result = ApiException.fail(httpResponse.statusMessage(), httpResponse.statusCode(), httpResponse.headers(), httpResponse.bodyAsString()); + } + } else if (response.cause() instanceof ApiException) { + result = Future.failedFuture(response.cause()); + } else { + result = ApiException.fail(500, response.cause() != null ? response.cause().getMessage() : null); + } + handler.handle(result); + }; + } + + /** + * Build the WebClient used to make HTTP requests. + * + * @param vertx Vertx + * @return WebClient + */ + protected WebClient buildWebClient(Vertx vertx, JsonObject config) { + + if (!config.containsKey("userAgent")) { + config.put("userAgent", "{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{{artifactVersion}}}/java{{/httpUserAgent}}"); + } + + return WebClient.create(vertx, new WebClientOptions(config)); + } + + + /** + * Update query and header parameters based on authentication settings. + * + * @param authNames The authentications to apply + */ + protected void updateParamsForAuth(String[] authNames, List queryParams, MultiMap headerParams) { + for (String authName : authNames) { + Authentication auth = authentications.get(authName); + if (auth == null) throw new RuntimeException("Authentication undefined: " + authName); + auth.applyToParams(queryParams, headerParams); + } + } +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/Configuration.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/Configuration.mustache new file mode 100644 index 00000000000..17710ae1600 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/Configuration.mustache @@ -0,0 +1,42 @@ +package {{invokerPackage}}; + +import io.vertx.core.Vertx; +import io.vertx.core.json.JsonObject; + +import java.util.Objects; + +public class Configuration { + + private static ApiClient defaultApiClient = null; + + /** + * Setup the default API client. + * Will be used by API instances when a client is not provided. + * + * @return Default API client + */ + public synchronized static ApiClient setupDefaultApiClient(Vertx vertx, JsonObject config) { + defaultApiClient = new ApiClient(vertx, config); + return defaultApiClient; + } + + /** + * Get the default API client, which would be used when creating API + * instances without providing an API client. + * + * @return Default API client + */ + public synchronized static ApiClient getDefaultApiClient() { + return defaultApiClient; + } + + /** + * Set the default API client, which would be used when creating API + * instances without providing an API client. + * + * @param apiClient API client + */ + public synchronized static void setDefaultApiClient(ApiClient apiClient) { + defaultApiClient = apiClient; + } +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/api.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/api.mustache new file mode 100644 index 00000000000..dcf9df4cc70 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/api.mustache @@ -0,0 +1,19 @@ +package {{package}}; + +{{#imports}}import {{import}}; +{{/imports}} +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.json.JsonObject; + +import java.util.*; + +public interface {{classname}} { + + {{#operations}} + {{#operation}} + void {{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}Handler> handler); + + {{/operation}} + {{/operations}} +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/apiException.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/apiException.mustache new file mode 100644 index 00000000000..6e9bbdbb88d --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/apiException.mustache @@ -0,0 +1,110 @@ +{{>licenseInfo}} + +package {{invokerPackage}}; + +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.MultiMap; + +{{>generatedAnnotation}} +public class ApiException extends{{#useRuntimeException}} RuntimeException {{/useRuntimeException}}{{^useRuntimeException}} Exception {{/useRuntimeException}}{ + private int code = 0; + private MultiMap responseHeaders = null; + private String responseBody = null; + + + public static AsyncResult fail(int failureCode, String message) { + return Future.failedFuture(new ApiException(failureCode, message)); + } + + public static AsyncResult fail(Throwable throwable) { + return Future.failedFuture(new ApiException(throwable)); + } + + public static AsyncResult fail(String message) { + return Future.failedFuture(new ApiException(message)); + } + + public static AsyncResult fail(String message, Throwable throwable, int code, MultiMap responseHeaders) { + return Future.failedFuture(new ApiException(message, throwable, code, responseHeaders, null)); + } + + public static AsyncResult fail(String message, Throwable throwable, int code, MultiMap responseHeaders, String responseBody) { + return Future.failedFuture(new ApiException(message, throwable, code, responseHeaders, responseBody)); + } + + public static AsyncResult fail(String message, int code, MultiMap responseHeaders, String responseBody) { + return Future.failedFuture(new ApiException(message, (Throwable) null, code, responseHeaders, responseBody)); + } + + public static AsyncResult fail(int code, MultiMap responseHeaders, String responseBody) { + return Future.failedFuture(new ApiException((String) null, (Throwable) null, code, responseHeaders, responseBody)); + } + + public ApiException() {} + + public ApiException(Throwable throwable) { + super(throwable); + } + + public ApiException(String message) { + super(message); + } + + public ApiException(String message, Throwable throwable, int code, MultiMap responseHeaders, String responseBody) { + super(message, throwable); + this.code = code; + this.responseHeaders = responseHeaders; + this.responseBody = responseBody; + } + + public ApiException(String message, int code, MultiMap responseHeaders, String responseBody) { + this(message, (Throwable) null, code, responseHeaders, responseBody); + } + + public ApiException(String message, Throwable throwable, int code, MultiMap responseHeaders) { + this(message, throwable, code, responseHeaders, null); + } + + public ApiException(int code, MultiMap responseHeaders, String responseBody) { + this((String) null, (Throwable) null, code, responseHeaders, responseBody); + } + + public ApiException(int code, String message) { + super(message); + this.code = code; + } + + public ApiException(int code, String message, MultiMap responseHeaders, String responseBody) { + this(code, message); + this.responseHeaders = responseHeaders; + this.responseBody = responseBody; + } + + /** + * Get the HTTP status code. + * + * @return HTTP status code + */ + public int getCode() { + return code; + } + + /** + * Get the HTTP response headers. + * + * @return A map of list of string + */ + public MultiMap getResponseHeaders() { + return responseHeaders; + } + + /** + * Get the HTTP response body. + * + * @return Response body in the form of string + */ + public String getResponseBody() { + return responseBody; + } +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/apiImpl.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/apiImpl.mustache new file mode 100644 index 00000000000..f4b5a723922 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/apiImpl.mustache @@ -0,0 +1,91 @@ +package {{package}}; + +{{#imports}}import {{import}}; +{{/imports}} + +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.MultiMap; +import io.vertx.core.json.JsonObject; + +import com.fasterxml.jackson.core.type.TypeReference; + +import java.util.*; + +import {{invokerPackage}}.ApiClient; +import {{invokerPackage}}.ApiException; +import {{invokerPackage}}.Configuration; +import {{invokerPackage}}.Pair; + +{{>generatedAnnotation}} +{{#operations}} +public class {{classname}}Impl implements {{classname}} { + + private ApiClient {{localVariablePrefix}}apiClient; + + public {{classname}}Impl() { + this(null); + } + + public {{classname}}Impl(ApiClient apiClient) { + this.{{localVariablePrefix}}apiClient = apiClient != null ? apiClient : Configuration.getDefaultApiClient(); + } + + public ApiClient getApiClient() { + return {{localVariablePrefix}}apiClient; + } + + public void setApiClient(ApiClient apiClient) { + this.{{localVariablePrefix}}apiClient = apiClient; + } + + {{#operation}} + /** + * {{summary}} + * {{notes}} + {{#allParams}} + * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} + {{/allParams}} + * @param resultHandler Asynchronous result handler + */ + public void {{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}Handler> resultHandler) { + Object {{localVariablePrefix}}localVarBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}; + {{#allParams}}{{#required}} + // verify the required parameter '{{paramName}}' is set + if ({{paramName}} == null) { + resultHandler.handle(ApiException.fail(400, "Missing the required parameter '{{paramName}}' when calling {{operationId}}")); + return; + } + {{/required}}{{/allParams}} + // create path and map variables + String {{localVariablePrefix}}localVarPath = "{{{path}}}"{{#pathParams}}.replaceAll("\\{" + "{{baseName}}" + "\\}", {{{paramName}}}.toString()){{/pathParams}}; + + // query params + List {{localVariablePrefix}}localVarQueryParams = new ArrayList<>(); + {{#queryParams}} + {{localVariablePrefix}}localVarQueryParams.addAll({{localVariablePrefix}}apiClient.parameterToPairs("{{#collectionFormat}}{{{collectionFormat}}}{{/collectionFormat}}", "{{baseName}}", {{paramName}})); + {{/queryParams}} + + // header params + MultiMap {{localVariablePrefix}}localVarHeaderParams = MultiMap.caseInsensitiveMultiMap(); + {{#headerParams}}if ({{paramName}} != null) + {{localVariablePrefix}}localVarHeaderParams.add("{{baseName}}", {{localVariablePrefix}}apiClient.parameterToString({{paramName}})); + {{/headerParams}} + + // form params + // TODO: sending files within multipart/form-data is not supported yet (because of vertx web-client) + Map {{localVariablePrefix}}localVarFormParams = new HashMap<>(); + {{#formParams}}if ({{paramName}} != null) {{localVariablePrefix}}localVarFormParams.put("{{baseName}}", {{paramName}}); + {{/formParams}} + + String[] {{localVariablePrefix}}localVarAccepts = { {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }; + String[] {{localVariablePrefix}}localVarContentTypes = { {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} }; + String[] {{localVariablePrefix}}localVarAuthNames = new String[] { {{#authMethods}}"{{name}}"{{#hasMore}}, {{/hasMore}}{{/authMethods}} }; + {{#returnType}} + TypeReference<{{{returnType}}}> {{localVariablePrefix}}localVarReturnType = new TypeReference<{{{returnType}}}>() {}; + {{localVariablePrefix}}apiClient.invokeAPI({{localVariablePrefix}}localVarPath, "{{httpMethod}}", {{localVariablePrefix}}localVarQueryParams, {{localVariablePrefix}}localVarBody, {{localVariablePrefix}}localVarHeaderParams, {{localVariablePrefix}}localVarFormParams, {{localVariablePrefix}}localVarAccepts, {{localVariablePrefix}}localVarContentTypes, {{localVariablePrefix}}localVarAuthNames, {{localVariablePrefix}}localVarReturnType, resultHandler);{{/returnType}}{{^returnType}} + {{localVariablePrefix}}apiClient.invokeAPI({{localVariablePrefix}}localVarPath, "{{httpMethod}}", {{localVariablePrefix}}localVarQueryParams, {{localVariablePrefix}}localVarBody, {{localVariablePrefix}}localVarHeaderParams, {{localVariablePrefix}}localVarFormParams, {{localVariablePrefix}}localVarAccepts, {{localVariablePrefix}}localVarContentTypes, {{localVariablePrefix}}localVarAuthNames, null, resultHandler);{{/returnType}} + } + {{/operation}} +} +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/api_test.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/api_test.mustache new file mode 100644 index 00000000000..753df13cd1d --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/api_test.mustache @@ -0,0 +1,70 @@ +{{>licenseInfo}} +package {{package}}; + +{{#imports}}import {{import}}; +{{/imports}} + +import {{invokerPackage}}.Configuration; + +import org.junit.Test; +import org.junit.Ignore; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.runner.RunWith; + +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.json.JsonObject; +import io.vertx.core.Vertx; +import io.vertx.ext.unit.junit.VertxUnitRunner; +import io.vertx.ext.unit.junit.RunTestOnContext; +import io.vertx.ext.unit.TestContext; +import io.vertx.ext.unit.Async; + +{{^fullJavaUtil}} +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +{{/fullJavaUtil}} + +/** + * API tests for {{classname}} + */ +@RunWith(VertxUnitRunner.class) +@Ignore +public class {{classname}}Test { + + private {{classname}} api; + + @Rule + public RunTestOnContext rule = new RunTestOnContext(); + + @BeforeClass + public void setupApiClient() { + JsonObject config = new JsonObject(); + Vertx vertx = rule.vertx(); + Configuration.setupDefaultApiClient(vertx, config); + + api = new {{classname}}Impl(); + } + {{#operations}}{{#operation}} + /** + * {{summary}} + * {{notes}} + * + * @param context Vertx test context for doing assertions + */ + @Test + public void {{operationId}}Test(TestContext context) { + Async async = context.async(); + {{#allParams}} + {{{dataType}}} {{paramName}} = null; + {{/allParams}} + api.{{operationId}}({{#allParams}}{{paramName}}, {{/allParams}}result -> { + // TODO: test validations + async.complete(); + }); + } + {{/operation}}{{/operations}} +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/auth/ApiKeyAuth.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/auth/ApiKeyAuth.mustache new file mode 100644 index 00000000000..43b1866e460 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/auth/ApiKeyAuth.mustache @@ -0,0 +1,64 @@ +{{>licenseInfo}} + +package {{invokerPackage}}.auth; + +import {{invokerPackage}}.Pair; +import io.vertx.core.MultiMap; + +import java.util.List; + +{{>generatedAnnotation}} +public class ApiKeyAuth implements Authentication { + private final String location; + private final String paramName; + + private String apiKey; + private String apiKeyPrefix; + + public ApiKeyAuth(String location, String paramName) { + this.location = location; + this.paramName = paramName; + } + + public String getLocation() { + return location; + } + + public String getParamName() { + return paramName; + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public String getApiKeyPrefix() { + return apiKeyPrefix; + } + + public void setApiKeyPrefix(String apiKeyPrefix) { + this.apiKeyPrefix = apiKeyPrefix; + } + + @Override + public void applyToParams(List queryParams, MultiMap headerParams) { + if (apiKey == null) { + return; + } + String value; + if (apiKeyPrefix != null) { + value = apiKeyPrefix + " " + apiKey; + } else { + value = apiKey; + } + if ("query".equals(location)) { + queryParams.add(new Pair(paramName, value)); + } else if ("header".equals(location)) { + headerParams.add(paramName, value); + } + } +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/auth/Authentication.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/auth/Authentication.mustache new file mode 100644 index 00000000000..5dccb93f2ac --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/auth/Authentication.mustache @@ -0,0 +1,18 @@ +{{>licenseInfo}} + +package {{invokerPackage}}.auth; + +import {{invokerPackage}}.Pair; +import io.vertx.core.MultiMap; + +import java.util.List; + +public interface Authentication { + /** + * Apply authentication settings to header and query params. + * + * @param queryParams List of query parameters + * @param headerParams Map of header parameters + */ + void applyToParams(List queryParams, MultiMap headerParams); +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/auth/HttpBasicAuth.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/auth/HttpBasicAuth.mustache new file mode 100644 index 00000000000..43862b508b6 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/auth/HttpBasicAuth.mustache @@ -0,0 +1,40 @@ +{{>licenseInfo}} + +package {{invokerPackage}}.auth; + +import {{invokerPackage}}.Pair; +import io.vertx.core.MultiMap; +import java.util.Base64; +import java.nio.charset.StandardCharsets; +import java.util.List; + +{{>generatedAnnotation}} +public class HttpBasicAuth implements Authentication { + private String username; + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public void applyToParams(List queryParams, MultiMap headerParams) { + if (username == null && password == null) { + return; + } + String str = (username == null ? "" : username) + ":" + (password == null ? "" : password); + headerParams.add("Authorization", "Basic " + Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8))); + } +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/auth/OAuth.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/auth/OAuth.mustache new file mode 100644 index 00000000000..f3c5b34ee11 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/auth/OAuth.mustache @@ -0,0 +1,28 @@ +{{>licenseInfo}} + +package {{invokerPackage}}.auth; + +import {{invokerPackage}}.Pair; +import io.vertx.core.MultiMap; + +import java.util.List; + +{{>generatedAnnotation}} +public class OAuth implements Authentication { + private String accessToken; + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + @Override + public void applyToParams(List queryParams, MultiMap headerParams) { + if (accessToken != null) { + headerParams.add("Authorization", "Bearer " + accessToken); + } + } +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/auth/OAuthFlow.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/auth/OAuthFlow.mustache new file mode 100644 index 00000000000..002e9572f33 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/auth/OAuthFlow.mustache @@ -0,0 +1,7 @@ +{{>licenseInfo}} + +package {{invokerPackage}}.auth; + +public enum OAuthFlow { + accessCode, implicit, password, application +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/build.gradle.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/build.gradle.mustache new file mode 100644 index 00000000000..69e643f3660 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/build.gradle.mustache @@ -0,0 +1,53 @@ +apply plugin: 'idea' +apply plugin: 'eclipse' + +group = '{{groupId}}' +version = '{{artifactVersion}}' + +repositories { + jcenter() +} + +apply plugin: 'java' +apply plugin: 'maven' + +sourceCompatibility = JavaVersion.VERSION_1_8 +targetCompatibility = JavaVersion.VERSION_1_8 + +install { + repositories.mavenInstaller { + pom.artifactId = '{{artifactId}}' + } +} + +task execute(type:JavaExec) { + main = System.getProperty('mainClass') + classpath = sourceSets.main.runtimeClasspath +} + +ext { + swagger_annotations_version = "1.5.15" + jackson_version = "{{^threetenbp}}2.8.9{{/threetenbp}}{{#threetenbp}}2.6.4{{/threetenbp}}" + vertx_version = "3.4.2" + junit_version = "4.12" +} + +dependencies { + compile "io.swagger:swagger-annotations:$swagger_annotations_version" + compile "io.vertx:vertx-web-client:$vertx_version" + compile "io.vertx:vertx-rx-java:$vertx_version" + compile "com.fasterxml.jackson.core:jackson-core:$jackson_version" + compile "com.fasterxml.jackson.core:jackson-annotations:$jackson_version" + compile "com.fasterxml.jackson.core:jackson-databind:$jackson_version" + {{#joda}} + compile "com.fasterxml.jackson.datatype:jackson-datatype-joda:$jackson_version" + {{/joda}} + {{#java8}} + compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version" + {{/java8}} + {{#threetenbp}} + compile "com.github.joschi.jackson:jackson-datatype-threetenbp:$jackson_version" + {{/threetenbp}} + testCompile "junit:junit:$junit_version" + testCompile "io.vertx:vertx-unit:$vertx_version" +} diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/pom.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/pom.mustache new file mode 100644 index 00000000000..51abd96bf82 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/pom.mustache @@ -0,0 +1,279 @@ + + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + {{artifactUrl}} + {{artifactDescription}} + + {{scmConnection}} + {{scmDeveloperConnection}} + {{scmUrl}} + + + + + {{licenseName}} + {{licenseUrl}} + repo + + + + + + {{developerName}} + {{developerEmail}} + {{developerOrganization}} + {{developerOrganizationUrl}} + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + pertest + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + + jar + test-jar + + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + + + add_sources + generate-sources + + add-source + + + + + src/main/java + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + + src/test/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + + + + + sign-artifacts + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + + + + + + io.swagger + swagger-annotations + ${swagger-core-version} + + + + + io.vertx + vertx-rx-java + ${vertx-version} + + + io.vertx + vertx-web-client + ${vertx-version} + + + + + com.fasterxml.jackson.core + jackson-core + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-version} + + {{#joda}} + + com.fasterxml.jackson.datatype + jackson-datatype-joda + ${jackson-version} + + {{/joda}} + {{#java8}} + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson-version} + + {{/java8}} + {{#threetenbp}} + + com.github.joschi.jackson + jackson-datatype-threetenbp + ${jackson-version} + + {{/threetenbp}} + + + + junit + junit + ${junit-version} + test + + + io.vertx + vertx-unit + ${vertx-version} + test + + + + + UTF-8 + 3.4.2 + 1.5.16 + {{^threetenbp}}2.8.9{{/threetenbp}}{{#threetenbp}}2.6.4{{/threetenbp}} + 4.12 + + diff --git a/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/rxApiImpl.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/rxApiImpl.mustache new file mode 100644 index 00000000000..09ca96895d6 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/vertx/rxApiImpl.mustache @@ -0,0 +1,58 @@ +package {{package}}.rxjava; + +{{#imports}}import {{import}}; +{{/imports}} + +import java.util.*; + +import rx.Single; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; + +{{>generatedAnnotation}} +{{#operations}} +public class {{classname}} { + + private final {{package}}.{{classname}} delegate; + + public {{classname}}({{package}}.{{classname}} delegate) { + this.delegate = delegate; + } + + public {{package}}.{{classname}} getDelegate() { + return delegate; + } + + {{#operation}} + /** + * {{summary}} + * {{notes}} + {{#allParams}} + * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} + {{/allParams}} + * @param resultHandler Asynchronous result handler + */ + public void {{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}Handler> resultHandler) { + delegate.{{operationId}}({{#allParams}}{{paramName}}, {{/allParams}}resultHandler); + } + + /** + * {{summary}} + * {{notes}} + {{#allParams}} + * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} + {{/allParams}} + * @return Asynchronous result handler (RxJava Single) + */ + public Single<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> rx{{operationIdCamelCase}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) { + return Single.create(new io.vertx.rx.java.SingleOnSubscribeAdapter<>(fut -> { + delegate.{{operationId}}({{#allParams}}{{paramName}}, {{/allParams}}fut); + })); + } + {{/operation}} + + public static {{classname}} newInstance({{package}}.{{classname}} arg) { + return arg != null ? new {{classname}}(arg) : null; + } +} +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/Java/model.mustache b/modules/swagger-codegen/src/main/resources/Java/model.mustache index a0fb114b74e..fd9f614d537 100644 --- a/modules/swagger-codegen/src/main/resources/Java/model.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/model.mustache @@ -16,8 +16,7 @@ import java.io.Serializable; {{/serializableModel}} {{#jackson}} {{#withXml}} -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.*; {{/withXml}} {{/jackson}} {{#withXml}} diff --git a/modules/swagger-codegen/src/main/resources/Java/pojo.mustache b/modules/swagger-codegen/src/main/resources/Java/pojo.mustache index d0a4c70f3a8..fa0a84b2c65 100644 --- a/modules/swagger-codegen/src/main/resources/Java/pojo.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/pojo.mustache @@ -24,7 +24,15 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#parcela {{#jackson}} @JsonProperty("{{baseName}}") {{#withXml}} + {{^isContainer}} @JacksonXmlProperty({{#isXmlAttribute}}isAttribute = true, {{/isXmlAttribute}}{{#xmlNamespace}}namespace="{{xmlNamespace}}", {{/xmlNamespace}}localName = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") + {{/isContainer}} + {{#isContainer}} + {{#isXmlWrapped}} + // items.xmlName={{items.xmlName}} + @JacksonXmlElementWrapper(useWrapping = {{isXmlWrapped}}, {{#xmlNamespace}}namespace="{{xmlNamespace}}", {{/xmlNamespace}}localName = "{{#items.xmlName}}{{items.xmlName}}{{/items.xmlName}}{{^items.xmlName}}{{items.baseName}}{{/items.xmlName}}") + {{/isXmlWrapped}} + {{/isContainer}} {{/withXml}} {{/jackson}} {{#withXml}} @@ -32,7 +40,20 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#parcela @XmlAttribute(name = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") {{/isXmlAttribute}} {{^isXmlAttribute}} + {{^isContainer}} @XmlElement({{#xmlNamespace}}namespace="{{xmlNamespace}}", {{/xmlNamespace}}name = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") + {{/isContainer}} + {{#isContainer}} + // Is a container wrapped={{isXmlWrapped}} + {{#items}} + // items.name={{name}} items.baseName={{baseName}} items.xmlName={{xmlName}} items.xmlNamespace={{xmlNamespace}} + // items.example={{example}} items.type={{datatype}} + @XmlElement({{#xmlNamespace}}namespace="{{xmlNamespace}}", {{/xmlNamespace}}name = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") + {{/items}} + {{#isXmlWrapped}} + @XmlElementWrapper({{#xmlNamespace}}namespace="{{xmlNamespace}}", {{/xmlNamespace}}name = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") + {{/isXmlWrapped}} + {{/isContainer}} {{/isXmlAttribute}} {{/withXml}} {{#gson}} @@ -174,7 +195,7 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#parcela } return o.toString().replace("\n", "\n "); } - + {{#parcelableModel}} public void writeToParcel(Parcel out, int flags) { {{#parent}} super.writeToParcel(out, flags); {{/parent}} {{#vars}} @@ -197,7 +218,7 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#parcela {{/isPrimitiveType}} {{/vars}} } - + public int describeContents() { return 0; } diff --git a/modules/swagger-codegen/src/main/resources/Java/pom.mustache b/modules/swagger-codegen/src/main/resources/Java/pom.mustache index bcc0cc4b13e..dc5b544e7c3 100644 --- a/modules/swagger-codegen/src/main/resources/Java/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/pom.mustache @@ -1,319 +1,336 @@ - 4.0.0 - {{groupId}} - {{artifactId}} - jar - {{artifactId}} - {{artifactVersion}} - {{artifactUrl}} - {{artifactDescription}} - - {{scmConnection}} - {{scmDeveloperConnection}} - {{scmUrl}} - - - 2.2.0 - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + {{artifactUrl}} + {{artifactDescription}} + + {{scmConnection}} + {{scmDeveloperConnection}} + {{scmUrl}} + - - - {{licenseName}} - {{licenseUrl}} - repo - - + + + {{licenseName}} + {{licenseUrl}} + repo + + - - - {{developerName}} - {{developerEmail}} - {{developerOrganization}} - {{developerOrganizationUrl}} - - + + + {{developerName}} + {{developerEmail}} + {{developerOrganization}} + {{developerOrganizationUrl}} + + - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.12 - - - - loggerPath - conf/log4j.properties - - - -Xms512m -Xmx1500m - methods - pertest - - - - maven-dependency-plugin - - - package - - copy-dependencies - - - ${project.build.directory}/lib - - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 2.2 - - - - jar - test-jar - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - 1.10 - - - add_sources - generate-sources - - add-source - - - - src/main/java - - - - - add_test_sources - generate-test-sources - - add-test-source - - - - src/test/java - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.6.1 - - {{#java8}} - 1.8 - 1.8 - {{/java8}} - {{^java8}} - 1.7 - 1.7 - {{/java8}} - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.4 - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - - - - - sign-artifacts - + - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - - sign-artifacts - verify - - sign - - - - - - - - + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + pertest + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + - - - io.swagger - swagger-annotations - ${swagger-annotations-version} - + + + org.apache.maven.plugins + maven-jar-plugin + 2.2 + + + + jar + test-jar + + + + + + - - - com.sun.jersey - jersey-client - ${jersey-version} - - - com.sun.jersey.contribs - jersey-multipart - ${jersey-version} - - - - - com.fasterxml.jackson.core - jackson-core - ${jackson-version} - - - com.fasterxml.jackson.core - jackson-annotations - ${jackson-version} - - - com.fasterxml.jackson.core - jackson-databind - ${jackson-version} - - - com.fasterxml.jackson.jaxrs - jackson-jaxrs-json-provider - ${jackson-version} - - {{#withXml}} - - - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - ${jackson-version} - + + org.codehaus.mojo + build-helper-maven-plugin + 1.10 + + + add_sources + generate-sources + + add-source + + + + + src/main/java + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + + src/test/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + {{#java8}} + + 1.8 + 1.8 + {{/java8}} + {{^java8}} + + 1.7 + 1.7 + {{/java8}} + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + - {{/withXml}} - {{#java8}} - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - ${jackson-version} - - {{/java8}} - {{^java8}} - - com.fasterxml.jackson.datatype - jackson-datatype-joda - ${jackson-version} - - - joda-time - joda-time - ${jodatime-version} - + + + sign-artifacts + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + + - - - com.brsanthu - migbase64 - 2.2 - - {{/java8}} + + + io.swagger + swagger-annotations + ${swagger-annotations-version} + - {{#supportJava6}} - - org.apache.commons - commons-lang3 - ${commons_lang3_version} - + + + com.sun.jersey + jersey-client + ${jersey-version} + + + com.sun.jersey.contribs + jersey-multipart + ${jersey-version} + - - commons-io - commons-io - ${commons_io_version} - - {{/supportJava6}} -{{#useBeanValidation}} - - - javax.validation - validation-api - 1.1.0.Final - provided - -{{/useBeanValidation}} + + + com.fasterxml.jackson.core + jackson-core + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson-version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-version} + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + ${jackson-version} + + {{#withXml}} - {{#parcelableModel}} - - - com.google.android - android - 4.1.1.4 - provided - - {{/parcelableModel}} + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + ${jackson-version} + - - - junit - junit - ${junit-version} - test - - - - UTF-8 - 1.5.15 - 1.19.4 - 2.8.9 - {{^java8}} - 2.9.9 - {{/java8}} - {{#supportJava6}} - 2.5 - 3.6 - {{/supportJava6}} - 1.0.0 - 4.12 - + {{/withXml}} + {{#joda}} + + com.fasterxml.jackson.datatype + jackson-datatype-joda + ${jackson-version} + + {{/joda}} + {{#java8}} + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson-version} + + {{/java8}} + {{#threetenbp}} + + com.github.joschi.jackson + jackson-datatype-threetenbp + ${jackson-version} + + {{/threetenbp}} + {{^java8}} + + + com.brsanthu + migbase64 + 2.2 + + {{/java8}} + {{#supportJava6}} + + org.apache.commons + commons-lang3 + ${commons_lang3_version} + + + commons-io + commons-io + ${commons_io_version} + + {{/supportJava6}} + {{#useBeanValidation}} + + + javax.validation + validation-api + 1.1.0.Final + provided + + {{/useBeanValidation}} + {{#parcelableModel}} + + + com.google.android + android + 4.1.1.4 + provided + + {{/parcelableModel}} + + + junit + junit + ${junit-version} + test + + + + UTF-8 + 1.5.15 + 1.19.4 + {{#supportJava6}} + 2.5 + 3.6 + {{/supportJava6}} + {{^threetenbp}}2.7.5{{/threetenbp}}{{#threetenbp}}2.6.4{{/threetenbp}} + 1.0.0 + 4.12 + diff --git a/modules/swagger-codegen/src/main/resources/Java/xmlAnnotation.mustache b/modules/swagger-codegen/src/main/resources/Java/xmlAnnotation.mustache index fd81a4cf5d8..04566fa1167 100644 --- a/modules/swagger-codegen/src/main/resources/Java/xmlAnnotation.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/xmlAnnotation.mustache @@ -1,6 +1,6 @@ {{#withXml}} -{{#jackson}} -@JacksonXmlRootElement({{#xmlNamespace}}namespace="{{xmlNamespace}}", {{/xmlNamespace}}localName = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{classname}}{{/xmlName}}") -{{/jackson}} + @XmlRootElement({{#xmlNamespace}}namespace="{{xmlNamespace}}", {{/xmlNamespace}}name = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{classname}}{{/xmlName}}") -@XmlAccessorType(XmlAccessType.FIELD){{/withXml}} \ No newline at end of file +@XmlAccessorType(XmlAccessType.FIELD) +{{#jackson}} +@JacksonXmlRootElement({{#xmlNamespace}}namespace="{{xmlNamespace}}", {{/xmlNamespace}}localName = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{classname}}{{/xmlName}}"){{/jackson}}{{/withXml}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaInflector/pom.mustache b/modules/swagger-codegen/src/main/resources/JavaInflector/pom.mustache index 15fc98b2cb6..1737c94615b 100644 --- a/modules/swagger-codegen/src/main/resources/JavaInflector/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaInflector/pom.mustache @@ -1,4 +1,5 @@ - + org.sonatype.oss oss-parent @@ -10,23 +11,40 @@ jar {{artifactId}} {{artifactVersion}} - - 2.2.0 - - - {{licenseName}} - {{licenseUrl}} - repo - + + {{licenseName}} + {{licenseUrl}} + repo + - + install target ${project.artifactId}-${project.version} + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + org.codehaus.mojo build-helper-maven-plugin @@ -40,7 +58,8 @@ - src/gen/java + + src/gen/java @@ -118,6 +137,7 @@ + UTF-8 1.0.0 1.0.14 9.2.9.v20150224 diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/api.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/api.mustache index 20cd3c87db5..d4542532e29 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/api.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/api.mustache @@ -10,6 +10,7 @@ import io.swagger.jaxrs.*; {{#imports}}import {{import}}; {{/imports}} +import java.util.Map; import java.util.List; import {{package}}.NotFoundException; @@ -62,7 +63,7 @@ public class {{classname}} { {{#subresourceOperation}}@Path("{{{path}}}"){{/subresourceOperation}} {{#hasConsumes}}@Consumes({ {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} }){{/hasConsumes}} {{#hasProduces}}@Produces({ {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }){{/hasProduces}} - @io.swagger.annotations.ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}", response = {{{returnType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = { + @io.swagger.annotations.ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}", response = {{{returnBaseType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = { {{#authMethods}}@io.swagger.annotations.Authorization(value = "{{name}}"{{#isOAuth}}, scopes = { {{#scopes}}@io.swagger.annotations.AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{#hasMore}}, {{/hasMore}}{{/scopes}} @@ -70,7 +71,7 @@ public class {{classname}} { {{/hasMore}}{{/authMethods}} }{{/hasAuthMethods}}, tags={ {{#vendorExtensions.x-tags}}"{{tag}}",{{/vendorExtensions.x-tags}} }) @io.swagger.annotations.ApiResponses(value = { {{#responses}} - @io.swagger.annotations.ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{returnType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}){{#hasMore}}, + @io.swagger.annotations.ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{baseType}}}.class{{#containerType}}, responseContainer = "{{{containerType}}}"{{/containerType}}){{#hasMore}}, {{/hasMore}}{{/responses}} }) public Response {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}},{{/allParams}}@Context SecurityContext securityContext) throws NotFoundException { diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf-cdi/api.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf-cdi/api.mustache index 63fdf2c8534..49567903a52 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf-cdi/api.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf-cdi/api.mustache @@ -17,6 +17,7 @@ import java.io.InputStream; import org.apache.cxf.jaxrs.ext.multipart.Attachment; import org.apache.cxf.jaxrs.ext.multipart.Multipart; +import java.util.Map; import java.util.List; {{#useBeanValidation}} import javax.validation.constraints.*; @@ -40,9 +41,9 @@ public class {{classname}} { @{{httpMethod}} {{#subresourceOperation}}@Path("{{{path}}}"){{/subresourceOperation}} - {{#hasConsumes}}@Consumes({ {{#consumes}}"{{mediaType}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} }){{/hasConsumes}} - {{#hasProduces}}@Produces({ {{#produces}}"{{mediaType}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }){{/hasProduces}} - @ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}", response = {{{returnType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = { + {{#hasConsumes}}@Consumes({ {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} }){{/hasConsumes}} + {{#hasProduces}}@Produces({ {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }){{/hasProduces}} + @ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}", response = {{{returnBaseType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = { {{#authMethods}}@Authorization(value = "{{name}}"{{#isOAuth}}, scopes = { {{#scopes}}@AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{#hasMore}}, {{/hasMore}}{{/scopes}} @@ -50,7 +51,7 @@ public class {{classname}} { {{/hasMore}}{{/authMethods}} }{{/hasAuthMethods}}, tags={ {{#vendorExtensions.x-tags}}"{{tag}}"{{#hasMore}}, {{/hasMore}}{{/vendorExtensions.x-tags}} }) @ApiResponses(value = { {{#responses}} - @ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{returnType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}){{#hasMore}},{{/hasMore}}{{/responses}} }) + @ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{baseType}}}.class{{#containerType}}, responseContainer = "{{{containerType}}}"{{/containerType}}){{#hasMore}},{{/hasMore}}{{/responses}} }) public Response {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) { return delegate.{{nickname}}({{#allParams}}{{#isFile}}{{paramName}}InputStream, {{paramName}}Detail{{/isFile}}{{^isFile}}{{paramName}}{{/isFile}}, {{/allParams}}securityContext); } diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf-cdi/pom.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf-cdi/pom.mustache index 78bea69ca7a..99a7dea4153 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf-cdi/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf-cdi/pom.mustache @@ -74,7 +74,7 @@ io.swagger swagger-annotations - [1.5.3,2) + [1.5.3,1.5.16] {{#useBeanValidation}} diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/api.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/api.mustache index bcaedef14b3..8bc7a22fd76 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/api.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/api.mustache @@ -22,6 +22,16 @@ import javax.validation.constraints.*; import javax.validation.Valid; {{/useBeanValidation}} +{{#appName}} +/** + * {{{appName}}} + * + {{#appDescription}} + *

{{{appDescription}}} + * + {{/appDescription}} + */ +{{/appName}} @Path("{{^useAnnotatedBasePath}}/{{/useAnnotatedBasePath}}{{#useAnnotatedBasePath}}{{contextPath}}{{/useAnnotatedBasePath}}") @Api(value = "/", description = "{{description}}") {{#addConsumesProducesJson}} @@ -32,6 +42,16 @@ public interface {{classname}} { {{#operations}} {{#operation}} + {{#summary}} + /** + * {{summary}} + * + {{#notes}} + * {{notes}} + * + {{/notes}} + */ + {{/summary}} @{{httpMethod}} {{#subresourceOperation}}@Path("{{{path}}}"){{/subresourceOperation}} {{#hasConsumes}} @@ -42,7 +62,7 @@ public interface {{classname}} { {{/hasProduces}} @ApiOperation(value = "{{{summary}}}", tags={ {{#vendorExtensions.x-tags}}"{{tag}}"{{#hasMore}}, {{/hasMore}}{{/vendorExtensions.x-tags}} }) @ApiResponses(value = { {{#responses}} - @ApiResponse(code = {{{code}}}, message = "{{{message}}}"{{^vendorExtensions.x-java-is-response-void}}, response = {{{baseType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{/vendorExtensions.x-java-is-response-void}}){{#hasMore}},{{/hasMore}}{{/responses}} }) + @ApiResponse(code = {{{code}}}, message = "{{{message}}}"{{^vendorExtensions.x-java-is-response-void}}, response = {{{baseType}}}.class{{#containerType}}, responseContainer = "{{{containerType}}}"{{/containerType}}{{/vendorExtensions.x-java-is-response-void}}){{#hasMore}},{{/hasMore}}{{/responses}} }) public {{>returnTypes}} {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); {{/operation}} } diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/apiServiceImpl.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/apiServiceImpl.mustache index d216ed02538..f677e3a835e 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/apiServiceImpl.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/apiServiceImpl.mustache @@ -25,9 +25,29 @@ import org.springframework.stereotype.Service; {{/useSpringAnnotationConfig}} {{#description}} {{/description}} +{{#appName}} +/** + * {{{appName}}} + * + {{#appDescription}} + *

{{{appDescription}}} + {{/appDescription}} + * + */ +{{/appName}} public class {{classname}}ServiceImpl implements {{classname}} { {{#operations}} {{#operation}} + {{#summary}} + /** + * {{summary}} + * + {{#notes}} + * {{notes}} + * + {{/notes}} + */ + {{/summary}} public {{>returnTypes}} {{nickname}}({{#allParams}}{{>queryParamsImpl}}{{>pathParamsImpl}}{{>headerParamsImpl}}{{>bodyParamsImpl}}{{>formParamsImpl}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) { // TODO: Implement... diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/api_test.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/api_test.mustache index d32c2e3a964..67a7db05386 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/api_test.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/api_test.mustache @@ -42,7 +42,15 @@ import org.springframework.test.context.web.WebAppConfiguration; /** - * API tests for {{classname}} + {{#appName}} + * {{{appName}}} + * + {{/appName}} + {{#appDescription}} + *

{{{appDescription}}} + * + {{/appDescription}} + * API tests for {{classname}} */ {{#generateSpringBootApplication}} @RunWith(SpringJUnit4ClassRunner.class) @@ -91,10 +99,14 @@ public class {{classname}}Test { {{#operations}}{{#operation}} /** + {{#summary}} * {{summary}} * + {{#notes}} * {{notes}} * + {{/notes}} + {{/summary}} * @throws ApiException * if the Api call fails */ diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/pojo.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/pojo.mustache index 8f56eb4c601..f1bcb38e57d 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/pojo.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/pojo.mustache @@ -6,16 +6,20 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlEnum; import javax.xml.bind.annotation.XmlEnumValue; +import com.fasterxml.jackson.annotation.JsonProperty; -{{#useJaxbAnnotations}} +{{#withXml}} @XmlAccessorType(XmlAccessType.FIELD) {{#hasVars}} @XmlType(name = "{{classname}}", propOrder = { {{#vars}}"{{name}}"{{^-last}}, {{/-last}}{{/vars}} }){{/hasVars}} {{^hasVars}}@XmlType(name = "{{classname}}"){{/hasVars}} {{^parent}}@XmlRootElement(name="{{classname}}"){{/parent}} -{{/useJaxbAnnotations}} +{{/withXml}} {{#description}} +/** + * {{{description}}} + **/ @ApiModel(description="{{{description}}}") {{/description}} public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} { @@ -24,12 +28,22 @@ public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} { {{>enumClass}}{{/isContainer}}{{/isEnum}}{{#items.isEnum}}{{#items}} {{^isContainer}}{{>enumClass}}{{/isContainer}}{{/items}}{{/items.isEnum}} -{{#useJaxbAnnotations}} +{{#withXml}} @XmlElement(name="{{baseName}}"{{#required}}, required = {{required}}{{/required}}) -{{/useJaxbAnnotations}} +{{/withXml}} @ApiModelProperty({{#example}}example = "{{{example}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}value = "{{{description}}}") - private {{{datatypeWithEnum}}} {{name}} = {{{defaultValue}}};{{/vars}} - +{{#description}} + /** + * {{{description}}} + **/ +{{/description}} +{{#isContainer}} + private {{{datatypeWithEnum}}} {{name}}{{#required}} = {{{defaultValue}}}{{/required}}{{^required}} = null{{/required}}; +{{/isContainer}} +{{^isContainer}} + private {{{datatypeWithEnum}}} {{name}} = {{{defaultValue}}}; +{{/isContainer}} + {{/vars}} {{#vars}} /** {{#description}} @@ -46,12 +60,22 @@ public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} { {{/maximum}} * @return {{name}} **/ + @JsonProperty("{{baseName}}") {{#vendorExtensions.extraAnnotation}} {{{vendorExtensions.extraAnnotation}}} {{/vendorExtensions.extraAnnotation}} -{{#useBeanValidation}}{{>beanValidation}}{{/useBeanValidation}} public {{{datatypeWithEnum}}} {{getter}}() { +{{#useBeanValidation}}{{>beanValidation}}{{/useBeanValidation}} {{#isEnum}}{{^isListContainer}}{{^isMapContainer}}public {{datatype}} {{getter}}() { + if ({{name}} == null) { + return null; + } + return {{name}}.value(); + }{{/isMapContainer}}{{/isListContainer}}{{/isEnum}}{{#isEnum}}{{#isListContainer}}public {{{datatypeWithEnum}}} {{getter}}() { return {{name}}; - } + }{{/isListContainer}}{{/isEnum}}{{#isEnum}}{{#isMapContainer}}public {{{datatypeWithEnum}}} {{getter}}() { + return {{name}}; + }{{/isMapContainer}}{{/isEnum}}{{^isEnum}}public {{{datatypeWithEnum}}} {{getter}}() { + return {{name}}; + }{{/isEnum}} {{^isReadOnly}} public void {{setter}}({{{datatypeWithEnum}}} {{name}}) { diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/pom.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/pom.mustache index df7a5a3bcb4..02c853a7559 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/pom.mustache @@ -4,6 +4,9 @@ {{artifactId}} jar {{artifactId}} + {{#appDescription}} + {{appDescription}} + {{/appDescription}} {{artifactVersion}} src/main/java diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/returnTypes.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/returnTypes.mustache index 8e6066c8c84..6af86ffe4e4 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/returnTypes.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/returnTypes.mustache @@ -1,4 +1,4 @@ {{#useGenericResponse}}Response{{/useGenericResponse}}{{! non-generic response: }}{{^useGenericResponse}}{{! -}}{{#returnContainer}}{{#isMapContainer}}Map{{/isMapContainer}}{{#isListContainer}}List<{{{returnType}}}>{{/isListContainer}}{{/returnContainer}}{{^returnContainer}}{{{returnType}}}{{/returnContainer}}{{! +}}{{{returnType}}}{{! }}{{/useGenericResponse}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/server/pom.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/server/pom.mustache index ddf991b6a37..c7be1d0b172 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/server/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/server/pom.mustache @@ -4,6 +4,9 @@ {{artifactId}} war {{artifactId}} + {{#appDescription}} + {{appDescription}} + {{/appDescription}} {{artifactVersion}} src/main/java @@ -241,7 +244,7 @@ 4.3.9.RELEASE {{/generateSpringApplication}} {{#generateSpringBootApplication}} - 1.4.7.RELEASE + 1.5.4.RELEASE {{/generateSpringBootApplication}} 3.1.11 2.8.9 diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/libraries/jersey1/api.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/libraries/jersey1/api.mustache index a3b9628f97b..386ebe68321 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/libraries/jersey1/api.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/libraries/jersey1/api.mustache @@ -15,6 +15,7 @@ import javax.validation.constraints.*; {{#imports}}import {{import}}; {{/imports}} +import java.util.Map; import java.util.List; import {{package}}.NotFoundException; @@ -42,7 +43,7 @@ public class {{classname}} { {{#subresourceOperation}}@Path("{{{path}}}"){{/subresourceOperation}} {{#hasConsumes}}@Consumes({ {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} }){{/hasConsumes}} {{#hasProduces}}@Produces({ {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }){{/hasProduces}} - @io.swagger.annotations.ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}", response = {{{returnType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = { + @io.swagger.annotations.ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}", response = {{{returnBaseType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = { {{#authMethods}}@io.swagger.annotations.Authorization(value = "{{name}}"{{#isOAuth}}, scopes = { {{#scopes}}@io.swagger.annotations.AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{#hasMore}}, {{/hasMore}}{{/scopes}} @@ -50,7 +51,7 @@ public class {{classname}} { {{/hasMore}}{{/authMethods}} }{{/hasAuthMethods}}, tags={ {{#vendorExtensions.x-tags}}"{{tag}}"{{#hasMore}}, {{/hasMore}}{{/vendorExtensions.x-tags}} }) @io.swagger.annotations.ApiResponses(value = { {{#responses}} - @io.swagger.annotations.ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{returnType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}){{#hasMore}},{{/hasMore}}{{/responses}} }) + @io.swagger.annotations.ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{baseType}}}.class{{#containerType}}, responseContainer = "{{{containerType}}}"{{/containerType}}){{#hasMore}},{{/hasMore}}{{/responses}} }) public Response {{nickname}}( {{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}, {{/allParams}}@Context SecurityContext securityContext) diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/api.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/api.mustache index 8bc376e5d2b..00740769465 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/api.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/api.mustache @@ -10,6 +10,7 @@ import io.swagger.jaxrs.*; {{#imports}}import {{import}}; {{/imports}} +import java.util.Map; import java.util.List; import {{package}}.NotFoundException; @@ -38,7 +39,7 @@ public class {{classname}} { {{#subresourceOperation}}@Path("{{{path}}}"){{/subresourceOperation}} {{#hasConsumes}}@Consumes({ {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} }){{/hasConsumes}} {{#hasProduces}}@Produces({ {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }){{/hasProduces}} - @io.swagger.annotations.ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}", response = {{{returnType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = { + @io.swagger.annotations.ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}", response = {{{returnBaseType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = { {{#authMethods}}@io.swagger.annotations.Authorization(value = "{{name}}"{{#isOAuth}}, scopes = { {{#scopes}}@io.swagger.annotations.AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{#hasMore}}, {{/hasMore}}{{/scopes}} @@ -46,7 +47,7 @@ public class {{classname}} { {{/hasMore}}{{/authMethods}} }{{/hasAuthMethods}}, tags={ {{#vendorExtensions.x-tags}}"{{tag}}",{{/vendorExtensions.x-tags}} }) @io.swagger.annotations.ApiResponses(value = { {{#responses}} - @io.swagger.annotations.ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{returnType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}){{#hasMore}}, + @io.swagger.annotations.ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{baseType}}}.class{{#containerType}}, responseContainer = "{{{containerType}}}"{{/containerType}}){{#hasMore}}, {{/hasMore}}{{/responses}} }) public Response {{nickname}}({{#isMultipart}}MultipartFormDataInput input,{{/isMultipart}}{{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{^isMultipart}}{{>formParams}},{{/isMultipart}}{{#isMultipart}}{{^isFormParam}},{{/isFormParam}}{{/isMultipart}}{{/allParams}}@Context SecurityContext securityContext) throws NotFoundException { diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/JacksonConfig.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/JacksonConfig.mustache index ed5de465670..923d26477d9 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/JacksonConfig.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/JacksonConfig.mustache @@ -9,7 +9,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.ObjectMapper; +{{#java8}} +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +{{/java8}} +{{^java8}} import com.fasterxml.jackson.datatype.joda.JodaModule; +{{/java8}} @Provider @Produces(MediaType.APPLICATION_JSON) @@ -25,9 +30,9 @@ public class JacksonConfig implements ContextResolver { {{#java8}} this.objectMapper.registerModule(new JavaTimeModule()); {{/java8}} -{{#joda}} +{{^java8}} this.objectMapper.registerModule(new JodaModule()); -{{/joda}} +{{/java8}} // sample to convert any DateTime to readable timestamps //this.objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true); diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/api.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/api.mustache index 733bf711a87..6a8bfba99fc 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/api.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/api.mustache @@ -9,6 +9,7 @@ import io.swagger.jaxrs.*; {{/imports}} import java.util.List; +import java.util.Map; import java.io.InputStream; @@ -34,7 +35,7 @@ public interface {{classname}} { {{#subresourceOperation}}@Path("{{{path}}}"){{/subresourceOperation}} {{#hasConsumes}}@Consumes({ {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} }){{/hasConsumes}} {{#hasProduces}}@Produces({ {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }){{/hasProduces}} - @io.swagger.annotations.ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}", response = {{{returnType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = { + @io.swagger.annotations.ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}", response = {{{returnBaseType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = { {{#authMethods}}@io.swagger.annotations.Authorization(value = "{{name}}"{{#isOAuth}}, scopes = { {{#scopes}}@io.swagger.annotations.AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{#hasMore}}, {{/hasMore}}{{/scopes}} @@ -42,7 +43,7 @@ public interface {{classname}} { {{/hasMore}}{{/authMethods}} }{{/hasAuthMethods}}, tags={ {{#vendorExtensions.x-tags}}"{{tag}}",{{/vendorExtensions.x-tags}} }) @io.swagger.annotations.ApiResponses(value = { {{#responses}} - @io.swagger.annotations.ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{returnType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}){{#hasMore}}, + @io.swagger.annotations.ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{baseType}}}.class{{#containerType}}, responseContainer = "{{{containerType}}}"{{/containerType}}){{#hasMore}}, {{/hasMore}}{{/responses}} }) public Response {{nickname}}({{#isMultipart}}MultipartFormDataInput input,{{/isMultipart}}{{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{^isMultipart}}{{>formParams}},{{/isMultipart}}{{#isMultipart}}{{^isFormParam}},{{/isFormParam}}{{/isMultipart}}{{/allParams}}@Context SecurityContext securityContext); {{/operation}} diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/enumOuterClass.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/enumOuterClass.mustache index 7aea7b92f22..77ae9521fa5 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/enumOuterClass.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/enumOuterClass.mustache @@ -1,3 +1,7 @@ public enum {{classname}} { - {{#allowableValues}}{{.}}{{^-last}}, {{/-last}}{{/allowableValues}} + {{#allowableValues}} + {{#enumVars}} + {{{name}}}{{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/enumVars}} + {{/allowableValues}} } \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/gradle.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/gradle.mustache index ae34de08166..60f4d69117b 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/gradle.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/gradle.mustache @@ -15,10 +15,16 @@ dependencies { providedCompile 'javax.annotation:javax.annotation-api:1.2' providedCompile 'org.jboss.spec.javax.servlet:jboss-servlet-api_3.0_spec:1.0.0.Final' compile 'org.jboss.resteasy:resteasy-jackson2-provider:3.0.11.Final' -{{#joda}} - compile 'com.fasterxml.jackson.datatype:jackson-datatype-joda:2.4.1' +{{#useBeanValidation}} + providedCompile 'javax.validation:validation-api:1.1.0.Final' +{{/useBeanValidation}} +{{^java8}} + compile 'com.fasterxml.jackson.datatype:jackson-datatype-joda:2.6.3' compile 'joda-time:joda-time:2.7' -{{/joda}} +{{/java8}} +{{#java8}} + compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.6.3' +{{/java8}} testCompile 'junit:junit:4.12', 'org.hamcrest:hamcrest-core:1.3' } diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/pojo.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/pojo.mustache index febff09ce6f..a2c7e8c000c 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/pojo.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/pojo.mustache @@ -3,6 +3,9 @@ import io.swagger.annotations.*; {{#description}}@ApiModel(description="{{{description}}}"){{/description}} {{>generatedAnnotation}} public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#serializableModel}}implements Serializable{{/serializableModel}} { +{{#serializableModel}} + private static final long serialVersionUID = 1L; +{{/serializableModel}} {{#vars}}{{#isEnum}} {{>enumClass}}{{/isEnum}}{{#items.isEnum}}{{#items}} diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/pom.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/pom.mustache index ce77499a933..e87d2c0df14 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/eap/pom.mustache @@ -9,6 +9,15 @@ src/main/java + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} + {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} + + org.apache.maven.plugins maven-war-plugin @@ -98,16 +107,6 @@ provided - - com.fasterxml.jackson.datatype - jackson-datatype-joda - 2.4.1 - - - joda-time - joda-time - 2.7 - io.swagger swagger-jaxrs @@ -140,27 +139,32 @@ {{#useBeanValidation}} - - - javax.validation - validation-api - 1.1.0.Final - provided - -{{/useBeanValidation}} -{{#joda}} - - com.fasterxml.jackson.datatype - jackson-datatype-joda - 2.1.1 - -{{/joda}} -{{#java8}} - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - 2.6.3 - + + + javax.validation + validation-api + 1.1.0.Final + provided + +{{/useBeanValidation}} +{{^java8}} + + joda-time + joda-time + 2.7 + + + com.fasterxml.jackson.datatype + jackson-datatype-joda + 2.6.3 + +{{/java8}} +{{#java8}} + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + 2.6.3 + {{/java8}} diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/gradle.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/gradle.mustache index ae34de08166..20dd8c939da 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/gradle.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/gradle.mustache @@ -14,6 +14,7 @@ dependencies { providedCompile 'org.jboss.resteasy:resteasy-multipart-provider:3.0.11.Final' providedCompile 'javax.annotation:javax.annotation-api:1.2' providedCompile 'org.jboss.spec.javax.servlet:jboss-servlet-api_3.0_spec:1.0.0.Final' + compile 'io.swagger:swagger-annotations:1.5.10' compile 'org.jboss.resteasy:resteasy-jackson2-provider:3.0.11.Final' {{#joda}} compile 'com.fasterxml.jackson.datatype:jackson-datatype-joda:2.4.1' diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/pojo.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/pojo.mustache index 3b4bfd95464..8abd4bcd94b 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/pojo.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/pojo.mustache @@ -3,6 +3,9 @@ import io.swagger.annotations.*; {{#description}}@ApiModel(description="{{{description}}}"){{/description}} {{>generatedAnnotation}} public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#serializableModel}}implements Serializable{{/serializableModel}} { +{{#serializableModel}} + private static final long serialVersionUID = 1L; +{{/serializableModel}} {{#vars}}{{#isEnum}}{{^isContainer}} {{>enumClass}}{{/isContainer}}{{/isEnum}}{{#items.isEnum}}{{#items}} diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/pom.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/pom.mustache index 7d37136fbd4..7e040124f4e 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/pom.mustache @@ -9,6 +9,15 @@ src/main/java + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} + {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} + + org.apache.maven.plugins maven-war-plugin @@ -49,6 +58,11 @@ + + io.swagger + swagger-annotations + ${swagger-core-version} + org.slf4j slf4j-log4j12 diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/queryParams.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/queryParams.mustache index 5a9a4398a03..292fbb4600f 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/queryParams.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/queryParams.mustache @@ -1 +1 @@ -{{#isQueryParam}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}} @QueryParam("{{baseName}}") {{{dataType}}} {{paramName}}{{/isQueryParam}} \ No newline at end of file +{{#isQueryParam}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{#defaultValue}} @DefaultValue("{{{defaultValue}}}"){{/defaultValue}} @QueryParam("{{baseName}}") {{{dataType}}} {{paramName}}{{/isQueryParam}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/api.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/api.mustache index 5b8f44ca3e9..4ccb9cc73aa 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/api.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/api.mustache @@ -8,39 +8,20 @@ import javax.ws.rs.core.Response; import io.swagger.annotations.*; +import java.util.Map; import java.util.List; -{{#useBeanValidation}} -import javax.validation.constraints.*; -{{/useBeanValidation}} +{{#useBeanValidation}}import javax.validation.constraints.*; +import javax.validation.Valid;{{/useBeanValidation}} @Path("/{{{baseName}}}") - -@Api(description = "the {{{baseName}}} API") -{{#hasConsumes}}@Consumes({ {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} }){{/hasConsumes}} -{{#hasProduces}}@Produces({ {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }){{/hasProduces}} -{{>generatedAnnotation}} - -public class {{classname}} { +@Api(description = "the {{{baseName}}} API"){{#hasConsumes}} +@Consumes({ {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} }){{/hasConsumes}}{{#hasProduces}} +@Produces({ {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }){{/hasProduces}} +{{>generatedAnnotation}}public {{#interfaceOnly}}interface{{/interfaceOnly}}{{^interfaceOnly}}class{{/interfaceOnly}} {{classname}} { {{#operations}} {{#operation}} - @{{httpMethod}} - {{#subresourceOperation}}@Path("{{{path}}}"){{/subresourceOperation}} - {{#hasConsumes}}@Consumes({ {{#consumes}}"{{mediaType}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} }){{/hasConsumes}} - {{#hasProduces}}@Produces({ {{#produces}}"{{mediaType}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }){{/hasProduces}} - @ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}", response = {{{returnType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = { - {{#authMethods}}@Authorization(value = "{{name}}"{{#isOAuth}}, scopes = { - {{#scopes}}@AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{#hasMore}}, - {{/hasMore}}{{/scopes}} - }{{/isOAuth}}){{#hasMore}}, - {{/hasMore}}{{/authMethods}} - }{{/hasAuthMethods}}, tags={ {{#vendorExtensions.x-tags}}"{{tag}}"{{#hasMore}}, {{/hasMore}}{{/vendorExtensions.x-tags}} }) - @ApiResponses(value = { {{#responses}} - @ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{returnType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}){{#hasMore}},{{/hasMore}}{{/responses}} }) - public Response {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},{{/hasMore}}{{/allParams}}) { - return Response.ok().entity("magic!").build(); - } +{{#interfaceOnly}}{{>apiInterface}}{{/interfaceOnly}}{{^interfaceOnly}}{{>apiMethod}}{{/interfaceOnly}} {{/operation}} } -{{/operations}} - +{{/operations}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/apiInterface.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/apiInterface.mustache new file mode 100644 index 00000000000..f0ad70a40ec --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/apiInterface.mustache @@ -0,0 +1,14 @@ + @{{httpMethod}}{{#subresourceOperation}} + @Path("{{{path}}}"){{/subresourceOperation}}{{#hasConsumes}} + @Consumes({ {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} }){{/hasConsumes}}{{#hasProduces}} + @Produces({ {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }){{/hasProduces}} + @ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}"{{#hasAuthMethods}}, authorizations = { + {{#authMethods}}@Authorization(value = "{{name}}"{{#isOAuth}}, scopes = { + {{#scopes}}@AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{#hasMore}}, + {{/hasMore}}{{/scopes}} + }{{/isOAuth}}){{#hasMore}}, + {{/hasMore}}{{/authMethods}} + }{{/hasAuthMethods}}, tags={ {{#vendorExtensions.x-tags}}"{{tag}}"{{#hasMore}}, {{/hasMore}}{{/vendorExtensions.x-tags}} }) + @ApiResponses(value = { {{#responses}} + @ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{baseType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}){{#hasMore}},{{/hasMore}}{{/responses}} }) + {{>returnTypeInterface}} {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},{{/hasMore}}{{/allParams}}) throws Exception; \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/apiMethod.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/apiMethod.mustache new file mode 100644 index 00000000000..d4ab5850ac2 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/apiMethod.mustache @@ -0,0 +1,16 @@ + @{{httpMethod}}{{#subresourceOperation}} + @Path("{{{path}}}"){{/subresourceOperation}}{{#hasConsumes}} + @Consumes({ {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} }){{/hasConsumes}}{{#hasProduces}} + @Produces({ {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }){{/hasProduces}} + @ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}", response = {{{returnBaseType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = { + {{#authMethods}}@Authorization(value = "{{name}}"{{#isOAuth}}, scopes = { + {{#scopes}}@AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{#hasMore}}, + {{/hasMore}}{{/scopes}} + }{{/isOAuth}}){{#hasMore}}, + {{/hasMore}}{{/authMethods}} + }{{/hasAuthMethods}}, tags={ {{#vendorExtensions.x-tags}}"{{tag}}"{{#hasMore}}, {{/hasMore}}{{/vendorExtensions.x-tags}} }) + @ApiResponses(value = { {{#responses}} + @ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{baseType}}}.class{{#containerType}}, responseContainer = "{{{containerType}}}"{{/containerType}}){{#hasMore}},{{/hasMore}}{{/responses}} }) + public Response {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},{{/hasMore}}{{/allParams}}) { + return Response.ok().entity("magic!").build(); + } \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/bodyParams.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/bodyParams.mustache index c7d1abfe527..be56da7535b 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/bodyParams.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/bodyParams.mustache @@ -1 +1 @@ -{{#isBodyParam}}{{{dataType}}} {{paramName}}{{/isBodyParam}} \ No newline at end of file +{{#isBodyParam}}{{#useBeanValidation}}@Valid {{/useBeanValidation}}{{{dataType}}} {{paramName}}{{/isBodyParam}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/headerParams.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/headerParams.mustache index 25d690c90ed..bf377adbd4d 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/headerParams.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/headerParams.mustache @@ -1 +1 @@ -{{#isHeaderParam}}@HeaderParam("{{baseName}}") {{{dataType}}} {{paramName}}{{/isHeaderParam}} \ No newline at end of file +{{#isHeaderParam}}@HeaderParam("{{baseName}}"){{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{#defaultValue}} @DefaultValue("{{{defaultValue}}}"){{/defaultValue}} {{#description}} @ApiParam("{{description}}"){{/description}} {{{dataType}}} {{paramName}}{{/isHeaderParam}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/model.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/model.mustache index a93761806d2..3fb7e3cfb9d 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/model.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/model.mustache @@ -2,8 +2,12 @@ package {{package}}; {{#imports}}import {{import}}; {{/imports}} +{{#serializableModel}} +import java.io.Serializable; +{{/serializableModel}} {{#useBeanValidation}} import javax.validation.constraints.*; +import javax.validation.Valid; {{/useBeanValidation}} {{#models}} diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/pojo.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/pojo.mustache index 79ba3649d67..73d96a3b16b 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/pojo.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/pojo.mustache @@ -1,5 +1,6 @@ import io.swagger.annotations.*; import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; {{#description}}@ApiModel(description = "{{{description}}}"){{/description}} public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#serializableModel}}implements Serializable{{/serializableModel}} { @@ -8,7 +9,7 @@ public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#seriali {{>enumClass}}{{/isContainer}}{{/isEnum}}{{#items.isEnum}}{{#items}} {{^isContainer}}{{>enumClass}}{{/isContainer}}{{/items}}{{/items.isEnum}} - private {{{datatypeWithEnum}}} {{name}} = {{{defaultValue}}};{{/vars}} + private {{#useBeanValidation}}@Valid{{/useBeanValidation}} {{{datatypeWithEnum}}} {{name}} = {{{defaultValue}}};{{/vars}} {{#vars}} /** @@ -29,6 +30,7 @@ public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#seriali {{#vendorExtensions.extraAnnotation}}{{{vendorExtensions.extraAnnotation}}}{{/vendorExtensions.extraAnnotation}} @ApiModelProperty({{#example}}example = "{{{example}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}value = "{{{description}}}") + @JsonProperty("{{baseName}}") {{#useBeanValidation}}{{>beanValidation}}{{/useBeanValidation}} public {{{datatypeWithEnum}}} {{getter}}() { return {{name}}; } diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/pom.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/pom.mustache index e469fdd90a2..b0316d58730 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/pom.mustache @@ -2,12 +2,17 @@ 4.0.0 {{groupId}} {{artifactId}} - war + {{#interfaceOnly}}jar{{/interfaceOnly}}{{^interfaceOnly}}war{{/interfaceOnly}} {{artifactId}} {{artifactVersion}} src/main/java - + {{#interfaceOnly}} + + org.apache.maven.plugins + maven-jar-plugin + 2.2 + {{/interfaceOnly}}{{^interfaceOnly}} org.apache.maven.plugins maven-war-plugin @@ -27,7 +32,7 @@ - + {{/interfaceOnly}} @@ -48,7 +53,7 @@ junit ${junit-version} test - + {{^interfaceOnly}} org.testng testng @@ -68,7 +73,7 @@ org.beanshell - + {{/interfaceOnly}} {{#useBeanValidation}} diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/queryParams.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/queryParams.mustache index 153b43d8fbd..b931cfea9c9 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/queryParams.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/queryParams.mustache @@ -1 +1 @@ -{{#isQueryParam}}@QueryParam("{{baseName}}"){{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{#defaultValue}} @DefaultValue("{{{defaultValue}}}"){{/defaultValue}} {{{dataType}}} {{paramName}}{{/isQueryParam}} \ No newline at end of file +{{#isQueryParam}}@QueryParam("{{baseName}}"){{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{#defaultValue}} @DefaultValue("{{{defaultValue}}}"){{/defaultValue}} {{#description}} @ApiParam("{{description}}"){{/description}} {{{dataType}}} {{paramName}}{{/isQueryParam}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/returnTypeInterface.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/returnTypeInterface.mustache new file mode 100644 index 00000000000..1bfa056f0c1 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/spec/returnTypeInterface.mustache @@ -0,0 +1 @@ +{{{returnType}}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/application.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/application.mustache index 2cfab81ea48..139e83e56cc 100644 --- a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/application.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/application.mustache @@ -15,6 +15,19 @@ # HOCON will fall back to substituting environment variable: #mykey = ${JAVA_HOME} +play.filters.headers.contentSecurityPolicy=null + +{{#useBeanValidation}} +# When using bean validation with the swagger api, the validator will check that every constraint is respected +# This is very useful when testing but could add a lot of overhead if you return a lot of data. Benchmark have +# shown that the time it takes to validate is exponential. +# If this is a concern in your application, or if you don't want to validate the output coming from your API for +# respecting its contract, set the "output" property below to "false". Since there is not a lot of data as input for +# an endpoint, I highly suggest you let the "input" property set to true. +useInputBeanValidation=true +useOutputBeanValidation=true +{{/useBeanValidation}} + {{#handleExceptions}} play.http.errorHandler="swagger.ErrorHandler" {{/handleExceptions}} @@ -44,7 +57,7 @@ akka { # ~~~~~ # The secret key is used to sign Play's session cookie. # This must be changed for production, but we don't recommend you change it in this file. -play.crypto.secret = "changeme" +play.http.secret.key = "changeme" ## Modules # https://www.playframework.com/documentation/latest/Modules @@ -61,7 +74,17 @@ play.modules { # in the root package (the "app" directory), or you can define them # explicitly below. # If there are any built-in modules that you want to disable, you can list them here. -#disabled += "" + {{#controllerOnly}} + disabled += "Module" + {{/controllerOnly}} + {{^useInterfaces}} + disabled += "Module" + {{/useInterfaces}} +} + +play.assets { +path = "/public" +urlPrefix = "/assets" } ## IDE @@ -263,7 +286,8 @@ csrf { # ~~~~~ # Defines security headers that prevent XSS attacks. # If enabled, then all options are set to the below configuration by default: -headers { +play.filters.headers { + # The X-Frame-Options header. If null, the header is not set. #frameOptions = "DENY" @@ -277,7 +301,13 @@ headers { #permittedCrossDomainPolicies = "master-only" # The Content-Security-Policy header. If null, the header is not set. -#contentSecurityPolicy = "default-src 'self'" +contentSecurityPolicy = "default-src 'self'" + +# The Referrer-Policy header. If null, the header is not set. +#referrerPolicy = "origin-when-cross-origin, strict-origin-when-cross-origin" + +# If true, allow an action to use .withHeaders to replace one or more of the above headers +#allowActionSpecificHeaders = false } ## Allowed hosts filter configuration diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/beanValidation.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/beanValidation.mustache index 16573060a9d..56b047acef0 100644 --- a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/beanValidation.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/beanValidation.mustache @@ -1,53 +1,56 @@ {{#required}} - @NotNull +@NotNull {{/required}} {{#pattern}} - @Pattern(regexp="{{{pattern}}}") +@Pattern(regexp="{{{pattern}}}") {{/pattern}} {{#minLength}} {{#maxLength}} - @Size(min={{minLength}},max={{maxLength}}) +@Size(min={{minLength}},max={{maxLength}}) {{/maxLength}} {{/minLength}} {{#minLength}} {{^maxLength}} - @Size(min={{minLength}}) +@Size(min={{minLength}}) {{/maxLength}} {{/minLength}} {{^minLength}} {{#maxLength}} - @Size(max={{maxLength}}) - {{/maxLength}} - {{/minLength}} +@Size(max={{maxLength}}) +{{/maxLength}} +{{/minLength}} {{#minItems}} {{#maxItems}} - @Size(min={{minItems}},max={{maxItems}}) +@Size(min={{minItems}},max={{maxItems}}) {{/maxItems}} {{/minItems}} {{#minItems}} {{^maxItems}} - @Size(min={{minItems}}) +@Size(min={{minItems}}) {{/maxItems}} {{/minItems}} {{^minItems}} {{#maxItems}} - @Size(max={{maxItems}}) +@Size(max={{maxItems}}) {{/maxItems}} {{/minItems}} {{! check for integer / number=decimal type}} {{#isInteger}} {{#minimum}} - @Min({{minimum}}) +@Min({{minimum}}) {{/minimum}} {{#maximum}} - @Max({{maximum}}) +@Max({{maximum}}) {{/maximum}} {{/isInteger}} {{^isInteger}} {{#minimum}} - @DecimalMin("{{minimum}}") +@DecimalMin("{{minimum}}") {{/minimum}} {{#maximum}} - @DecimalMax("{{maximum}}") +@DecimalMax("{{maximum}}") {{/maximum}} -{{/isInteger}} \ No newline at end of file +{{/isInteger}} +{{^isPrimitiveType}} +@Valid +{{/isPrimitiveType}} diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/build.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/build.mustache index 8d345a9347f..cf57cb439ef 100644 --- a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/build.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/build.mustache @@ -4,13 +4,12 @@ version := "1.0-SNAPSHOT" lazy val root = (project in file(".")).enablePlugins(PlayJava) -scalaVersion := "2.11.7" +scalaVersion := "2.12.2" -libraryDependencies ++= Seq( -javaJdbc, -cache, -javaWs{{#useSwaggerUI}}, -"org.webjars" % "swagger-ui" % "2.2.10-1"{{/useSwaggerUI}}{{#useBeanValidation}}, -"javax.validation" % "validation-api" % "1.1.0.Final" +{{#useSwaggerUI}} +libraryDependencies += "org.webjars" % "swagger-ui" % "3.1.5" +{{/useSwaggerUI}} +{{#useBeanValidation}} +libraryDependencies += "javax.validation" % "validation-api" % "1.1.0.Final" {{/useBeanValidation}} -) +libraryDependencies += guice diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/buildproperties.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/buildproperties.mustache index 59e7c05b62f..cf19fd026fd 100644 --- a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/buildproperties.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/buildproperties.mustache @@ -1 +1 @@ -sbt.version=0.13.11 \ No newline at end of file +sbt.version=0.13.15 \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/conversionBegin.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/conversionBegin.mustache index 1f030198921..19954287202 100644 --- a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/conversionBegin.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/conversionBegin.mustache @@ -1 +1 @@ -{{#isBoolean}}Boolean.valueOf({{/isBoolean}}{{#isInteger}}Integer.parseInt({{/isInteger}}{{#isDouble}}Double.parseDouble({{/isDouble}}{{#isLong}}Long.parseLong({{/isLong}}{{#isFloat}}Float.parseFloat({{/isFloat}}{{#isString}}(String){{/isString}} \ No newline at end of file +{{#isBoolean}}Boolean.valueOf({{/isBoolean}}{{#isInteger}}Integer.parseInt({{/isInteger}}{{#isDouble}}Double.parseDouble({{/isDouble}}{{#isLong}}Long.parseLong({{/isLong}}{{#isFloat}}Float.parseFloat({{/isFloat}}{{#isUuid}}UUID.fromString({{/isUuid}}{{#isDateTime}}OffsetDateTime.parse({{/isDateTime}}{{#isDate}}LocalDate.parse({{/isDate}}{{#isByteArray}}{{/isByteArray}}{{#isNumber}}new BigDecimal({{/isNumber}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/conversionEnd.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/conversionEnd.mustache index 3686f93ac6a..cc6940fb4df 100644 --- a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/conversionEnd.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/conversionEnd.mustache @@ -1 +1 @@ -{{#isBoolean}}){{/isBoolean}}{{#isInteger}}){{/isInteger}}{{#isDouble}}){{/isDouble}}{{#isLong}}){{/isLong}}{{#isFloat}}){{/isFloat}}{{#isString}}{{/isString}} \ No newline at end of file +{{#isBoolean}}){{/isBoolean}}{{#isInteger}}){{/isInteger}}{{#isDouble}}){{/isDouble}}{{#isLong}}){{/isLong}}{{#isFloat}}){{/isFloat}}{{#isUuid}}){{/isUuid}}{{#isDateTime}}){{/isDateTime}}{{#isDate}}){{/isDate}}{{#isByteArray}}.getBytes(){{/isByteArray}}{{#isNumber}}){{/isNumber}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/enumClass.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/enumClass.mustache index c5c3143cb94..5d5086497d9 100644 --- a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/enumClass.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/enumClass.mustache @@ -20,7 +20,7 @@ {{/allowableValues}} {{/gson}} - private {{{datatype}}} value; + private final {{{datatype}}} value; {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}({{{datatype}}} value) { this.value = value; diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/enumOuterClass.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/enumOuterClass.mustache index 76c2cbf5a76..4bd1206fd55 100644 --- a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/enumOuterClass.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/enumOuterClass.mustache @@ -18,7 +18,7 @@ public enum {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum {{/-last}}{{#-last}};{{/-last}}{{/enumVars}}{{/allowableValues}} {{/gson}} - private {{{dataType}}} value; + private final {{{dataType}}} value; {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}({{{dataType}}} value) { this.value = value; diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/exampleReturnTypes.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/exampleReturnTypes.mustache new file mode 100644 index 00000000000..395e3889c20 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/exampleReturnTypes.mustache @@ -0,0 +1 @@ +{{#returnContainer}}{{#isMapContainer}}Map{{/isMapContainer}}{{#isListContainer}}List{{/isListContainer}}{{/returnContainer}}{{^returnContainer}}{{{returnType}}}{{/returnContainer}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/itemConversionBegin.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/itemConversionBegin.mustache new file mode 100644 index 00000000000..42b024ec340 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/itemConversionBegin.mustache @@ -0,0 +1 @@ +{{#items.isBoolean}}Boolean.valueOf({{/items.isBoolean}}{{#items.isInteger}}Integer.parseInt({{/items.isInteger}}{{#items.isDouble}}Double.parseDouble({{/items.isDouble}}{{#items.isLong}}Long.parseLong({{/items.isLong}}{{#items.isFloat}}Float.parseFloat({{/items.isFloat}}{{#items.isUuid}}UUID.fromString({{/items.isUuid}}{{#items.isDateTime}}OffsetDateTime.parse({{/items.isDateTime}}{{#items.isDate}}LocalDate.parse({{/items.isDate}}{{#isByteArray}}{{/isByteArray}}{{#isNumber}}new BigDecimal({{/isNumber}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/itemConversionEnd.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/itemConversionEnd.mustache new file mode 100644 index 00000000000..66edac4ea93 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/itemConversionEnd.mustache @@ -0,0 +1 @@ +{{#items.isBoolean}}){{/items.isBoolean}}{{#items.isInteger}}){{/items.isInteger}}{{#items.isDouble}}){{/items.isDouble}}{{#items.isLong}}){{/items.isLong}}{{#items.isFloat}}){{/items.isFloat}}{{#items.isUuid}}){{/items.isUuid}}{{#items.isDateTime}}){{/items.isDateTime}}{{#items.isDate}}){{/items.isDate}}{{#isByteArray}}.getBytes(){{/isByteArray}}{{#isNumber}}){{/isNumber}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/model.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/model.mustache index 4b37d4c35d7..2f149aac0a1 100644 --- a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/model.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/model.mustache @@ -1,15 +1,13 @@ package {{package}}; -import java.util.Objects; {{#imports}}import {{import}}; {{/imports}} {{#serializableModel}} import java.io.Serializable; {{/serializableModel}} -{{#useBeanValidation}} -import javax.validation.constraints.*; import com.fasterxml.jackson.annotation.*; -{{/useBeanValidation}} +import java.util.Set; +import javax.validation.*; {{#models}} {{#model}} {{#isEnum}} diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/module.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/module.mustache new file mode 100644 index 00000000000..4dae1f944e7 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/module.mustache @@ -0,0 +1,15 @@ +import com.google.inject.AbstractModule; + +import controllers.*; + +public class Module extends AbstractModule { + + @Override + protected void configure() { + {{#apiInfo}} + {{#apis}} + bind({{classname}}ControllerImpInterface.class).to({{classname}}ControllerImp.class); + {{/apis}} + {{/apiInfo}} + } +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/newApi.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/newApi.mustache index 1c23543a7e7..6862b7402a3 100644 --- a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/newApi.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/newApi.mustache @@ -18,7 +18,14 @@ public class {{classname}}ControllerImp {{#useInterfaces}}implements {{classname {{#useInterfaces}}@Override{{/useInterfaces}} public {{>returnTypes}} {{operationId}}({{#allParams}}{{>pathParams}}{{>queryParams}}{{>bodyParams}}{{>formParams}}{{>headerParams}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) {{#handleExceptions}}throws Exception{{/handleExceptions}} { //Do your magic!!! - {{#returnType}}{{#isResponseFile}}return new FileInputStream("replace this");{{/isResponseFile}}{{^isResponseFile}}return new {{>returnTypesNoVoidNoAbstract}}();{{/isResponseFile}}{{/returnType}} + {{#returnType}} + {{#isResponseFile}} + return new FileInputStream("replace this"); + {{/isResponseFile}} + {{^isResponseFile}} + return new {{>returnTypesNoVoidNoAbstract}}({{#vendorExtensions.missingReturnInfoIfNeeded}}{{vendorExtensions.missingReturnInfoIfNeeded}}{{/vendorExtensions.missingReturnInfoIfNeeded}}); + {{/isResponseFile}} + {{/returnType}} } {{/operation}} diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/newApiController.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/newApiController.mustache index a778e8ba0d4..9f20541d056 100644 --- a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/newApiController.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/newApiController.mustache @@ -7,16 +7,21 @@ import play.mvc.Controller; import play.mvc.Result; import play.mvc.Http; import java.util.List; +import java.util.Map; import java.util.ArrayList; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.JsonNode; import com.google.inject.Inject; +import java.io.File; +{{^handleExceptions}} import java.io.IOException; +{{/handleExceptions}} import swagger.SwaggerUtils; import com.fasterxml.jackson.core.type.TypeReference; {{#useBeanValidation}} import javax.validation.constraints.*; +import play.Configuration; {{/useBeanValidation}} {{#wrapCalls}} @@ -27,13 +32,23 @@ import swagger.SwaggerUtils.ApiAction; {{#operations}} public class {{classname}}Controller extends Controller { - private final {{classname}}ControllerImp imp; + {{^controllerOnly}} + private final {{classname}}ControllerImp{{#useInterfaces}}Interface{{/useInterfaces}} imp; + {{/controllerOnly}} private final ObjectMapper mapper; + {{#useBeanValidation}} + private final Configuration configuration; + {{/useBeanValidation}} @Inject - private {{classname}}Controller({{classname}}ControllerImp imp) { + private {{classname}}Controller({{#useBeanValidation}}Configuration configuration{{^controllerOnly}}, {{/controllerOnly}}{{/useBeanValidation}}{{^controllerOnly}}{{classname}}ControllerImp{{#useInterfaces}}Interface{{/useInterfaces}} imp{{/controllerOnly}}) { + {{^controllerOnly}} this.imp = imp; + {{/controllerOnly}} mapper = new ObjectMapper(); + {{#useBeanValidation}} + this.configuration = configuration; + {{/useBeanValidation}} } {{#operation}} @@ -44,95 +59,175 @@ public class {{classname}}Controller extends Controller { {{^collectionFormat}} JsonNode node{{paramName}} = request().body().asJson(); {{{dataType}}} {{paramName}}; - {{^required}} if (node{{paramName}} != null) { - {{paramName}} = mapper.readValue(node{{paramName}}.toString(), {{#isMapContainer}}new TypeReference>(){}{{/isMapContainer}}{{#isListContainer}}new TypeReference>(){}{{/isListContainer}}{{^isListContainer}}{{^isMapContainer}}{{{dataType}}}.class{{/isMapContainer}}{{/isListContainer}});{{/required}} - {{#required}}{{paramName}} = mapper.readValue(node{{paramName}}.toString(), {{#isMapContainer}}new TypeReference>(){}{{/isMapContainer}}{{#isListContainer}}new TypeReference>(){}{{/isListContainer}}{{^isListContainer}}{{^isMapContainer}}{{{dataType}}}.class{{/isMapContainer}}{{/isListContainer}});{{/required}} - {{^required}} + {{paramName}} = mapper.readValue(node{{paramName}}.toString(), {{#isContainer}}new TypeReference<{{{dataType}}}>(){}{{/isContainer}}{{^isContainer}}{{{dataType}}}.class{{/isContainer}}); + {{#useBeanValidation}} + if (configuration.getBoolean("useInputBeanValidation")) { + {{#isListContainer}} + for ({{{baseType}}} curItem : {{paramName}}) { + SwaggerUtils.validate(curItem); + } + {{/isListContainer}} + {{#isMapContainer}} + for (Map.Entry entry : {{paramName}}.entrySet()) { + SwaggerUtils.validate(entry.getValue()); + } + {{/isMapContainer}} + {{^isContainer}} + SwaggerUtils.validate({{paramName}}); + {{/isContainer}} + } + {{/useBeanValidation}} } else { + {{#required}} + throw new IllegalArgumentException("'{{baseName}}' parameter is required"); + {{/required}} + {{^required}} {{paramName}} = null; - }{{/required}} + {{/required}} + } {{/collectionFormat}} {{/bodyParams}} {{#queryParams}} {{#collectionFormat}} - List {{paramName}}List = SwaggerUtils.parametersToList("{{collectionFormat}}", "{{paramName}}", request().getQueryString("{{baseName}}")); + String[] {{paramName}}Array = request().queryString().get("{{baseName}}"); + {{#required}} + if ({{paramName}}Array == null) { + throw new IllegalArgumentException("'{{baseName}}' parameter is required"); + } + {{/required}} + List {{paramName}}List = SwaggerUtils.parametersToList("{{collectionFormat}}", {{paramName}}Array); {{{dataType}}} {{paramName}} = new Array{{{dataType}}}(); for (String curParam : {{paramName}}List) { //noinspection UseBulkOperation - {{paramName}}.add({{>conversionBegin}}curParam{{>conversionEnd}}); + {{paramName}}.add({{>itemConversionBegin}}curParam{{>itemConversionEnd}}); } {{/collectionFormat}} {{^collectionFormat}} - String value{{paramName}} = request().getQueryString("{{paramName}}"); + String value{{paramName}} = request().getQueryString("{{baseName}}"); {{{dataType}}} {{paramName}}; - {{^required}} if (value{{paramName}} != null) { - {{paramName}} = {{>conversionBegin}}value{{paramName}}{{>conversionEnd}};{{/required}} - {{#required}}{{paramName}} = {{>conversionBegin}}value{{paramName}}{{>conversionEnd}};{{/required}} - {{^required}} + {{paramName}} = {{>conversionBegin}}value{{paramName}}{{>conversionEnd}}; } else { + {{#required}} + throw new IllegalArgumentException("'{{baseName}}' parameter is required"); + {{/required}} + {{^required}} {{paramName}} = {{>paramDefaultValue}}; - }{{/required}} + {{/required}} + } {{/collectionFormat}} {{/queryParams}} {{#formParams}} {{^notFile}} {{{dataType}}} {{paramName}} = request().body().asMultipartFormData().getFile("{{baseName}}"); - {{#required}}if (({{paramName}} == null || ((File) {{paramName}}.getFile()).length() == 0)) { - throw new RuntimeException("File cannot be empty"); + {{#required}} + if (({{paramName}} == null || ((File) {{paramName}}.getFile()).length() == 0)) { + throw new IllegalArgumentException("'{{baseName}}' file cannot be empty"); } {{/required}} {{/notFile}} {{#notFile}} {{#collectionFormat}} - List {{paramName}}List = SwaggerUtils.parametersToList("{{collectionFormat}}", "{{paramName}}", (request().body().asMultipartFormData().asFormUrlEncoded().get("{{baseName}}"))[0]); + String[] {{paramName}}Array = request().body().asMultipartFormData().asFormUrlEncoded().get("{{baseName}}"); + {{#required}} + if ({{paramName}}Array == null) { + throw new IllegalArgumentException("'{{baseName}}' parameter is required"); + } + {{/required}} + List {{paramName}}List = SwaggerUtils.parametersToList("{{collectionFormat}}", {{paramName}}Array); {{{dataType}}} {{paramName}} = new Array{{{dataType}}}(); for (String curParam : {{paramName}}List) { //noinspection UseBulkOperation - {{paramName}}.add({{>conversionBegin}}curParam{{>conversionEnd}}); + {{paramName}}.add({{>itemConversionBegin}}curParam{{>itemConversionEnd}}); } {{/collectionFormat}} {{^collectionFormat}} String value{{paramName}} = (request().body().asMultipartFormData().asFormUrlEncoded().get("{{baseName}}"))[0]; {{{dataType}}} {{paramName}}; - {{^required}} if (value{{paramName}} != null) { - {{paramName}} = {{>conversionBegin}}value{{paramName}}{{>conversionEnd}};{{/required}} - {{#required}}{{paramName}} = {{>conversionBegin}}value{{paramName}}{{>conversionEnd}};{{/required}} - {{^required}} + {{paramName}} = {{>conversionBegin}}value{{paramName}}{{>conversionEnd}}; } else { + {{#required}} + throw new IllegalArgumentException("'{{baseName}}' parameter is required"); + {{/required}} + {{^required}} {{paramName}} = {{>paramDefaultValue}}; - }{{/required}} + {{/required}} + } {{/collectionFormat}} {{/notFile}} {{/formParams}} {{#headerParams}} {{#collectionFormat}} - List {{paramName}}List = SwaggerUtils.parametersToList("{{collectionFormat}}", "{{paramName}}", request().getHeader("{{baseName}}")); + String[] {{paramName}}Array = request().headers().get("{{baseName}}"); + {{#required}} + if ({{paramName}}Array == null) { + throw new IllegalArgumentException("'{{baseName}}' parameter is required"); + } + {{/required}} + List {{paramName}}List = SwaggerUtils.parametersToList("{{collectionFormat}}", {{paramName}}Array); {{{dataType}}} {{paramName}} = new Array{{{dataType}}}(); for (String curParam : {{paramName}}List) { //noinspection UseBulkOperation - {{paramName}}.add({{>conversionBegin}}curParam{{>conversionEnd}}); + {{paramName}}.add({{>itemConversionBegin}}curParam{{>itemConversionEnd}}); } {{/collectionFormat}} {{^collectionFormat}} String value{{paramName}} = request().getHeader("{{baseName}}"); {{{dataType}}} {{paramName}}; - {{^required}} if (value{{paramName}} != null) { - {{paramName}} = {{>conversionBegin}}value{{paramName}}{{>conversionEnd}};{{/required}} - {{#required}}{{paramName}} = {{>conversionBegin}}value{{paramName}}{{>conversionEnd}};{{/required}} - {{^required}} + {{paramName}} = {{>conversionBegin}}value{{paramName}}{{>conversionEnd}}; } else { + {{#required}} + throw new IllegalArgumentException("'{{baseName}}' parameter is required"); + {{/required}} + {{^required}} {{paramName}} = {{>paramDefaultValue}}; - }{{/required}} + {{/required}} + } {{/collectionFormat}} {{/headerParams}} + {{^controllerOnly}} {{#returnType}}{{>returnTypesNoVoid}} obj = {{/returnType}}imp.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); - {{#returnType}}{{^isResponseFile}}JsonNode result = mapper.valueToTree(obj); - return ok(result);{{/isResponseFile}}{{#isResponseFile}}return ok(obj);{{/isResponseFile}}{{/returnType}} - {{^returnType}}return ok();{{/returnType}} + {{#returnType}} + {{^isResponseFile}} + {{^returnTypeIsPrimitive}} + {{#useBeanValidation}} + if (configuration.getBoolean("useOutputBeanValidation")) { + {{#isListContainer}} + for ({{{returnType}}} curItem : obj) { + SwaggerUtils.validate(curItem); + } + {{/isListContainer}} + {{#isMapContainer}} + for (Map.Entry entry : obj.entrySet()) { + SwaggerUtils.validate(entry.getValue()); + } + {{/isMapContainer}} + {{^returnContainer}} + SwaggerUtils.validate(obj); + {{/returnContainer}} + } + {{/useBeanValidation}} + {{/returnTypeIsPrimitive}} + {{/isResponseFile}} + {{/returnType}} + {{#returnType}} + {{^isResponseFile}}JsonNode result = mapper.valueToTree(obj); + return ok(result); + {{/isResponseFile}} + {{#isResponseFile}} + return ok(obj); + {{/isResponseFile}} + {{/returnType}} + {{^returnType}} + return ok(); + {{/returnType}} + {{/controllerOnly}} + {{#controllerOnly}} + return ok(); + {{/controllerOnly}} } {{/operation}} } diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/newApiInterface.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/newApiInterface.mustache index 37800943816..918ee1f85b1 100644 --- a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/newApiInterface.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/newApiInterface.mustache @@ -13,6 +13,7 @@ import javax.validation.constraints.*; {{/useBeanValidation}} {{#operations}} +@SuppressWarnings("RedundantThrows") public interface {{classname}}ControllerImpInterface { {{#operation}} {{>returnTypes}} {{operationId}}({{#allParams}}{{>pathParams}}{{>queryParams}}{{>bodyParams}}{{>formParams}}{{>headerParams}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) {{#handleExceptions}}throws Exception{{/handleExceptions}}; diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/paramDefaultValue.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/paramDefaultValue.mustache index 733d31a6d72..a5587249639 100644 --- a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/paramDefaultValue.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/paramDefaultValue.mustache @@ -1 +1 @@ -{{#isBoolean}}false{{/isBoolean}}{{#isInteger}}0{{/isInteger}}{{#isDouble}}0.0{{/isDouble}}{{#isLong}}0L{{/isLong}}{{#isFloat}}0.0{{/isFloat}}{{#isString}}""{{/isString}} \ No newline at end of file +{{#defaultValue}}{{#isString}}"{{/isString}}{{{defaultValue}}}{{#isString}}"{{/isString}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/plugins.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/plugins.mustache index ea1f1061fbc..66fbf368ae6 100644 --- a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/plugins.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/plugins.mustache @@ -1,2 +1,2 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.13") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.3") diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/pojo.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/pojo.mustache index 1811924571a..c7ce1c4dadb 100644 --- a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/pojo.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/pojo.mustache @@ -1,7 +1,12 @@ +import java.util.Objects; +{{#useBeanValidation}} +import javax.validation.constraints.*; +{{/useBeanValidation}} /** * {{#description}}{{.}}{{/description}}{{^description}}{{classname}}{{/description}} */ {{>generatedAnnotation}} +@SuppressWarnings({"UnusedReturnValue", "WeakerAccess"}) public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#serializableModel}}implements Serializable{{/serializableModel}} { {{#vars}} {{#isEnum}} @@ -39,11 +44,11 @@ public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#seriali public {{classname}} add{{nameInCamelCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) { {{^required}} - if (this.{{name}} == null) { - this.{{name}} = {{{defaultValue}}}; + if ({{name}} == null) { + {{name}} = {{{defaultValue}}}; } {{/required}} - this.{{name}}.add({{name}}Item); + {{name}}.add({{name}}Item); return this; } {{/isListContainer}} @@ -97,7 +102,7 @@ public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#seriali return false; }{{#hasVars}} {{classname}} {{classVarName}} = ({{classname}}) o; - return {{#vars}}Objects.equals(this.{{name}}, {{classVarName}}.{{name}}){{#hasMore}} && + return {{#vars}}Objects.equals({{name}}, {{classVarName}}.{{name}}){{#hasMore}} && {{/hasMore}}{{/vars}}{{#parent}} && super.equals(o){{/parent}};{{/hasVars}}{{^hasVars}} return true;{{/hasVars}} @@ -108,6 +113,7 @@ public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#seriali return Objects.hash({{#vars}}{{name}}{{#hasMore}}, {{/hasMore}}{{/vars}}{{#parent}}{{#hasVars}}, {{/hasVars}}super.hashCode(){{/parent}}); } + @SuppressWarnings("StringBufferReplaceableByString") @Override public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/routes.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/routes.mustache index 9932b658857..9ab016de8c7 100644 --- a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/routes.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/routes.mustache @@ -3,20 +3,21 @@ # ~~~~ {{#useSwaggerUI}} -GET /api controllers.ApiDocController.api + GET /api controllers.ApiDocController.api {{/useSwaggerUI}} {{#apiInfo}} -{{#apis}} + {{#apis}} -#Functions for {{{baseName}}} API -{{#operations}} -{{#operation}} -{{httpMethod}} {{{contextPath}}}{{{path}}} controllers.{{classname}}Controller.{{operationId}}({{#pathParams}}{{paramName}}: {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/pathParams}}) -{{/operation}} -{{/operations}} -{{/apis}} + #Functions for {{{baseName}}} API + {{#operations}} + {{#operation}} + {{httpMethod}} {{{contextPath}}}{{{path}}} controllers.{{classname}}Controller.{{operationId}}({{#pathParams}}{{paramName}}: {{#isUuid}}java.util.UUID{{/isUuid}}{{^isUuid}}{{{dataType}}}{{/isUuid}}{{#hasMore}}, {{/hasMore}}{{/pathParams}}) + {{/operation}} + {{/operations}} + {{/apis}} {{/apiInfo}} # Map static resources from the /public folder to the /assets URL path -GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset) \ No newline at end of file +GET /assets/*file controllers.Assets.at(file) +GET /versionedAssets/*file controllers.Assets.versioned(file) \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/swaggerUtils.mustache b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/swaggerUtils.mustache index bb380418fe4..8b472f131d0 100644 --- a/modules/swagger-codegen/src/main/resources/JavaPlayFramework/swaggerUtils.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaPlayFramework/swaggerUtils.mustache @@ -9,66 +9,77 @@ import java.lang.annotation.Target; import java.text.SimpleDateFormat; import java.util.*; +{{#useBeanValidation}} +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +{{/useBeanValidation}} + public class SwaggerUtils { -{{#handleExceptions}} +{{#wrapCalls}} @With(ApiCall.class) @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface ApiAction { } -{{/handleExceptions}} +{{/wrapCalls}} + +{{#useBeanValidation}} + public static void validate(T obj) { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + Validator validator = factory.getValidator(); + Set> constraintViolations = validator.validate(obj); + if (constraintViolations.size() > 0) { + StringBuilder errors = new StringBuilder(); + for (ConstraintViolation contraintes : constraintViolations) { + errors.append(String.format("%s.%s %s\n", + contraintes.getRootBeanClass().getSimpleName(), + contraintes.getPropertyPath(), + contraintes.getMessage())); + } + throw new RuntimeException("Bean validation : " + errors); + } + } +{{/useBeanValidation}} - public static List parametersToList(String collectionFormat, String name, Object value){ + public static List parametersToList(String collectionFormat, String[] values){ List params = new ArrayList<>(); - // preconditions - if (name == null || name.isEmpty() || value == null) return params; - - Collection valueCollection = null; - if (value instanceof Collection) { - valueCollection = (Collection) value; - } else { - params.add(parameterToString(value)); - return params; - } - - if (valueCollection.isEmpty()){ + if (values == null) { return params; } - // get the collection format - collectionFormat = (collectionFormat == null || collectionFormat.isEmpty() ? "csv" : collectionFormat); // default: csv - - // create the params based on the collection format - if (collectionFormat.equals("multi")) { - for (Object item : valueCollection) { - params.add(parameterToString(item)); + if (values.length >= 1 && collectionFormat.equals("multi")) { + params.addAll(Arrays.asList(values)); + } else { + collectionFormat = (collectionFormat == null || collectionFormat.isEmpty() ? "csv" : collectionFormat); // default: csv + + String delimiter = ","; + + switch(collectionFormat) { + case "csv": { + delimiter = ","; + break; + } + case "ssv": { + delimiter = " "; + break; + } + case "tsv": { + delimiter = "\t"; + break; + } + case "pipes": { + delimiter = "|"; + break; + } } - return params; + params = Arrays.asList(values[0].split(delimiter)); } - String delimiter = ","; - - if (collectionFormat.equals("csv")) { - delimiter = ","; - } else if (collectionFormat.equals("ssv")) { - delimiter = " "; - } else if (collectionFormat.equals("tsv")) { - delimiter = "\t"; - } else if (collectionFormat.equals("pipes")) { - delimiter = "|"; - } - - StringBuilder sb = new StringBuilder() ; - for (Object item : valueCollection) { - sb.append(delimiter); - sb.append(parameterToString(item)); - } - - params.add(sb.substring(1)); - return params; } diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/api.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/api.mustache index 9b1acf30d1b..58bfee5f8ba 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/api.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/api.mustache @@ -7,12 +7,19 @@ package {{package}}; {{#imports}}import {{import}}; {{/imports}} - +{{#jdk8-no-delegate}} +import com.fasterxml.jackson.databind.ObjectMapper; +{{/jdk8-no-delegate}} import io.swagger.annotations.*; -{{#jdk8}} +{{#jdk8-no-delegate}} +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; -{{/jdk8}} +{{/jdk8-no-delegate}} import org.springframework.http.ResponseEntity; +{{#useBeanValidation}} +import org.springframework.validation.annotation.Validated; +{{/useBeanValidation}} import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; @@ -22,24 +29,56 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.multipart.MultipartFile; +{{#jdk8-no-delegate}} +import javax.servlet.http.HttpServletRequest; +{{/jdk8-no-delegate}} +{{#useBeanValidation}} +import javax.validation.Valid; +import javax.validation.constraints.*; +{{/useBeanValidation}} +{{#jdk8-no-delegate}} +import java.io.IOException; +{{/jdk8-no-delegate}} import java.util.List; -{{#useOptional}} +{{#jdk8-no-delegate}} +import java.util.Optional; +{{/jdk8-no-delegate}} +{{^jdk8-no-delegate}} + {{#useOptional}} import java.util.Optional; -{{/useOptional}} + {{/useOptional}} +{{/jdk8-no-delegate}} {{#async}} import java.util.concurrent.{{^jdk8}}Callable{{/jdk8}}{{#jdk8}}CompletableFuture{{/jdk8}}; {{/async}} -{{#useBeanValidation}} -import javax.validation.constraints.*; -import javax.validation.Valid; -{{/useBeanValidation}} {{>generatedAnnotation}} @Api(value = "{{{baseName}}}", description = "the {{{baseName}}} API") {{#operations}} public interface {{classname}} { +{{#jdk8}} + + {{^isDelegate}} + Logger log = LoggerFactory.getLogger({{classname}}.class); + + default Optional getObjectMapper() { + return Optional.empty(); + } + + default Optional getRequest() { + return Optional.empty(); + } + + default Optional getAcceptHeader() { + return getRequest().map(r -> r.getHeader("Accept")); + } + {{/isDelegate}} + {{#isDelegate}} + {{classname}}Delegate getDelegate(); + {{/isDelegate}} +{{/jdk8}} {{#operation}} - @ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}", response = {{{returnType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = { + @ApiOperation(value = "{{{summary}}}", nickname = "{{{operationId}}}", notes = "{{{notes}}}"{{#returnBaseType}}, response = {{{returnBaseType}}}.class{{/returnBaseType}}{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = { {{#authMethods}}@Authorization(value = "{{name}}"{{#isOAuth}}, scopes = { {{#scopes}}@AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{#hasMore}}, {{/hasMore}}{{/scopes}} @@ -47,10 +86,14 @@ public interface {{classname}} { {{/hasMore}}{{/authMethods}} }{{/hasAuthMethods}}, tags={ {{#vendorExtensions.x-tags}}"{{tag}}",{{/vendorExtensions.x-tags}} }) @ApiResponses(value = { {{#responses}} - @ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{dataType}}}.class{{#containerType}}, responseContainer = "{{{containerType}}}"{{/containerType}}){{#hasMore}},{{/hasMore}}{{/responses}} }) - {{#implicitHeaders}}@ApiImplicitParams({ - {{#headerParams}}{{>implicitHeader}}{{/headerParams}} - }){{/implicitHeaders}} + @ApiResponse(code = {{{code}}}, message = "{{{message}}}"{{#baseType}}, response = {{{baseType}}}.class{{/baseType}}{{#containerType}}, responseContainer = "{{{containerType}}}"{{/containerType}}){{#hasMore}},{{/hasMore}}{{/responses}} }) + {{#implicitHeaders}} + @ApiImplicitParams({ + {{#headerParams}} + {{>implicitHeader}} + {{/headerParams}} + }) + {{/implicitHeaders}} @RequestMapping(value = "{{{path}}}",{{#singleContentTypes}} produces = "{{{vendorExtensions.x-accepts}}}", consumes = "{{{vendorExtensions.x-contentType}}}",{{/singleContentTypes}}{{^singleContentTypes}}{{#hasProduces}} @@ -58,8 +101,26 @@ public interface {{classname}} { consumes = { {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} },{{/hasConsumes}}{{/singleContentTypes}} method = RequestMethod.{{httpMethod}}) {{#jdk8}}default {{/jdk8}}{{#responseWrapper}}{{.}}<{{/responseWrapper}}ResponseEntity<{{>returnTypes}}>{{#responseWrapper}}>{{/responseWrapper}} {{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},{{/hasMore}}{{/allParams}}){{^jdk8}};{{/jdk8}}{{#jdk8}} { - // do some magic! - return {{#async}}CompletableFuture.completedFuture({{/async}}new ResponseEntity<{{>returnTypes}}>(HttpStatus.OK){{#async}}){{/async}}; + {{^isDelegate}} + if(getObjectMapper().isPresent() && getAcceptHeader().isPresent()) { + {{#examples}} + if (getAcceptHeader().get().contains("{{{contentType}}}")) { + try { + return {{#async}}CompletableFuture.completedFuture({{/async}}new ResponseEntity<>(getObjectMapper().get().readValue("{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{example}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{>exampleReturnTypes}}.class), HttpStatus.NOT_IMPLEMENTED){{#async}}){{/async}}; + } catch (IOException e) { + log.error("Couldn't serialize response for content type {{{contentType}}}", e); + return {{#async}}CompletableFuture.completedFuture({{/async}}new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR){{#async}}){{/async}}; + } + } + {{/examples}} + } else { + log.warn("ObjectMapper or HttpServletRequest not configured in default {{classname}} interface so no example is generated"); + } + return {{#async}}CompletableFuture.completedFuture({{/async}}new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED){{#async}}){{/async}}; + {{/isDelegate}} + {{#isDelegate}} + return getDelegate().{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + {{/isDelegate}} }{{/jdk8}} {{/operation}} diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/apiController.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/apiController.mustache index cb76830b77a..4d56cdab40a 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/apiController.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/apiController.mustache @@ -1,16 +1,21 @@ package {{package}}; -{{^jdk8-no-delegate}} +{{^jdk8}} {{#imports}}import {{import}}; {{/imports}} - +{{/jdk8}} +{{^isDelegate}} +import com.fasterxml.jackson.databind.ObjectMapper; +{{/isDelegate}} +{{^jdk8}} import io.swagger.annotations.*; - +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -{{/jdk8-no-delegate}} +{{/jdk8}} import org.springframework.stereotype.Controller; -{{^jdk8-no-delegate}} +{{^jdk8}} import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; @@ -18,42 +23,125 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.multipart.MultipartFile; -import java.util.List; -{{#useOptional}} -import java.util.Optional; -{{/useOptional}} -{{#async}} -import java.util.concurrent.Callable; -{{/async}}{{/jdk8-no-delegate}} -{{#useBeanValidation}} + {{#useBeanValidation}} import javax.validation.constraints.*; import javax.validation.Valid; -{{/useBeanValidation}} + {{/useBeanValidation}} +{{/jdk8}} +{{^isDelegate}} +import javax.servlet.http.HttpServletRequest; + {{#jdk8}} +import java.util.Optional; + {{/jdk8}} +{{/isDelegate}} +{{^jdk8-no-delegate}} + {{#useOptional}} +import java.util.Optional; + {{/useOptional}} +{{/jdk8-no-delegate}} +{{^jdk8}} + {{^isDelegate}} +import java.io.IOException; + {{/isDelegate}} +import java.util.List; + {{#async}} +import java.util.concurrent.Callable; + {{/async}} +{{/jdk8}} {{>generatedAnnotation}} @Controller {{#operations}} public class {{classname}}Controller implements {{classname}} { + {{#isDelegate}} private final {{classname}}Delegate delegate; @org.springframework.beans.factory.annotation.Autowired public {{classname}}Controller({{classname}}Delegate delegate) { this.delegate = delegate; - }{{/isDelegate}} + } + {{#jdk8}} -{{^jdk8-no-delegate}}{{#operation}} - public {{#async}}Callable<{{/async}}ResponseEntity<{{>returnTypes}}>{{#async}}>{{/async}} {{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}}, - {{/hasMore}}{{/allParams}}) { - // do some magic!{{^isDelegate}}{{^async}} - return new ResponseEntity<{{>returnTypes}}>(HttpStatus.OK);{{/async}}{{#async}} + @Override + public {{classname}}Delegate getDelegate() { + return delegate; + } + {{/jdk8}} +{{/isDelegate}} +{{^isDelegate}} + {{^jdk8}} + private static final Logger log = LoggerFactory.getLogger({{classname}}Controller.class); + + {{/jdk8}} + private final ObjectMapper objectMapper; + + private final HttpServletRequest request; + + @org.springframework.beans.factory.annotation.Autowired + public {{classname}}Controller(ObjectMapper objectMapper, HttpServletRequest request) { + this.objectMapper = objectMapper; + this.request = request; + } + {{#jdk8}} + + @Override + public Optional getObjectMapper() { + return Optional.ofNullable(objectMapper); + } + + @Override + public Optional getRequest() { + return Optional.ofNullable(request); + } + {{/jdk8}} + +{{/isDelegate}} +{{^jdk8}} +{{#operation}} + public {{#async}}Callable<{{/async}}ResponseEntity<{{>returnTypes}}>{{#async}}>{{/async}} {{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},{{/hasMore}}{{/allParams}}) { + {{^isDelegate}} + {{^async}} + String accept = request.getHeader("Accept"); + {{#examples}} + if (accept != null && accept.contains("{{{contentType}}}")) { + try { + return new ResponseEntity<{{>returnTypes}}>(objectMapper.readValue("{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{example}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{>exampleReturnTypes}}.class), HttpStatus.NOT_IMPLEMENTED); + } catch (IOException e) { + log.error("Couldn't serialize response for content type {{{contentType}}}", e); + return new ResponseEntity<{{>returnTypes}}>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + {{/examples}} + return new ResponseEntity<{{>returnTypes}}>(HttpStatus.NOT_IMPLEMENTED); + {{/async}} + {{#async}} return new CallablereturnTypes}}>>() { @Override - public ResponseEntity<{{>returnTypes}}> call() throws Exception { - return new ResponseEntity<{{>returnTypes}}>(HttpStatus.OK); + public ResponseEntity<{{>returnTypes}}> call() { + String accept = request.getHeader("Accept"); + {{#examples}} + if (accept != null && accept.contains("{{{contentType}}}")) { + try { + return new ResponseEntity<{{>returnTypes}}>(objectMapper.readValue("{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{example}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{>exampleReturnTypes}}.class), HttpStatus.NOT_IMPLEMENTED); + } catch (IOException e) { + log.error("Couldn't serialize response for content type {{{contentType}}}", e); + return new ResponseEntity<{{>returnTypes}}>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + {{/examples}} + return new ResponseEntity<{{>returnTypes}}>(HttpStatus.NOT_IMPLEMENTED); } - };{{/async}}{{/isDelegate}}{{#isDelegate}} - return delegate.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{/isDelegate}} + }; + {{/async}} + {{/isDelegate}} + {{#isDelegate}} + return delegate.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + {{/isDelegate}} } -{{/operation}}{{/jdk8-no-delegate}} + +{{/operation}} +{{/jdk8}} } {{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/apiDelegate.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/apiDelegate.mustache index eed583ea916..482cdb1b2fb 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/apiDelegate.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/apiDelegate.mustache @@ -2,13 +2,33 @@ package {{package}}; {{#imports}}import {{import}}; {{/imports}} - -import io.swagger.annotations.*;{{#jdk8}} -import org.springframework.http.HttpStatus;{{/jdk8}} +{{#jdk8}} +import com.fasterxml.jackson.databind.ObjectMapper; +{{/jdk8}} +import io.swagger.annotations.*; +{{#jdk8}} +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +{{/jdk8}} import org.springframework.http.ResponseEntity; import org.springframework.web.multipart.MultipartFile; +{{#jdk8}} +import java.io.IOException; +{{/jdk8}} +{{#jdk8}} +import javax.servlet.http.HttpServletRequest; +{{/jdk8}} import java.util.List; +{{#jdk8}} +import java.util.Optional; +{{/jdk8}} +{{^jdk8}} + {{#useOptional}} +import java.util.Optional; + {{/useOptional}} +{{/jdk8}} {{#async}} import java.util.concurrent.{{^jdk8}}Callable{{/jdk8}}{{#jdk8}}CompletableFuture{{/jdk8}}; {{/async}} @@ -16,11 +36,26 @@ import java.util.concurrent.{{^jdk8}}Callable{{/jdk8}}{{#jdk8}}CompletableFuture {{#operations}} /** * A delegate to be called by the {@link {{classname}}Controller}}. - * Should be implemented as a controller but without the {@link org.springframework.stereotype.Controller} annotation. - * Instead, use spring to autowire this class into the {@link {{classname}}Controller}. + * Implement this interface with a {@link org.springframework.stereotype.Service} annotated class. */ {{>generatedAnnotation}} public interface {{classname}}Delegate { +{{#jdk8}} + + Logger log = LoggerFactory.getLogger({{classname}}.class); + + default Optional getObjectMapper() { + return Optional.empty(); + } + + default Optional getRequest() { + return Optional.empty(); + } + + default Optional getAcceptHeader() { + return getRequest().map(r -> r.getHeader("Accept")); + } +{{/jdk8}} {{#operation}} /** @@ -28,8 +63,21 @@ public interface {{classname}}Delegate { */ {{#jdk8}}default {{/jdk8}}{{#responseWrapper}}{{.}}<{{/responseWrapper}}ResponseEntity<{{>returnTypes}}>{{#responseWrapper}}>{{/responseWrapper}} {{operationId}}({{#allParams}}{{^isFile}}{{{dataType}}}{{/isFile}}{{#isFile}}MultipartFile{{/isFile}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{^jdk8}};{{/jdk8}}{{#jdk8}} { - // do some magic! - return {{#async}}CompletableFuture.completedFuture({{/async}}new ResponseEntity<{{>returnTypes}}>(HttpStatus.OK){{#async}}){{/async}}; + if(getObjectMapper().isPresent() && getAcceptHeader().isPresent()) { + {{#examples}} + if (getAcceptHeader().get().contains("{{{contentType}}}")) { + try { + return {{#async}}CompletableFuture.completedFuture({{/async}}new ResponseEntity<>(getObjectMapper().get().readValue("{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{example}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{>exampleReturnTypes}}.class), HttpStatus.NOT_IMPLEMENTED){{#async}}){{/async}}; + } catch (IOException e) { + log.error("Couldn't serialize response for content type {{{contentType}}}", e); + return {{#async}}CompletableFuture.completedFuture({{/async}}new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR){{#async}}){{/async}}; + } + } + {{/examples}} + } else { + log.warn("ObjectMapper or HttpServletRequest not configured in default {{classname}} interface so no example is generated"); + } + return {{#async}}CompletableFuture.completedFuture({{/async}}new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED){{#async}}){{/async}}; }{{/jdk8}} {{/operation}} diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/beanValidationCore.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/beanValidationCore.mustache index 84a26690f9b..3e7954e4a87 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/beanValidationCore.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/beanValidationCore.mustache @@ -1,20 +1,20 @@ -{{#pattern}} @Pattern(regexp="{{{pattern}}}"){{/pattern}}{{! +{{#pattern}}@Pattern(regexp="{{{pattern}}}") {{/pattern}}{{! minLength && maxLength set -}}{{#minLength}}{{#maxLength}} @Size(min={{minLength}},max={{maxLength}}){{/maxLength}}{{/minLength}}{{! +}}{{#minLength}}{{#maxLength}}@Size(min={{minLength}},max={{maxLength}}) {{/maxLength}}{{/minLength}}{{! minLength set, maxLength not -}}{{#minLength}}{{^maxLength}} @Size(min={{minLength}}){{/maxLength}}{{/minLength}}{{! +}}{{#minLength}}{{^maxLength}}@Size(min={{minLength}}) {{/maxLength}}{{/minLength}}{{! minLength not set, maxLength set -}}{{^minLength}}{{#maxLength}} @Size(max={{maxLength}}){{/maxLength}}{{/minLength}}{{! +}}{{^minLength}}{{#maxLength}}@Size(max={{maxLength}}) {{/maxLength}}{{/minLength}}{{! @Size: minItems && maxItems set -}}{{#minItems}}{{#maxItems}} @Size(min={{minItems}},max={{maxItems}}){{/maxItems}}{{/minItems}}{{! +}}{{#minItems}}{{#maxItems}}@Size(min={{minItems}},max={{maxItems}}) {{/maxItems}}{{/minItems}}{{! @Size: minItems set, maxItems not -}}{{#minItems}}{{^maxItems}} @Size(min={{minItems}}){{/maxItems}}{{/minItems}}{{! +}}{{#minItems}}{{^maxItems}}@Size(min={{minItems}}) {{/maxItems}}{{/minItems}}{{! @Size: minItems not set && maxItems set -}}{{^minItems}}{{#maxItems}} @Size(max={{maxItems}}){{/maxItems}}{{/minItems}}{{! +}}{{^minItems}}{{#maxItems}}@Size(max={{maxItems}}) {{/maxItems}}{{/minItems}}{{! check for integer or long / all others=decimal type with @Decimal* isInteger set -}}{{#isInteger}}{{#minimum}} @Min({{minimum}}){{/minimum}}{{#maximum}} @Max({{maximum}}){{/maximum}}{{/isInteger}}{{! +}}{{#isInteger}}{{#minimum}}@Min({{minimum}}){{/minimum}}{{#maximum}} @Max({{maximum}}) {{/maximum}}{{/isInteger}}{{! isLong set -}}{{#isLong}}{{#minimum}} @Min({{minimum}}){{/minimum}}{{#maximum}} @Max({{maximum}}){{/maximum}}{{/isLong}}{{! +}}{{#isLong}}{{#minimum}}@Min({{minimum}}){{/minimum}}{{#maximum}} @Max({{maximum}}) {{/maximum}}{{/isLong}}{{! Not Integer, not Long => we have a decimal value! -}}{{^isInteger}}{{^isLong}}{{#minimum}} @DecimalMin("{{minimum}}"){{/minimum}}{{#maximum}} @DecimalMax("{{maximum}}"){{/maximum}}{{/isLong}}{{/isInteger}} \ No newline at end of file +}}{{^isInteger}}{{^isLong}}{{#minimum}}@DecimalMin("{{minimum}}"){{/minimum}}{{#maximum}} @DecimalMax("{{maximum}}") {{/maximum}}{{/isLong}}{{/isInteger}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/beanValidationQueryParams.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/beanValidationQueryParams.mustache index c4ff01d7e55..9cca8cb8874 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/beanValidationQueryParams.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/beanValidationQueryParams.mustache @@ -1 +1 @@ -{{#required}} @NotNull{{/required}}{{>beanValidationCore}} \ No newline at end of file +{{#required}}@NotNull {{/required}}{{>beanValidationCore}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/customInstantDeserializer.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/customInstantDeserializer.mustache new file mode 100644 index 00000000000..b7b8e251bdb --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/customInstantDeserializer.mustache @@ -0,0 +1,232 @@ +package {{configPackage}}; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonTokenId; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.datatype.threetenbp.DateTimeUtils; +import com.fasterxml.jackson.datatype.threetenbp.DecimalUtils; +import com.fasterxml.jackson.datatype.threetenbp.deser.ThreeTenDateTimeDeserializerBase; +import com.fasterxml.jackson.datatype.threetenbp.function.BiFunction; +import com.fasterxml.jackson.datatype.threetenbp.function.Function; +import org.threeten.bp.DateTimeException; +import org.threeten.bp.Instant; +import org.threeten.bp.OffsetDateTime; +import org.threeten.bp.ZoneId; +import org.threeten.bp.ZonedDateTime; +import org.threeten.bp.format.DateTimeFormatter; +import org.threeten.bp.temporal.Temporal; +import org.threeten.bp.temporal.TemporalAccessor; + +import java.io.IOException; +import java.math.BigDecimal; + +/** + * Deserializer for ThreeTen temporal {@link Instant}s, {@link OffsetDateTime}, and {@link ZonedDateTime}s. + * Adapted from the jackson threetenbp InstantDeserializer to add support for deserializing rfc822 format. + * + * @author Nick Williams + */ +public class CustomInstantDeserializer + extends ThreeTenDateTimeDeserializerBase { + private static final long serialVersionUID = 1L; + + public static final CustomInstantDeserializer INSTANT = new CustomInstantDeserializer( + Instant.class, DateTimeFormatter.ISO_INSTANT, + new Function() { + @Override + public Instant apply(TemporalAccessor temporalAccessor) { + return Instant.from(temporalAccessor); + } + }, + new Function() { + @Override + public Instant apply(FromIntegerArguments a) { + return Instant.ofEpochMilli(a.value); + } + }, + new Function() { + @Override + public Instant apply(FromDecimalArguments a) { + return Instant.ofEpochSecond(a.integer, a.fraction); + } + }, + null + ); + + public static final CustomInstantDeserializer OFFSET_DATE_TIME = new CustomInstantDeserializer( + OffsetDateTime.class, DateTimeFormatter.ISO_OFFSET_DATE_TIME, + new Function() { + @Override + public OffsetDateTime apply(TemporalAccessor temporalAccessor) { + return OffsetDateTime.from(temporalAccessor); + } + }, + new Function() { + @Override + public OffsetDateTime apply(FromIntegerArguments a) { + return OffsetDateTime.ofInstant(Instant.ofEpochMilli(a.value), a.zoneId); + } + }, + new Function() { + @Override + public OffsetDateTime apply(FromDecimalArguments a) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(a.integer, a.fraction), a.zoneId); + } + }, + new BiFunction() { + @Override + public OffsetDateTime apply(OffsetDateTime d, ZoneId z) { + return d.withOffsetSameInstant(z.getRules().getOffset(d.toLocalDateTime())); + } + } + ); + + public static final CustomInstantDeserializer ZONED_DATE_TIME = new CustomInstantDeserializer( + ZonedDateTime.class, DateTimeFormatter.ISO_ZONED_DATE_TIME, + new Function() { + @Override + public ZonedDateTime apply(TemporalAccessor temporalAccessor) { + return ZonedDateTime.from(temporalAccessor); + } + }, + new Function() { + @Override + public ZonedDateTime apply(FromIntegerArguments a) { + return ZonedDateTime.ofInstant(Instant.ofEpochMilli(a.value), a.zoneId); + } + }, + new Function() { + @Override + public ZonedDateTime apply(FromDecimalArguments a) { + return ZonedDateTime.ofInstant(Instant.ofEpochSecond(a.integer, a.fraction), a.zoneId); + } + }, + new BiFunction() { + @Override + public ZonedDateTime apply(ZonedDateTime zonedDateTime, ZoneId zoneId) { + return zonedDateTime.withZoneSameInstant(zoneId); + } + } + ); + + protected final Function fromMilliseconds; + + protected final Function fromNanoseconds; + + protected final Function parsedToValue; + + protected final BiFunction adjust; + + protected CustomInstantDeserializer(Class supportedType, + DateTimeFormatter parser, + Function parsedToValue, + Function fromMilliseconds, + Function fromNanoseconds, + BiFunction adjust) { + super(supportedType, parser); + this.parsedToValue = parsedToValue; + this.fromMilliseconds = fromMilliseconds; + this.fromNanoseconds = fromNanoseconds; + this.adjust = adjust == null ? new BiFunction() { + @Override + public T apply(T t, ZoneId zoneId) { + return t; + } + } : adjust; + } + + @SuppressWarnings("unchecked") + protected CustomInstantDeserializer(CustomInstantDeserializer base, DateTimeFormatter f) { + super((Class) base.handledType(), f); + parsedToValue = base.parsedToValue; + fromMilliseconds = base.fromMilliseconds; + fromNanoseconds = base.fromNanoseconds; + adjust = base.adjust; + } + + @Override + protected JsonDeserializer withDateFormat(DateTimeFormatter dtf) { + if (dtf == _formatter) { + return this; + } + return new CustomInstantDeserializer(this, dtf); + } + + @Override + public T deserialize(JsonParser parser, DeserializationContext context) throws IOException { + //NOTE: Timestamps contain no timezone info, and are always in configured TZ. Only + //string values have to be adjusted to the configured TZ. + switch (parser.getCurrentTokenId()) { + case JsonTokenId.ID_NUMBER_FLOAT: { + BigDecimal value = parser.getDecimalValue(); + long seconds = value.longValue(); + int nanoseconds = DecimalUtils.extractNanosecondDecimal(value, seconds); + return fromNanoseconds.apply(new FromDecimalArguments( + seconds, nanoseconds, getZone(context))); + } + + case JsonTokenId.ID_NUMBER_INT: { + long timestamp = parser.getLongValue(); + if (context.isEnabled(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS)) { + return this.fromNanoseconds.apply(new FromDecimalArguments( + timestamp, 0, this.getZone(context) + )); + } + return this.fromMilliseconds.apply(new FromIntegerArguments( + timestamp, this.getZone(context) + )); + } + + case JsonTokenId.ID_STRING: { + String string = parser.getText().trim(); + if (string.length() == 0) { + return null; + } + if (string.endsWith("+0000")) { + string = string.substring(0, string.length() - 5) + "Z"; + } + T value; + try { + TemporalAccessor acc = _formatter.parse(string); + value = parsedToValue.apply(acc); + if (context.isEnabled(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)) { + return adjust.apply(value, this.getZone(context)); + } + } catch (DateTimeException e) { + throw _peelDTE(e); + } + return value; + } + } + throw context.mappingException("Expected type float, integer, or string."); + } + + private ZoneId getZone(DeserializationContext context) { + // Instants are always in UTC, so don't waste compute cycles + return (_valueClass == Instant.class) ? null : DateTimeUtils.timeZoneToZoneId(context.getTimeZone()); + } + + private static class FromIntegerArguments { + public final long value; + public final ZoneId zoneId; + + private FromIntegerArguments(long value, ZoneId zoneId) { + this.value = value; + this.zoneId = zoneId; + } + } + + private static class FromDecimalArguments { + public final long integer; + public final int fraction; + public final ZoneId zoneId; + + private FromDecimalArguments(long integer, int fraction, ZoneId zoneId) { + this.integer = integer; + this.fraction = fraction; + this.zoneId = zoneId; + } + } +} diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/formParams.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/formParams.mustache index 8178a79d5e7..7305792be1f 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/formParams.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/formParams.mustache @@ -1 +1 @@ -{{#isFormParam}}{{#notFile}}@ApiParam(value = "{{{description}}}"{{#required}}, required=true{{/required}}{{#allowableValues}}, allowableValues="{{#values}}{{{.}}}{{^-last}}, {{/-last}}{{#-last}}{{/-last}}{{/values}}"{{/allowableValues}}{{#defaultValue}}, defaultValue="{{{defaultValue}}}"{{/defaultValue}}) @RequestPart(value="{{baseName}}"{{#required}}, required=true{{/required}}{{^required}}, required=false{{/required}}) {{{dataType}}} {{paramName}}{{/notFile}}{{#isFile}}@ApiParam(value = "file detail") @RequestPart("file") MultipartFile {{baseName}}{{/isFile}}{{/isFormParam}} \ No newline at end of file +{{#isFormParam}}{{#notFile}}@ApiParam(value = "{{{description}}}"{{#required}}, required=true{{/required}}{{#allowableValues}}, allowableValues="{{#values}}{{{.}}}{{^-last}}, {{/-last}}{{#-last}}{{/-last}}{{/values}}"{{/allowableValues}}{{#defaultValue}}, defaultValue="{{{defaultValue}}}"{{/defaultValue}}) @RequestParam(value="{{baseName}}"{{#required}}, required=true{{/required}}{{^required}}, required=false{{/required}}) {{{dataType}}} {{paramName}}{{/notFile}}{{#isFile}}@ApiParam(value = "file detail") {{#useBeanValidation}}@Valid{{/useBeanValidation}} @RequestPart("file") MultipartFile {{baseName}}{{/isFile}}{{/isFormParam}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-boot/RFC3339DateFormat.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-boot/RFC3339DateFormat.mustache index afe24ed9aba..d5dff8ac631 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-boot/RFC3339DateFormat.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-boot/RFC3339DateFormat.mustache @@ -9,6 +9,8 @@ import java.util.Date; public class RFC3339DateFormat extends ISO8601DateFormat { + private static final long serialVersionUID = 1L; + // Same as ISO8601DateFormat but serializing milliseconds. @Override public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-boot/jacksonConfiguration.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-boot/jacksonConfiguration.mustache new file mode 100644 index 00000000000..e96aa772c68 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-boot/jacksonConfiguration.mustache @@ -0,0 +1,23 @@ +package {{configPackage}}; + +import com.fasterxml.jackson.datatype.threetenbp.ThreeTenModule; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.threeten.bp.Instant; +import org.threeten.bp.OffsetDateTime; +import org.threeten.bp.ZonedDateTime; + +@Configuration +public class JacksonConfiguration { + + @Bean + @ConditionalOnMissingBean(ThreeTenModule.class) + ThreeTenModule threeTenModule() { + ThreeTenModule module = new ThreeTenModule(); + module.addDeserializer(Instant.class, CustomInstantDeserializer.INSTANT); + module.addDeserializer(OffsetDateTime.class, CustomInstantDeserializer.OFFSET_DATE_TIME); + module.addDeserializer(ZonedDateTime.class, CustomInstantDeserializer.ZONED_DATE_TIME); + return module; + } +} diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-boot/pom.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-boot/pom.mustache index 5791d79040b..520edf0cbb3 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-boot/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-boot/pom.mustache @@ -9,12 +9,12 @@ {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} ${java.version} ${java.version} - 2.6.1 + 2.7.0 org.springframework.boot spring-boot-starter-parent - 1.4.7.RELEASE + 1.5.4.RELEASE src/main/java @@ -71,17 +71,21 @@ jackson-datatype-jsr310 {{/java8}} - {{^java8}} + {{#joda}} com.fasterxml.jackson.datatype jackson-datatype-joda + {{/joda}} + {{#threetenbp}} + - joda-time - joda-time + com.github.joschi.jackson + jackson-datatype-threetenbp + 2.6.4 - {{/java8}} + {{/threetenbp}} {{#useBeanValidation}} diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-cloud/clientConfiguration.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-cloud/clientConfiguration.mustache index 2437b30237e..3e86330c91b 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-cloud/clientConfiguration.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-cloud/clientConfiguration.mustache @@ -24,10 +24,10 @@ public class ClientConfiguration { {{#authMethods}} {{#isBasic}} - @Value("${ {{{title}}}.security.{{{name}}}.username:}") + {{=<% %>=}}@Value("${<%title%>.security.<%name%>.username:}")<%={{ }}=%> private String {{{name}}}Username; - @Value("${ {{{title}}}.security.{{{name}}}.password:}") + {{=<% %>=}}@Value("${<%title%>.security.<%name%>.password:}")<%={{ }}=%> private String {{{name}}}Password; @Bean @@ -38,7 +38,7 @@ public class ClientConfiguration { {{/isBasic}} {{#isApiKey}} - @Value("${ {{{title}}}.security.{{{name}}}.key:}") + {{=<% %>=}}@Value("${<%title%>.security.<%name%>.key:}")<%={{ }}=%> private String {{{name}}}Key; @Bean @@ -102,4 +102,4 @@ public class ClientConfiguration { {{/isImplicit}} {{/isOAuth}} {{/authMethods}} -} \ No newline at end of file +} diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-cloud/jacksonConfiguration.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-cloud/jacksonConfiguration.mustache new file mode 100644 index 00000000000..e96aa772c68 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-cloud/jacksonConfiguration.mustache @@ -0,0 +1,23 @@ +package {{configPackage}}; + +import com.fasterxml.jackson.datatype.threetenbp.ThreeTenModule; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.threeten.bp.Instant; +import org.threeten.bp.OffsetDateTime; +import org.threeten.bp.ZonedDateTime; + +@Configuration +public class JacksonConfiguration { + + @Bean + @ConditionalOnMissingBean(ThreeTenModule.class) + ThreeTenModule threeTenModule() { + ThreeTenModule module = new ThreeTenModule(); + module.addDeserializer(Instant.class, CustomInstantDeserializer.INSTANT); + module.addDeserializer(OffsetDateTime.class, CustomInstantDeserializer.OFFSET_DATE_TIME); + module.addDeserializer(ZonedDateTime.class, CustomInstantDeserializer.ZONED_DATE_TIME); + return module; + } +} diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-cloud/pom.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-cloud/pom.mustache index 9806a725e1e..4360f32990f 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-cloud/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-cloud/pom.mustache @@ -14,7 +14,7 @@ org.springframework.boot spring-boot-starter-parent - 1.4.7.RELEASE + 1.5.4.RELEASE src/main/java @@ -25,7 +25,7 @@ org.springframework.cloud spring-cloud-starter-parent - Camden.SR1 + Dalston.SR1 pom import @@ -66,17 +66,21 @@ jackson-datatype-jsr310 {{/java8}} - {{^java8}} + {{#joda}} com.fasterxml.jackson.datatype jackson-datatype-joda + {{/joda}} + {{#threetenbp}} + - joda-time - joda-time + com.github.joschi.jackson + jackson-datatype-threetenbp + 2.6.4 - {{/java8}} + {{/threetenbp}} {{#useBeanValidation}} diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-mvc/RFC3339DateFormat.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-mvc/RFC3339DateFormat.mustache index 2e60a5c4b97..597120b5b23 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-mvc/RFC3339DateFormat.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-mvc/RFC3339DateFormat.mustache @@ -9,6 +9,8 @@ import java.util.Date; public class RFC3339DateFormat extends ISO8601DateFormat { + private static final long serialVersionUID = 1L; + // Same as ISO8601DateFormat but serializing milliseconds. @Override public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-mvc/pom.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-mvc/pom.mustache index e47034fde91..7bae0894a1c 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-mvc/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-mvc/pom.mustache @@ -93,13 +93,18 @@ io.springfox springfox-swagger2 ${springfox-version} + + + com.fasterxml.jackson.core + jackson-annotations + + io.springfox springfox-swagger-ui ${springfox-version} - {{#withXml}} @@ -111,24 +116,29 @@ {{/withXml}} {{#java8}} + com.fasterxml.jackson.datatype jackson-datatype-jsr310 ${jackson-version} {{/java8}} - {{^java8}} + {{#joda}} + com.fasterxml.jackson.datatype jackson-datatype-joda ${jackson-version} + {{/joda}} + {{#threetenbp}} + - joda-time - joda-time - 2.9.4 + com.github.joschi.jackson + jackson-datatype-threetenbp + ${jackson-threetenbp-version} - {{/java8}} + {{/threetenbp}} junit @@ -159,8 +169,9 @@ 1.7.21 4.12 2.5 - 2.4.0 + 2.7.0 2.8.9 + 2.6.4 4.3.9.RELEASE diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-mvc/swaggerUiConfiguration.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-mvc/swaggerUiConfiguration.mustache index b6493859fcf..563a76915f8 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-mvc/swaggerUiConfiguration.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/libraries/spring-mvc/swaggerUiConfiguration.mustache @@ -2,6 +2,9 @@ package {{configPackage}}; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +{{#threetenbp}} +import com.fasterxml.jackson.datatype.threetenbp.ThreeTenModule; +{{/threetenbp}} import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; @@ -13,6 +16,11 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +{{#threetenbp}} +import org.threeten.bp.Instant; +import org.threeten.bp.OffsetDateTime; +import org.threeten.bp.ZonedDateTime; +{{/threetenbp}} import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.util.List; diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/model.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/model.mustache index 39125314ca7..180cb243f07 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/model.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/model.mustache @@ -7,6 +7,7 @@ import java.util.Objects; import java.io.Serializable; {{/serializableModel}} {{#useBeanValidation}} +import org.springframework.validation.annotation.Validated; import javax.validation.Valid; import javax.validation.constraints.*; {{/useBeanValidation}} diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/pathParams.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/pathParams.mustache index 62342d01bfa..e433fa121a8 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/pathParams.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/pathParams.mustache @@ -1 +1 @@ -{{#isPathParam}}{{#useBeanValidation}}{{>beanValidationPathParams}}{{/useBeanValidation}}@ApiParam(value = "{{{description}}}"{{#required}},required=true{{/required}}{{#allowableValues}}, allowableValues="{{{allowableValues}}}"{{/allowableValues}} {{#defaultValue}}, defaultValue="{{{defaultValue}}}"{{/defaultValue}}) @PathVariable("{{baseName}}") {{>optionalDataType}} {{paramName}}{{/isPathParam}} \ No newline at end of file +{{#isPathParam}}{{#useBeanValidation}}{{>beanValidationPathParams}}{{/useBeanValidation}}@ApiParam(value = "{{{description}}}"{{#required}},required=true{{/required}}{{#allowableValues}}, allowableValues = "{{#enumVars}}{{#lambdaEscapeDoubleQuote}}{{{value}}}{{/lambdaEscapeDoubleQuote}}{{^-last}}, {{/-last}}{{#-last}}{{/-last}}{{/enumVars}}"{{/allowableValues}}{{#defaultValue}}, defaultValue="{{{defaultValue}}}"{{/defaultValue}}) @PathVariable("{{baseName}}") {{>optionalDataType}} {{paramName}}{{/isPathParam}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/pojo.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/pojo.mustache index b969f1e2ac6..5830c643aeb 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/pojo.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/pojo.mustache @@ -2,6 +2,7 @@ * {{#description}}{{.}}{{/description}}{{^description}}{{classname}}{{/description}} */{{#description}} @ApiModel(description = "{{{description}}}"){{/description}} +{{#useBeanValidation}}@Validated{{/useBeanValidation}} {{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{>xmlAnnotation}} public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#serializableModel}}implements Serializable{{/serializableModel}} { {{#serializableModel}} @@ -29,6 +30,7 @@ public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#seriali @SerializedName("{{baseName}}") {{/gson}} {{#isContainer}} + {{#useBeanValidation}}@Valid{{/useBeanValidation}} private {{{datatypeWithEnum}}} {{name}}{{#required}} = {{{defaultValue}}}{{/required}}{{^required}} = null{{/required}}; {{/isContainer}} {{^isContainer}} diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/queryParams.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/queryParams.mustache index 222831b72f4..182152c6e5a 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/queryParams.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/queryParams.mustache @@ -1 +1 @@ -{{#isQueryParam}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}@ApiParam(value = "{{{description}}}"{{#required}}, required = true{{/required}}{{#allowableValues}}, allowableValues = "{{#values}}{{{.}}}{{^-last}}, {{/-last}}{{#-last}}{{/-last}}{{/values}}"{{/allowableValues}}{{#defaultValue}}, defaultValue = "{{{defaultValue}}}"{{/defaultValue}}) @RequestParam(value = "{{baseName}}"{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}{{#defaultValue}}, defaultValue="{{{defaultValue}}}"{{/defaultValue}}) {{>optionalDataType}} {{paramName}}{{/isQueryParam}} \ No newline at end of file +{{#isQueryParam}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}@ApiParam(value = "{{{description}}}"{{#required}}, required = true{{/required}}{{#allowableValues}}, allowableValues = "{{#values}}{{{.}}}{{^-last}}, {{/-last}}{{#-last}}{{/-last}}{{/values}}"{{/allowableValues}}{{#defaultValue}}, defaultValue = "{{{defaultValue}}}"{{/defaultValue}}) {{#useBeanValidation}}@Valid{{/useBeanValidation}} @RequestParam(value = "{{baseName}}"{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}{{#defaultValue}}, defaultValue="{{{defaultValue}}}"{{/defaultValue}}) {{>optionalDataType}} {{paramName}}{{/isQueryParam}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/swaggerDocumentationConfig.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/swaggerDocumentationConfig.mustache index c9976db8149..a0099256420 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/swaggerDocumentationConfig.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/swaggerDocumentationConfig.mustache @@ -36,10 +36,14 @@ public class SwaggerDocumentationConfig { .directModelSubstitute(java.time.LocalDate.class, java.sql.Date.class) .directModelSubstitute(java.time.OffsetDateTime.class, java.util.Date.class) {{/java8}} - {{^java8}} + {{#joda}} .directModelSubstitute(org.joda.time.LocalDate.class, java.sql.Date.class) .directModelSubstitute(org.joda.time.DateTime.class, java.util.Date.class) - {{/java8}} + {{/joda}} + {{#threetenbp}} + .directModelSubstitute(org.threeten.bp.LocalDate.class, java.sql.Date.class) + .directModelSubstitute(org.threeten.bp.OffsetDateTime.class, java.util.Date.class) + {{/threetenbp}} .apiInfo(apiInfo()); } diff --git a/modules/swagger-codegen/src/main/resources/JavaVertXServer/enumOuterClass.mustache b/modules/swagger-codegen/src/main/resources/JavaVertXServer/enumOuterClass.mustache index 7aea7b92f22..2308e8773d1 100644 --- a/modules/swagger-codegen/src/main/resources/JavaVertXServer/enumOuterClass.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaVertXServer/enumOuterClass.mustache @@ -1,3 +1,36 @@ -public enum {{classname}} { - {{#allowableValues}}{{.}}{{^-last}}, {{/-last}}{{/allowableValues}} +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{description}}{{/description}} + */ +public enum {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} { + {{#allowableValues}}{{#enumVars}} + {{{name}}}({{{value}}}){{^-last}}, + {{/-last}}{{#-last}};{{/-last}}{{/enumVars}}{{/allowableValues}} + + private {{{dataType}}} value; + + {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}({{{dataType}}} value) { + this.value = value; + } + + @JsonValue + public {{{dataType}}} getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} fromValue(String text) { + for ({{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) { + if (String.valueOf(b.value).equals(text)) { + return b; + } + } + return null; + } } \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/Javascript/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Javascript/ApiClient.mustache index 01660219ed8..4c5a5d60e7f 100644 --- a/modules/swagger-codegen/src/main/resources/Javascript/ApiClient.mustache +++ b/modules/swagger-codegen/src/main/resources/Javascript/ApiClient.mustache @@ -91,6 +91,10 @@ this.agent = new superagent.agent(); } + /* + * Allow user to override superagent agent + */ + this.requestAgent = null; }; {{#emitJSDoc}} /** @@ -357,6 +361,7 @@ * @param {String} httpMethod The HTTP method to use. * @param {Object.} pathParams A map of path parameters and their values. * @param {Object.} queryParams A map of query parameters and their values. + * @param {Object.} collectionQueryParams A map of collection query parameters and their values. * @param {Object.} headerParams A map of header parameters and their values. * @param {Object.} formParams A map of form parameters and their values. * @param {Object} bodyParam The value to pass as the request body. @@ -369,7 +374,7 @@ * @returns {{#usePromises}}{Promise} A {@link https://www.promisejs.org/|Promise} object{{/usePromises}}{{^usePromises}}{Object} The SuperAgent request object{{/usePromises}}. */ {{/emitJSDoc}} exports.prototype.callApi = function callApi(path, httpMethod, pathParams, - queryParams, headerParams, formParams, bodyParam, authNames, contentTypes, accepts, + queryParams, collectionQueryParams, headerParams, formParams, bodyParam, authNames, contentTypes, accepts, returnType{{^usePromises}}, callback{{/usePromises}}) { var _this = this; @@ -379,6 +384,25 @@ // apply authentications this.applyAuthToRequest(request, authNames); + // set collection query parameters + for (var key in collectionQueryParams) { + if (collectionQueryParams.hasOwnProperty(key)) { + var param = collectionQueryParams[key]; + if (param.collectionFormat === 'csv') { + // SuperAgent normally percent-encodes all reserved characters in a query parameter. However, + // commas are used as delimiters for the 'csv' collectionFormat so they must not be encoded. We + // must therefore construct and encode 'csv' collection query parameters manually. + if (param.value != null) { + var value = param.value.map(this.paramToString).map(encodeURIComponent).join(','); + request.query(encodeURIComponent(key) + "=" + value); + } + } else { + // All other collection query parameters should be treated as ordinary query parameters. + queryParams[key] = this.buildCollectionParam(param.value, param.collectionFormat); + } + } + } + // set query parameters if (httpMethod.toUpperCase() === 'GET' && this.cache === false) { queryParams['_'] = new Date().getTime(); @@ -388,6 +412,12 @@ // set header parameters request.set(this.defaultHeaders).set(this.normalizeParams(headerParams)); + + // set requestAgent if it is set by user + if (this.requestAgent) { + request.agent(this.requestAgent); + } + // set request timeout request.timeout(this.timeout); diff --git a/modules/swagger-codegen/src/main/resources/Javascript/api.mustache b/modules/swagger-codegen/src/main/resources/Javascript/api.mustache index b012bfe458c..fedb65f421b 100644 --- a/modules/swagger-codegen/src/main/resources/Javascript/api.mustache +++ b/modules/swagger-codegen/src/main/resources/Javascript/api.mustache @@ -27,7 +27,7 @@ * * @alias module:<#invokerPackage><&invokerPackage>/<#apiPackage>/ * @class - * @param {module:<#invokerPackage><&invokerPackage>/ApiClient} apiClient Optional API client implementation to use, + * @param {module:<#invokerPackage><&invokerPackage>/ApiClient} [apiClient] Optional API client implementation to use, * default to {@link module:<#invokerPackage><&invokerPackage>/ApiClient#instance} if unspecified. */ var exports = function(apiClient) { @@ -65,8 +65,14 @@ var pathParams = {<#pathParams> '': <#required><^required>opts['']<#hasMore>, }; - var queryParams = {<#queryParams> - '': <#collectionFormat>this.apiClient.buildCollectionParam(<#required><^required>opts[''], '')<^collectionFormat><#required><^required>opts['']<#hasMore>, + var queryParams = {<#queryParams><^collectionFormat> + '': <#required><^required>opts[''], + }; + var collectionQueryParams = {<#queryParams><#collectionFormat> + '': { + value: <#required><^required>opts[''], + collectionFormat: '' + }, }; var headerParams = {<#headerParams> '': <#required><^required>opts['']<#hasMore>, @@ -82,7 +88,7 @@ return this.apiClient.callApi( '<&path>', '', - pathParams, queryParams, headerParams, formParams, postBody, + pathParams, queryParams, collectionQueryParams, headerParams, formParams, postBody, authNames, contentTypes, accepts, returnType<^usePromises>, callback ); } diff --git a/modules/swagger-codegen/src/main/resources/Javascript/es6/.babelrc.mustache b/modules/swagger-codegen/src/main/resources/Javascript/es6/.babelrc.mustache index bcb6ee8de93..67b369ed370 100644 --- a/modules/swagger-codegen/src/main/resources/Javascript/es6/.babelrc.mustache +++ b/modules/swagger-codegen/src/main/resources/Javascript/es6/.babelrc.mustache @@ -1,3 +1,3 @@ { - "presets": ["es2015", "stage-0"] -} \ No newline at end of file + "presets": ["env", "stage-0"] +} diff --git a/modules/swagger-codegen/src/main/resources/Javascript/es6/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Javascript/es6/ApiClient.mustache index 17e9000179d..0e2a92da149 100644 --- a/modules/swagger-codegen/src/main/resources/Javascript/es6/ApiClient.mustache +++ b/modules/swagger-codegen/src/main/resources/Javascript/es6/ApiClient.mustache @@ -78,6 +78,12 @@ export default class ApiClient { if (typeof window === 'undefined') { this.agent = new superagent.agent(); } + + /* + * Allow user to override superagent agent + */ + this.requestAgent = null; + } {{#emitJSDoc}}/** @@ -396,6 +402,11 @@ export default class ApiClient { // set header parameters request.set(this.defaultHeaders).set(this.normalizeParams(headerParams)); + // set requestAgent if it is set by user + if (this.requestAgent) { + request.agent(this.requestAgent); + } + // set request timeout request.timeout(this.timeout); diff --git a/modules/swagger-codegen/src/main/resources/Javascript/es6/api.mustache b/modules/swagger-codegen/src/main/resources/Javascript/es6/api.mustache index f913650b614..0615a0461d4 100644 --- a/modules/swagger-codegen/src/main/resources/Javascript/es6/api.mustache +++ b/modules/swagger-codegen/src/main/resources/Javascript/es6/api.mustache @@ -17,7 +17,7 @@ export default class { * * @alias module:<#invokerPackage><&invokerPackage>/<#apiPackage>/ * @class - * @param {module:<#invokerPackage><&invokerPackage>/ApiClient} apiClient Optional API client implementation to use, + * @param {module:<#invokerPackage><&invokerPackage>/ApiClient} [apiClient] Optional API client implementation to use, * default to {@link module:<#invokerPackage><&invokerPackage>/ApiClient#instance} if unspecified. */ constructor(apiClient) { diff --git a/modules/swagger-codegen/src/main/resources/Javascript/es6/package.mustache b/modules/swagger-codegen/src/main/resources/Javascript/es6/package.mustache index 0998928468f..997c5e24055 100644 --- a/modules/swagger-codegen/src/main/resources/Javascript/es6/package.mustache +++ b/modules/swagger-codegen/src/main/resources/Javascript/es6/package.mustache @@ -2,7 +2,7 @@ "name": "{{{projectName}}}", "version": "{{{projectVersion}}}", "description": "{{{projectDescription}}}", - "license": "Unlicense", + "license": "{{licenseName}}", "main": "{{sourceFolder}}{{#invokerPackage}}/{{invokerPackage}}{{/invokerPackage}}/index.js", "scripts": { "test": "mocha --compilers js:babel-core/register --recursive" @@ -12,12 +12,12 @@ }, "dependencies": { "babel": "^6.23.0", - "babel-cli": "^6.24.1", + "babel-cli": "^6.26.0", "superagent": "3.5.2" }, "devDependencies": { - "babel-core": "6.18.0", - "babel-preset-es2015": "^6.24.1", + "babel-core": "6.26.0", + "babel-preset-env": "^1.6.1", "babel-preset-stage-0": "^6.24.1", "expect.js": "~0.3.1", "mocha": "~2.3.4", diff --git a/modules/swagger-codegen/src/main/resources/Javascript/package.mustache b/modules/swagger-codegen/src/main/resources/Javascript/package.mustache index bdb18166c75..79d0d8f6721 100644 --- a/modules/swagger-codegen/src/main/resources/Javascript/package.mustache +++ b/modules/swagger-codegen/src/main/resources/Javascript/package.mustache @@ -2,7 +2,7 @@ "name": "{{{projectName}}}", "version": "{{{projectVersion}}}", "description": "{{{projectDescription}}}", - "license": "Unlicense", + "license": "{{licenseName}}", "main": "{{sourceFolder}}{{#invokerPackage}}/{{invokerPackage}}{{/invokerPackage}}/index.js", "scripts": { "test": "./node_modules/mocha/bin/mocha --recursive" diff --git a/modules/swagger-codegen/src/main/resources/META-INF/services/io.swagger.codegen.CodegenConfig b/modules/swagger-codegen/src/main/resources/META-INF/services/io.swagger.codegen.CodegenConfig index d0feabe6507..5c5d4220bad 100644 --- a/modules/swagger-codegen/src/main/resources/META-INF/services/io.swagger.codegen.CodegenConfig +++ b/modules/swagger-codegen/src/main/resources/META-INF/services/io.swagger.codegen.CodegenConfig @@ -1,10 +1,9 @@ +io.swagger.codegen.languages.AdaCodegen io.swagger.codegen.languages.AkkaScalaClientCodegen io.swagger.codegen.languages.AndroidClientCodegen io.swagger.codegen.languages.Apache2ConfigCodegen io.swagger.codegen.languages.ApexClientCodegen -io.swagger.codegen.languages.AspNet5ServerCodegen io.swagger.codegen.languages.AspNetCoreServerCodegen -io.swagger.codegen.languages.AsyncScalaClientCodegen io.swagger.codegen.languages.BashClientCodegen io.swagger.codegen.languages.CSharpClientCodegen io.swagger.codegen.languages.ClojureClientCodegen @@ -14,6 +13,7 @@ io.swagger.codegen.languages.CsharpDotNet2ClientCodegen io.swagger.codegen.languages.DartClientCodegen io.swagger.codegen.languages.ElixirClientCodegen io.swagger.codegen.languages.EiffelClientCodegen +io.swagger.codegen.languages.ErlangClientCodegen io.swagger.codegen.languages.ErlangServerCodegen io.swagger.codegen.languages.FinchServerCodegen io.swagger.codegen.languages.FlashClientCodegen @@ -21,6 +21,7 @@ io.swagger.codegen.languages.FlaskConnexionCodegen io.swagger.codegen.languages.GoClientCodegen io.swagger.codegen.languages.GoServerCodegen io.swagger.codegen.languages.GroovyClientCodegen +io.swagger.codegen.languages.HaskellHttpClientCodegen io.swagger.codegen.languages.HaskellServantCodegen io.swagger.codegen.languages.JMeterCodegen io.swagger.codegen.languages.JavaCXFClientCodegen @@ -39,6 +40,7 @@ io.swagger.codegen.languages.JavascriptClientCodegen io.swagger.codegen.languages.JavascriptClosureAngularClientCodegen io.swagger.codegen.languages.JavaVertXServerCodegen io.swagger.codegen.languages.KotlinClientCodegen +io.swagger.codegen.languages.LuaClientCodegen io.swagger.codegen.languages.LumenServerCodegen io.swagger.codegen.languages.NancyFXServerCodegen io.swagger.codegen.languages.NodeJSServerCodegen @@ -50,11 +52,16 @@ io.swagger.codegen.languages.PowerShellClientCodegen io.swagger.codegen.languages.PistacheServerCodegen io.swagger.codegen.languages.PythonClientCodegen io.swagger.codegen.languages.Qt5CPPGenerator +io.swagger.codegen.languages.RClientCodegen io.swagger.codegen.languages.Rails5ServerCodegen io.swagger.codegen.languages.RestbedCodegen io.swagger.codegen.languages.RubyClientCodegen +io.swagger.codegen.languages.RustClientCodegen +io.swagger.codegen.languages.RustServerCodegen io.swagger.codegen.languages.ScalaClientCodegen +io.swagger.codegen.languages.ScalaLagomServerCodegen io.swagger.codegen.languages.ScalatraServerCodegen +io.swagger.codegen.languages.ScalazClientCodegen io.swagger.codegen.languages.SilexServerCodegen io.swagger.codegen.languages.SinatraServerCodegen io.swagger.codegen.languages.SlimFrameworkServerCodegen @@ -68,10 +75,11 @@ io.swagger.codegen.languages.Swift4Codegen io.swagger.codegen.languages.Swift3Codegen io.swagger.codegen.languages.SwiftCodegen io.swagger.codegen.languages.TizenClientCodegen -io.swagger.codegen.languages.TypeScriptAngular2ClientCodegen +io.swagger.codegen.languages.TypeScriptAureliaClientCodegen io.swagger.codegen.languages.TypeScriptAngularClientCodegen +io.swagger.codegen.languages.TypeScriptAngularJsClientCodegen io.swagger.codegen.languages.TypeScriptFetchClientCodegen io.swagger.codegen.languages.TypeScriptJqueryClientCodegen io.swagger.codegen.languages.TypeScriptNodeClientCodegen io.swagger.codegen.languages.UndertowCodegen -io.swagger.codegen.languages.ZendExpressivePathHandlerServerCodegen +io.swagger.codegen.languages.ZendExpressivePathHandlerServerCodegen \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/MSF4J/api.mustache b/modules/swagger-codegen/src/main/resources/MSF4J/api.mustache index 4e89112dde1..ab7c3030940 100644 --- a/modules/swagger-codegen/src/main/resources/MSF4J/api.mustache +++ b/modules/swagger-codegen/src/main/resources/MSF4J/api.mustache @@ -37,7 +37,7 @@ public class {{classname}} { {{#subresourceOperation}}@Path("{{{path}}}"){{/subresourceOperation}} {{#hasConsumes}}@Consumes({ {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} }){{/hasConsumes}} {{#hasProduces}}@Produces({ {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }){{/hasProduces}} - @io.swagger.annotations.ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}", response = {{{returnType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = { + @io.swagger.annotations.ApiOperation(value = "{{{summary}}}", notes = "{{{notes}}}", response = {{{returnBaseType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = { {{#authMethods}}@io.swagger.annotations.Authorization(value = "{{name}}"{{#isOAuth}}, scopes = { {{#scopes}}@io.swagger.annotations.AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{#hasMore}}, {{/hasMore}}{{/scopes}} @@ -45,7 +45,7 @@ public class {{classname}} { {{/hasMore}}{{/authMethods}} }{{/hasAuthMethods}}, tags={ {{#vendorExtensions.x-tags}}"{{tag}}",{{/vendorExtensions.x-tags}} }) @io.swagger.annotations.ApiResponses(value = { {{#responses}} - @io.swagger.annotations.ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{returnType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}){{#hasMore}}, + @io.swagger.annotations.ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{returnBaseType}}}.class{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}){{#hasMore}}, {{/hasMore}}{{/responses}} }) public Response {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},{{/hasMore}}{{/allParams}}) throws NotFoundException { diff --git a/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/README.md b/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/README.md deleted file mode 100644 index 664e1755933..00000000000 --- a/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# TypeScript-Fetch - -This generator creates TypeScript/JavaScript client that utilizes [Fetch API](https://fetch.spec.whatwg.org/). The generated Node module can be used in the following environments: - -Environment -* Node.js -* Webpack -* Browserify - -Language level -* ES5 - you must have a Promises/A+ library installed -* ES6 - -Module system -* CommonJS -* ES6 module system - -It can be used in both TypeScript and JavaScript. In TypeScript, the definition should be automatically resolved via `package.json`. ([Reference](http://www.typescriptlang.org/docs/handbook/typings-for-npm-packages.html)) - -### Installation ### - -`swagger-codegen` does not generate JavaScript directly. The generated Node module comes with `package.json` that bundles `typescript` and `typings` so it can self-compile during `prepublish` stage. The should be run automatically during `npm install` or `npm publish`. - -CAVEAT: Due to [privilege implications](https://docs.npmjs.com/misc/scripts#user), `npm` would skip all scripts if the user is `root`. You would need to manually run it with `npm run prepublish` or run `npm install --unsafe-perm`. - -#### NPM #### -You may publish the module to NPM. In this case, you would be able to install the module as any other NPM module. It maybe useful to use [scoped packages](https://docs.npmjs.com/misc/scope). - -You can also use `npm link` to link the module. However, this would not modify `package.json` of the installing project, as such you would need to relink every time you deploy that project. - -You can also directly install the module using `npm install file_path`. If you do `npm install file_path --save`, NPM will save relative path to `package.json`. In this case, `npm install` and `npm shrinkwrap` may misbehave. You would need to manually edit `package.json` and replace it with absolute path. - -Regardless of which method you deployed your NPM module, the ES6 module syntaxes are as follows: -``` -import * as localName from 'npmName'; -import {operationId} from 'npmName'; -``` -The CommonJS syntax is as follows: -``` -import localName = require('npmName'); -``` - -#### Direct copy/symlink #### -You may also simply copy or symlink the generated module into a directory under your project. The syntax of this is as follows: - -With ES6 module syntax, the following syntaxes are supported: -``` -import * as localName from './symlinkDir'; -import {operationId} from './symlinkDir'; -``` -The CommonJS syntax is as follows: -``` -import localName = require('./symlinkDir')'; -``` diff --git a/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/api.mustache b/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/api.mustache deleted file mode 100644 index 23be9a91356..00000000000 --- a/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/api.mustache +++ /dev/null @@ -1,214 +0,0 @@ -{{>licenseInfo}} -import * as querystring from "querystring"; -import * as url from "url"; - -import * as isomorphicFetch from "isomorphic-fetch"; -{{^supportsES6}} -import * as assign from "core-js/library/fn/object/assign"; -{{/supportsES6}} - -interface Dictionary { [index: string]: T; } -export interface FetchAPI { (url: string, init?: any): Promise; } - -const BASE_PATH = "{{{basePath}}}".replace(/\/+$/, ""); - -export interface FetchArgs { - url: string; - options: any; -} - -export class BaseAPI { - basePath: string; - fetch: FetchAPI; - - constructor(fetch: FetchAPI = isomorphicFetch, basePath: string = BASE_PATH) { - this.basePath = basePath; - this.fetch = fetch; - } -}; - -{{#models}} -{{#model}} -{{#description}} -/** - * {{{description}}} - */ -{{/description}} -{{^isEnum}} -export interface {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{ -{{/isEnum}} -{{#isEnum}} -export type {{{classname}}} = {{#allowableValues}}{{#values}}"{{{.}}}"{{^-last}} | {{/-last}}{{/values}}{{/allowableValues}}; -{{/isEnum}} -{{#vars}} -{{#description}} - /** - * {{{description}}} - */ -{{/description}} - "{{name}}"{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{datatype}}}{{/isEnum}}; -{{/vars}} -{{^isEnum}} -} -{{/isEnum}} - -{{#hasEnums}} -{{#vars}} -{{#isEnum}} -export type {{{enumName}}} = {{#allowableValues}}{{#values}}"{{{.}}}"{{^-last}} | {{/-last}}{{/values}}{{/allowableValues}}; -{{/isEnum}} -{{/vars}} -{{/hasEnums}} -{{/model}} -{{/models}} - -{{#apiInfo}} -{{#apis}} -{{#operations}} - -/** - * {{classname}} - fetch parameter creator{{#description}} - * {{&description}}{{/description}} - */ -export const {{classname}}FetchParamCreator = { -{{#operation}} - /** - * {{¬es}} - {{#summary}} - * @summary {{&summary}} - {{/summary}} - {{#allParams}} - * @param {{paramName}} {{description}} - {{/allParams}} - */ - {{nickname}}({{#hasParams}}params: { {{#allParams}} "{{paramName}}"{{^required}}?{{/required}}: {{{dataType}}};{{/allParams}} }, {{/hasParams}}options?: any): FetchArgs { -{{#allParams}} -{{#required}} - // verify required parameter "{{paramName}}" is set - if (params["{{paramName}}"] == null) { - throw new Error("Missing required parameter {{paramName}} when calling {{nickname}}"); - } -{{/required}} -{{/allParams}} - const baseUrl = `{{{path}}}`{{#pathParams}} - .replace(`{${"{{baseName}}"}}`, `${ params["{{paramName}}"] }`){{/pathParams}}; - let urlObj = url.parse(baseUrl, true); -{{#hasQueryParams}} - urlObj.query = {{#supportsES6}}Object.{{/supportsES6}}assign({}, urlObj.query, { - {{#queryParams}} - "{{baseName}}": params["{{paramName}}"], - {{/queryParams}} - }); -{{/hasQueryParams}} - let fetchOptions: RequestInit = {{#supportsES6}}Object.{{/supportsES6}}assign({}, { method: "{{httpMethod}}" }, options); - - let contentTypeHeader: Dictionary = {}; -{{#hasFormParams}} - contentTypeHeader = { "Content-Type": "application/x-www-form-urlencoded" }; - fetchOptions.body = querystring.stringify({ - {{#formParams}} - "{{baseName}}": params["{{paramName}}"], - {{/formParams}} - }); -{{/hasFormParams}} -{{#hasBodyParam}} - contentTypeHeader = { "Content-Type": "application/json" };{{#bodyParam}} - if (params["{{paramName}}"]) { - fetchOptions.body = JSON.stringify(params["{{paramName}}"] || {}); - }{{/bodyParam}} -{{/hasBodyParam}} -{{#hasHeaderParams}} - fetchOptions.headers = {{#supportsES6}}Object.{{/supportsES6}}assign({ - {{#headerParams}}"{{baseName}}": params["{{paramName}}"],{{/headerParams}} - }, contentTypeHeader, fetchOptions.headers); -{{/hasHeaderParams}} -{{^hasHeaderParams}} - if (contentTypeHeader) { - fetchOptions.headers = {{#supportsES6}}Object.{{/supportsES6}}assign({}, contentTypeHeader, fetchOptions.headers); - } -{{/hasHeaderParams}} - return { - url: url.format(urlObj), - options: fetchOptions, - }; - }, -{{/operation}} -}; - -/** - * {{classname}} - functional programming interface{{#description}} - * {{&description}}{{/description}} - */ -export const {{classname}}Fp = { -{{#operation}} - /** - * {{¬es}} - {{#summary}} - * @summary {{&summary}} - {{/summary}} - {{#allParams}} - * @param {{paramName}} {{description}} - {{/allParams}} - */ - {{nickname}}({{#hasParams}}params: { {{#allParams}}"{{paramName}}"{{^required}}?{{/required}}: {{{dataType}}}; {{/allParams}} }, {{/hasParams}}options?: any): (fetch?: FetchAPI, basePath?: string) => Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}any{{/returnType}}> { - const fetchArgs = {{classname}}FetchParamCreator.{{nickname}}({{#hasParams}}params, {{/hasParams}}options); - return (fetch: FetchAPI = isomorphicFetch, basePath: string = BASE_PATH) => { - return fetch(basePath + fetchArgs.url, fetchArgs.options).then((response) => { - if (response.status >= 200 && response.status < 300) { - return response{{#returnType}}.json(){{/returnType}}; - } else { - throw response; - } - }); - }; - }, -{{/operation}} -}; - -/** - * {{classname}} - object-oriented interface{{#description}} - * {{&description}}{{/description}} - */ -export class {{classname}} extends BaseAPI { -{{#operation}} - /** - * {{¬es}} - {{#summary}} - * @summary {{&summary}} - {{/summary}} - {{#allParams}} - * @param {{paramName}} {{description}} - {{/allParams}} - */ - {{nickname}}({{#hasParams}}params: { {{#allParams}} "{{paramName}}"{{^required}}?{{/required}}: {{{dataType}}};{{/allParams}} }, {{/hasParams}}options?: any) { - return {{classname}}Fp.{{nickname}}({{#hasParams}}params, {{/hasParams}}options)(this.fetch, this.basePath); - } -{{/operation}} -}; - -/** - * {{classname}} - factory interface{{#description}} - * {{&description}}{{/description}} - */ -export const {{classname}}Factory = function (fetch?: FetchAPI, basePath?: string) { - return { -{{#operation}} - /** - * {{¬es}} - {{#summary}} - * @summary {{&summary}} - {{/summary}} - {{#allParams}} - * @param {{paramName}} {{description}} - {{/allParams}} - */ - {{nickname}}({{#hasParams}}params: { {{#allParams}} "{{paramName}}"{{^required}}?{{/required}}: {{{dataType}}};{{/allParams}} }, {{/hasParams}}options?: any) { - return {{classname}}Fp.{{nickname}}({{#hasParams}}params, {{/hasParams}}options)(fetch, basePath); - }, -{{/operation}} - }; -}; - -{{/operations}} -{{/apis}} -{{/apiInfo}} diff --git a/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/git_push.sh.mustache deleted file mode 100755 index e153ce23ecf..00000000000 --- a/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/git_push.sh.mustache +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/sh -# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ -# -# Usage example: /bin/sh ./git_push.sh wing328 swagger-petstore-perl "minor update" - -git_user_id=$1 -git_repo_id=$2 -release_note=$3 - -if [ "$git_user_id" = "" ]; then - git_user_id="{{{gitUserId}}}" - echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" -fi - -if [ "$git_repo_id" = "" ]; then - git_repo_id="{{{gitRepoId}}}" - echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" -fi - -if [ "$release_note" = "" ]; then - release_note="{{{releaseNote}}}" - echo "[INFO] No command line input provided. Set \$release_note to $release_note" -fi - -# Initialize the local directory as a Git repository -git init - -# Adds the files in the local repository and stages them for commit. -git add . - -# Commits the tracked changes and prepares them to be pushed to a remote repository. -git commit -m "$release_note" - -# Sets the new remote -git_remote=`git remote` -if [ "$git_remote" = "" ]; then # git remote not defined - - if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." - git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git - else - git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git - fi - -fi - -git pull origin master - -# Pushes (Forces) the changes in the local repository up to the remote repository -echo "Git pushing to https://github.com/${git_user_id}/${git_repo_id}.git" -git push origin master 2>&1 | grep -v 'To https' - diff --git a/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/typings.json.mustache b/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/typings.json.mustache deleted file mode 100644 index e5bd78f06fe..00000000000 --- a/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/typings.json.mustache +++ /dev/null @@ -1,9 +0,0 @@ -{ - "version": false, - "dependencies": {}, - "globalDependencies": { {{^supportsES6}} - "core-js": "registry:dt/core-js#0.0.0+20160317120654",{{/supportsES6}} - "node": "registry:dt/node#4.0.0+20160423143914", - "isomorphic-fetch": "registry:dt/isomorphic-fetch#0.0.0+20160505171433" - } -} diff --git a/modules/swagger-codegen/src/main/resources/akka-scala/README.mustache b/modules/swagger-codegen/src/main/resources/akka-scala/README.mustache new file mode 100644 index 00000000000..85c72cd1d16 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/akka-scala/README.mustache @@ -0,0 +1,129 @@ +# NAME + +{{appName}} + +{{#appDescription}}{{{appDescription}}}{{/appDescription}} + +# VERSION + +Automatically generated by the [Swagger Codegen](https://github.com/swagger-api/swagger-codegen) project: + +- API version: {{appVersion}} +- Package version: {{moduleVersion}} +{{^hideGenerationTimestamp}} +- Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} +- Build package: {{generatorClass}} +{{#infoUrl}} +For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + +# Requirements + +Building the API client library requires [Maven](https://maven.apache.org/) to be installed. + +## Installation + +To install the API client library to your local Maven repository, simply execute: + +```shell +mvn install +``` + +To deploy it to a remote Maven repository instead, configure the settings of the repository and execute: + +```shell +mvn deploy +``` + +Refer to the [official documentation](https://maven.apache.org/plugins/maven-deploy-plugin/usage.html) for more information. + +### Maven users + +Add this dependency to your project's POM: + +```xml + + {{{groupId}}} + {{{artifactId}}} + {{{artifactVersion}}} + compile + +``` + +### Gradle users + +Add this dependency to your project's build file: + +```groovy +compile "{{{groupId}}}:{{{artifactId}}}:{{{artifactVersion}}}" +``` + +### SBT users + +```scala +libraryDependencies += "{{{groupId}}}" % "{{{artifactId}}}" % "{{{artifactVersion}}}" +``` + +## Documentation for API Endpoints + +All URIs are relative to *{{basePath}}* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + +## Documentation for Models + +{{#models}}{{#model}} - [{{classname}}]({{modelDocPath}}{{classname}}.md) +{{/model}}{{/models}} + +## Documentation for Authorization + +{{^authMethods}}All endpoints do not require authorization. +{{/authMethods}}Authentication schemes defined for the API: +{{#authMethods}}### {{name}} + +{{#isApiKey}}- **Type**: API key +- **API key parameter name**: {{keyParamName}} +- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}} +{{/isApiKey}} +{{#isBasic}}- **Type**: HTTP basic authentication +{{/isBasic}} +{{#isOAuth}}- **Type**: OAuth +- **Flow**: {{flow}} +- **Authorization URL**: {{authorizationUrl}} +- **Scopes**: {{^scopes}}N/A{{/scopes}} +{{#scopes}} - {{scope}}: {{description}} +{{/scopes}} +{{/isOAuth}} + +{{/authMethods}} + + +# BUILDING YOUR LIBRARY + +See the homepage `https://github.com/swagger-api/swagger-codegen` for full details. +But briefly, clone the git repository, build the codegen codebase, set up your build +config file, then run the API build script. You will need git, Java 7 or 8 and Apache +maven 3.0.3 or better already installed. + +Your library files will be built under `WWW::MyProjectName`. + + $ git clone https://github.com/swagger-api/swagger-codegen.git + $ cd swagger-codegen + $ mvn package + $ java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jar generate \ + -i [URL or file path to JSON swagger API spec] \ + -l akka-scala \ + -c /path/to/config/file.json \ + -o /path/to/output/folder + +Bang, all done. Run the `autodoc` script in the `bin` directory to see the API +you just built. + +## Author + +{{#apiInfo}}{{#apis}}{{^hasMore}}{{infoEmail}} +{{/hasMore}}{{/apis}}{{/apiInfo}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/akka-scala/build.sbt.mustache b/modules/swagger-codegen/src/main/resources/akka-scala/build.sbt.mustache new file mode 100644 index 00000000000..6da2572974e --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/akka-scala/build.sbt.mustache @@ -0,0 +1,32 @@ +version := "{{artifactVersion}}" + +name := "{{artifactId}}" + +organization := "{{groupId}}" + +scalaVersion := "2.11.8" + +libraryDependencies ++= Seq( + "io.swagger" % "swagger-core" % "1.5.15", + "com.typesafe" % "config" % "1.2.1", + "com.typesafe.akka" % "akka-actor_2.10" % "2.3.9", + "io.spray" % "spray-client" % "1.3.1", + "joda-time" % "joda-time" % "2.2", + "org.joda" % "joda-convert" % "1.2", + "org.json4s" % "json4s-jackson_2.10" % "3.2.11", + "org.scalatest" %% "scalatest" % "2.2.4" % "test", + "junit" % "junit" % "4.8.1" % "test" +) + +resolvers ++= Seq( + Resolver.mavenLocal +) + +scalacOptions := Seq( + "-unchecked", + "-deprecation", + "-feature" +) + +publishArtifact in (Compile, packageDoc) := false + diff --git a/modules/swagger-codegen/src/main/resources/akka-scala/pom.mustache b/modules/swagger-codegen/src/main/resources/akka-scala/pom.mustache index 3a5c4d0e036..73066dfe256 100644 --- a/modules/swagger-codegen/src/main/resources/akka-scala/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/akka-scala/pom.mustache @@ -6,9 +6,6 @@ jar {{artifactId}} {{artifactVersion}} - - 2.2.0 - @@ -21,6 +18,26 @@ + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + org.apache.maven.plugins maven-surefire-plugin @@ -72,6 +89,7 @@ org.codehaus.mojo build-helper-maven-plugin + 3.0.0 add_sources @@ -106,7 +124,8 @@ maven-compiler-plugin 3.6.1 - 1.7 + + 1.7 1.7 @@ -209,6 +228,7 @@ + UTF-8 2.10.4 3.2.11 3.2.11 @@ -221,6 +241,6 @@ 4.8.1 3.1.5 - 2.1.3 + 2.2.0 diff --git a/modules/swagger-codegen/src/main/resources/android/build.mustache b/modules/swagger-codegen/src/main/resources/android/build.mustache index 8a5855dc0c4..2900eaf0bcd 100644 --- a/modules/swagger-codegen/src/main/resources/android/build.mustache +++ b/modules/swagger-codegen/src/main/resources/android/build.mustache @@ -8,7 +8,12 @@ buildscript { jcenter() } dependencies { + {{#androidGradleVersion}} + classpath 'com.android.tools.build:gradle:{{{androidGradleVersion}}}' + {{/androidGradleVersion}} + {{^androidGradleVersion}} classpath 'com.android.tools.build:gradle:2.3.+' + {{/androidGradleVersion}} {{#useAndroidMavenGradlePlugin}} classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' {{/useAndroidMavenGradlePlugin}} @@ -28,12 +33,27 @@ apply plugin: 'com.github.dcendents.android-maven' {{/useAndroidMavenGradlePlugin}} android { + {{#androidSdkVersion}} + compileSdkVersion {{{androidSdkVersion}}} + {{/androidSdkVersion}} + {{^androidSdkVersion}} compileSdkVersion 25 + {{/androidSdkVersion}} + {{#androidBuildToolsVersion}} + buildToolsVersion '{{{androidBuildToolsVersion}}}' + {{/androidBuildToolsVersion}} + {{^androidBuildToolsVersion}} buildToolsVersion '25.0.2' + {{/androidBuildToolsVersion}} useLibrary 'org.apache.http.legacy' defaultConfig { minSdkVersion 14 + {{#androidSdkVersion}} + targetSdkVersion {{{androidSdkVersion}}} + {{/androidSdkVersion}} + {{^androidSdkVersion}} targetSdkVersion 25 + {{/androidSdkVersion}} } compileOptions { {{#java8}} diff --git a/modules/swagger-codegen/src/main/resources/android/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/android/git_push.sh.mustache index a9b0f28edfb..f65b794638f 100755 --- a/modules/swagger-codegen/src/main/resources/android/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/android/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/android/libraries/volley/apiInvoker.mustache b/modules/swagger-codegen/src/main/resources/android/libraries/volley/apiInvoker.mustache index 09e2b177fab..bf3a586dfcc 100644 --- a/modules/swagger-codegen/src/main/resources/android/libraries/volley/apiInvoker.mustache +++ b/modules/swagger-codegen/src/main/resources/android/libraries/volley/apiInvoker.mustache @@ -363,24 +363,32 @@ public class ApiInvoker { } public String invokeAPI(String host, String path, String method, List queryParams, Object body, Map headerParams, Map formParams, String contentType, String[] authNames) throws ApiException, InterruptedException, ExecutionException, TimeoutException { - RequestFuture future = RequestFuture.newFuture(); - Request request = createRequest(host, path, method, queryParams, body, headerParams, formParams, contentType, authNames, future, future); - if(request != null) { - mRequestQueue.add(request); - return future.get(connectionTimeout, TimeUnit.SECONDS); - } else { - return "no data"; + try { + RequestFuture future = RequestFuture.newFuture(); + Request request = createRequest(host, path, method, queryParams, body, headerParams, formParams, contentType, authNames, future, future); + if(request != null) { + mRequestQueue.add(request); + return future.get(connectionTimeout, TimeUnit.SECONDS); + } else { + return "no data"; + } + } catch (UnsupportedEncodingException ex) { + throw new ApiException(0, "UnsupportedEncodingException"); } } public void invokeAPI(String host, String path, String method, List queryParams, Object body, Map headerParams, Map formParams, String contentType, String[] authNames, Response.Listener stringRequest, Response.ErrorListener errorListener) throws ApiException { - Request request = createRequest(host, path, method, queryParams, body, headerParams, formParams, contentType, authNames, stringRequest, errorListener); - if (request != null) { - mRequestQueue.add(request); + try { + Request request = createRequest(host, path, method, queryParams, body, headerParams, formParams, contentType, authNames, stringRequest, errorListener); + if (request != null) { + mRequestQueue.add(request); + } + } catch (UnsupportedEncodingException ex) { + throw new ApiException(0, "UnsupportedEncodingException"); } } - public Request createRequest(String host, String path, String method, List queryParams, Object body, Map headerParams, Map formParams, String contentType, String[] authNames, Response.Listener stringRequest, Response.ErrorListener errorListener) throws ApiException { + public Request createRequest(String host, String path, String method, List queryParams, Object body, Map headerParams, Map formParams, String contentType, String[] authNames, Response.Listener stringRequest, Response.ErrorListener errorListener) throws ApiException, UnsupportedEncodingException { StringBuilder b = new StringBuilder(); b.append("?"); diff --git a/modules/swagger-codegen/src/main/resources/android/libraries/volley/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/android/libraries/volley/git_push.sh.mustache index b3c88d4be09..3e868678e81 100644 --- a/modules/swagger-codegen/src/main/resources/android/libraries/volley/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/android/libraries/volley/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/android/pom.mustache b/modules/swagger-codegen/src/main/resources/android/pom.mustache index a7bb0ad4b30..8af5d29fb1b 100644 --- a/modules/swagger-codegen/src/main/resources/android/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/android/pom.mustache @@ -1,155 +1,175 @@ - 4.0.0 - {{groupId}} - {{artifactId}} - jar - {{artifactId}} - {{artifactVersion}} - - scm:git:git@github.com:swagger-api/swagger-mustache.git - scm:git:git@github.com:swagger-api/swagger-codegen.git - https://github.com/swagger-api/swagger-codegen - - - 2.2.0 - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} + + scm:git:git@github.com:swagger-api/swagger-mustache.git + scm:git:git@github.com:swagger-api/swagger-codegen.git + https://github.com/swagger-api/swagger-codegen + - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.12 - - - - loggerPath - conf/log4j.properties - - - -Xms512m -Xmx1500m - methods - pertest - - - - maven-dependency-plugin - - - package - - copy-dependencies - - - ${project.build.directory}/lib - - - - + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + pertest + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + - - - org.apache.maven.plugins - maven-jar-plugin - 2.2 - - - - jar - test-jar - - - - - - + + + org.apache.maven.plugins + maven-jar-plugin + 2.2 + + + + jar + test-jar + + + + + + - - org.codehaus.mojo - build-helper-maven-plugin - - - add_sources - generate-sources - - add-source - - - - src/main/java - - - - - add_test_sources - generate-test-sources - - add-test-source - - - - src/test/java - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.6.1 - - {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} - {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} - - - - - - - io.swagger - swagger-annotations - ${swagger-core-version} - - - com.google.code.gson - gson - ${gson-version} - - - org.apache.httpcomponents - httpclient - ${httpclient-version} - compile - - - org.apache.httpcomponents - httpmime - ${httpclient-version} - compile - + + org.codehaus.mojo + build-helper-maven-plugin + + + add_sources + generate-sources + + add-source + + + + + src/main/java + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + + src/test/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} + {{#java8}}1.8{{/java8}}{{^java8}}1.7{{/java8}} + + + + + + + io.swagger + swagger-annotations + ${swagger-core-version} + + + com.google.code.gson + gson + ${gson-version} + + + org.apache.httpcomponents + httpclient + ${httpclient-version} + compile + + + org.apache.httpcomponents + httpmime + ${httpclient-version} + compile + - - - junit - junit - ${junit-version} - test - - - - - sonatype-snapshots - https://oss.sonatype.org/content/repositories/snapshots - - - - 1.5.15 - 2.3.1 - 4.8.1 - 1.0.0 - 4.8.1 - 4.3.6 - + + + junit + junit + ${junit-version} + test + + + + + sonatype-snapshots + https://oss.sonatype.org/content/repositories/snapshots + + + + UTF-8 + 1.5.15 + 2.3.1 + 4.8.1 + 1.0.0 + 4.8.1 + 4.3.6 + diff --git a/modules/swagger-codegen/src/main/resources/apex/api.mustache b/modules/swagger-codegen/src/main/resources/apex/api.mustache index 700c66eb15c..ebbef7283f6 100644 --- a/modules/swagger-codegen/src/main/resources/apex/api.mustache +++ b/modules/swagger-codegen/src/main/resources/apex/api.mustache @@ -88,13 +88,13 @@ public class {{classname}} { {{/headerParams}} }{{/hasHeaderParams}}{{^hasHeaderParams}}(){{/hasHeaderParams}}, {{#hasProduces}} - new List{ {{#produces}}'{{mediaType}}'{{#hasMore}}, {{/hasMore}}{{/produces}} }, + new List{ {{#produces}}'{{{mediaType}}}'{{#hasMore}}, {{/hasMore}}{{/produces}} }, {{/hasProduces}} {{^hasProduces}} new List(), {{/hasProduces}} {{#hasConsumes}} - new List{ {{#consumes}}'{{mediaType}}'{{#hasMore}}, {{/hasMore}}{{/consumes}} }, + new List{ {{#consumes}}'{{{mediaType}}}'{{#hasMore}}, {{/hasMore}}{{/consumes}} }, {{/hasConsumes}} {{^hasConsumes}} new List(), diff --git a/modules/swagger-codegen/src/main/resources/apex/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/apex/git_push.sh.mustache index e153ce23ecf..a2d75234837 100644 --- a/modules/swagger-codegen/src/main/resources/apex/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/apex/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/Dockerfile.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/Dockerfile.mustache index 6e42c14542a..553d5ce5ce9 100644 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/Dockerfile.mustache +++ b/modules/swagger-codegen/src/main/resources/aspnetcore/Dockerfile.mustache @@ -2,9 +2,8 @@ FROM microsoft/dotnet:1.0.3-sdk-projectjson ENV DOTNET_CLI_TELEMETRY_OPTOUT 1 -RUN mkdir -p /app/{{packageName}} -COPY . /app/{{packageName}} WORKDIR /app/{{packageName}} +COPY . /app/{{packageName}} EXPOSE 5000/tcp diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/Program.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/Program.mustache index ab465c42516..52fdd13d052 100644 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/Program.mustache +++ b/modules/swagger-codegen/src/main/resources/aspnetcore/Program.mustache @@ -5,28 +5,32 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore; namespace {{packageName}} { + ///

+ /// Program + /// public class Program { + /// + /// Main + /// + /// public static void Main(string[] args) { - var host = new WebHostBuilder() - .UseKestrel(options => - { - // options.ThreadCount = 4; - // options.UseHttps("cert.pfx", "certpassword"); - options.NoDelay = true; - options.UseConnectionLogging(); - }) - .UseUrls("http://+:5000" /*, "https://+:5001" */) - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() + BuildWebHost(args).Run(); + } + + /// + /// Build Web Host + /// + /// + /// Webhost + public static IWebHost BuildWebHost(string[] args) => + WebHost.CreateDefaultBuilder(args) .UseStartup() .Build(); - - host.Run(); - } } } diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/Project.csproj.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/Project.csproj.mustache new file mode 100644 index 00000000000..d958ce2e4f0 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/aspnetcore/Project.csproj.mustache @@ -0,0 +1,25 @@ + + + {{packageName}} + {{packageName}} + netcoreapp2.0 + true + true + {{packageName}} + {{packageName}} + + + + + + + + + + + + + + + + diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/Project.xproj.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/Project.xproj.mustache deleted file mode 100644 index 16f3716730d..00000000000 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/Project.xproj.mustache +++ /dev/null @@ -1,19 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - {{packageGuid}} - {{packageName}} - .\obj - .\bin\ - v4.6 - - - 2.0 - - - \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/Properties/launchSettings.json b/modules/swagger-codegen/src/main/resources/aspnetcore/Properties/launchSettings.json index 45a5f3319a5..21acfed207b 100644 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/Properties/launchSettings.json +++ b/modules/swagger-codegen/src/main/resources/aspnetcore/Properties/launchSettings.json @@ -11,7 +11,7 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, - "launchUrl": "swagger/ui/index.html", + "launchUrl": "swagger/", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -19,7 +19,7 @@ "web": { "commandName": "Project", "launchBrowser": true, - "launchUrl": "http://localhost:5000/swagger/ui/index.html", + "launchUrl": "http://localhost:5000/swagger/", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/README.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/README.mustache index 6788ea980eb..03f23c1916a 100644 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/README.mustache +++ b/modules/swagger-codegen/src/main/resources/aspnetcore/README.mustache @@ -1,4 +1,4 @@ -# {{packageName}} - ASP.NET Core 1.0 Server +# {{packageName}} - ASP.NET Core 2.0 Server {{#appDescription}} {{{appDescription}}} diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/Solution.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/Solution.mustache index c86909a4c8b..80e5f652101 100644 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/Solution.mustache +++ b/modules/swagger-codegen/src/main/resources/aspnetcore/Solution.mustache @@ -1,13 +1,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.26114.2 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AFF6BF88-8A7D-4736-AF81-31BCE86B19BD}" - ProjectSection(SolutionItems) = preProject - global.json = global.json - EndProjectSection -EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "{{packageName}}", "src\{{packageName}}\{{packageName}}.xproj", "{{packageGuid}}" +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "{{packageName}}", "src\{{packageName}}\{{packageName}}.csproj", "{{packageGuid}}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -23,4 +18,4 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection -EndGlobal \ No newline at end of file +EndGlobal diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/Startup.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/Startup.mustache index 80709bd068f..47009226f2d 100644 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/Startup.mustache +++ b/modules/swagger-codegen/src/main/resources/aspnetcore/Startup.mustache @@ -1,81 +1,97 @@ {{>partial_header}} using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Threading.Tasks; -using System.Xml.XPath; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; -using Swashbuckle.Swagger.Model; -using Swashbuckle.SwaggerGen.Annotations; +using Swashbuckle.AspNetCore.Swagger; +using Swashbuckle.AspNetCore.SwaggerGen; namespace {{packageName}} { + /// + /// Startup + /// public class Startup { private readonly IHostingEnvironment _hostingEnv; - public IConfigurationRoot Configuration { get; } + private IConfiguration Configuration { get; } - public Startup(IHostingEnvironment env) + /// + /// Constructor + /// + /// + /// + public Startup(IHostingEnvironment env, IConfiguration configuration) { _hostingEnv = env; - - var builder = new ConfigurationBuilder() - .SetBasePath(env.ContentRootPath) - .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) - .AddEnvironmentVariables(); - Configuration = builder.Build(); + Configuration = configuration; } - - - // This method gets called by the runtime. Use this method to add services to the container. + + /// + /// This method gets called by the runtime. Use this method to add services to the container. + /// + /// public void ConfigureServices(IServiceCollection services) { // Add framework services. - services.AddMvc() - .AddJsonOptions( - opts => { opts.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); }); - - services.AddSwaggerGen(); - - services.ConfigureSwaggerGen(options => - { - options.SingleApiVersion(new Info + services + .AddMvc() + .AddJsonOptions(opts => { - Version = "v1", - Title = "{{packageName}}", - Description = "{{packageName}} (ASP.NET Core 1.0)" + opts.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + opts.SerializerSettings.Converters.Add(new StringEnumConverter { + CamelCaseText = true + }); }); - options.DescribeAllEnumsAsStrings(); - - var comments = new XPathDocument($"{AppContext.BaseDirectory}{Path.DirectorySeparatorChar}{_hostingEnv.ApplicationName}.xml"); - options.OperationFilter(comments); - options.ModelFilter(comments); - }); - + services + .AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new Info + { + Version = "v1", + Title = "{{packageName}}", + Description = "{{packageName}} (ASP.NET Core 2.0)" + }); + c.CustomSchemaIds(type => type.FriendlyId(true)); + c.DescribeAllEnumsAsStrings(); + c.IncludeXmlComments($"{AppContext.BaseDirectory}{Path.DirectorySeparatorChar}{_hostingEnv.ApplicationName}.xml"); + }); } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + /// + /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + /// + /// + /// + /// public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { - loggerFactory.AddConsole(Configuration.GetSection("Logging")); - loggerFactory.AddDebug(); - - app.UseMvc(); - - app.UseDefaultFiles(); - app.UseStaticFiles(); + app + .UseMvc() + .UseDefaultFiles() + .UseStaticFiles() + .UseSwagger() + .UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1/swagger.json", "{{packageName}}"); + }); - app.UseSwagger(); - app.UseSwaggerUi(); + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + //TODO: Enable production exception handling (https://docs.microsoft.com/en-us/aspnet/core/fundamentals/error-handling) + // app.UseExceptionHandler("/Home/Error"); + } } } } diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/build.bat.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/build.bat.mustache index d3d6188037a..7b6756bc287 100644 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/build.bat.mustache +++ b/modules/swagger-codegen/src/main/resources/aspnetcore/build.bat.mustache @@ -5,5 +5,5 @@ dotnet restore src\{{packageName}} dotnet build src\{{packageName}} -echo Now, run the following to start the project: dotnet run -p src\{{packageName}}\project.json web. +echo Now, run the following to start the project: dotnet run -p src\{{packageName}}\{{packageName}}.csproj --launch-profile web. echo. diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/build.sh.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/build.sh.mustache index 779b4874d27..561c2cb0812 100644 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/build.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/aspnetcore/build.sh.mustache @@ -5,4 +5,4 @@ dotnet restore src/{{packageName}}/ && \ dotnet build src/{{packageName}}/ && \ - echo "Now, run the following to start the project: dotnet run -p src/{{packageName}}/project.json web" + echo "Now, run the following to start the project: dotnet run -p src/{{packageName}}/{{packageName}}.csproj --launch-profile web" diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/controller.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/controller.mustache index e7cbb54cfda..e89ca80cc0d 100644 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/controller.mustache +++ b/modules/swagger-codegen/src/main/resources/aspnetcore/controller.mustache @@ -1,15 +1,17 @@ {{>partial_header}} using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.IO; using System.Linq; using System.Net; using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Primitives; +using Swashbuckle.AspNetCore.SwaggerGen; using Newtonsoft.Json; -using Swashbuckle.SwaggerGen.Annotations; +using {{packageName}}.Attributes; using {{packageName}}.Models; namespace {{packageName}}.Controllers @@ -20,7 +22,6 @@ namespace {{packageName}}.Controllers [Description("{{description}}")]{{/description}} public class {{classname}}Controller : Controller { {{#operation}} - /// /// {{#summary}}{{summary}}{{/summary}} /// @@ -29,17 +30,28 @@ namespace {{packageName}}.Controllers /// {{message}}{{/responses}} [{{httpMethod}}] [Route("{{{basePathWithoutHost}}}{{{path}}}")] - [SwaggerOperation("{{operationId}}")]{{#returnType}} - [SwaggerResponse(200, type: typeof({{&returnType}}))]{{/returnType}} - public virtual {{#returnType}}IActionResult{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}}({{#allParams}}{{>pathParam}}{{>queryParam}}{{>bodyParam}}{{>formParam}}{{>headerParam}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) - { {{#returnType}} + [ValidateModelState] + [SwaggerOperation("{{operationId}}")]{{#responses}}{{#returnType}} + [SwaggerResponse({{code}}, typeof({{&returnType}}), "{{message}}")]{{/returnType}}{{/responses}} + public virtual IActionResult {{operationId}}({{#allParams}}{{>pathParam}}{{>queryParam}}{{>bodyParam}}{{>formParam}}{{>headerParam}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) + { {{#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}})); +{{/dataType}} +{{^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}} +{{#returnType}} string exampleJson = null; {{#isListCollection}}{{>listReturn}}{{/isListCollection}}{{^isListCollection}}{{#isMapContainer}}{{>mapReturn}}{{/isMapContainer}}{{^isMapContainer}}{{>objectReturn}}{{/isMapContainer}}{{/isListCollection}} {{!TODO: defaultResponse, examples, auth, consumes, produces, nickname, externalDocs, imports, security}} + //TODO: Change the data returned return new ObjectResult(example);{{/returnType}}{{^returnType}} throw new NotImplementedException();{{/returnType}} } -{{/operation}} + {{/operation}} } {{/operations}} } diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/enumClass.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/enumClass.mustache index 1a5850e28a9..ce4e3054ea3 100644 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/enumClass.mustache +++ b/modules/swagger-codegen/src/main/resources/aspnetcore/enumClass.mustache @@ -3,12 +3,11 @@ /// {{#description}} /// {{{description}}}{{/description}} public enum {{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} - { - {{#allowableValues}}{{#enumVars}} + { {{#allowableValues}}{{#enumVars}} /// /// Enum {{name}} for {{{value}}} /// [EnumMember(Value = {{#isLong}}"{{/isLong}}{{#isInteger}}"{{/isInteger}}{{#isFloat}}"{{/isFloat}}{{#isDouble}}"{{/isDouble}}{{{value}}}{{#isLong}}"{{/isLong}}{{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isFloat}}"{{/isFloat}})] - {{name}}{{#isLong}} = {{{value}}}{{/isLong}}{{#isInteger}} = {{{value}}}{{/isInteger}}{{^-last}}, + {{name}}{{#isLong}} = {{{value}}}{{/isLong}}{{#isInteger}} = {{{value}}}{{/isInteger}}{{^isInteger}} = {{-index}}{{/isInteger}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}} - } \ No newline at end of file + } diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/global.json b/modules/swagger-codegen/src/main/resources/aspnetcore/global.json deleted file mode 100644 index 175b2d57e38..00000000000 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/global.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "projects": [ "src" ], - "sdk": { - "version": "1.0.0-preview2-003121", - "runtime": "coreclr" - } -} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/model.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/model.mustache index d359e60f7c1..00521d0a5f2 100644 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/model.mustache +++ b/modules/swagger-codegen/src/main/resources/aspnetcore/model.mustache @@ -6,83 +6,38 @@ using System.Text; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; using Newtonsoft.Json; {{#models}} {{#model}} namespace {{packageName}}.Models -{ -{{#isEnum}}{{>enumClass}}{{/isEnum}}{{^isEnum}} +{ {{#isEnum}}{{>enumClass}}{{/isEnum}}{{^isEnum}} /// /// {{description}} /// [DataContract] - public partial class {{classname}} : {{#parent}}{{{parent}}}, {{/parent}} IEquatable<{{classname}}> - { - {{#vars}} - {{#isEnum}} - {{>enumClass}} - {{/isEnum}} - {{#items.isEnum}} - {{#items}} - {{>enumClass}} - {{/items}} - {{/items.isEnum}} - {{/vars}} - {{#vars}} - {{#isEnum}} + public partial class {{classname}} : {{#parent}}{{{parent}}}, {{/parent}}IEquatable<{{classname}}> + { {{#vars}}{{#isEnum}}{{>enumClass}}{{/isEnum}}{{#items.isEnum}}{{#items}}{{>enumClass}}{{/items}}{{/items.isEnum}} /// /// {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{description}}{{/description}} /// {{#description}} /// {{description}} {{/description}} + {{#required}} + [Required] + {{/required}} [DataMember(Name="{{baseName}}")] + {{#isEnum}} public {{{datatypeWithEnum}}}{{#isEnum}}{{^isContainer}}?{{/isContainer}}{{/isEnum}} {{name}} { get; set; } {{/isEnum}} - {{/vars}} - - /// - /// Initializes a new instance of the class. - /// -{{#vars}} /// {{#description}}{{description}}{{/description}}{{^description}}{{name}}{{/description}}{{#required}} (required){{/required}}{{#defaultValue}} (default to {{defaultValue}}){{/defaultValue}}. -{{/vars}} - public {{classname}}({{#readWriteVars}}{{{datatypeWithEnum}}}{{#isEnum}}{{^isContainer}}?{{/isContainer}}{{/isEnum}} {{name}} = {{#defaultValue}}{{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}default({{{datatypeWithEnum}}}{{#isEnum}}{{^isContainer}}?{{/isContainer}}{{/isEnum}}){{/defaultValue}}{{^-last}}, {{/-last}}{{/readWriteVars}}) - { - {{#vars}}{{#required}}// to ensure "{{name}}" is required (not null) - if ({{name}} == null) - { - throw new InvalidDataException("{{name}} is a required property for {{classname}} and cannot be null"); - } - else - { - this.{{name}} = {{name}}; - } - {{/required}}{{/vars}}{{#vars}}{{^required}}{{#defaultValue}}// use default value if no "{{name}}" provided - if ({{name}} == null) - { - this.{{name}} = {{{defaultValue}}}; - } - else - { - this.{{name}} = {{name}}; - } - {{/defaultValue}}{{^defaultValue}}this.{{name}} = {{name}}; - {{/defaultValue}}{{/required}}{{/vars}} - } - - {{#vars}} {{^isEnum}} - /// - /// {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{description}}{{/description}} - /// - {{#description}} - /// {{description}} - {{/description}} - [DataMember(Name="{{baseName}}")] public {{{datatype}}} {{name}} { get; {{#isReadOnly}}private {{/isReadOnly}}set; } {{/isEnum}} + {{#hasMore}} + {{/hasMore}} {{/vars}} /// @@ -118,8 +73,7 @@ namespace {{packageName}}.Models { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals(({{classname}})obj); + return obj.GetType() == GetType() && Equals(({{classname}})obj); } /// @@ -129,20 +83,19 @@ namespace {{packageName}}.Models /// Boolean public bool Equals({{classname}} other) { - if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return {{#vars}}{{#isNotContainer}} ( - this.{{name}} == other.{{name}} || - this.{{name}} != null && - this.{{name}}.Equals(other.{{name}}) + {{name}} == other.{{name}} || + {{name}} != null && + {{name}}.Equals(other.{{name}}) ){{#hasMore}} && {{/hasMore}}{{/isNotContainer}}{{^isNotContainer}} ( - this.{{name}} == other.{{name}} || - this.{{name}} != null && - this.{{name}}.SequenceEqual(other.{{name}}) + {{name}} == other.{{name}} || + {{name}} != null && + {{name}}.SequenceEqual(other.{{name}}) ){{#hasMore}} && {{/hasMore}}{{/isNotContainer}}{{/vars}}{{^vars}}false{{/vars}}; } @@ -152,20 +105,20 @@ namespace {{packageName}}.Models /// Hash code public override int GetHashCode() { - // credit: http://stackoverflow.com/a/263416/677735 unchecked // Overflow is fine, just wrap { - int hash = 41; + var hashCode = 41; // Suitable nullity checks etc, of course :) {{#vars}} - if (this.{{name}} != null) - hash = hash * 59 + this.{{name}}.GetHashCode(); + if ({{name}} != null) + hashCode = hashCode * 59 + {{name}}.GetHashCode(); {{/vars}} - return hash; + return hashCode; } } #region Operators + #pragma warning disable 1591 public static bool operator ==({{classname}} left, {{classname}} right) { @@ -177,8 +130,8 @@ namespace {{packageName}}.Models return !Equals(left, right); } + #pragma warning restore 1591 #endregion Operators - } {{/isEnum}} {{/model}} diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/project.json.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/project.json.mustache deleted file mode 100644 index 46a9071fde4..00000000000 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/project.json.mustache +++ /dev/null @@ -1,89 +0,0 @@ -{ - "title": "Swagger UI", - "version": "{{packageVersion}}-*", - "copyright": "{{packageName}}", - "description": "{{packageName}}", - "dependencies": { - "Microsoft.NETCore.App": { - "version": "1.0.0", - "type": "platform" - }, - "Microsoft.AspNetCore.Mvc": "1.0.0", - "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", - "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", - "Microsoft.AspNetCore.Server.Kestrel.Https": "1.0.0", - "Microsoft.AspNetCore.StaticFiles": "1.0.0", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0", - "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0", - "Microsoft.Extensions.Configuration.Json": "1.0.0", - "Microsoft.Extensions.Logging": "1.0.0", - "Microsoft.Extensions.Logging.Console": "1.0.0", - "Microsoft.Extensions.Logging.Debug": "1.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", - "Microsoft.EntityFrameworkCore": "1.0.0", - "Swashbuckle.SwaggerGen": "6.0.0-beta901", - "Swashbuckle.SwaggerUi": "6.0.0-beta901", - "Newtonsoft.Json": "9.0.1" - }, - - "tools": { - "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final", - - "Microsoft.Extensions.SecretManager.Tools": { - "imports": [ - "netstandard1.6", - "portable-net45+win8+dnxcore50", - "portable-net45+win8" - ], - "version": "1.0.0-preview2-final" - } - }, - - "frameworks": { - "netcoreapp1.0": { - "imports": [ - "dotnet5.6", - "dnxcore50", - "netstandard1.6", - "portable-net452+win81" - ] - } - }, - - "buildOptions": { - "emitEntryPoint": true, - "preserveCompilationContext": true, - "xmlDoc": true, - "compile": { - "exclude": [ - "wwwroot", - "node_modules", - "bower_components" - ] - } - }, - - "runtimeOptions": { - "configProperties": { - "System.GC.Server": true - } - }, - - "publishOptions": { - "include": [ - "wwwroot", - "Views", - "Areas/**/Views", - "appsettings.json", - "web.config" - ], - "exclude": [ - "**.user", - "**.vspscc" - ] - }, - - "scripts": { - "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ] - } -} diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/validateModel.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/validateModel.mustache new file mode 100644 index 00000000000..9f850f71d93 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/aspnetcore/validateModel.mustache @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace {{packageName}}.Attributes +{ + /// + /// Model state validation attribute + /// + public class ValidateModelStateAttribute : ActionFilterAttribute + { + /// + /// Called before the action method is invoked + /// + /// + public override void OnActionExecuting(ActionExecutingContext context) + { + if (!context.ModelState.IsValid) + { + context.Result = new BadRequestObjectResult(context.ModelState); + } + } + } +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/wwwroot/index.html b/modules/swagger-codegen/src/main/resources/aspnetcore/wwwroot/index.html index c8c055b34f7..cde1f2f90b9 100644 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/wwwroot/index.html +++ b/modules/swagger-codegen/src/main/resources/aspnetcore/wwwroot/index.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/asyncscala/api.mustache b/modules/swagger-codegen/src/main/resources/asyncscala/api.mustache deleted file mode 100644 index 3b823902094..00000000000 --- a/modules/swagger-codegen/src/main/resources/asyncscala/api.mustache +++ /dev/null @@ -1,67 +0,0 @@ -package {{package}} - -{{#imports}}import {{import}} -{{/imports}} -import com.wordnik.swagger.client._ -import scala.concurrent.Future -import collection.mutable - -{{#operations}} -class {{classname}}(client: TransportClient, config: SwaggerConfig) extends ApiClient(client, config) { - - {{#operation}} - def {{operationId}}({{#allParams}}{{^required}}{{paramName}}: Option[{{dataType}}] = {{#defaultValue}}Some({{defaultValue}}){{/defaultValue}}{{^defaultValue}}None{{/defaultValue}}{{#hasMore}},{{/hasMore}} - {{/required}}{{#required}}{{paramName}}: {{dataType}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{#hasMore}}, - {{/hasMore}}{{/required}}{{/allParams}})(implicit reader: ClientResponseReader[{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}]{{#bodyParams}}, writer: RequestWriter[{{dataType}}]{{/bodyParams}}){{#returnType}}: Future[{{returnType}}]{{/returnType}}{{^returnType}}: Future[Unit]{{/returnType}} = { - // create path and map variables - val path = (addFmt("{{{path}}}"){{#pathParams}} - replaceAll ("\\{" + "{{baseName}}" + "\\}",{{paramName}}.toString){{/pathParams}}) - - // query params - val queryParams = new mutable.HashMap[String, String] - val headerParams = new mutable.HashMap[String, String] - - {{#allParams}} - {{#required}} - {{^isPrimitiveType}} - if ({{paramName}} == null) throw new Exception("Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}") - {{/isPrimitiveType}} - {{#isString}} - if ({{paramName}} == null) throw new Exception("Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}") - - {{/isString}} - {{/required}} - {{/allParams}} - {{#queryParams}} - {{^required}} - {{paramName}} match { - case Some(param) => queryParams += "{{baseName}}" -> param.toString - case _ => queryParams - } - {{/required}} - {{#required}} - queryParams += "{{baseName}}" -> {{paramName}}.toString - {{/required}} - {{/queryParams}} - {{#headerParams}} - {{^required}} - {{paramName}} match { - case Some(param) => headerParams += "{{baseName}}" -> param.toString - case _ => headerParams - } - {{/required}} - {{#required}} - headerParams += "{{baseName}}" -> {{paramName}}.toString - {{/required}} - {{/headerParams}} - - val resFuture = client.submit("{{httpMethod}}", path, queryParams.toMap, headerParams.toMap, {{#bodyParam}}writer.write({{paramName}}){{/bodyParam}}{{^bodyParam}}"{{emptyBodyParam}}"{{/bodyParam}}) - resFuture flatMap { resp => - process(reader.read(resp)) - } - } - - {{/operation}} - -} -{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/asyncscala/client.mustache b/modules/swagger-codegen/src/main/resources/asyncscala/client.mustache deleted file mode 100644 index d48d031860e..00000000000 --- a/modules/swagger-codegen/src/main/resources/asyncscala/client.mustache +++ /dev/null @@ -1,25 +0,0 @@ -package {{invokerPackage}} - -{{#imports}}import {{import}} -{{/imports}} -import {{apiPackage}}._ - -import com.wordnik.swagger.client._ - -import java.io.Closeable - -class {{clientName}}(config: SwaggerConfig) extends Closeable { - val locator = config.locator - val name = config.name - - private[this] val client = transportClient - - protected def transportClient: TransportClient = new RestClient(config) - {{#apiInfo}}{{#apis}} - val {{classVarName}} = new {{classname}}(client, config) - {{/apis}}{{/apiInfo}} - - def close() { - client.close() - } -} diff --git a/modules/swagger-codegen/src/main/resources/asyncscala/model.mustache b/modules/swagger-codegen/src/main/resources/asyncscala/model.mustache deleted file mode 100644 index 68d361c4c59..00000000000 --- a/modules/swagger-codegen/src/main/resources/asyncscala/model.mustache +++ /dev/null @@ -1,14 +0,0 @@ -package {{package}} - -import org.joda.time.DateTime -import java.util.UUID - -{{#models}} - -{{#model}} -case class {{classname}} ( - {{#vars}}{{name}}: {{^required}}Option[{{/required}}{{datatype}}{{^required}}]{{/required}}{{#hasMore}},{{/hasMore}}{{#description}} // {{description}}{{/description}} - {{/vars}} -) -{{/model}} -{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/asyncscala/sbt.mustache b/modules/swagger-codegen/src/main/resources/asyncscala/sbt.mustache deleted file mode 100644 index b96e132d8a7..00000000000 --- a/modules/swagger-codegen/src/main/resources/asyncscala/sbt.mustache +++ /dev/null @@ -1,12 +0,0 @@ -organization := "{{package}}" - -name := "{{projectName}}-client" - -libraryDependencies ++= Seq( - "com.wordnik.swagger" %% "swagger-async-httpclient" % "0.3.5", - "joda-time" % "joda-time" % "2.3", - "org.joda" % "joda-convert" % "1.3.1", - "ch.qos.logback" % "logback-classic" % "1.0.13" % "provided", - "org.scalatest" %% "scalatest" % "2.2.1" % "test", - "junit" % "junit" % "4.11" % "test" -) diff --git a/modules/swagger-codegen/src/main/resources/bash/README.mustache b/modules/swagger-codegen/src/main/resources/bash/README.mustache index bbb85fb225b..db1abc26ffd 100644 --- a/modules/swagger-codegen/src/main/resources/bash/README.mustache +++ b/modules/swagger-codegen/src/main/resources/bash/README.mustache @@ -3,7 +3,7 @@ ## Overview This is a Bash client script for accessing {{appName}} service. -The script uses cURL underneath for making all REST calls. +The script uses cURL underneath for making all REST calls. ## Usage @@ -86,3 +86,41 @@ fi ### Zsh In Zsh, the generated `_{{scriptName}}` Zsh completion file must be copied to one of the folders under `$FPATH` variable. + + +## Documentation for API Endpoints + +All URIs are relative to *{{basePathWithoutHost}}* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + +## Documentation For Models + +{{#models}}{{#model}} - [{{{classname}}}]({{modelDocPath}}{{{classname}}}.md) +{{/model}}{{/models}} + +## Documentation For Authorization + +{{^authMethods}} All endpoints do not require authorization. +{{/authMethods}}{{#authMethods}}{{#last}} Authentication schemes defined for the API:{{/last}}{{/authMethods}} +{{#authMethods}}## {{{name}}} + +{{#isApiKey}}- **Type**: API key +- **API key parameter name**: {{{keyParamName}}} +- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}} +{{/isApiKey}} +{{#isBasic}}- **Type**: HTTP basic authentication +{{/isBasic}} +{{#isOAuth}}- **Type**: OAuth +- **Flow**: {{{flow}}}{{#authorizationUrl}} +- **Authorization URL**: {{{authorizationUrl}}}{{/authorizationUrl}}{{#tokenUrl}} +- **Token URL**: {{{tokenUrl}}}{{/tokenUrl}} +- **Scopes**:{{^scopes}} N/A{{/scopes}} +{{#scopes}} - **{{{scope}}}**: {{{description}}} +{{/scopes}} +{{/isOAuth}} + +{{/authMethods}} diff --git a/modules/swagger-codegen/src/main/resources/bash/api_doc.mustache b/modules/swagger-codegen/src/main/resources/bash/api_doc.mustache new file mode 100644 index 00000000000..8b663e4772f --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/bash/api_doc.mustache @@ -0,0 +1,47 @@ +# {{classname}}{{#description}} +{{description}}{{/description}} + +All URIs are relative to *{{basePathWithoutHost}}* + +Method | HTTP request | Description +------------- | ------------- | ------------- +{{#operations}}{{#operation}}[**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} +{{/operation}}{{/operations}} + +{{#operations}} +{{#operation}} +## **{{{operationId}}}** + +{{{summary}}}{{#notes}} + +{{{notes}}}{{/notes}} + +### Example +```bash +{{scriptName}} {{operationId}}{{#allParams}}{{#isPathParam}} {{baseName}}=value{{/isPathParam}}{{#isQueryParam}} {{#isContainer}} Specify as: {{#vendorExtensions}}{{#x-codegen-collection-multi}} {{baseName}}=value1 {{baseName}}=value2 {{baseName}}=...{{/x-codegen-collection-multi}}{{#x-codegen-collection-csv}} {{baseName}}="value1,value2,..."{{/x-codegen-collection-csv}}{{#x-codegen-collection-pipes}} {{baseName}}="value1|value2|..."{{/x-codegen-collection-pipes}}{{#x-codegen-collection-ssv}} {{baseName}}="value1 value2 ..."{{/x-codegen-collection-ssv}}{{#x-codegen-collection-tsv}} {{baseName}}="value1\\tvalue2\\t..."{{/x-codegen-collection-tsv}}{{/vendorExtensions}}{{/isContainer}}{{^isContainer}} {{baseName}}=value{{/isContainer}}{{/isQueryParam}}{{#isHeaderParam}} {{baseName}}:value{{/isHeaderParam}}{{#isBodyParam}}{{#vendorExtensions}}{{#x-codegen-body-example}} '{{{x-codegen-body-example}}}'{{/x-codegen-body-example}}{{/vendorExtensions}}{{/isBodyParam}}{{/allParams}} +``` + +### Parameters +{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}} +Name | Type | Description | Notes +------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}} +{{#allParams}} **{{paramName}}** | {{#isFile}}**{{dataType}}**{{/isFile}}{{^isFile}}{{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{dataType}}**]({{baseType}}.md){{/isPrimitiveType}}{{/isFile}} | {{{description}}} |{{^required}} [optional]{{/required}}{{#defaultValue}} [default to {{defaultValue}}]{{/defaultValue}} +{{/allParams}} + +### Return type + +{{#returnType}}{{#returnTypeIsPrimitive}}**{{{returnType}}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{{returnType}}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}(empty response body){{/returnType}} + +### Authorization + +{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{{name}}}](../README.md#{{{name}}}){{^-last}}, {{/-last}}{{/authMethods}} + +### HTTP request headers + + - **Content-Type**: {{#consumes}}{{{mediaType}}}{{#hasMore}}, {{/hasMore}}{{/consumes}}{{^consumes}}Not Applicable{{/consumes}} + - **Accept**: {{#produces}}{{{mediaType}}}{{#hasMore}}, {{/hasMore}}{{/produces}}{{^produces}}Not Applicable{{/produces}} + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +{{/operation}} +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/bash/client.mustache b/modules/swagger-codegen/src/main/resources/bash/client.mustache index bd17edbb3c5..168c9817add 100644 --- a/modules/swagger-codegen/src/main/resources/bash/client.mustache +++ b/modules/swagger-codegen/src/main/resources/bash/client.mustache @@ -25,6 +25,9 @@ # {{#externalDocs}}{{url}}{{/externalDocs}} # +# For improved pattern matching in case statemets +shopt -s extglob + ############################################################################### # # Make sure Bash is at least in version 4.3 @@ -48,7 +51,7 @@ fi ## # The filename of this script for help messages -script_name=`basename "$0"` +script_name=$(basename "$0") ## # Map for headers passed after operation as KEY:VALUE @@ -62,6 +65,32 @@ declare -A header_arguments # can be provided for the same parameter if allowed by API specification declare -A operation_parameters +## +# Declare colors with autodection if output is terminal +if [ -t 1 ]; then + RED="$(tput setaf 1)" + GREEN="$(tput setaf 2)" + YELLOW="$(tput setaf 3)" + BLUE="$(tput setaf 4)" + MAGENTA="$(tput setaf 5)" + CYAN="$(tput setaf 6)" + WHITE="$(tput setaf 7)" + BOLD="$(tput bold)" + OFF="$(tput sgr0)" +else + RED="" + GREEN="" + YELLOW="" + BLUE="" + MAGENTA="" + CYAN="" + WHITE="" + BOLD="" + OFF="" +fi + +declare -a result_color_table=( "$WHITE" "$WHITE" "$GREEN" "$YELLOW" "$WHITE" "$MAGENTA" "$WHITE" ) + ## # This array stores the minimum number of required occurences for parameter # 0 - optional @@ -133,9 +162,17 @@ host="{{#x-codegen-host-env}}${{x-codegen-host-env}}{{/x-codegen-host-env}}" # The user credentials for basic authentication basic_auth_credential="{{#x-codegen-basicauth-env}}${{x-codegen-basicauth-env}}{{/x-codegen-basicauth-env}}" +{{#hasAuthMethods}} +{{#authMethods}} +{{#isApiKey}} +{{#isKeyInHeader}} ## # The user API key apikey_auth_credential="{{#x-codegen-apikey-env}}${{x-codegen-apikey-env}}{{/x-codegen-apikey-env}}" +{{/isKeyInHeader}} +{{/isApiKey}} +{{/authMethods}} +{{/hasAuthMethods}} ## # If true, the script will only output the actual cURL command that would be @@ -208,9 +245,10 @@ url_escape() { -e 's/(/%28/g' \ -e 's/)/%29/g' \ -e 's/:/%3A/g' \ - -e 's/?/%3F/g' <<<$raw_url); + -e 's/\t/%09/g' \ + -e 's/?/%3F/g' <<<"$raw_url"); - echo $value + echo "$value" } ############################################################################## @@ -220,12 +258,12 @@ url_escape() { # ############################################################################## lookup_mime_type() { - local mime_type=$1 + local mime_type="$1" if [[ ${mime_type_abbreviations[$mime_type]} ]]; then - echo ${mime_type_abbreviations[$mime_type]} + echo "${mime_type_abbreviations[$mime_type]}" else - echo $1 + echo "$mime_type" fi } @@ -237,12 +275,12 @@ lookup_mime_type() { ############################################################################## header_arguments_to_curl() { local headers_curl="" - local api_key_header="" - local api_key_header_in_cli="" {{#hasAuthMethods}} {{#authMethods}} {{#isApiKey}} {{#isKeyInHeader}} + local api_key_header="" + local api_key_header_in_cli="" api_key_header="{{keyParamName}}" {{/isKeyInHeader}} {{/isApiKey}} @@ -251,9 +289,17 @@ header_arguments_to_curl() { for key in "${!header_arguments[@]}"; do headers_curl+="-H \"${key}: ${header_arguments[${key}]}\" " +{{#hasAuthMethods}} +{{#authMethods}} +{{#isApiKey}} +{{#isKeyInHeader}} if [[ "${key}XX" == "${api_key_header}XX" ]]; then api_key_header_in_cli="YES" fi +{{/isKeyInHeader}} +{{/isApiKey}} +{{/authMethods}} +{{/hasAuthMethods}} done {{#hasAuthMethods}} {{#authMethods}} @@ -285,14 +331,12 @@ header_arguments_to_curl() { ############################################################################## body_parameters_to_json() { local body_json="-d '{" - local body_parameter_count=${#body_parameters[@]} local count=0 for key in "${!body_parameters[@]}"; do - body_json+="\"${key}\": ${body_parameters[${key}]}" - if [[ $count -lt $body_parameter_count-1 ]]; then + if [[ $((count++)) -gt 0 ]]; then body_json+=", " fi - ((count+=1)) + body_json+="\"${key}\": ${body_parameters[${key}]}" done body_json+="}'" @@ -305,41 +349,47 @@ body_parameters_to_json() { ############################################################################## # -# Check if provided parameters match specification requirements +# Helper method for showing error because for example echo in +# build_request_path() is evaluated as part of command line not printed on +# output. Anyway better idea for resource clean up ;-). +# +############################################################################## +ERROR_MSG="" +function finish { + if [[ -n "$ERROR_MSG" ]]; then + echo >&2 "${OFF}${RED}$ERROR_MSG" + echo >&2 "${OFF}Check usage: '${script_name} --help'" + fi +} +trap finish EXIT + + +############################################################################## +# +# Validate and build request path including query parameters # ############################################################################## -validate_request_parameters() { +build_request_path() { local path_template=$1 local -n path_params=$2 local -n query_params=$3 - # First replace all path parameters in the path - for pparam in "${path_params[@]}"; do - regexp="(.*)(\{$pparam\})(.*)" - if [[ $path_template =~ $regexp ]]; then - path_template=${BASH_REMATCH[1]}${operation_parameters[$pparam]}${BASH_REMATCH[3]} - fi - done - - # Now append query parameters - if any - if [[ ${#query_params[@]} -gt 0 ]]; then - path_template+="?" - fi - local query_parameter_count=${#query_params[@]} - local count=0 - for qparam in "${query_params[@]}"; do - # Get the array of parameter values - local parameter_values=($(echo "${operation_parameters[$qparam]}" | sed -e 's/'":::"'/\n/g' | while read line; do echo $line | sed 's/[\t ]/'":::"'/g'; done)) + # + # Check input paramaters count against minimum and maximum required + # + if [[ "$force" = false ]]; then + local was_error="" + for qparam in "${query_params[@]}" "${path_params[@]}"; do + local parameter_values + mapfile -t parameter_values < <(sed -e 's/'":::"'/\n/g' <<<"${operation_parameters[$qparam]}") - # - # Check if the number of provided values is not less than minimum - # required - # - if [[ "$force" = false ]]; then + # + # Check if the number of provided values is not less than minimum required + # if [[ ${#parameter_values[@]} -lt ${operation_parameters_minimum_occurences["${operation}:::${qparam}"]} ]]; then - echo "Error: Too few values provided for '${qparam}' parameter" - exit 1 + echo "ERROR: Too few values provided for '${qparam}' parameter." + was_error=true fi # @@ -347,220 +397,129 @@ validate_request_parameters() { # if [[ ${operation_parameters_maximum_occurences["${operation}:::${qparam}"]} -gt 0 \ && ${#parameter_values[@]} -gt ${operation_parameters_maximum_occurences["${operation}:::${qparam}"]} ]]; then - if [[ "$force" = false ]]; then - echo "Error: Too many values provided for '${qparam}' parameter" - exit 1 - fi + echo "ERROR: Too many values provided for '${qparam}' parameter" + was_error=true fi - fi - - if [[ "${operation_parameters_collection_type[${operation}:::${qparam}]}" == "" ]]; then - local vcount=0 - for qvalue in "${parameter_values[@]}"; do - path_template+="${qparam}=${qvalue}" - - if [[ $vcount -lt ${#parameter_values[@]}-1 ]]; then - path_template+="&" - fi - ((vcount+=1)) - done - elif [[ "${operation_parameters_collection_type["${operation}:::${qparam}"]}" == "multi" ]]; then - local vcount=0 - for qvalue in "${parameter_values[@]}"; do - path_template+="${qparam}=${qvalue}" - - if [[ $vcount -lt ${#parameter_values[@]}-1 ]]; then - path_template+="&" - fi - ((vcount+=1)) - done - elif [[ "${operation_parameters_collection_type["${operation}:::${qparam}"]}" == "csv" ]]; then - path_template+="${qparam}=" - local vcount=0 - for qvalue in "${parameter_values[@]}"; do - path_template+="${qvalue}" - - if [[ $vcount -lt ${#parameter_values[@]}-1 ]]; then - path_template+="," - fi - ((vcount+=1)) - done - elif [[ "${operation_parameters_collection_type["${operation}:::${qparam}"]}" == "ssv" ]]; then - path_template+="${qparam}=" - for qvalue in "${parameter_values[@]}"; do - path_template+="${qvalue}" - - if [[ $vcount -lt ${#parameter_values[@]}-1 ]]; then - path_template+=" " - fi - ((vcount+=1)) - done - elif [[ "${operation_parameters_collection_type["${operation}:::${qparam}"]}" == "tsv" ]]; then - path_template+="${qparam}=" - for qvalue in "${parameter_values[@]}"; do - path_template+="${qvalue}" - - if [[ $vcount -lt ${#parameter_values[@]}-1 ]]; then - path_template+="\t" - fi - ((vcount+=1)) - done - else - echo -e "" - echo -e "Error: Unsupported collection format " - echo -e "" + done + if [[ -n "$was_error" ]]; then exit 1 fi - - - if [[ $count -lt $query_parameter_count-1 ]]; then - path_template+="&" - fi - ((count+=1)) - done - -} - - - -############################################################################## -# -# Build request path including query parameters -# -############################################################################## -build_request_path() { - local path_template=$1 - local -n path_params=$2 - local -n query_params=$3 - + fi # First replace all path parameters in the path for pparam in "${path_params[@]}"; do - regexp="(.*)(\{$pparam\})(.*)" - if [[ $path_template =~ $regexp ]]; then + local path_regex="(.*)(\\{$pparam\\})(.*)" + if [[ $path_template =~ $path_regex ]]; then path_template=${BASH_REMATCH[1]}${operation_parameters[$pparam]}${BASH_REMATCH[3]} fi done local query_request_part="" - local query_parameter_count=${#query_params[@]} local count=0 for qparam in "${query_params[@]}"; do # Get the array of parameter values - local parameter_values=($(echo "${operation_parameters[$qparam]}" | sed -e 's/'":::"'/\n/g' | while read line; do echo $line | sed 's/[\t ]/'":::"'/g'; done)) local parameter_value="" + local parameter_values + mapfile -t parameter_values < <(sed -e 's/'":::"'/\n/g' <<<"${operation_parameters[$qparam]}") - # - # Check if the number of provided values is not less than minimum - # required - # - if [[ "$force" = false ]]; then - if [[ ${#parameter_values[@]} -lt ${operation_parameters_minimum_occurences["${operation}:::${qparam}"]} ]]; then - echo "Error: Too few values provided for '${qparam}' parameter" - exit 1 - fi - - # - # Check if the number of provided values is not more than maximum - # - if [[ ${operation_parameters_maximum_occurences["${operation}:::${qparam}"]} -gt 0 \ - && ${#parameter_values[@]} -gt ${operation_parameters_maximum_occurences["${operation}:::${qparam}"]} ]]; then - if [[ "$force" = false ]]; then - echo "Error: Too many values provided for '${qparam}' parameter" - exit 1 - fi + 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 + parameter_value+="${qparam}=${parameter_values}" +{{#x-codegen-apikey-env}} + elif [[ -n "$MATRIX_API_KEY" ]]; then + parameter_value+="${qparam}=${{x-codegen-apikey-env}}" +{{/x-codegen-apikey-env}} + else + echo "Missing ApiKey!!! {{#x-codegen-apikey-env}}Define env variable {{x-codegen-apikey-env}} like 'export {{x-codegen-apikey-env}}=...' or{{/x-codegen-apikey-env}}{{^x-codegen-apikey-env}}You have to{{/x-codegen-apikey-env}} provide on command line option '{{keyParamName}}=...'" + exit 1 + fi + continue + fi{{/isKeyInQuery}}{{/isApiKey}}{{/authMethods}}{{/hasAuthMethods}} # # Append parameters without specific cardinality # - if [[ "${operation_parameters_collection_type["${operation}:::${qparam}"]}" == "" ]]; then + local collection_type="${operation_parameters_collection_type["${operation}:::${qparam}"]}" + if [[ "${collection_type}" == "" ]]; then local vcount=0 for qvalue in "${parameter_values[@]}"; do - parameter_value+="${qparam}=${qvalue}" - - if [[ $vcount -lt ${#parameter_values[@]}-1 ]]; then + if [[ $((vcount++)) -gt 0 ]]; then parameter_value+="&" fi - ((vcount+=1)) + parameter_value+="${qparam}=${qvalue}" done # # Append parameters specified as 'mutli' collections i.e. param=value1¶m=value2&... # - elif [[ "${operation_parameters_collection_type["${operation}:::${qparam}"]}" == "multi" ]]; then + elif [[ "${collection_type}" == "multi" ]]; then local vcount=0 for qvalue in "${parameter_values[@]}"; do - parameter_value+="${qparam}=${qvalue}" - - if [[ $vcount -lt ${#parameter_values[@]}-1 ]]; then + if [[ $((vcount++)) -gt 0 ]]; then parameter_value+="&" fi - ((vcount+=1)) + parameter_value+="${qparam}=${qvalue}" done # # Append parameters specified as 'csv' collections i.e. param=value1,value2,... # - elif [[ "${operation_parameters_collection_type["${operation}:::${qparam}"]}" == "csv" ]]; then + elif [[ "${collection_type}" == "csv" ]]; then parameter_value+="${qparam}=" local vcount=0 for qvalue in "${parameter_values[@]}"; do - parameter_value+="${qvalue}" - - if [[ $vcount -lt ${#parameter_values[@]}-1 ]]; then + if [[ $((vcount++)) -gt 0 ]]; then parameter_value+="," fi - ((vcount+=1)) + parameter_value+="${qvalue}" done # # Append parameters specified as 'ssv' collections i.e. param="value1 value2 ..." # - elif [[ "${operation_parameters_collection_type["${operation}:::${qparam}"]}" == "ssv" ]]; then + elif [[ "${collection_type}" == "ssv" ]]; then parameter_value+="${qparam}=" local vcount=0 for qvalue in "${parameter_values[@]}"; do - parameter_value+="${qvalue}" - - if [[ $vcount -lt ${#parameter_values[@]}-1 ]]; then + if [[ $((vcount++)) -gt 0 ]]; then parameter_value+=" " fi - ((vcount+=1)) + parameter_value+="${qvalue}" done # # Append parameters specified as 'tsv' collections i.e. param="value1\tvalue2\t..." # - elif [[ "${operation_parameters_collection_type["${operation}:::${qparam}"]}" == "tsv" ]]; then + elif [[ "${collection_type}" == "tsv" ]]; then parameter_value+="${qparam}=" local vcount=0 for qvalue in "${parameter_values[@]}"; do - parameter_value+="${qvalue}" - - if [[ $vcount -lt ${#parameter_values[@]}-1 ]]; then - parameter_value+="\t" + if [[ $((vcount++)) -gt 0 ]]; then + parameter_value+="\\t" fi - ((vcount+=1)) + parameter_value+="${qvalue}" done + else + echo "Unsupported collection format \"${collection_type}\"" + exit 1 fi if [[ -n "${parameter_value}" ]]; then query_request_part+="${parameter_value}" fi - if [[ $count -lt $query_parameter_count-1 && -n "${parameter_value}" ]]; then - query_request_part+="&" - fi - - ((count+=1)) done # Now append query parameters - if any if [[ -n "${query_request_part}" ]]; then - path_template+="?$(echo ${query_request_part} | sed s'/&$//')" + path_template+="?${query_request_part}" fi - echo $path_template + echo "$path_template" } @@ -573,54 +532,56 @@ build_request_path() { print_help() { cat <$(tput sgr0)] - [-ac|--accept $(tput setaf 2)$(tput sgr0)] [-ct,--content-type $(tput setaf 2)$(tput sgr0)] - [--host $(tput setaf 6)$(tput sgr0)] [--dry-run] $(tput setaf 3)$(tput sgr0) [-h|--help] [$(tput setaf 4)$(tput sgr0)] - [$(tput setaf 5)$(tput sgr0)] [$(tput setaf 5)$(tput sgr0)] + ${GREEN}${script_name}${OFF} [-h|--help] [-V|--version] [--about] [${RED}${OFF}] + [-ac|--accept ${GREEN}${OFF}] [-ct,--content-type ${GREEN}${OFF}] + [--host ${CYAN}${OFF}] [--dry-run] [-nc|--no-colors] ${YELLOW}${OFF} [-h|--help] + [${BLUE}${OFF}] [${MAGENTA}${OFF}] [${MAGENTA}${OFF}] - - $(tput setaf 6)$(tput sgr0) - endpoint of the REST service without basepath + - ${CYAN}${OFF} - endpoint of the REST service without basepath {{#x-codegen-host-env}} Can also be specified in {{x-codegen-host-env}} environment variable.{{/x-codegen-host-env}} - - $(tput setaf 1)$(tput sgr0) - any valid cURL options can be passed before $(tput setaf 3)$(tput sgr0) - - $(tput setaf 2)$(tput sgr0) - either full mime-type or one of supported abbreviations: + - ${RED}${OFF} - any valid cURL options can be passed before ${YELLOW}${OFF} + - ${GREEN}${OFF} - either full mime-type or one of supported abbreviations: (text, html, md, csv, css, rtf, json, xml, yaml, js, bin, rdf, jpg, png, gif, bmp, tiff) - - $(tput setaf 4)$(tput sgr0) - HTTP headers can be passed in the form $(tput setaf 3)HEADER$(tput sgr0):$(tput setaf 4)VALUE$(tput sgr0) - - $(tput setaf 5)$(tput sgr0) - REST operation parameters can be passed in the following + - ${BLUE}${OFF} - HTTP headers can be passed in the form ${YELLOW}HEADER${OFF}:${BLUE}VALUE${OFF} + - ${MAGENTA}${OFF} - REST operation parameters can be passed in the following forms: - * $(tput setaf 3)KEY$(tput sgr0)=$(tput setaf 4)VALUE$(tput sgr0) - path or query parameters - - $(tput setaf 5)$(tput sgr0) - simple JSON body content (first level only) can be build + * ${YELLOW}KEY${OFF}=${BLUE}VALUE${OFF} - path or query parameters + - ${MAGENTA}${OFF} - simple JSON body content (first level only) can be build using the following arguments: - * $(tput setaf 3)KEY$(tput sgr0)==$(tput setaf 4)VALUE$(tput sgr0) - body parameters which will be added to body - JSON as '{ ..., "$(tput setaf 3)KEY$(tput sgr0)": "$(tput setaf 4)VALUE$(tput sgr0)", ... }' - * $(tput setaf 3)KEY$(tput sgr0):=$(tput setaf 4)VALUE$(tput sgr0) - body parameters which will be added to body - JSON as '{ ..., "$(tput setaf 3)KEY$(tput sgr0)": $(tput setaf 4)VALUE$(tput sgr0), ... }' + * ${YELLOW}KEY${OFF}==${BLUE}VALUE${OFF} - body parameters which will be added to body + JSON as '{ ..., "${YELLOW}KEY${OFF}": "${BLUE}VALUE${OFF}", ... }' + * ${YELLOW}KEY${OFF}:=${BLUE}VALUE${OFF} - body parameters which will be added to body + JSON as '{ ..., "${YELLOW}KEY${OFF}": ${BLUE}VALUE${OFF}, ... }' EOF {{#hasAuthMethods}} - echo -e "$(tput bold)$(tput setaf 7)Authentication methods$(tput sgr0)" + echo -e "${BOLD}${WHITE}Authentication methods${OFF}" echo -e "" {{#authMethods}} {{#isBasic}} - echo -e " - $(tput setaf 4)Basic AUTH$(tput sgr0) - add '-u :' before $(tput setaf 3)$(tput sgr0)" - {{#x-codegen-basicauth-env}}echo -e " or export $(tput setaf 1){{x-codegen-basicauth-env}}=':'$(tput sgr0)"{{/x-codegen-basicauth-env}} + echo -e " - ${BLUE}Basic AUTH${OFF} - add '-u :' before ${YELLOW}${OFF}" + {{#x-codegen-basicauth-env}}echo -e " or export ${RED}{{x-codegen-basicauth-env}}=':'${OFF}"{{/x-codegen-basicauth-env}} {{/isBasic}} {{#isApiKey}} {{#isKeyInHeader}} - echo -e " - $(tput setaf 4)Api-key$(tput sgr0) - add '$(tput setaf 1){{keyParamName}}:$(tput sgr0)' after $(tput setaf 3)$(tput sgr0)" + echo -e " - ${BLUE}Api-key${OFF} - add '${RED}{{keyParamName}}:${OFF}' after ${YELLOW}${OFF}" {{/isKeyInHeader}} {{#isKeyInQuery}} - echo -e " - $(tput setaf 4)Api-key$(tput sgr0) - add '$(tput setaf 1){{keyParamName}}=$(tput sgr0)' after $(tput setaf 3)$(tput sgr0)" + echo -e " - ${BLUE}Api-key${OFF} - add '${RED}{{keyParamName}}=${OFF}' after ${YELLOW}${OFF}" {{/isKeyInQuery}} - {{#x-codegen-apikey-env}}echo -e " or export $(tput setaf 1){{x-codegen-apikey-env}}=''$(tput sgr0)"{{/x-codegen-apikey-env}} + {{#x-codegen-apikey-env}}echo -e " or export ${RED}{{x-codegen-apikey-env}}=''${OFF}"{{/x-codegen-apikey-env}} {{/isApiKey}} {{#isOAuth}} - echo -e " - $(tput setaf 5)OAuth2 (flow: {{flow}})$(tput sgr0)" + echo -e " - ${MAGENTA}OAuth2 (flow: {{flow}})${OFF}"{{#authorizationUrl}} echo -e " Authorization URL: " - echo -e " * {{authorizationUrl}}" + echo -e " * {{authorizationUrl}}"{{/authorizationUrl}}{{#tokenUrl}} + echo -e " Token URL: " + echo -e " * {{tokenUrl}}"{{/tokenUrl}} echo -e " Scopes:" {{#scopes}} echo -e " * {{scope}} - {{description}}" @@ -629,15 +590,15 @@ EOF {{/authMethods}} echo "" {{/hasAuthMethods}} - echo -e "$(tput bold)$(tput setaf 7)Operations (grouped by tags)$(tput sgr0)" + echo -e "${BOLD}${WHITE}Operations (grouped by tags)${OFF}" {{#apiInfo}} {{#apis}} echo "" - echo -e "$(tput bold)$(tput setaf 7)[{{classVarName}}]$(tput sgr0)" -read -d '' ops <$(tput sgr0)\t\t\t\tSpecify the host URL " + echo -e "${BOLD}${WHITE}Options${OFF}" + echo -e " -h,--help\\t\\t\\t\\tPrint this help" + echo -e " -V,--version\\t\\t\\t\\tPrint API version" + echo -e " --about\\t\\t\\t\\tPrint the information about service" + echo -e " --host ${CYAN}${OFF}\\t\\t\\t\\tSpecify the host URL " {{#swagger}} -{{#host}}echo -e " \t\t\t\t(e.g. 'https://{{host}}')"{{/host}} -{{^host}}echo -e " \t\t\t\t(e.g. 'https://127.0.0.1:8080')"{{/host}} +{{#host}}echo -e " \\t\\t\\t\\t(e.g. 'https://{{host}}')"{{/host}} +{{^host}}echo -e " \\t\\t\\t\\t(e.g. 'https://127.0.0.1:8080')"{{/host}} {{/swagger}} - echo -e " --force\t\t\t\tForce command invocation in spite of missing" - echo -e " \t\t\t\trequired parameters or wrong content type" - echo -e " --dry-run\t\t\t\tPrint out the cURL command without" - echo -e " \t\t\t\texecuting it" - echo -e " -ac,--accept $(tput setaf 3)$(tput sgr0)\t\tSet the 'Accept' header in the request" - echo -e " -ct,--content-type $(tput setaf 3)$(tput sgr0)\tSet the 'Content-type' header in " - echo -e " \tthe request" + echo -e " --force\\t\\t\\t\\tForce command invocation in spite of missing" + echo -e " \\t\\t\\t\\trequired parameters or wrong content type" + echo -e " --dry-run\\t\\t\\t\\tPrint out the cURL command without" + echo -e " \\t\\t\\t\\texecuting it" + echo -e " -nc,--no-colors\\t\\t\\tEnforce print without colors, otherwise autodected" + echo -e " -ac,--accept ${YELLOW}${OFF}\\t\\tSet the 'Accept' header in the request" + echo -e " -ct,--content-type ${YELLOW}${OFF}\\tSet the 'Content-type' header in " + echo -e " \\tthe request" echo "" } @@ -672,16 +634,16 @@ echo " $ops" | column -t -s ';' ############################################################################## print_about() { echo "" - echo -e "$(tput bold)$(tput setaf 7){{appName}} command line client (API version {{#swagger}}{{#info}}{{version}}{{/info}}{{/swagger}})$(tput sgr0)" + echo -e "${BOLD}${WHITE}{{appName}} command line client (API version {{#swagger}}{{#info}}{{version}}{{/info}}{{/swagger}})${OFF}" echo "" echo -e "License: {{#swagger}}{{#info}}{{#license}}{{name}}{{/license}}{{/info}}{{/swagger}}" echo -e "Contact: {{#swagger}}{{#info}}{{#contact}}{{email}}{{/contact}}{{/info}}{{/swagger}}" echo "" -read -d '' appdescription </dev/null 2>&1 || { echo >&2 "Error: You do not have 'cURL' installed."; exit 1; } -type sed >/dev/null 2>&1 || { echo >&2 "Error: You do not have 'sed' installed."; exit 1; } -type column >/dev/null 2>&1 || { echo >&2 "Error: You do not have 'bsdmainutils' installed."; exit 1; } +type curl >/dev/null 2>&1 || { echo >&2 "ERROR: You do not have 'cURL' installed."; exit 1; } +type sed >/dev/null 2>&1 || { echo >&2 "ERROR: You do not have 'sed' installed."; exit 1; } +type column >/dev/null 2>&1 || { echo >&2 "ERROR: You do not have 'bsdmainutils' installed."; exit 1; } # # Process command line @@ -988,6 +937,18 @@ case $key in --dry-run) print_curl=true ;; + -nc|--no-colors) + RED="" + GREEN="" + YELLOW="" + BLUE="" + MAGENTA="" + CYAN="" + WHITE="" + BOLD="" + OFF="" + result_color_table=( "" "" "" "" "" "" "" ) + ;; {{#apiInfo}} {{#apis}} {{#operations}} @@ -1003,7 +964,7 @@ case $key in # Parse body arguments and convert them into top level # JSON properties passed in the body content as strings if [[ "$operation" ]]; then - IFS='==' read body_key sep body_value <<< "$key" + IFS='==' read -r body_key sep body_value <<< "$key" body_parameters[${body_key}]="\"${body_value}\"" fi ;; @@ -1011,15 +972,17 @@ case $key in # Parse body arguments and convert them into top level # JSON properties passed in the body content without qoutes if [[ "$operation" ]]; then - IFS=':=' read body_key sep body_value <<< "$key" + # ignore error about 'sep' being unused + # shellcheck disable=SC2034 + IFS=':=' read -r body_key sep body_value <<< "$key" body_parameters[${body_key}]=${body_value} fi ;; - *:*) + +([^=]):*) # Parse header arguments and convert them into curl # only after the operation argument if [[ "$operation" ]]; then - IFS=':' read header_name header_value <<< "$key" + IFS=':' read -r header_name header_value <<< "$key" {{#hasAuthMethods}} {{#authMethods}} {{#isApiKey}} @@ -1042,13 +1005,13 @@ case $key in ;; -) body_content_temp_file=$(mktemp) - cat - > $body_content_temp_file + cat - > "$body_content_temp_file" ;; *=*) # Parse operation arguments and convert them into curl # only after the operation argument if [[ "$operation" ]]; then - IFS='=' read parameter_name parameter_value <<< "$key" + IFS='=' read -r parameter_name parameter_value <<< "$key" if [[ -z "${operation_parameters[$parameter_name]+foo}" ]]; then operation_parameters[$parameter_name]=$(url_escape "${parameter_value}") else @@ -1076,15 +1039,13 @@ done # Check if user provided host name if [[ -z "$host" ]]; then - echo "Error: No hostname provided!!!" - echo "Check usage: '${script_name} --help'" + ERROR_MSG="ERROR: No hostname provided!!! {{#x-codegen-host-env}}Define env variable {{x-codegen-host-env}} like 'export {{x-codegen-host-env}}=...' or{{/x-codegen-host-env}}{{^x-codegen-host-env}}You have to {{/x-codegen-host-env}} provide on command line option '--host ...'" exit 1 fi # Check if user specified operation ID if [[ -z "$operation" ]]; then - echo "Error: No operation specified!" - echo "Check available operations: '${script_name} --help'" + ERROR_MSG="ERROR: No operation specified!!!" exit 1 fi @@ -1103,8 +1064,6 @@ case $operation in {{/apis}} {{/apiInfo}} *) - echo "Error: Unknown operation: $operation" - echo "" - print_help + ERROR_MSG="ERROR: Unknown operation: $operation" exit 1 esac diff --git a/modules/swagger-codegen/src/main/resources/bash/model_doc.mustache b/modules/swagger-codegen/src/main/resources/bash/model_doc.mustache new file mode 100644 index 00000000000..ad08afe6397 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/bash/model_doc.mustache @@ -0,0 +1,11 @@ +{{#models}}{{#model}}# {{name}} + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +{{#vars}}**{{name}}** | {{#isPrimitiveType}}**{{datatype}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{datatype}}**]({{complexType}}.md){{/isPrimitiveType}} | {{title}} | {{^required}}[optional] {{/required}}{{#readOnly}}[readonly] {{/readOnly}}{{#defaultValue}}[default to {{{.}}}]{{/defaultValue}} +{{/vars}} + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + +{{/model}}{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/clojure/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/clojure/git_push.sh.mustache index e153ce23ecf..a2d75234837 100755 --- a/modules/swagger-codegen/src/main/resources/clojure/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/clojure/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/codegen/generatorClass.mustache b/modules/swagger-codegen/src/main/resources/codegen/generatorClass.mustache index 788ef78834b..b794f96a121 100644 --- a/modules/swagger-codegen/src/main/resources/codegen/generatorClass.mustache +++ b/modules/swagger-codegen/src/main/resources/codegen/generatorClass.mustache @@ -121,7 +121,7 @@ public class {{generatorClass}} extends DefaultCodegen implements CodegenConfig /** * 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 reseved words + * those terms here. This logic is only called if a variable matches the reserved words * * @return the escaped term */ diff --git a/modules/swagger-codegen/src/main/resources/codegen/pom.mustache b/modules/swagger-codegen/src/main/resources/codegen/pom.mustache index 11e4a76e337..2d01469c986 100644 --- a/modules/swagger-codegen/src/main/resources/codegen/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/codegen/pom.mustache @@ -1,102 +1,123 @@ - 4.0.0 - io.swagger - {{name}}-swagger-codegen - jar - {{name}}-swagger-codegen - 1.0.0 - - 2.2.0 - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.12 - - - - loggerPath - conf/log4j.properties - - - -Xms512m -Xmx1500m - methods - pertest - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + 4.0.0 + io.swagger + {{name}}-swagger-codegen + jar + {{name}}-swagger-codegen + 1.0.0 + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + pertest + + - - - org.apache.maven.plugins - maven-jar-plugin - 2.2 - - - - jar - test-jar - - - - - - + + + org.apache.maven.plugins + maven-jar-plugin + 2.2 + + + + jar + test-jar + + + + + + - - org.codehaus.mojo - build-helper-maven-plugin - - - add_sources - generate-sources - - add-source - - - - src/main/java - - - - - add_test_sources - generate-test-sources - - add-test-source - - - - src/test/java - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.6.1 - - 1.7 - 1.7 - - - - - - - io.swagger - swagger-codegen - ${swagger-codegen-version} - provided - - - - {{swaggerCodegenVersion}} - 1.0.0 - 4.8.1 - + + org.codehaus.mojo + build-helper-maven-plugin + + + add_sources + generate-sources + + add-source + + + + + src/main/java + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + + src/test/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + + 1.7 + 1.7 + + + + + + + io.swagger + swagger-codegen + ${swagger-codegen-version} + provided + + + + UTF-8 + {{swaggerCodegenVersion}} + 1.0.0 + 4.8.1 + diff --git a/modules/swagger-codegen/src/main/resources/cpprest/api-header.mustache b/modules/swagger-codegen/src/main/resources/cpprest/api-header.mustache index 94fc2cc9517..8493906176d 100644 --- a/modules/swagger-codegen/src/main/resources/cpprest/api-header.mustache +++ b/modules/swagger-codegen/src/main/resources/cpprest/api-header.mustache @@ -5,15 +5,17 @@ * {{description}} */ -#ifndef {{classname}}_H_ -#define {{classname}}_H_ +#ifndef {{apiHeaderGuardPrefix}}_{{classname}}_H_ +#define {{apiHeaderGuardPrefix}}_{{classname}}_H_ {{{defaultInclude}}} -#include "ApiClient.h" +#include "../ApiClient.h" {{#imports}}{{{import}}} {{/imports}} +#include + {{#apiNamespaceDeclarations}} namespace {{this}} { {{/apiNamespaceDeclarations}} @@ -32,8 +34,14 @@ public: /// /// {{notes}} /// - {{#allParams}}/// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}{{/allParams}} - pplx::task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> {{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + {{#allParams}} + /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}} + pplx::task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> {{operationId}}( + {{#allParams}} + {{^required}}boost::optional<{{/required}}{{{dataType}}}{{^required}}>{{/required}} {{paramName}}{{#hasMore}},{{/hasMore}} + {{/allParams}} + ); {{/operation}} protected: @@ -44,6 +52,6 @@ protected: } {{/apiNamespaceDeclarations}} -#endif /* {{classname}}_H_ */ +#endif /* {{apiHeaderGuardPrefix}}_{{classname}}_H_ */ {{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/cpprest/api-source.mustache b/modules/swagger-codegen/src/main/resources/cpprest/api-source.mustache index 343a2697817..d55a7ef020f 100644 --- a/modules/swagger-codegen/src/main/resources/cpprest/api-source.mustache +++ b/modules/swagger-codegen/src/main/resources/cpprest/api-source.mustache @@ -26,19 +26,19 @@ using namespace {{modelNamespace}}; } {{#operation}} -pplx::task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> {{classname}}::{{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) +pplx::task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> {{classname}}::{{operationId}}({{#allParams}}{{^required}}boost::optional<{{/required}}{{{dataType}}}{{^required}}>{{/required}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) { {{#allParams}}{{#required}}{{^isPrimitiveType}}{{^isContainer}} // verify the required parameter '{{paramName}}' is set if ({{paramName}} == nullptr) { - throw ApiException(400, U("Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}")); + throw ApiException(400, utility::conversions::to_string_t("Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}")); } {{/isContainer}}{{/isPrimitiveType}}{{/required}}{{/allParams}} std::shared_ptr apiConfiguration( m_ApiClient->getConfiguration() ); - utility::string_t path = U("{{{path}}}"); - {{#pathParams}}boost::replace_all(path, U("{") U("{{baseName}}") U("}"), ApiClient::parameterToString({{{paramName}}})); + utility::string_t path = utility::conversions::to_string_t("{{{path}}}"); + {{#pathParams}}boost::replace_all(path, utility::conversions::to_string_t("{") + utility::conversions::to_string_t("{{baseName}}") + utility::conversions::to_string_t("}"), ApiClient::parameterToString({{{paramName}}})); {{/pathParams}} std::map queryParams; @@ -48,7 +48,7 @@ pplx::task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/r std::unordered_set responseHttpContentTypes; {{#produces}} - responseHttpContentTypes.insert( U("{{mediaType}}") ); + responseHttpContentTypes.insert( utility::conversions::to_string_t("{{{mediaType}}}") ); {{/produces}} utility::string_t responseHttpContentType; @@ -57,27 +57,27 @@ pplx::task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/r if ( responseHttpContentTypes.size() == 0 ) { {{#vendorExtensions.x-codegen-response.isString}} - responseHttpContentType = U("text/plain"); + responseHttpContentType = utility::conversions::to_string_t("text/plain"); {{/vendorExtensions.x-codegen-response.isString}} {{^vendorExtensions.x-codegen-response.isString}} - responseHttpContentType = U("application/json"); + responseHttpContentType = utility::conversions::to_string_t("application/json"); {{/vendorExtensions.x-codegen-response.isString}} } // JSON - else if ( responseHttpContentTypes.find(U("application/json")) != responseHttpContentTypes.end() ) + else if ( responseHttpContentTypes.find(utility::conversions::to_string_t("application/json")) != responseHttpContentTypes.end() ) { - responseHttpContentType = U("application/json"); + responseHttpContentType = utility::conversions::to_string_t("application/json"); } // multipart formdata - else if( responseHttpContentTypes.find(U("multipart/form-data")) != responseHttpContentTypes.end() ) + else if( responseHttpContentTypes.find(utility::conversions::to_string_t("multipart/form-data")) != responseHttpContentTypes.end() ) { - responseHttpContentType = U("multipart/form-data"); + responseHttpContentType = utility::conversions::to_string_t("multipart/form-data"); } {{#vendorExtensions.x-codegen-response.isString}} // plain text - else if( responseHttpContentTypes.find(U("text/plain")) != responseHttpContentTypes.end() ) + else if( responseHttpContentTypes.find(utility::conversions::to_string_t("text/plain")) != responseHttpContentTypes.end() ) { - responseHttpContentType = U("text/plain"); + responseHttpContentType = utility::conversions::to_string_t("text/plain"); } {{/vendorExtensions.x-codegen-response.isString}} {{#vendorExtensions.x-codegen-response-ishttpcontent}} @@ -90,48 +90,67 @@ pplx::task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/r {{^vendorExtensions.x-codegen-response-ishttpcontent}} else { - throw ApiException(400, U("{{classname}}->{{operationId}} does not produce any supported media type")); + throw ApiException(400, utility::conversions::to_string_t("{{classname}}->{{operationId}} does not produce any supported media type")); } {{/vendorExtensions.x-codegen-response-ishttpcontent}} - headerParams[U("Accept")] = responseHttpContentType; + headerParams[utility::conversions::to_string_t("Accept")] = responseHttpContentType; std::unordered_set consumeHttpContentTypes; {{#consumes}} - consumeHttpContentTypes.insert( U("{{mediaType}}") ); + consumeHttpContentTypes.insert( utility::conversions::to_string_t("{{{mediaType}}}") ); {{/consumes}} {{#allParams}} {{^isBodyParam}} {{^isPathParam}} - {{^isPrimitiveType}}{{^isContainer}}if ({{paramName}} != nullptr){{/isContainer}}{{/isPrimitiveType}} + {{#required}} + {{^isPrimitiveType}} + {{^isContainer}} + if ({{paramName}} != nullptr) + {{/isContainer}} + {{/isPrimitiveType}} + {{/required}} + {{^required}} + {{^isPrimitiveType}} + {{^isContainer}} + if ({{paramName}} && *{{paramName}} != nullptr) + {{/isContainer}} + {{/isPrimitiveType}} + {{#isPrimitiveType}} + if ({{paramName}}) + {{/isPrimitiveType}} + {{#isContainer}} + if ({{paramName}}) + {{/isContainer}} + {{/required}} { {{#isContainer}} {{#isQueryParam}} - queryParams[U("{{baseName}}")] = ApiClient::parameterToArrayString<{{items.datatype}}>({{paramName}}); + queryParams[utility::conversions::to_string_t("{{baseName}}")] = ApiClient::parameterToArrayString<{{items.datatype}}>({{^required}}*{{/required}}{{paramName}}); {{/isQueryParam}} {{#isHeaderParam}} - headerParams[U("{{baseName}}")] = ApiClient::parameterToArrayString<{{items.datatype}}>({{paramName}}); + headerParams[utility::conversions::to_string_t("{{baseName}}")] = ApiClient::parameterToArrayString<{{items.datatype}}>({{^required}}*{{/required}}{{paramName}}); {{/isHeaderParam}} {{#isFormParam}} {{^isFile}} - formParams[ U("{{baseName}}") ] = ApiClient::parameterToArrayString<{{items.datatype}}>({{paramName}}); + formParams[ utility::conversions::to_string_t("{{baseName}}") ] = ApiClient::parameterToArrayString<{{items.datatype}}>({{^required}}*{{/required}}{{paramName}}); {{/isFile}} {{/isFormParam}} {{/isContainer}} {{^isContainer}} {{#isQueryParam}} - queryParams[U("{{baseName}}")] = ApiClient::parameterToString({{paramName}}); + queryParams[utility::conversions::to_string_t("{{baseName}}")] = ApiClient::parameterToString({{^required}}*{{/required}}{{paramName}}); {{/isQueryParam}} {{#isHeaderParam}} - headerParams[U("{{baseName}}")] = ApiClient::parameterToString({{paramName}}); + headerParams[utility::conversions::to_string_t("{{baseName}}")] = ApiClient::parameterToString({{^required}}*{{/required}}{{paramName}}); {{/isHeaderParam}} {{#isFormParam}} {{#isFile}} - fileParams[ U("{{baseName}}") ] = {{paramName}}; + fileParams[ utility::conversions::to_string_t("{{baseName}}") ] = {{^required}}*{{/required}}{{paramName}}; {{/isFile}} {{^isFile}} - formParams[ U("{{baseName}}") ] = ApiClient::parameterToString({{paramName}}); + formParams[ utility::conversions::to_string_t("{{baseName}}") ] = ApiClient::parameterToString({{^required}}*{{/required}}{{paramName}}); {{/isFile}} {{/isFormParam}} {{/isContainer}} @@ -144,9 +163,9 @@ pplx::task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/r utility::string_t requestHttpContentType; // use JSON if possible - if ( consumeHttpContentTypes.size() == 0 || consumeHttpContentTypes.find(U("application/json")) != consumeHttpContentTypes.end() ) + if ( consumeHttpContentTypes.size() == 0 || consumeHttpContentTypes.find(utility::conversions::to_string_t("application/json")) != consumeHttpContentTypes.end() ) { - requestHttpContentType = U("application/json"); + requestHttpContentType = utility::conversions::to_string_t("application/json"); {{#bodyParam}} web::json::value json; @@ -177,9 +196,9 @@ pplx::task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/r {{/bodyParam}} } // multipart formdata - else if( consumeHttpContentTypes.find(U("multipart/form-data")) != consumeHttpContentTypes.end() ) + else if( consumeHttpContentTypes.find(utility::conversions::to_string_t("multipart/form-data")) != consumeHttpContentTypes.end() ) { - requestHttpContentType = U("multipart/form-data"); + requestHttpContentType = utility::conversions::to_string_t("multipart/form-data"); {{#bodyParam}} std::shared_ptr multipart(new MultipartFormData); {{#isPrimitiveType}} @@ -197,51 +216,48 @@ pplx::task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/r {{/items.isDateTime}}{{^items.isDateTime}}jsonArray.push_back( item.get() ? item->toJson() : web::json::value::null() ); {{/items.isDateTime}}{{/items.isString}}{{/items.isPrimitiveType}} } - multipart->add(ModelBase::toHttpContent(U("{{paramName}}"), web::json::value::array(jsonArray), U("application/json"))); + multipart->add(ModelBase::toHttpContent(utility::conversions::to_string_t("{{paramName}}"), web::json::value::array(jsonArray), utility::conversions::to_string_t("application/json"))); } {{/isListContainer}} {{^isListContainer}} - {{#isString}}multipart->add(ModelBase::toHttpContent(U("{{paramName}}"), {{paramName}})); + {{#isString}}multipart->add(ModelBase::toHttpContent(utility::conversions::to_string_t("{{paramName}}"), {{paramName}})); {{/isString}} {{^isString}} if({{paramName}}.get()) { - {{paramName}}->toMultipart(multipart, U("{{paramName}}")); + {{paramName}}->toMultipart(multipart, utility::conversions::to_string_t("{{paramName}}")); } {{/isString}} {{/isListContainer}} {{/isPrimitiveType}} httpBody = multipart; - requestHttpContentType += U("; boundary=") + multipart->getBoundary(); + requestHttpContentType += utility::conversions::to_string_t("; boundary=") + multipart->getBoundary(); {{/bodyParam}} } else { - throw ApiException(415, U("{{classname}}->{{operationId}} does not consume any supported media type")); + throw ApiException(415, utility::conversions::to_string_t("{{classname}}->{{operationId}} does not consume any supported media type")); } - //Set the request content type in the header. - headerParams[U("Content-Type")] = requestHttpContentType; - {{#authMethods}} // authentication ({{name}}) required {{#isApiKey}} {{#isKeyInHeader}} { - utility::string_t apiKey = apiConfiguration->getApiKey(U("{{keyParamName}}")); + utility::string_t apiKey = apiConfiguration->getApiKey(utility::conversions::to_string_t("{{keyParamName}}")); if ( apiKey.size() > 0 ) { - headerParams[U("{{keyParamName}}")] = apiKey; + headerParams[utility::conversions::to_string_t("{{keyParamName}}")] = apiKey; } } {{/isKeyInHeader}} {{#isKeyInQuery}} { - utility::string_t apiKey = apiConfiguration->getApiKey(U("{{keyParamName}}")); + utility::string_t apiKey = apiConfiguration->getApiKey(utility::conversions::to_string_t("{{keyParamName}}")); if ( apiKey.size() > 0 ) { - queryParams[U("{{keyParamName}}")] = apiKey; + queryParams[utility::conversions::to_string_t("{{keyParamName}}")] = apiKey; } } {{/isKeyInQuery}} @@ -254,7 +270,7 @@ pplx::task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/r {{/isOAuth}} {{/authMethods}} - return m_ApiClient->callApi(path, U("{{httpMethod}}"), queryParams, httpBody, headerParams, formParams, fileParams, requestHttpContentType) + return m_ApiClient->callApi(path, utility::conversions::to_string_t("{{httpMethod}}"), queryParams, httpBody, headerParams, formParams, fileParams, requestHttpContentType) .then([=](web::http::http_response response) { // 1xx - informational : OK @@ -265,18 +281,18 @@ pplx::task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/r if (response.status_code() >= 400) { throw ApiException(response.status_code() - , U("error calling {{operationId}}: ") + response.reason_phrase() + , utility::conversions::to_string_t("error calling {{operationId}}: ") + response.reason_phrase() , std::make_shared(response.extract_utf8string(true).get())); } // check response content type - if(response.headers().has(U("Content-Type"))) + if(response.headers().has(utility::conversions::to_string_t("Content-Type"))) { - utility::string_t contentType = response.headers()[U("Content-Type")]; + utility::string_t contentType = response.headers()[utility::conversions::to_string_t("Content-Type")]; if( contentType.find(responseHttpContentType) == std::string::npos ) { throw ApiException(500 - , U("error calling {{operationId}}: unexpected response type: ") + contentType + , utility::conversions::to_string_t("error calling {{operationId}}: unexpected response type: ") + contentType , std::make_shared(response.extract_utf8string(true).get())); } } @@ -307,7 +323,7 @@ pplx::task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/r {{{returnType}}} result({{{defaultResponse}}}); {{/returnContainer}} - if(responseHttpContentType == U("application/json")) + if(responseHttpContentType == utility::conversions::to_string_t("application/json")) { web::json::value json = web::json::value::parse(response); @@ -333,18 +349,18 @@ pplx::task<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/r {{/vendorExtensions.x-codegen-response.isPrimitiveType}}{{^vendorExtensions.x-codegen-response.isPrimitiveType}}{{#vendorExtensions.x-codegen-response.isString}}result = ModelBase::stringFromJson(json); {{/vendorExtensions.x-codegen-response.isString}}{{^vendorExtensions.x-codegen-response.isString}}result->fromJson(json);{{/vendorExtensions.x-codegen-response.isString}}{{/vendorExtensions.x-codegen-response.isPrimitiveType}}{{/isMapContainer}}{{/isListContainer}} }{{#vendorExtensions.x-codegen-response.isString}} - else if(responseHttpContentType == U("text/plain")) + else if(responseHttpContentType == utility::conversions::to_string_t("text/plain")) { result = response; }{{/vendorExtensions.x-codegen-response.isString}} - // else if(responseHttpContentType == U("multipart/form-data")) + // else if(responseHttpContentType == utility::conversions::to_string_t("multipart/form-data")) // { // TODO multipart response parsing // } else { throw ApiException(500 - , U("error calling {{operationId}}: unsupported response type")); + , utility::conversions::to_string_t("error calling {{operationId}}: unsupported response type")); } return result; diff --git a/modules/swagger-codegen/src/main/resources/cpprest/apiclient-header.mustache b/modules/swagger-codegen/src/main/resources/cpprest/apiclient-header.mustache index f5194b2185f..72b2c53671b 100644 --- a/modules/swagger-codegen/src/main/resources/cpprest/apiclient-header.mustache +++ b/modules/swagger-codegen/src/main/resources/cpprest/apiclient-header.mustache @@ -5,8 +5,8 @@ * This is an API client responsible for stating the HTTP calls */ -#ifndef ApiClient_H_ -#define ApiClient_H_ +#ifndef {{apiHeaderGuardPrefix}}_ApiClient_H_ +#define {{apiHeaderGuardPrefix}}_ApiClient_H_ {{{defaultInclude}}} #include "ApiConfiguration.h" @@ -48,7 +48,7 @@ public: for( size_t i = 0; i < value.size(); i++) { - if( i > 0) ss << U(", "); + if( i > 0) ss << utility::conversions::to_string_t(", "); ss << ApiClient::parameterToString(value[i]); } @@ -75,4 +75,4 @@ protected: } {{/apiNamespaceDeclarations}} -#endif /* ApiClient_H_ */ +#endif /* {{apiHeaderGuardPrefix}}_ApiClient_H_ */ diff --git a/modules/swagger-codegen/src/main/resources/cpprest/apiclient-source.mustache b/modules/swagger-codegen/src/main/resources/cpprest/apiclient-source.mustache index 59ca1c180f8..fae8c4c70b3 100644 --- a/modules/swagger-codegen/src/main/resources/cpprest/apiclient-source.mustache +++ b/modules/swagger-codegen/src/main/resources/cpprest/apiclient-source.mustache @@ -67,17 +67,17 @@ pplx::task ApiClient::callApi( { if (postBody != nullptr && formParams.size() != 0) { - throw ApiException(400, U("Cannot have body and form params")); + throw ApiException(400, utility::conversions::to_string_t("Cannot have body and form params")); } if (postBody != nullptr && fileParams.size() != 0) { - throw ApiException(400, U("Cannot have body and file params")); + throw ApiException(400, utility::conversions::to_string_t("Cannot have body and file params")); } - if (fileParams.size() > 0 && contentType != U("multipart/form-data")) + if (fileParams.size() > 0 && contentType != utility::conversions::to_string_t("multipart/form-data")) { - throw ApiException(400, U("Operations with file parameters must be called with multipart/form-data")); + throw ApiException(400, utility::conversions::to_string_t("Operations with file parameters must be called with multipart/form-data")); } web::http::client::http_client client(m_Configuration->getBaseUrl(), m_Configuration->getHttpConfig()); @@ -100,10 +100,10 @@ pplx::task ApiClient::callApi( uploadData.add(ModelBase::toHttpContent(kvp.first, kvp.second)); } std::stringstream data; - postBody->writeTo(data); + uploadData.writeTo(data); auto bodyString = data.str(); auto length = bodyString.size(); - request.set_body(concurrency::streams::bytestream::open_istream(std::move(bodyString)), length, contentType); + request.set_body(concurrency::streams::bytestream::open_istream(std::move(bodyString)), length, utility::conversions::to_string_t("multipart/form-data; boundary=") + uploadData.getBoundary()); } else { @@ -117,14 +117,17 @@ pplx::task ApiClient::callApi( } else { - if (contentType == U("application/json")) + if (contentType == utility::conversions::to_string_t("application/json")) { web::json::value body_data = web::json::value::object(); for (auto& kvp : formParams) { body_data[kvp.first] = ModelBase::toJson(kvp.second); } - request.set_body(body_data); + if (!formParams.empty()) + { + request.set_body(body_data); + } } else { @@ -133,7 +136,10 @@ pplx::task ApiClient::callApi( { formData.append_query(kvp.first, kvp.second); } - request.set_body(formData.query(), U("application/x-www-form-urlencoded")); + if (!formParams.empty()) + { + request.set_body(formData.query(), utility::conversions::to_string_t("application/x-www-form-urlencoded")); + } } } } diff --git a/modules/swagger-codegen/src/main/resources/cpprest/apiconfiguration-header.mustache b/modules/swagger-codegen/src/main/resources/cpprest/apiconfiguration-header.mustache index c554fb21bfc..35375760b7a 100644 --- a/modules/swagger-codegen/src/main/resources/cpprest/apiconfiguration-header.mustache +++ b/modules/swagger-codegen/src/main/resources/cpprest/apiconfiguration-header.mustache @@ -5,8 +5,8 @@ * This class represents a single item of a multipart-formdata request. */ -#ifndef ApiConfiguration_H_ -#define ApiConfiguration_H_ +#ifndef {{apiHeaderGuardPrefix}}_ApiConfiguration_H_ +#define {{apiHeaderGuardPrefix}}_ApiConfiguration_H_ {{{defaultInclude}}} @@ -49,4 +49,4 @@ protected: {{#apiNamespaceDeclarations}} } {{/apiNamespaceDeclarations}} -#endif /* ApiConfiguration_H_ */ +#endif /* {{apiHeaderGuardPrefix}}_ApiConfiguration_H_ */ diff --git a/modules/swagger-codegen/src/main/resources/cpprest/apiconfiguration-source.mustache b/modules/swagger-codegen/src/main/resources/cpprest/apiconfiguration-source.mustache index 6e0d4e9147e..d4a6a660471 100644 --- a/modules/swagger-codegen/src/main/resources/cpprest/apiconfiguration-source.mustache +++ b/modules/swagger-codegen/src/main/resources/cpprest/apiconfiguration-source.mustache @@ -55,7 +55,7 @@ utility::string_t ApiConfiguration::getApiKey( const utility::string_t& prefix) { return result->second; } - return U(""); + return utility::conversions::to_string_t(""); } void ApiConfiguration::setApiKey( const utility::string_t& prefix, const utility::string_t& apiKey ) diff --git a/modules/swagger-codegen/src/main/resources/cpprest/apiexception-header.mustache b/modules/swagger-codegen/src/main/resources/cpprest/apiexception-header.mustache index cc01d446ddf..6a842d64ac9 100644 --- a/modules/swagger-codegen/src/main/resources/cpprest/apiexception-header.mustache +++ b/modules/swagger-codegen/src/main/resources/cpprest/apiexception-header.mustache @@ -5,8 +5,8 @@ * This is the exception being thrown in case the api call was not successful */ -#ifndef ApiException_H_ -#define ApiException_H_ +#ifndef {{apiHeaderGuardPrefix}}_ApiException_H_ +#define {{apiHeaderGuardPrefix}}_ApiException_H_ {{{defaultInclude}}} @@ -46,4 +46,4 @@ protected: } {{/apiNamespaceDeclarations}} -#endif /* ApiBase_H_ */ +#endif /* {{apiHeaderGuardPrefix}}_ApiBase_H_ */ diff --git a/modules/swagger-codegen/src/main/resources/cpprest/cmake-lists.mustache b/modules/swagger-codegen/src/main/resources/cpprest/cmake-lists.mustache index c0ad03eaf83..442b04ec660 100644 --- a/modules/swagger-codegen/src/main/resources/cpprest/cmake-lists.mustache +++ b/modules/swagger-codegen/src/main/resources/cpprest/cmake-lists.mustache @@ -42,7 +42,7 @@ endif( NOT DEFINED CPPREST_ROOT ) include_directories(${PROJECT_SOURCE_DIR} api model ${CPPREST_INCLUDE_DIR}) #SUPPORTING FILES -set(SUPPORTING_FILES "ApiClient" "ApiConfiguration" "ApiException" "HttpContent" "IHttpBody" "JsonBody" "ModelBase" "MultipartFormData") +set(SUPPORTING_FILES "ApiClient" "ApiConfiguration" "ApiException" "HttpContent" "IHttpBody" "JsonBody" "ModelBase" "MultipartFormData" "Object") #SOURCE FILES file(GLOB SOURCE_FILES "api/*" "model/*") diff --git a/modules/swagger-codegen/src/main/resources/cpprest/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/cpprest/git_push.sh.mustache index c7d7c390acf..c7a337655c3 100644 --- a/modules/swagger-codegen/src/main/resources/cpprest/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/cpprest/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/cpprest/httpcontent-header.mustache b/modules/swagger-codegen/src/main/resources/cpprest/httpcontent-header.mustache index 004971cf7f6..59cf42ae8c0 100644 --- a/modules/swagger-codegen/src/main/resources/cpprest/httpcontent-header.mustache +++ b/modules/swagger-codegen/src/main/resources/cpprest/httpcontent-header.mustache @@ -5,8 +5,8 @@ * This class represents a single item of a multipart-formdata request. */ -#ifndef HttpContent_H_ -#define HttpContent_H_ +#ifndef {{modelHeaderGuardPrefix}}_HttpContent_H_ +#define {{modelHeaderGuardPrefix}}_HttpContent_H_ {{{defaultInclude}}} @@ -54,4 +54,4 @@ protected: } {{/modelNamespaceDeclarations}} -#endif /* HttpContent_H_ */ +#endif /* {{modelHeaderGuardPrefix}}_HttpContent_H_ */ diff --git a/modules/swagger-codegen/src/main/resources/cpprest/ihttpbody-header.mustache b/modules/swagger-codegen/src/main/resources/cpprest/ihttpbody-header.mustache index 76e5f211e86..e9c77b263eb 100644 --- a/modules/swagger-codegen/src/main/resources/cpprest/ihttpbody-header.mustache +++ b/modules/swagger-codegen/src/main/resources/cpprest/ihttpbody-header.mustache @@ -5,8 +5,8 @@ * This is the interface for contents that can be sent to a remote HTTP server. */ -#ifndef IHttpBody_H_ -#define IHttpBody_H_ +#ifndef {{modelHeaderGuardPrefix}}_IHttpBody_H_ +#define {{modelHeaderGuardPrefix}}_IHttpBody_H_ {{{defaultInclude}}} #include @@ -27,4 +27,4 @@ public: } {{/modelNamespaceDeclarations}} -#endif /* IHttpBody_H_ */ +#endif /* {{modelHeaderGuardPrefix}}_IHttpBody_H_ */ diff --git a/modules/swagger-codegen/src/main/resources/cpprest/jsonbody-header.mustache b/modules/swagger-codegen/src/main/resources/cpprest/jsonbody-header.mustache index 4face5a0dd6..312ebb0dab4 100644 --- a/modules/swagger-codegen/src/main/resources/cpprest/jsonbody-header.mustache +++ b/modules/swagger-codegen/src/main/resources/cpprest/jsonbody-header.mustache @@ -5,8 +5,8 @@ * This is a JSON http body which can be submitted via http */ -#ifndef JsonBody_H_ -#define JsonBody_H_ +#ifndef {{modelHeaderGuardPrefix}}_JsonBody_H_ +#define {{modelHeaderGuardPrefix}}_JsonBody_H_ {{{defaultInclude}}} #include "IHttpBody.h" @@ -34,4 +34,4 @@ protected: } {{/modelNamespaceDeclarations}} -#endif /* JsonBody_H_ */ +#endif /* {{modelHeaderGuardPrefix}}_JsonBody_H_ */ diff --git a/modules/swagger-codegen/src/main/resources/cpprest/model-header.mustache b/modules/swagger-codegen/src/main/resources/cpprest/model-header.mustache index d15f49a2ad6..61738f52e1d 100644 --- a/modules/swagger-codegen/src/main/resources/cpprest/model-header.mustache +++ b/modules/swagger-codegen/src/main/resources/cpprest/model-header.mustache @@ -5,12 +5,12 @@ * {{description}} */ -#ifndef {{classname}}_H_ -#define {{classname}}_H_ +#ifndef {{modelHeaderGuardPrefix}}_{{classname}}_H_ +#define {{modelHeaderGuardPrefix}}_{{classname}}_H_ {{^parent}} {{{defaultInclude}}} -#include "ModelBase.h" +#include "../ModelBase.h" {{/parent}} {{#imports}}{{{this}}} @@ -72,6 +72,6 @@ protected: } {{/modelNamespaceDeclarations}} -#endif /* {{classname}}_H_ */ +#endif /* {{modelHeaderGuardPrefix}}_{{classname}}_H_ */ {{/model}} {{/models}} diff --git a/modules/swagger-codegen/src/main/resources/cpprest/model-source.mustache b/modules/swagger-codegen/src/main/resources/cpprest/model-source.mustache index c2bbe153570..77c92cbaf0c 100644 --- a/modules/swagger-codegen/src/main/resources/cpprest/model-source.mustache +++ b/modules/swagger-codegen/src/main/resources/cpprest/model-source.mustache @@ -57,11 +57,11 @@ web::json::value {{classname}}::toJson() const {{^required}} if(m_{{name}}IsSet) { - val[U("{{baseName}}")] = ModelBase::toJson(m_{{name}}); + val[utility::conversions::to_string_t("{{baseName}}")] = ModelBase::toJson(m_{{name}}); } {{/required}} {{#required}} - val[U("{{baseName}}")] = ModelBase::toJson(m_{{name}}); + val[utility::conversions::to_string_t("{{baseName}}")] = ModelBase::toJson(m_{{name}}); {{/required}} {{/isMapContainer}} {{/isListContainer}} @@ -74,12 +74,12 @@ web::json::value {{classname}}::toJson() const jsonArray.push_back(ModelBase::toJson(item)); } {{#required}} - val[U("{{baseName}}")] = web::json::value::array(jsonArray); + val[utility::conversions::to_string_t("{{baseName}}")] = web::json::value::array(jsonArray); {{/required}} {{^required}} if(jsonArray.size() > 0) { - val[U("{{baseName}}")] = web::json::value::array(jsonArray); + val[utility::conversions::to_string_t("{{baseName}}")] = web::json::value::array(jsonArray); } {{/required}} } @@ -90,17 +90,17 @@ web::json::value {{classname}}::toJson() const for( auto& item : m_{{name}} ) { web::json::value tmp = web::json::value::object(); - tmp[U("key")] = ModelBase::toJson(item.first); - tmp[U("value")] = ModelBase::toJson(item.second); + tmp[utility::conversions::to_string_t("key")] = ModelBase::toJson(item.first); + tmp[utility::conversions::to_string_t("value")] = ModelBase::toJson(item.second); jsonArray.push_back(tmp); } {{#required}} - val[U("{{baseName}}")] = web::json::value::array(jsonArray); + val[utility::conversions::to_string_t("{{baseName}}")] = web::json::value::array(jsonArray); {{/required}} {{^required}} if(jsonArray.size() > 0) { - val[U("{{baseName}}")] = web::json::value::array(jsonArray); + val[utility::conversions::to_string_t("{{baseName}}")] = web::json::value::array(jsonArray); } {{/required}} } @@ -111,11 +111,11 @@ web::json::value {{classname}}::toJson() const {{^required}} if(m_{{name}}IsSet) { - val[U("{{baseName}}")] = ModelBase::toJson(m_{{name}}); + val[utility::conversions::to_string_t("{{baseName}}")] = ModelBase::toJson(m_{{name}}); } {{/required}} {{#required}} - val[U("{{baseName}}")] = ModelBase::toJson(m_{{name}}); + val[utility::conversions::to_string_t("{{baseName}}")] = ModelBase::toJson(m_{{name}}); {{/required}} {{/isPrimitiveType}} {{/isMapContainer}} @@ -138,13 +138,13 @@ void {{classname}}::fromJson(web::json::value& val) {{^isListContainer}} {{^isMapContainer}} {{^required}} - if(val.has_field(U("{{baseName}}"))) + if(val.has_field(utility::conversions::to_string_t("{{baseName}}"))) { - {{setter}}(ModelBase::{{baseType}}FromJson(val[U("{{baseName}}")])); + {{setter}}(ModelBase::{{baseType}}FromJson(val[utility::conversions::to_string_t("{{baseName}}")])); } {{/required}} {{#required}} - {{setter}}(ModelBase::{{baseType}}FromJson(val[U("{{baseName}}")])); + {{setter}}(ModelBase::{{baseType}}FromJson(val[utility::conversions::to_string_t("{{baseName}}")])); {{/required}} {{/isMapContainer}} {{/isListContainer}} @@ -154,10 +154,10 @@ void {{classname}}::fromJson(web::json::value& val) m_{{name}}.clear(); std::vector jsonArray; {{^required}} - if(val.has_field(U("{{baseName}}"))) + if(val.has_field(utility::conversions::to_string_t("{{baseName}}"))) { {{/required}} - for( auto& item : val[U("{{baseName}}")].as_array() ) + for( auto& item : val[utility::conversions::to_string_t("{{baseName}}")].as_array() ) { {{#items.isPrimitiveType}} m_{{name}}.push_back(ModelBase::{{items.baseType}}FromJson(item)); @@ -195,26 +195,26 @@ void {{classname}}::fromJson(web::json::value& val) m_{{name}}.clear(); std::vector jsonArray; {{^required}} - if(val.has_field(U("{{baseName}}"))) + if(val.has_field(utility::conversions::to_string_t("{{baseName}}"))) { {{/required}} - for( auto& item : val[U("{{baseName}}")].as_array() ) + for( auto& item : val[utility::conversions::to_string_t("{{baseName}}")].as_array() ) { utility::string_t key; - if(item.has_field(U("key"))) + if(item.has_field(utility::conversions::to_string_t("key"))) { - key = ModelBase::stringFromJson(item[U("key")]); + key = ModelBase::stringFromJson(item[utility::conversions::to_string_t("key")]); } {{#items.isPrimitiveType}} - m_{{name}}.insert(std::pair( key, ModelBase::{{items.baseType}}FromJson(item[U("value")]))); + m_{{name}}.insert(std::pair( key, ModelBase::{{items.baseType}}FromJson(item[utility::conversions::to_string_t("value")]))); {{/items.isPrimitiveType}} {{^items.isPrimitiveType}} {{#items.isString}} - m_{{name}}.insert(std::pair( key, ModelBase::stringFromJson(item[U("value")]))); + m_{{name}}.insert(std::pair( key, ModelBase::stringFromJson(item[utility::conversions::to_string_t("value")]))); {{/items.isString}} {{^items.isString}} {{#items.isDateTime}} - m_{{name}}.insert(std::pair( key, ModelBase::dateFromJson(item[U("value")]))); + m_{{name}}.insert(std::pair( key, ModelBase::dateFromJson(item[utility::conversions::to_string_t("value")]))); {{/items.isDateTime}} {{^items.isDateTime}} if(item.is_null()) @@ -224,7 +224,7 @@ void {{classname}}::fromJson(web::json::value& val) else { {{{items.datatype}}} newItem({{{items.defaultValue}}}); - newItem->fromJson(item[U("value")]); + newItem->fromJson(item[utility::conversions::to_string_t("value")]); m_{{name}}.insert(std::pair( key, newItem )); } {{/items.isDateTime}} @@ -240,20 +240,20 @@ void {{classname}}::fromJson(web::json::value& val) {{^isMapContainer}} {{^isPrimitiveType}} {{^required}} - if(val.has_field(U("{{baseName}}"))) + if(val.has_field(utility::conversions::to_string_t("{{baseName}}"))) { {{#isString}} - {{setter}}(ModelBase::stringFromJson(val[U("{{baseName}}")])); + {{setter}}(ModelBase::stringFromJson(val[utility::conversions::to_string_t("{{baseName}}")])); {{/isString}} {{^isString}} {{#isDateTime}} - {{setter}}(ModelBase::dateFromJson(val[U("{{baseName}}")])); + {{setter}}(ModelBase::dateFromJson(val[utility::conversions::to_string_t("{{baseName}}")])); {{/isDateTime}} {{^isDateTime}} - if(!val[U("{{baseName}}")].is_null()) + if(!val[utility::conversions::to_string_t("{{baseName}}")].is_null()) { {{{datatype}}} newItem({{{defaultValue}}}); - newItem->fromJson(val[U("{{baseName}}")]); + newItem->fromJson(val[utility::conversions::to_string_t("{{baseName}}")]); {{setter}}( newItem ); } {{/isDateTime}} @@ -262,20 +262,20 @@ void {{classname}}::fromJson(web::json::value& val) {{/required}} {{#required}} {{#isString}} - {{setter}}(ModelBase::stringFromJson(val[U("{{baseName}}")])); + {{setter}}(ModelBase::stringFromJson(val[utility::conversions::to_string_t("{{baseName}}")])); {{/isString}} {{^isString}} {{#isDateTime}} {{setter}} - (ModelBase::dateFromJson(val[U("{{baseName}}")])); + (ModelBase::dateFromJson(val[utility::conversions::to_string_t("{{baseName}}")])); {{/isDateTime}} {{^isDateTime}} {{#vendorExtensions.x-codegen-file}} - {{setter}}(ModelBase::fileFromJson(val[U("{{baseName}}")])); + {{setter}}(ModelBase::fileFromJson(val[utility::conversions::to_string_t("{{baseName}}")])); {{/vendorExtensions.x-codegen-file}} {{^vendorExtensions.x-codegen-file}} {{{datatype}}} new{{name}}({{{defaultValue}}}); - new{{name}}->fromJson(val[U("{{baseName}}")]); + new{{name}}->fromJson(val[utility::conversions::to_string_t("{{baseName}}")]); {{setter}}( new{{name}} ); {{/vendorExtensions.x-codegen-file}} {{/isDateTime}} @@ -291,9 +291,9 @@ void {{classname}}::fromJson(web::json::value& val) void {{classname}}::toMultipart(std::shared_ptr multipart, const utility::string_t& prefix) const { utility::string_t namePrefix = prefix; - if(namePrefix.size() > 0 && namePrefix[namePrefix.size() - 1] != U('.')) + if(namePrefix.size() > 0 && namePrefix.substr(namePrefix.size() - 1) != utility::conversions::to_string_t(".")) { - namePrefix += U("."); + namePrefix += utility::conversions::to_string_t("."); } {{#vars}} @@ -303,11 +303,11 @@ void {{classname}}::toMultipart(std::shared_ptr multipart, co {{^required}} if(m_{{name}}IsSet) { - multipart->add(ModelBase::toHttpContent(namePrefix + U("{{baseName}}"), m_{{name}})); + multipart->add(ModelBase::toHttpContent(namePrefix + utility::conversions::to_string_t("{{baseName}}"), m_{{name}})); } {{/required}} {{#required}} - multipart->add(ModelBase::toHttpContent(namePrefix + U("{{baseName}}"), m_{{name}})); + multipart->add(ModelBase::toHttpContent(namePrefix + utility::conversions::to_string_t("{{baseName}}"), m_{{name}})); {{/required}} {{/isListContainer}} {{/isMapContainer}} @@ -319,11 +319,11 @@ void {{classname}}::toMultipart(std::shared_ptr multipart, co { jsonArray.push_back(ModelBase::toJson(item)); } - {{#required}}multipart->add(ModelBase::toHttpContent(namePrefix + U("{{baseName}}"), web::json::value::array(jsonArray), U("application/json"))); + {{#required}}multipart->add(ModelBase::toHttpContent(namePrefix + utility::conversions::to_string_t("{{baseName}}"), web::json::value::array(jsonArray), utility::conversions::to_string_t("application/json"))); {{/required}}{{^required}} if(jsonArray.size() > 0) { - multipart->add(ModelBase::toHttpContent(namePrefix + U("{{baseName}}"), web::json::value::array(jsonArray), U("application/json"))); + multipart->add(ModelBase::toHttpContent(namePrefix + utility::conversions::to_string_t("{{baseName}}"), web::json::value::array(jsonArray), utility::conversions::to_string_t("application/json"))); } {{/required}} } @@ -334,15 +334,15 @@ void {{classname}}::toMultipart(std::shared_ptr multipart, co for( auto& item : m_{{name}} ) { web::json::value tmp = web::json::value::object(); - tmp[U("key")] = ModelBase::toJson(item.first); - tmp[U("value")] = ModelBase::toJson(item.second); + tmp[utility::conversions::to_string_t("key")] = ModelBase::toJson(item.first); + tmp[utility::conversions::to_string_t("value")] = ModelBase::toJson(item.second); jsonArray.push_back(tmp); } - {{#required}}multipart->add(ModelBase::toHttpContent(namePrefix + U("{{baseName}}"), web::json::value::array(jsonArray), U("application/json"))); + {{#required}}multipart->add(ModelBase::toHttpContent(namePrefix + utility::conversions::to_string_t("{{baseName}}"), web::json::value::array(jsonArray), utility::conversions::to_string_t("application/json"))); {{/required}}{{^required}} if(jsonArray.size() > 0) { - multipart->add(ModelBase::toHttpContent(namePrefix + U("{{baseName}}"), web::json::value::array(jsonArray), U("application/json"))); + multipart->add(ModelBase::toHttpContent(namePrefix + utility::conversions::to_string_t("{{baseName}}"), web::json::value::array(jsonArray), utility::conversions::to_string_t("application/json"))); } {{/required}} } @@ -353,29 +353,29 @@ void {{classname}}::toMultipart(std::shared_ptr multipart, co {{^required}} if(m_{{name}}IsSet) { - {{#isString}}multipart->add(ModelBase::toHttpContent(namePrefix + U("{{baseName}}"), m_{{name}})); - {{/isString}}{{^isString}}{{#isDateTime}}multipart->add(ModelBase::toHttpContent(namePrefix + U("{{baseName}}"), m_{{name}})); + {{#isString}}multipart->add(ModelBase::toHttpContent(namePrefix + utility::conversions::to_string_t("{{baseName}}"), m_{{name}})); + {{/isString}}{{^isString}}{{#isDateTime}}multipart->add(ModelBase::toHttpContent(namePrefix + utility::conversions::to_string_t("{{baseName}}"), m_{{name}})); {{/isDateTime}}{{^isDateTime}}if (m_{{name}}.get()) { - m_{{name}}->toMultipart(multipart, U("{{baseName}}.")); + m_{{name}}->toMultipart(multipart, utility::conversions::to_string_t("{{baseName}}.")); } {{/isDateTime}}{{/isString}} } {{/required}} {{#required}} {{#isString}} - multipart->add(ModelBase::toHttpContent(namePrefix + U("{{baseName}}"), m_{{name}})); + multipart->add(ModelBase::toHttpContent(namePrefix + utility::conversions::to_string_t("{{baseName}}"), m_{{name}})); {{/isString}} {{^isString}} {{#isDateTime}} - multipart->add(ModelBase::toHttpContent(namePrefix + U("{{baseName}}"), m_{{name}})); + multipart->add(ModelBase::toHttpContent(namePrefix + utility::conversions::to_string_t("{{baseName}}"), m_{{name}})); {{/isDateTime}} {{^isDateTime}} {{#vendorExtensions.x-codegen-file}} - multipart->add(ModelBase::toHttpContent(namePrefix + U("{{baseName}}"), m_{{name}})); + multipart->add(ModelBase::toHttpContent(namePrefix + utility::conversions::to_string_t("{{baseName}}"), m_{{name}})); {{/vendorExtensions.x-codegen-file}} {{^vendorExtensions.x-codegen-file}} - m_{{name}}->toMultipart(multipart, U("{{baseName}}.")); + m_{{name}}->toMultipart(multipart, utility::conversions::to_string_t("{{baseName}}.")); {{/vendorExtensions.x-codegen-file}} {{/isDateTime}} {{/isString}} @@ -389,9 +389,9 @@ void {{classname}}::toMultipart(std::shared_ptr multipart, co void {{classname}}::fromMultiPart(std::shared_ptr multipart, const utility::string_t& prefix) { utility::string_t namePrefix = prefix; - if(namePrefix.size() > 0 && namePrefix[namePrefix.size() - 1] != U('.')) + if(namePrefix.size() > 0 && namePrefix.substr(namePrefix.size() - 1) != utility::conversions::to_string_t(".")) { - namePrefix += U("."); + namePrefix += utility::conversions::to_string_t("."); } {{#vars}} @@ -399,13 +399,13 @@ void {{classname}}::fromMultiPart(std::shared_ptr multipart, {{^isListContainer}} {{^isMapContainer}} {{^required}} - if(multipart->hasContent(U("{{baseName}}"))) + if(multipart->hasContent(utility::conversions::to_string_t("{{baseName}}"))) { - {{setter}}(ModelBase::{{baseType}}FromHttpContent(multipart->getContent(U("{{baseName}}")))); + {{setter}}(ModelBase::{{baseType}}FromHttpContent(multipart->getContent(utility::conversions::to_string_t("{{baseName}}")))); } {{/required}} {{#required}} - {{setter}}(ModelBase::{{baseType}}FromHttpContent(multipart->getContent(U("{{baseName}}")))); + {{setter}}(ModelBase::{{baseType}}FromHttpContent(multipart->getContent(utility::conversions::to_string_t("{{baseName}}")))); {{/required}} {{/isMapContainer}} {{/isListContainer}} @@ -414,11 +414,11 @@ void {{classname}}::fromMultiPart(std::shared_ptr multipart, { m_{{name}}.clear(); {{^required}} - if(multipart->hasContent(U("{{baseName}}"))) + if(multipart->hasContent(utility::conversions::to_string_t("{{baseName}}"))) { {{/required}} - web::json::value jsonArray = web::json::value::parse(ModelBase::stringFromHttpContent(multipart->getContent(U("{{baseName}}")))); + web::json::value jsonArray = web::json::value::parse(ModelBase::stringFromHttpContent(multipart->getContent(utility::conversions::to_string_t("{{baseName}}")))); for( auto& item : jsonArray.as_array() ) { {{#isPrimitiveType}} @@ -456,28 +456,28 @@ void {{classname}}::fromMultiPart(std::shared_ptr multipart, { m_{{name}}.clear(); {{^required}} - if(multipart->hasContent(U("{{baseName}}"))) + if(multipart->hasContent(utility::conversions::to_string_t("{{baseName}}"))) { {{/required}} - web::json::value jsonArray = web::json::value::parse(ModelBase::stringFromHttpContent(multipart->getContent(U("{{baseName}}")))); + web::json::value jsonArray = web::json::value::parse(ModelBase::stringFromHttpContent(multipart->getContent(utility::conversions::to_string_t("{{baseName}}")))); for( auto& item : jsonArray.as_array() ) { utility::string_t key; - if(item.has_field(U("key"))) + if(item.has_field(utility::conversions::to_string_t("key"))) { - key = ModelBase::stringFromJson(item[U("key")]); + key = ModelBase::stringFromJson(item[utility::conversions::to_string_t("key")]); } {{#items.isPrimitiveType}} - m_{{name}}.insert(std::pair( key, ModelBase::{{items.baseType}}FromJson(item[U("value")]))); + m_{{name}}.insert(std::pair( key, ModelBase::{{items.baseType}}FromJson(item[utility::conversions::to_string_t("value")]))); {{/items.isPrimitiveType}} {{^items.isPrimitiveType}} {{#items.isString}} - m_{{name}}.insert(std::pair( key, ModelBase::stringFromJson(item[U("value")]))); + m_{{name}}.insert(std::pair( key, ModelBase::stringFromJson(item[utility::conversions::to_string_t("value")]))); {{/items.isString}} {{^items.isString}} {{#items.isDateTime}} - m_{{name}}.insert(std::pair( key, ModelBase::dateFromJson(item[U("value")]))); + m_{{name}}.insert(std::pair( key, ModelBase::dateFromJson(item[utility::conversions::to_string_t("value")]))); {{/items.isDateTime}} {{^items.isDateTime}} if(item.is_null()) @@ -487,7 +487,7 @@ void {{classname}}::fromMultiPart(std::shared_ptr multipart, else { {{{items.datatype}}} newItem({{{items.defaultValue}}}); - newItem->fromJson(item[U("value")]); + newItem->fromJson(item[utility::conversions::to_string_t("value")]); m_{{name}}.insert(std::pair( key, newItem )); } {{/items.isDateTime}} @@ -503,20 +503,20 @@ void {{classname}}::fromMultiPart(std::shared_ptr multipart, {{^isMapContainer}} {{^isPrimitiveType}} {{^required}} - if(multipart->hasContent(U("{{baseName}}"))) + if(multipart->hasContent(utility::conversions::to_string_t("{{baseName}}"))) { {{#isString}} - {{setter}}(ModelBase::stringFromHttpContent(multipart->getContent(U("{{baseName}}")))); + {{setter}}(ModelBase::stringFromHttpContent(multipart->getContent(utility::conversions::to_string_t("{{baseName}}")))); {{/isString}} {{^isString}} {{#isDateTime}} - {{setter}}(ModelBase::dateFromHttpContent(multipart->getContent(U("{{baseName}}")))); + {{setter}}(ModelBase::dateFromHttpContent(multipart->getContent(utility::conversions::to_string_t("{{baseName}}")))); {{/isDateTime}} {{^isDateTime}} - if(multipart->hasContent(U("{{baseName}}"))) + if(multipart->hasContent(utility::conversions::to_string_t("{{baseName}}"))) { {{{datatype}}} newItem({{{defaultValue}}}); - newItem->fromMultiPart(multipart, U("{{baseName}}.")); + newItem->fromMultiPart(multipart, utility::conversions::to_string_t("{{baseName}}.")); {{setter}}( newItem ); } {{/isDateTime}} @@ -525,19 +525,19 @@ void {{classname}}::fromMultiPart(std::shared_ptr multipart, {{/required}} {{#required}} {{#isString}} - {{setter}}(ModelBase::stringFromHttpContent(multipart->getContent(U("{{baseName}}")))); + {{setter}}(ModelBase::stringFromHttpContent(multipart->getContent(utility::conversions::to_string_t("{{baseName}}")))); {{/isString}} {{^isString}} {{#isDateTime}} - {{setter}}(ModelBase::dateFromHttpContent(multipart->getContent(U("{{baseName}}")))); + {{setter}}(ModelBase::dateFromHttpContent(multipart->getContent(utility::conversions::to_string_t("{{baseName}}")))); {{/isDateTime}} {{^isDateTime}} {{#vendorExtensions.x-codegen-file}} - {{setter}}(multipart->getContent(U("{{baseName}}"))); + {{setter}}(multipart->getContent(utility::conversions::to_string_t("{{baseName}}"))); {{/vendorExtensions.x-codegen-file}} {{^vendorExtensions.x-codegen-file}} {{{datatype}}} new{{name}}({{{defaultValue}}}); - new{{name}}->fromMultiPart(multipart, U("{{baseName}}.")); + new{{name}}->fromMultiPart(multipart, utility::conversions::to_string_t("{{baseName}}.")); {{setter}}( new{{name}} ); {{/vendorExtensions.x-codegen-file}} {{/isDateTime}} diff --git a/modules/swagger-codegen/src/main/resources/cpprest/modelbase-header.mustache b/modules/swagger-codegen/src/main/resources/cpprest/modelbase-header.mustache index a74dfffbb74..a68bc0cc227 100644 --- a/modules/swagger-codegen/src/main/resources/cpprest/modelbase-header.mustache +++ b/modules/swagger-codegen/src/main/resources/cpprest/modelbase-header.mustache @@ -5,8 +5,8 @@ * This is the base class for all model classes */ -#ifndef ModelBase_H_ -#define ModelBase_H_ +#ifndef {{modelHeaderGuardPrefix}}_ModelBase_H_ +#define {{modelHeaderGuardPrefix}}_ModelBase_H_ {{{defaultInclude}}} #include "HttpContent.h" @@ -51,13 +51,13 @@ public: static bool boolFromJson(web::json::value& val); static std::shared_ptr fileFromJson(web::json::value& val); - static std::shared_ptr toHttpContent( const utility::string_t& name, const utility::string_t& value, const utility::string_t& contentType = U("")); - static std::shared_ptr toHttpContent( const utility::string_t& name, const utility::datetime& value, const utility::string_t& contentType = U("")); + static std::shared_ptr toHttpContent( const utility::string_t& name, const utility::string_t& value, const utility::string_t& contentType = utility::conversions::to_string_t("")); + static std::shared_ptr toHttpContent( const utility::string_t& name, const utility::datetime& value, const utility::string_t& contentType = utility::conversions::to_string_t("")); static std::shared_ptr toHttpContent( const utility::string_t& name, std::shared_ptr value ); - static std::shared_ptr toHttpContent( const utility::string_t& name, const web::json::value& value, const utility::string_t& contentType = U("application/json") ); - static std::shared_ptr toHttpContent( const utility::string_t& name, int32_t value, const utility::string_t& contentType = U("") ); - static std::shared_ptr toHttpContent( const utility::string_t& name, int64_t value, const utility::string_t& contentType = U("") ); - static std::shared_ptr toHttpContent( const utility::string_t& name, double value, const utility::string_t& contentType = U("") ); + static std::shared_ptr toHttpContent( const utility::string_t& name, const web::json::value& value, const utility::string_t& contentType = utility::conversions::to_string_t("application/json") ); + static std::shared_ptr toHttpContent( const utility::string_t& name, int32_t value, const utility::string_t& contentType = utility::conversions::to_string_t("") ); + static std::shared_ptr toHttpContent( const utility::string_t& name, int64_t value, const utility::string_t& contentType = utility::conversions::to_string_t("") ); + static std::shared_ptr toHttpContent( const utility::string_t& name, double value, const utility::string_t& contentType = utility::conversions::to_string_t("") ); static int64_t int64_tFromHttpContent(std::shared_ptr val); static int32_t int32_tFromHttpContent(std::shared_ptr val); @@ -66,6 +66,7 @@ public: static utility::datetime dateFromHttpContent(std::shared_ptr val); static bool boolFromHttpContent(std::shared_ptr val); static double doubleFromHttpContent(std::shared_ptr val); + static web::json::value valueFromHttpContent(std::shared_ptr val); static utility::string_t toBase64( utility::string_t value ); @@ -77,4 +78,4 @@ public: } {{/modelNamespaceDeclarations}} -#endif /* ModelBase_H_ */ +#endif /* {{modelHeaderGuardPrefix}}_ModelBase_H_ */ diff --git a/modules/swagger-codegen/src/main/resources/cpprest/modelbase-source.mustache b/modules/swagger-codegen/src/main/resources/cpprest/modelbase-source.mustache index 6df224f185a..86f976d1453 100644 --- a/modules/swagger-codegen/src/main/resources/cpprest/modelbase-source.mustache +++ b/modules/swagger-codegen/src/main/resources/cpprest/modelbase-source.mustache @@ -39,10 +39,10 @@ web::json::value ModelBase::toJson(bool value) { web::json::value ModelBase::toJson( std::shared_ptr content ) { web::json::value value; - value[U("ContentDisposition")] = ModelBase::toJson(content->getContentDisposition()); - value[U("ContentType")] = ModelBase::toJson(content->getContentType()); - value[U("FileName")] = ModelBase::toJson(content->getFileName()); - value[U("InputStream")] = web::json::value::string( ModelBase::toBase64(content->getData()) ); + value[utility::conversions::to_string_t("ContentDisposition")] = ModelBase::toJson(content->getContentDisposition()); + value[utility::conversions::to_string_t("ContentType")] = ModelBase::toJson(content->getContentType()); + value[utility::conversions::to_string_t("FileName")] = ModelBase::toJson(content->getFileName()); + value[utility::conversions::to_string_t("InputStream")] = web::json::value::string( ModelBase::toBase64(content->getData()) ); return value; } @@ -50,21 +50,21 @@ std::shared_ptr ModelBase::fileFromJson(web::json::value& val) { std::shared_ptr content(new HttpContent); - if(val.has_field(U("ContentDisposition"))) + if(val.has_field(utility::conversions::to_string_t("ContentDisposition"))) { - content->setContentDisposition( ModelBase::stringFromJson(val[U("ContentDisposition")]) ); + content->setContentDisposition( ModelBase::stringFromJson(val[utility::conversions::to_string_t("ContentDisposition")]) ); } - if(val.has_field(U("ContentType"))) + if(val.has_field(utility::conversions::to_string_t("ContentType"))) { - content->setContentType( ModelBase::stringFromJson(val[U("ContentType")]) ); + content->setContentType( ModelBase::stringFromJson(val[utility::conversions::to_string_t("ContentType")]) ); } - if(val.has_field(U("FileName"))) + if(val.has_field(utility::conversions::to_string_t("FileName"))) { - content->setFileName( ModelBase::stringFromJson(val[U("FileName")]) ); + content->setFileName( ModelBase::stringFromJson(val[utility::conversions::to_string_t("FileName")]) ); } - if(val.has_field(U("InputStream"))) + if(val.has_field(utility::conversions::to_string_t("InputStream"))) { - content->setData( ModelBase::fromBase64( ModelBase::stringFromJson(val[U("InputStream")]) ) ); + content->setData( ModelBase::fromBase64( ModelBase::stringFromJson(val[utility::conversions::to_string_t("InputStream")]) ) ); } return content; @@ -79,7 +79,7 @@ std::shared_ptr ModelBase::toHttpContent( const utility::string_t& { std::shared_ptr content(new HttpContent); content->setName( name ); - content->setContentDisposition( U("form-data") ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); content->setContentType( contentType ); content->setData( std::shared_ptr( new std::stringstream( utility::conversions::to_utf8string(value) ) ) ); return content; @@ -88,7 +88,7 @@ std::shared_ptr ModelBase::toHttpContent( const utility::string_t& { std::shared_ptr content( new HttpContent ); content->setName( name ); - content->setContentDisposition( U("form-data") ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); content->setContentType( contentType ); content->setData( std::shared_ptr( new std::stringstream( utility::conversions::to_utf8string(value.to_string(utility::datetime::ISO_8601) ) ) ) ); return content; @@ -107,7 +107,7 @@ std::shared_ptr ModelBase::toHttpContent( const utility::string_t& { std::shared_ptr content( new HttpContent ); content->setName( name ); - content->setContentDisposition( U("form-data") ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); content->setContentType( contentType ); content->setData( std::shared_ptr( new std::stringstream( utility::conversions::to_utf8string(value.serialize()) ) ) ); return content; @@ -116,7 +116,7 @@ std::shared_ptr ModelBase::toHttpContent( const utility::string_t& { std::shared_ptr content( new HttpContent ); content->setName( name ); - content->setContentDisposition( U("form-data") ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); content->setContentType( contentType ); std::stringstream* valueAsStringStream = new std::stringstream(); (*valueAsStringStream) << value; @@ -127,7 +127,7 @@ std::shared_ptr ModelBase::toHttpContent( const utility::string_t& { std::shared_ptr content( new HttpContent ); content->setName( name ); - content->setContentDisposition( U("form-data") ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); content->setContentType( contentType ); std::stringstream* valueAsStringStream = new std::stringstream(); (*valueAsStringStream) << value; @@ -138,7 +138,7 @@ std::shared_ptr ModelBase::toHttpContent( const utility::string_t& { std::shared_ptr content( new HttpContent ); content->setName( name ); - content->setContentDisposition( U("form-data") ); + content->setContentDisposition( utility::conversions::to_string_t("form-data") ); content->setContentType( contentType ); std::stringstream* valueAsStringStream = new std::stringstream(); (*valueAsStringStream) << value; @@ -244,12 +244,12 @@ std::shared_ptr ModelBase::fromBase64( const utility::string_t& en result->write( outBuf, 1 ); return result; default: - throw web::json::json_exception( U( "Invalid Padding in Base 64!" ) ); + throw web::json::json_exception( utility::conversions::to_string_t( "Invalid Padding in Base 64!" ).c_str() ); } } else { - throw web::json::json_exception( U( "Non-Valid Character in Base 64!" ) ); + throw web::json::json_exception( utility::conversions::to_string_t( "Non-Valid Character in Base 64!" ).c_str() ); } ++cursor; } @@ -277,7 +277,7 @@ float ModelBase::floatFromJson(web::json::value& val) } utility::string_t ModelBase::stringFromJson(web::json::value& val) { - return val.is_string() ? val.as_string() : U(""); + return val.is_string() ? val.as_string() : utility::conversions::to_string_t(""); } utility::datetime ModelBase::dateFromJson(web::json::value& val) @@ -355,6 +355,12 @@ double ModelBase::doubleFromHttpContent(std::shared_ptr val) return result; } +web::json::value ModelBase::valueFromHttpContent(std::shared_ptr val) +{ + utility::string_t str = ModelBase::stringFromHttpContent(val); + return web::json::value::parse(str); +} + {{#modelNamespaceDeclarations}} } {{/modelNamespaceDeclarations}} diff --git a/modules/swagger-codegen/src/main/resources/cpprest/multipart-header.mustache b/modules/swagger-codegen/src/main/resources/cpprest/multipart-header.mustache index 917b8cf3cc5..17dba42a34a 100644 --- a/modules/swagger-codegen/src/main/resources/cpprest/multipart-header.mustache +++ b/modules/swagger-codegen/src/main/resources/cpprest/multipart-header.mustache @@ -5,8 +5,8 @@ * This class represents a container for building a application/x-multipart-formdata requests. */ -#ifndef MultipartFormData_H_ -#define MultipartFormData_H_ +#ifndef {{modelHeaderGuardPrefix}}_MultipartFormData_H_ +#define {{modelHeaderGuardPrefix}}_MultipartFormData_H_ {{{defaultInclude}}} #include "IHttpBody.h" @@ -47,4 +47,4 @@ protected: } {{/modelNamespaceDeclarations}} -#endif /* MultipartFormData_H_ */ +#endif /* {{modelHeaderGuardPrefix}}_MultipartFormData_H_ */ diff --git a/modules/swagger-codegen/src/main/resources/cpprest/object-header.mustache b/modules/swagger-codegen/src/main/resources/cpprest/object-header.mustache new file mode 100644 index 00000000000..5ad0497e5a0 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/cpprest/object-header.mustache @@ -0,0 +1,50 @@ +{{>licenseInfo}} +/* + * Object.h + * + * This is the implementation of a JSON object. + */ + +#ifndef {{modelHeaderGuardPrefix}}_Object_H_ +#define {{modelHeaderGuardPrefix}}_Object_H_ + +{{{defaultInclude}}} +#include "ModelBase.h" + +#include +#include + +{{#modelNamespaceDeclarations}} +namespace {{this}} { +{{/modelNamespaceDeclarations}} + +class {{declspec}} Object : public ModelBase +{ +public: + Object(); + virtual ~Object(); + + ///////////////////////////////////////////// + /// ModelBase overrides + void validate() override; + + web::json::value toJson() const override; + void fromJson(web::json::value& json) override; + + void toMultipart(std::shared_ptr multipart, const utility::string_t& namePrefix) const override; + void fromMultiPart(std::shared_ptr multipart, const utility::string_t& namePrefix) override; + + ///////////////////////////////////////////// + /// Object manipulation + web::json::value getValue(const utility::string_t& key) const; + void setValue(const utility::string_t& key, const web::json::value& value); + +private: + web::json::value m_object; +}; + +{{#modelNamespaceDeclarations}} +} +{{/modelNamespaceDeclarations}} + +#endif /* {{modelHeaderGuardPrefix}}_Object_H_ */ diff --git a/modules/swagger-codegen/src/main/resources/cpprest/object-source.mustache b/modules/swagger-codegen/src/main/resources/cpprest/object-source.mustache new file mode 100644 index 00000000000..2bc1d86eb1f --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/cpprest/object-source.mustache @@ -0,0 +1,69 @@ +{{>licenseInfo}} +#include "Object.h" + +{{#modelNamespaceDeclarations}} +namespace {{this}} { +{{/modelNamespaceDeclarations}} + +Object::Object() +{ + m_object = web::json::value::object(); +} + +Object::~Object() +{ +} + +void Object::validate() +{ + // TODO: implement validation +} + +web::json::value Object::toJson() const +{ + return m_object; +} + +void Object::fromJson(web::json::value& val) +{ + if (val.is_object()) + { + m_object = val; + } +} + +void Object::toMultipart(std::shared_ptr multipart, const utility::string_t& prefix) const +{ + utility::string_t namePrefix = prefix; + if(namePrefix.size() > 0 && namePrefix.substr(namePrefix.size() - 1) != utility::conversions::to_string_t(".")) + { + namePrefix += utility::conversions::to_string_t("."); + } + multipart->add(ModelBase::toHttpContent(namePrefix + utility::conversions::to_string_t("object"), m_object)); +} + +void Object::fromMultiPart(std::shared_ptr multipart, const utility::string_t& prefix) +{ + utility::string_t namePrefix = prefix; + if(namePrefix.size() > 0 && namePrefix.substr(namePrefix.size() - 1) != utility::conversions::to_string_t(".")) + { + namePrefix += utility::conversions::to_string_t("."); + } + + m_object = ModelBase::valueFromHttpContent(multipart->getContent(namePrefix + utility::conversions::to_string_t("object"))); +} + +web::json::value Object::getValue(const utility::string_t& key) const +{ + return m_object.at(key); +} + + +void Object::setValue(const utility::string_t& key, const web::json::value& value) +{ + m_object[key] = value; +} + +{{#modelNamespaceDeclarations}} +} +{{/modelNamespaceDeclarations}} diff --git a/modules/swagger-codegen/src/main/resources/csharp-dotnet2/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/ApiClient.mustache new file mode 100644 index 00000000000..4c8d13c5a99 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/ApiClient.mustache @@ -0,0 +1,295 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text.RegularExpressions; +using System.IO; +using System.Web; +using System.Linq; +using System.Net; +using System.Text; +using Newtonsoft.Json; +using RestSharp; +using RestSharp.Extensions; + +namespace {{clientPackage}} +{ + /// + /// API client is mainly responible for making the HTTP call to the API backend. + /// + public class ApiClient + { + private readonly Dictionary _defaultHeaderMap = new Dictionary(); + + /// + /// Initializes a new instance of the class. + /// + /// The base path. + public ApiClient(String basePath="{{{basePath}}}") + { + BasePath = basePath; + RestClient = new RestClient(BasePath); + } + + /// + /// Gets or sets the base path. + /// + /// The base path + public string BasePath { get; set; } + + /// + /// Gets or sets the RestClient. + /// + /// An instance of the RestClient + public RestClient RestClient { get; set; } + + /// + /// Gets the default header. + /// + public Dictionary DefaultHeader + { + get { return _defaultHeaderMap; } + } + + /// + /// Makes the HTTP request (Sync). + /// + /// URL path. + /// HTTP method. + /// Query parameters. + /// HTTP body (POST request). + /// Header parameters. + /// Form parameters. + /// File parameters. + /// Authentication settings. + /// Object + public Object CallApi(String path, RestSharp.Method method, Dictionary queryParams, String postBody, + Dictionary headerParams, Dictionary formParams, + Dictionary fileParams, String[] authSettings) + { + + var request = new RestRequest(path, method); + + UpdateParamsForAuth(queryParams, headerParams, authSettings); + + // add default header, if any + foreach(var defaultHeader in _defaultHeaderMap) + request.AddHeader(defaultHeader.Key, defaultHeader.Value); + + // add header parameter, if any + foreach(var param in headerParams) + request.AddHeader(param.Key, param.Value); + + // add query parameter, if any + foreach(var param in queryParams) + request.AddParameter(param.Key, param.Value, ParameterType.GetOrPost); + + // add form parameter, if any + foreach(var param in formParams) + request.AddParameter(param.Key, param.Value, ParameterType.GetOrPost); + + // add file parameter, if any + foreach(var param in fileParams) + request.AddFile(param.Value.Name, param.Value.Writer, param.Value.FileName, param.Value.ContentType); + + if (postBody != null) // http body (model) parameter + request.AddParameter("application/json", postBody, ParameterType.RequestBody); + + return (Object)RestClient.Execute(request); + + } + + /// + /// Add default header. + /// + /// Header field name. + /// Header field value. + /// + public void AddDefaultHeader(string key, string value) + { + _defaultHeaderMap.Add(key, value); + } + + /// + /// Escape string (url-encoded). + /// + /// String to be escaped. + /// Escaped string. + public string EscapeString(string str) + { + return RestSharp.Contrib.HttpUtility.UrlEncode(str); + } + + /// + /// Create FileParameter based on Stream. + /// + /// Parameter name. + /// Input stream. + /// FileParameter. + public FileParameter ParameterToFile(string name, Stream stream) + { + if (stream is FileStream) + return FileParameter.Create(name, stream.ReadAsBytes(), Path.GetFileName(((FileStream)stream).Name)); + else + return FileParameter.Create(name, stream.ReadAsBytes(), "no_file_name_provided"); + } + + /// + /// If parameter is DateTime, output in a formatted string (default ISO 8601), customizable with Configuration.DateTime. + /// If parameter is a list of string, join the list with ",". + /// Otherwise just return the string. + /// + /// The parameter (header, path, query, form). + /// Formatted string. + public string ParameterToString(object obj) + { + if (obj is DateTime) + // Return a formatted date string - Can be customized with Configuration.DateTimeFormat + // Defaults to an ISO 8601, using the known as a Round-trip date/time pattern ("o") + // https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Anchor_8 + // For example: 2009-06-15T13:45:30.0000000 + return ((DateTime)obj).ToString (Configuration.DateTimeFormat); + else if (obj is List) + return String.Join(",", (obj as List).ToArray()); + else + return Convert.ToString (obj); + } + + /// + /// Deserialize the JSON string into a proper object. + /// + /// HTTP body (e.g. string, JSON). + /// Object type. + /// HTTP headers. + /// Object representation of the JSON string. + public object Deserialize(string content, Type type, IList headers=null) + { + if (type == typeof(Object)) // return an object + { + return content; + } + + if (type == typeof(Stream)) + { + var filePath = String.IsNullOrEmpty(Configuration.TempFolderPath) + ? Path.GetTempPath() + : Configuration.TempFolderPath; + + var fileName = filePath + Guid.NewGuid(); + if (headers != null) + { + var regex = new Regex(@"Content-Disposition:.*filename=['""]?([^'""\s]+)['""]?$"); + var match = regex.Match(headers.ToString()); + if (match.Success) + fileName = filePath + match.Value.Replace("\"", "").Replace("'", ""); + } + File.WriteAllText(fileName, content); + return new FileStream(fileName, FileMode.Open); + + } + + if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object + { + return DateTime.Parse(content, null, System.Globalization.DateTimeStyles.RoundtripKind); + } + + if (type == typeof(String) || type.Name.StartsWith("System.Nullable")) // return primitive type + { + return ConvertType(content, type); + } + + // at this point, it must be a model (json) + try + { + return JsonConvert.DeserializeObject(content, type); + } + catch (IOException e) + { + throw new ApiException(500, e.Message); + } + } + + /// + /// Serialize an object into JSON string. + /// + /// Object. + /// JSON string. + public string Serialize(object obj) + { + try + { + return obj != null ? JsonConvert.SerializeObject(obj) : null; + } + catch (Exception e) + { + throw new ApiException(500, e.Message); + } + } + + /// + /// Get the API key with prefix. + /// + /// API key identifier (authentication scheme). + /// API key with prefix. + public string GetApiKeyWithPrefix (string apiKeyIdentifier) + { + var apiKeyValue = ""; + Configuration.ApiKey.TryGetValue (apiKeyIdentifier, out apiKeyValue); + var apiKeyPrefix = ""; + if (Configuration.ApiKeyPrefix.TryGetValue (apiKeyIdentifier, out apiKeyPrefix)) + return apiKeyPrefix + " " + apiKeyValue; + else + return apiKeyValue; + } + + /// + /// Update parameters based on authentication. + /// + /// Query parameters. + /// Header parameters. + /// Authentication settings. + public void UpdateParamsForAuth(Dictionary queryParams, Dictionary headerParams, string[] authSettings) + { + if (authSettings == null || authSettings.Length == 0) + return; + + foreach (string auth in authSettings) + { + // determine which one to use + switch(auth) + { + {{#authMethods}} + case "{{name}}": + {{#isApiKey}}{{#isKeyInHeader}}headerParams["{{keyParamName}}"] = GetApiKeyWithPrefix("{{keyParamName}}");{{/isKeyInHeader}}{{#isKeyInQuery}}queryParams["{{keyParamName}}"] = GetApiKeyWithPrefix("{{keyParamName}}");{{/isKeyInQuery}}{{/isApiKey}}{{#isBasic}}headerParams["Authorization"] = "Basic " + Base64Encode(Configuration.Username + ":" + Configuration.Password);{{/isBasic}} + {{#isOAuth}}//TODO support oauth{{/isOAuth}} + break; + {{/authMethods}} + default: + //TODO show warning about security definition not found + break; + } + } + } + + /// + /// Encode string in base64 format. + /// + /// String to be encoded. + /// Encoded string. + public static string Base64Encode(string text) + { + var textByte = System.Text.Encoding.UTF8.GetBytes(text); + return System.Convert.ToBase64String(textByte); + } + + /// + /// Dynamically cast the object into target type. + /// + /// Object to be casted + /// Target type + /// Casted object + public static Object ConvertType(Object fromObject, Type toObject) { + return Convert.ChangeType(fromObject, toObject); + } + + } +} diff --git a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/ApiException.mustache b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/ApiException.mustache similarity index 97% rename from modules/swagger-codegen/src/main/resources/CsharpDotNet2/ApiException.mustache rename to modules/swagger-codegen/src/main/resources/csharp-dotnet2/ApiException.mustache index dd618e0a99c..1bc6d7c5a6f 100644 --- a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/ApiException.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/ApiException.mustache @@ -1,6 +1,6 @@ using System; -namespace {{packageName}}.Client { +namespace {{clientPackage}} { /// /// API Exception /// diff --git a/modules/swagger-codegen/src/main/resources/csharp-dotnet2/Configuration.mustache b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/Configuration.mustache new file mode 100644 index 00000000000..a175367ab74 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/Configuration.mustache @@ -0,0 +1,132 @@ +using System; +using System.Reflection; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace {{clientPackage}} +{ + /// + /// Represents a set of configuration settings + /// + public class Configuration + { + + /// + /// Version of the package. + /// + /// Version of the package. + public const string Version = "{{packageVersion}}"; + + /// + /// Gets or sets the default API client for making HTTP calls. + /// + /// The API client. + public static ApiClient DefaultApiClient = new ApiClient(); + + /// + /// Gets or sets the username (HTTP basic authentication). + /// + /// The username. + public static String Username { get; set; } + + /// + /// Gets or sets the password (HTTP basic authentication). + /// + /// The password. + public static String Password { get; set; } + + /// + /// Gets or sets the API key based on the authentication name. + /// + /// The API key. + public static Dictionary ApiKey = new Dictionary(); + + /// + /// Gets or sets the prefix (e.g. Token) of the API key based on the authentication name. + /// + /// The prefix of the API key. + public static Dictionary ApiKeyPrefix = new Dictionary(); + + private static string _tempFolderPath = Path.GetTempPath(); + + /// + /// Gets or sets the temporary folder path to store the files downloaded from the server. + /// + /// Folder path. + public static String TempFolderPath + { + get { return _tempFolderPath; } + + set + { + if (String.IsNullOrEmpty(value)) + { + _tempFolderPath = value; + return; + } + + // create the directory if it does not exist + if (!Directory.Exists(value)) + Directory.CreateDirectory(value); + + // check if the path contains directory separator at the end + if (value[value.Length - 1] == Path.DirectorySeparatorChar) + _tempFolderPath = value; + else + _tempFolderPath = value + Path.DirectorySeparatorChar; + } + } + + private const string ISO8601_DATETIME_FORMAT = "o"; + + private static string _dateTimeFormat = ISO8601_DATETIME_FORMAT; + + /// + /// Gets or sets the the date time format used when serializing in the ApiClient + /// By default, it's set to ISO 8601 - "o", for others see: + /// https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx + /// and https://msdn.microsoft.com/en-us/library/8kb3ddd4(v=vs.110).aspx + /// No validation is done to ensure that the string you're providing is valid + /// + /// The DateTimeFormat string + public static String DateTimeFormat + { + get + { + return _dateTimeFormat; + } + set + { + if (string.IsNullOrEmpty(value)) + { + // Never allow a blank or null string, go back to the default + _dateTimeFormat = ISO8601_DATETIME_FORMAT; + return; + } + + // Caution, no validation when you choose date time format other than ISO 8601 + // Take a look at the above links + _dateTimeFormat = value; + } + } + + /// + /// Returns a string with essential information for debugging. + /// + public static String ToDebugReport() + { + String report = "C# SDK ({{packageName}}) Debug Report:\n"; + report += " OS: " + Environment.OSVersion + "\n"; + report += " .NET Framework Version: " + Assembly + .GetExecutingAssembly() + .GetReferencedAssemblies() + .Where(x => x.Name == "System.Core").First().Version.ToString() + "\n"; + report += " Version of the API: {{version}}\n"; + report += " SDK Package Version: {{packageVersion}}\n"; + + return report; + } + } +} diff --git a/modules/swagger-codegen/src/main/resources/csharp-dotnet2/README.mustache b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/README.mustache new file mode 100644 index 00000000000..89fa3938d97 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/README.mustache @@ -0,0 +1,153 @@ +# {{packageName}} - the C# library for the {{appName}} + +{{#appDescription}} +{{{appDescription}}} +{{/appDescription}} + +This C# SDK is automatically generated by the [Swagger Codegen](https://github.com/swagger-api/swagger-codegen) project: + +- API version: {{appVersion}} +- SDK version: {{packageVersion}} +{{^hideGenerationTimestamp}} +- Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} +- Build package: {{generatorClass}} +{{#infoUrl}} + For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + + +## Frameworks supported +{{^supportUWP}} +- .NET 2.0 +{{/supportUWP}} +{{#supportUWP}} +- UWP +{{/supportUWP}} + + +## Dependencies +- Mono compiler +- Newtonsoft.Json.7.0.1 +- RestSharp.Net2.1.1.11 + +Note: NuGet is downloaded by the mono compilation script and packages are installed with it. No dependency DLLs are bundled with this generator + + +## Installation +Run the following command to generate the DLL +- [Mac/Linux] `/bin/sh compile-mono.sh` +- [Windows] TODO + +Then include the DLL (under the `bin` folder) in the C# project, and use the namespaces: +```csharp +using {{apiPackage}}; +using {{clientPackage}}; +using {{modelPackage}}; +``` + +## Getting Started + +```csharp +using System; +using System.Diagnostics; +using {{apiPackage}}; +using {{clientPackage}}; +using {{modelPackage}}; + +namespace Example +{ + public class {{operationId}}Example + { + public void main() + { + {{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#operation}}{{#-first}}{{#hasAuthMethods}}{{#authMethods}}{{#isBasic}} + // Configure HTTP basic authorization: {{{name}}} + Configuration.Default.Username = "YOUR_USERNAME"; + Configuration.Default.Password = "YOUR_PASSWORD";{{/isBasic}}{{#isApiKey}} + // Configure API key authorization: {{{name}}} + Configuration.Default.ApiKey.Add("{{{keyParamName}}}", "YOUR_API_KEY"); + // Uncomment below to setup prefix (e.g. Bearer) for API key, if needed + // Configuration.Default.ApiKeyPrefix.Add("{{{keyParamName}}}", "Bearer");{{/isApiKey}}{{#isOAuth}} + // Configure OAuth2 access token for authorization: {{{name}}} + Configuration.Default.AccessToken = "YOUR_ACCESS_TOKEN";{{/isOAuth}}{{/authMethods}} + {{/hasAuthMethods}} + + var apiInstance = new {{classname}}(); + {{#allParams}} + {{#isPrimitiveType}} + var {{paramName}} = {{{example}}}; // {{{dataType}}} | {{{description}}}{{^required}} (optional) {{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} + {{/isPrimitiveType}} + {{^isPrimitiveType}} + var {{paramName}} = new {{{dataType}}}(); // {{{dataType}}} | {{{description}}}{{^required}} (optional) {{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} + {{/isPrimitiveType}} + {{/allParams}} + + try + { + {{#summary}} + // {{{.}}} + {{/summary}} + {{#returnType}}{{{.}}} result = {{/returnType}}apiInstance.{{{operationId}}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{#returnType}} + Debug.WriteLine(result);{{/returnType}} + } + catch (Exception e) + { + Debug.Print("Exception when calling {{classname}}.{{operationId}}: " + e.Message ); + } + } + } +}{{/-first}}{{/operation}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}} +``` + + +## Documentation for API Endpoints + +All URIs are relative to *{{{basePath}}}* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{{summary}}}{{/summary}} +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + + +## Documentation for Models + +{{#modelPackage}} +{{#models}}{{#model}} - [{{{modelPackage}}}.{{{classname}}}]({{modelDocPath}}{{{classname}}}.md) +{{/model}}{{/models}} +{{/modelPackage}} +{{^modelPackage}} +No model defined in this package +{{/modelPackage}} + + +## Documentation for Authorization + +{{^authMethods}} +All endpoints do not require authorization. +{{/authMethods}} +{{#authMethods}} +{{#last}} +Authentication schemes defined for the API: +{{/last}} +{{/authMethods}} +{{#authMethods}} + +### {{name}} + +{{#isApiKey}}- **Type**: API key +- **API key parameter name**: {{keyParamName}} +- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}} +{{/isApiKey}} +{{#isBasic}}- **Type**: HTTP basic authentication +{{/isBasic}} +{{#isOAuth}}- **Type**: OAuth +- **Flow**: {{flow}} +- **Authorization URL**: {{authorizationUrl}} +- **Scopes**: {{^scopes}}N/A{{/scopes}} +{{#scopes}} - {{scope}}: {{description}} +{{/scopes}} +{{/isOAuth}} + +{{/authMethods}} diff --git a/modules/swagger-codegen/src/main/resources/csharp-dotnet2/api.mustache b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/api.mustache new file mode 100644 index 00000000000..4a7ccbbe378 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/api.mustache @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using RestSharp; +using {{clientPackage}}; +{{#hasImport}}using {{modelPackage}}; +{{/hasImport}} + +namespace {{apiPackage}} +{ + {{#operations}} + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public interface I{{classname}} + { + {{#operation}} + /// + /// {{summary}} {{notes}} + /// + {{#allParams}}/// {{description}} + {{/allParams}}/// {{#returnType}}{{returnType}}{{/returnType}} + {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{nickname}} ({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + {{/operation}} + } + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public class {{classname}} : I{{classname}} + { + /// + /// Initializes a new instance of the class. + /// + /// an instance of ApiClient (optional) + /// + public {{classname}}(ApiClient apiClient = null) + { + if (apiClient == null) // use the default one in Configuration + this.ApiClient = Configuration.DefaultApiClient; + else + this.ApiClient = apiClient; + } + + /// + /// Initializes a new instance of the class. + /// + /// + public {{classname}}(String basePath) + { + this.ApiClient = new ApiClient(basePath); + } + + /// + /// Sets the base path of the API client. + /// + /// The base path + /// The base path + public void SetBasePath(String basePath) + { + this.ApiClient.BasePath = basePath; + } + + /// + /// Gets the base path of the API client. + /// + /// The base path + /// The base path + public String GetBasePath(String basePath) + { + return this.ApiClient.BasePath; + } + + /// + /// Gets or sets the API client. + /// + /// An instance of the ApiClient + public ApiClient ApiClient {get; set;} + + {{#operation}} + /// + /// {{summary}} {{notes}} + /// + {{#allParams}}/// {{description}} + {{/allParams}}/// {{#returnType}}{{returnType}}{{/returnType}} + public {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{nickname}} ({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) + { + {{#allParams}}{{#required}} + // verify the required parameter '{{paramName}}' is set + if ({{paramName}} == null) throw new ApiException(400, "Missing required parameter '{{paramName}}' when calling {{nickname}}"); + {{/required}}{{/allParams}} + + var path = "{{{path}}}"; + path = path.Replace("{format}", "json"); + {{#pathParams}}path = path.Replace("{" + "{{baseName}}" + "}", ApiClient.ParameterToString({{{paramName}}})); + {{/pathParams}} + + var queryParams = new Dictionary(); + var headerParams = new Dictionary(); + var formParams = new Dictionary(); + var fileParams = new Dictionary(); + String postBody = null; + + {{#queryParams}} if ({{paramName}} != null) queryParams.Add("{{baseName}}", ApiClient.ParameterToString({{paramName}})); // query parameter + {{/queryParams}} + {{#headerParams}} if ({{paramName}} != null) headerParams.Add("{{baseName}}", ApiClient.ParameterToString({{paramName}})); // header parameter + {{/headerParams}} + {{#formParams}}if ({{paramName}} != null) {{#isFile}}fileParams.Add("{{baseName}}", ApiClient.ParameterToFile("{{baseName}}", {{paramName}}));{{/isFile}}{{^isFile}}formParams.Add("{{baseName}}", ApiClient.ParameterToString({{paramName}})); // form parameter{{/isFile}} + {{/formParams}} + {{#bodyParam}}postBody = ApiClient.Serialize({{paramName}}); // http body (model) parameter + {{/bodyParam}} + + // authentication setting, if any + String[] authSettings = new String[] { {{#authMethods}}"{{name}}"{{#hasMore}}, {{/hasMore}}{{/authMethods}} }; + + // make the HTTP request + IRestResponse response = (IRestResponse) ApiClient.CallApi(path, Method.{{httpMethod}}, queryParams, postBody, headerParams, formParams, fileParams, authSettings); + + if (((int)response.StatusCode) >= 400) + throw new ApiException ((int)response.StatusCode, "Error calling {{nickname}}: " + response.Content, response.Content); + else if (((int)response.StatusCode) == 0) + throw new ApiException ((int)response.StatusCode, "Error calling {{nickname}}: " + response.ErrorMessage, response.ErrorMessage); + + {{#returnType}}return ({{{returnType}}}) ApiClient.Deserialize(response.Content, typeof({{{returnType}}}), response.Headers);{{/returnType}}{{^returnType}}return;{{/returnType}} + } + + {{/operation}} + } + {{/operations}} +} diff --git a/modules/swagger-codegen/src/main/resources/csharp-dotnet2/api_doc.mustache b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/api_doc.mustache new file mode 100644 index 00000000000..d8a074e11f3 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/api_doc.mustache @@ -0,0 +1,97 @@ +# {{apiPackage}}.{{classname}}{{#description}} +{{description}}{{/description}} + +All URIs are relative to *{{{basePath}}}* + +Method | HTTP request | Description +------------- | ------------- | ------------- +{{#operations}}{{#operation}}[**{{operationId}}**]({{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} +{{/operation}}{{/operations}} + +{{#operations}} +{{#operation}} + +# **{{{operationId}}}** +> {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}} ({{#allParams}}{{{dataType}}} {{paramName}}{{^required}}{{#optionalMethodArgument}} = null{{/optionalMethodArgument}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) + +{{{summary}}}{{#notes}} + +{{{notes}}}{{/notes}} + +### Example +```csharp +using System; +using System.Diagnostics; +using {{apiPackage}}; +using {{clientPackage}}; +using {{modelPackage}}; + +namespace Example +{ + public class {{operationId}}Example + { + public void main() + { + {{#hasAuthMethods}}{{#authMethods}}{{#isBasic}} + // Configure HTTP basic authorization: {{{name}}} + Configuration.Default.Username = "YOUR_USERNAME"; + Configuration.Default.Password = "YOUR_PASSWORD";{{/isBasic}}{{#isApiKey}} + // Configure API key authorization: {{{name}}} + Configuration.Default.ApiKey.Add("{{{keyParamName}}}", "YOUR_API_KEY"); + // Uncomment below to setup prefix (e.g. Bearer) for API key, if needed + // Configuration.Default.ApiKeyPrefix.Add("{{{keyParamName}}}", "Bearer");{{/isApiKey}}{{#isOAuth}} + // Configure OAuth2 access token for authorization: {{{name}}} + Configuration.Default.AccessToken = "YOUR_ACCESS_TOKEN";{{/isOAuth}}{{/authMethods}} + {{/hasAuthMethods}} + + var apiInstance = new {{classname}}(); + {{#allParams}} + {{#isPrimitiveType}} + var {{paramName}} = {{{example}}}; // {{{dataType}}} | {{{description}}}{{^required}} (optional) {{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} + {{/isPrimitiveType}} + {{^isPrimitiveType}} + var {{paramName}} = new {{{dataType}}}(); // {{{dataType}}} | {{{description}}}{{^required}} (optional) {{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} + {{/isPrimitiveType}} + {{/allParams}} + + try + { + {{#summary}} + // {{{.}}} + {{/summary}} + {{#returnType}}{{returnType}} result = {{/returnType}}apiInstance.{{{operationId}}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{#returnType}} + Debug.WriteLine(result);{{/returnType}} + } + catch (Exception e) + { + Debug.Print("Exception when calling {{classname}}.{{operationId}}: " + e.Message ); + } + } + } +} +``` + +### Parameters +{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}} +Name | Type | Description | Notes +------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}} +{{#allParams}} **{{paramName}}** | {{#isFile}}**{{{dataType}}}**{{/isFile}}{{#isPrimitiveType}}**{{{dataType}}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}[**{{{dataType}}}**]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} | {{^required}}[optional] {{/required}}{{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}} +{{/allParams}} + +### Return type + +{{#returnType}}{{#returnTypeIsPrimitive}}**{{{returnType}}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{{returnType}}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}void (empty response body){{/returnType}} + +### Authorization + +{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{{name}}}](../README.md#{{{name}}}){{^-last}}, {{/-last}}{{/authMethods}} + +### HTTP request headers + + - **Content-Type**: {{#consumes}}{{{mediaType}}}{{#hasMore}}, {{/hasMore}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} + - **Accept**: {{#produces}}{{{mediaType}}}{{#hasMore}}, {{/hasMore}}{{/produces}}{{^produces}}Not defined{{/produces}} + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +{{/operation}} +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/compile-mono.sh.mustache b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/compile-mono.sh.mustache similarity index 100% rename from modules/swagger-codegen/src/main/resources/CsharpDotNet2/compile-mono.sh.mustache rename to modules/swagger-codegen/src/main/resources/csharp-dotnet2/compile-mono.sh.mustache diff --git a/modules/swagger-codegen/src/main/resources/csharp-dotnet2/model.mustache b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/model.mustache new file mode 100644 index 00000000000..61dbfe63909 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/model.mustache @@ -0,0 +1,53 @@ +using System; +using System.Text; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.Serialization; +using Newtonsoft.Json; + +{{#models}} +{{#model}} +namespace {{modelPackage}} { + + /// + /// {{description}} + /// + [DataContract] + public class {{classname}}{{#parent}} : {{{parent}}}{{/parent}} { + {{#vars}} + /// + /// {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{{description}}}{{/description}} + /// {{#description}} + /// {{{description}}}{{/description}} + [DataMember(Name="{{baseName}}", EmitDefaultValue=false)] + [JsonProperty(PropertyName = "{{baseName}}")] + public {{{datatype}}} {{name}} { get; set; } + + {{/vars}} + + /// + /// Get the string presentation of the object + /// + /// String presentation of the object + public override string ToString() { + var sb = new StringBuilder(); + sb.Append("class {{classname}} {\n"); + {{#vars}} + sb.Append(" {{name}}: ").Append({{name}}).Append("\n"); + {{/vars}} + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Get the JSON string presentation of the object + /// + /// JSON string presentation of the object + public {{#parent}} new {{/parent}}string ToJson() { + return JsonConvert.SerializeObject(this, Formatting.Indented); + } + +} +{{/model}} +{{/models}} +} diff --git a/modules/swagger-codegen/src/main/resources/csharp-dotnet2/model_doc.mustache b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/model_doc.mustache new file mode 100644 index 00000000000..e7c3ed75934 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/model_doc.mustache @@ -0,0 +1,14 @@ +{{#models}} +{{#model}} +# {{{modelPackage}}}.{{{classname}}} +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +{{#vars}}**{{name}}** | {{#isPrimitiveType}}**{{datatype}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{datatype}}**]({{complexType}}.md){{/isPrimitiveType}} | {{description}} | {{^required}}[optional] {{/required}}{{#readOnly}}[readonly] {{/readOnly}}{{#defaultValue}}[default to {{{.}}}]{{/defaultValue}} +{{/vars}} + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + +{{/model}} +{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/CsharpDotNet2/packages.config.mustache b/modules/swagger-codegen/src/main/resources/csharp-dotnet2/packages.config.mustache similarity index 100% rename from modules/swagger-codegen/src/main/resources/CsharpDotNet2/packages.config.mustache rename to modules/swagger-codegen/src/main/resources/csharp-dotnet2/packages.config.mustache diff --git a/modules/swagger-codegen/src/main/resources/csharp/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/csharp/ApiClient.mustache index 536e660a94d..c0a8a132029 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/ApiClient.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/ApiClient.mustache @@ -49,11 +49,11 @@ namespace {{packageName}}.Client /// /// Initializes a new instance of the class - /// with default configuration and base path ({{{basePath}}}). + /// with default configuration. /// public ApiClient() { - Configuration = Configuration.Default; + Configuration = {{packageName}}.Client.Configuration.Default; RestClient = new RestClient("{{{basePath}}}"); {{#netStandard}} RestClient.IgnoreResponseStatusCode = true; @@ -65,14 +65,11 @@ namespace {{packageName}}.Client /// with default base path ({{{basePath}}}). /// /// An instance of Configuration. - public ApiClient(Configuration config = null) + public ApiClient(Configuration config) { - if (config == null) - Configuration = Configuration.Default; - else - Configuration = config; + Configuration = config ?? {{packageName}}.Client.Configuration.Default; - RestClient = new RestClient("{{{basePath}}}"); + RestClient = new RestClient(Configuration.BasePath); {{#netStandard}} RestClient.IgnoreResponseStatusCode = true; {{/netStandard}} @@ -92,7 +89,7 @@ namespace {{packageName}}.Client {{#netStandard}} RestClient.IgnoreResponseStatusCode = true; {{/netStandard}} - Configuration = Configuration.Default; + Configuration = Client.Configuration.Default; } /// @@ -103,10 +100,15 @@ namespace {{packageName}}.Client public static ApiClient Default; /// - /// Gets or sets the Configuration. + /// Gets or sets an instance of the IReadableConfiguration. /// - /// An instance of the Configuration. - public Configuration Configuration { get; set; } + /// An instance of the IReadableConfiguration. + /// + /// helps us to avoid modifying possibly global + /// configuration values from within a given client. It does not gaurantee thread-safety + /// of the instance in any way. + /// + public IReadableConfiguration Configuration { get; set; } /// /// Gets or sets the RestClient. @@ -116,7 +118,7 @@ namespace {{packageName}}.Client // Creates and sets up a RestRequest prior to a call. private RestRequest PrepareRequest( - String path, {{^netStandard}}RestSharp.{{/netStandard}}Method method, Dictionary queryParams, Object postBody, + String path, {{^netStandard}}RestSharp.{{/netStandard}}Method method, List> queryParams, Object postBody, Dictionary headerParams, Dictionary formParams, Dictionary fileParams, Dictionary pathParams, String contentType) @@ -163,24 +165,12 @@ namespace {{packageName}}.Client if (postBody != null) // http body (model or byte[]) parameter { - if (postBody.GetType() == typeof(String)) - { - {{#netStandard}} - request.AddParameter(new Parameter { Value = postBody, Type = ParameterType.RequestBody, ContentType = "application/json" }); - {{/netStandard}} - {{^netStandard}} - request.AddParameter("application/json", postBody, ParameterType.RequestBody); - {{/netStandard}} - } - else if (postBody.GetType() == typeof(byte[])) - { - {{#netStandard}} - request.AddParameter(new Parameter { Value = postBody, Type = ParameterType.RequestBody, ContentType = contentType }); - {{/netStandard}} - {{^netStandard}} - request.AddParameter(contentType, postBody, ParameterType.RequestBody); - {{/netStandard}} - } + {{#netStandard}} + request.AddParameter(new Parameter { Value = postBody, Type = ParameterType.RequestBody, ContentType = contentType }); + {{/netStandard}} + {{^netStandard}} + request.AddParameter(contentType, postBody, ParameterType.RequestBody); + {{/netStandard}} } return request; @@ -200,7 +190,7 @@ namespace {{packageName}}.Client /// Content Type of the request /// Object public Object CallApi( - String path, {{^netStandard}}RestSharp.{{/netStandard}}Method method, Dictionary queryParams, Object postBody, + String path, {{^netStandard}}RestSharp.{{/netStandard}}Method method, List> queryParams, Object postBody, Dictionary headerParams, Dictionary formParams, Dictionary fileParams, Dictionary pathParams, String contentType) @@ -210,7 +200,8 @@ namespace {{packageName}}.Client pathParams, contentType); // set timeout - RestClient.Timeout = Configuration.Timeout; + {{#netStandard}}RestClient.Timeout = TimeSpan.FromMilliseconds(Configuration.Timeout);{{/netStandard}} + {{^netStandard}}RestClient.Timeout = Configuration.Timeout;{{/netStandard}} // set user agent RestClient.UserAgent = Configuration.UserAgent; @@ -246,7 +237,7 @@ namespace {{packageName}}.Client /// Content type. /// The Task instance. public async System.Threading.Tasks.Task CallApiAsync( - String path, {{^netStandard}}RestSharp.{{/netStandard}}Method method, Dictionary queryParams, Object postBody, + String path, {{^netStandard}}RestSharp.{{/netStandard}}Method method, List> queryParams, Object postBody, Dictionary headerParams, Dictionary formParams, Dictionary fileParams, Dictionary pathParams, String contentType) @@ -334,6 +325,7 @@ namespace {{packageName}}.Client return response.RawBytes; } + // TODO: ? if (type.IsAssignableFrom(typeof(Stream))) if (type == typeof(Stream)) { if (headers != null) @@ -395,9 +387,25 @@ namespace {{packageName}}.Client } } + /// + ///Check if the given MIME is a JSON MIME. + ///JSON MIME examples: + /// application/json + /// application/json; charset=UTF8 + /// APPLICATION/JSON + /// application/vnd.company+json + /// + /// MIME + /// Returns True if MIME type is json. + public bool IsJsonMime(String mime) + { + var jsonRegex = new Regex("(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"); + return mime != null && (jsonRegex.IsMatch(mime) || mime.Equals("application/json-patch+json")); + } + /// /// Select the Content-Type header's value from the given content-type array: - /// if JSON exists in the given array, use it; + /// if JSON type exists in the given array, use it; /// otherwise use the first one defined in 'consumes' /// /// The Content-Type array to select from. @@ -405,11 +413,14 @@ namespace {{packageName}}.Client public String SelectHeaderContentType(String[] contentTypes) { if (contentTypes.Length == 0) - return null; - - if (contentTypes.Contains("application/json", StringComparer.OrdinalIgnoreCase)) return "application/json"; + foreach (var contentType in contentTypes) + { + if (IsJsonMime(contentType.ToLower())) + return contentType; + } + return contentTypes[0]; // use the first content type specified in 'consumes' } @@ -443,31 +454,34 @@ namespace {{packageName}}.Client /// /// Dynamically cast the object into target type. - /// Ref: http://stackoverflow.com/questions/4925718/c-dynamic-runtime-cast /// - /// Object to be casted - /// Target type + /// Object to be casted + /// Target type /// Casted object - {{#supportsAsync}}public static dynamic ConvertType(dynamic source, Type dest){{/supportsAsync}}{{^supportsAsync}}public static object ConvertType(T source, Type dest) where T : class{{/supportsAsync}} + {{#supportsAsync}} + public static dynamic ConvertType(dynamic fromObject, Type toObject) + {{/supportsAsync}} + {{^supportsAsync}} + public static object ConvertType(T fromObject, Type toObject) where T : class + {{/supportsAsync}} { - return Convert.ChangeType(source, dest); + return Convert.ChangeType(fromObject, toObject); } /// /// Convert stream to byte array - /// Credit/Ref: http://stackoverflow.com/a/221941/677735 /// - /// Input stream to be converted + /// Input stream to be converted /// Byte array - public static byte[] ReadAsBytes(Stream input) + public static byte[] ReadAsBytes(Stream inputStream) { - byte[] buffer = new byte[16*1024]; + byte[] buf = new byte[16*1024]; using (MemoryStream ms = new MemoryStream()) { - int read; - while ((read = input.Read(buffer, 0, buffer.Length)) > 0) + int count; + while ((count = inputStream.Read(buf, 0, buf.Length)) > 0) { - ms.Write(buffer, 0, read); + ms.Write(buf, 0, count); } return ms.ToArray(); } @@ -543,5 +557,39 @@ namespace {{packageName}}.Client } {{/supportsUWP}} {{/netStandard}} + + /// + /// Convert params to key/value pairs. + /// Use collectionFormat to properly format lists and collections. + /// + /// Key name. + /// Value object. + /// A list of KeyValuePairs + public IEnumerable> ParameterToKeyValuePairs(string collectionFormat, string name, object value) + { + var parameters = new List>(); + + if (IsCollection(value) && collectionFormat == "multi") + { + var valueCollection = value as IEnumerable; + parameters.AddRange(from object item in valueCollection select new KeyValuePair(name, ParameterToString(item))); + } + else + { + parameters.Add(new KeyValuePair(name, ParameterToString(value))); + } + + return parameters; + } + + /// + /// Check if generic object is a collection. + /// + /// + /// True if object is a collection type + private static bool IsCollection(object value) + { + return value is IList || value is ICollection; + } } } diff --git a/modules/swagger-codegen/src/main/resources/csharp/Configuration.mustache b/modules/swagger-codegen/src/main/resources/csharp/Configuration.mustache index c4133bcc681..9e4cf95b55d 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/Configuration.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/Configuration.mustache @@ -1,6 +1,9 @@ {{>partial_header}} using System; using System.Reflection; +{{^net35}} +using System.Collections.Concurrent; +{{/net35}} using System.Collections.Generic; using System.IO; using System.Linq; @@ -11,62 +14,9 @@ namespace {{packageName}}.Client /// /// Represents a set of configuration settings /// - {{>visibility}} class Configuration + {{>visibility}} class Configuration : IReadableConfiguration { - /// - /// Initializes a new instance of the Configuration class with different settings - /// - /// Api client - /// Dictionary of default HTTP header - /// Username - /// Password - /// accessToken - /// Dictionary of API key - /// Dictionary of API key prefix - /// Temp folder path - /// DateTime format string - /// HTTP connection timeout (in milliseconds) - /// HTTP user agent - public Configuration(ApiClient apiClient = null, - Dictionary defaultHeader = null, - string username = null, - string password = null, - string accessToken = null, - Dictionary apiKey = null, - Dictionary apiKeyPrefix = null, - string tempFolderPath = null, - string dateTimeFormat = null, - int timeout = 100000, - string userAgent = "{{#httpUserAgent}}{{.}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{packageVersion}}/csharp{{/httpUserAgent}}" - ) - { - setApiClientUsingDefault(apiClient); - - Username = username; - Password = password; - AccessToken = accessToken; - UserAgent = userAgent; - - if (defaultHeader != null) - DefaultHeader = defaultHeader; - if (apiKey != null) - ApiKey = apiKey; - if (apiKeyPrefix != null) - ApiKeyPrefix = apiKeyPrefix; - - TempFolderPath = tempFolderPath; - DateTimeFormat = dateTimeFormat; - Timeout = {{#netStandard}}TimeSpan.FromMilliseconds({{/netStandard}}timeout{{#netStandard}}){{/netStandard}}; - } - - /// - /// Initializes a new instance of the Configuration class. - /// - /// Api client. - public Configuration(ApiClient apiClient) - { - setApiClientUsingDefault(apiClient); - } + #region Constants /// /// Version of the package. @@ -75,157 +25,252 @@ namespace {{packageName}}.Client public const string Version = "{{packageVersion}}"; /// - /// Gets or sets the default Configuration. + /// Identifier for ISO 8601 DateTime Format /// - /// Configuration. - public static Configuration Default = new Configuration(); + /// See https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Anchor_8 for more information. + // ReSharper disable once InconsistentNaming + public const string ISO8601_DATETIME_FORMAT = "o"; + + #endregion Constants + + #region Static Members + + private static readonly object GlobalConfigSync = new { }; + private static Configuration _globalConfiguration; /// /// Default creation of exceptions for a given method name and response object /// public static readonly ExceptionFactory DefaultExceptionFactory = (methodName, response) => { - int status = (int) response.StatusCode; - if (status >= 400) return new ApiException(status, String.Format("Error calling {0}: {1}", methodName, response.Content), response.Content); - {{^netStandard}} - if (status == 0) return new ApiException(status, String.Format("Error calling {0}: {1}", methodName, response.ErrorMessage), response.ErrorMessage); - {{/netStandard}} + var status = (int)response.StatusCode; + if (status >= 400) + { + return new ApiException(status, + string.Format("Error calling {0}: {1}", methodName, response.Content), + response.Content); + } + {{^netStandard}}if (status == 0) + { + return new ApiException(status, + string.Format("Error calling {0}: {1}", methodName, response.ErrorMessage), response.ErrorMessage); + }{{/netStandard}} return null; }; /// - /// Gets or sets the HTTP timeout (milliseconds) of ApiClient. Default to 100000 milliseconds. + /// Gets or sets the default Configuration. /// - /// Timeout. - public {{^netStandard}}int{{/netStandard}}{{#netStandard}}TimeSpan?{{/netStandard}} Timeout + /// Configuration. + public static Configuration Default { - get { return ApiClient.RestClient.Timeout; } - + get { return _globalConfiguration; } set { - if (ApiClient != null) - ApiClient.RestClient.Timeout = value; + lock (GlobalConfigSync) + { + _globalConfiguration = value; + } } } + #endregion Static Members + + #region Private Members + /// - /// Gets or sets the default API client for making HTTP calls. + /// Gets or sets the API key based on the authentication name. /// - /// The API client. - public ApiClient ApiClient; + /// The API key. + private IDictionary _apiKey = null; /// - /// Set the ApiClient using Default or ApiClient instance. + /// Gets or sets the prefix (e.g. Token) of the API key based on the authentication name. /// - /// An instance of ApiClient. - /// - public void setApiClientUsingDefault (ApiClient apiClient = null) - { - if (apiClient == null) - { - if (Default != null && Default.ApiClient == null) - Default.ApiClient = new ApiClient(); + /// The prefix of the API key. + private IDictionary _apiKeyPrefix = null; - ApiClient = Default != null ? Default.ApiClient : new ApiClient(); - } - else - { - if (Default != null && Default.ApiClient == null) - Default.ApiClient = apiClient; + private string _dateTimeFormat = ISO8601_DATETIME_FORMAT; + private string _tempFolderPath = Path.GetTempPath(); - ApiClient = apiClient; - } + #endregion Private Members + + #region Constructors + + static Configuration() + { + _globalConfiguration = new GlobalConfiguration(); } - private Dictionary _defaultHeaderMap = new Dictionary(); + /// + /// Initializes a new instance of the class + /// + public Configuration() + { + UserAgent = "{{#httpUserAgent}}{{.}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{packageVersion}}/csharp{{/httpUserAgent}}"; + BasePath = "{{{basePath}}}"; + DefaultHeader = new {{^net35}}Concurrent{{/net35}}Dictionary(); + ApiKey = new {{^net35}}Concurrent{{/net35}}Dictionary(); + ApiKeyPrefix = new {{^net35}}Concurrent{{/net35}}Dictionary(); + + // Setting Timeout has side effects (forces ApiClient creation). + Timeout = 100000; + } /// - /// Gets or sets the default header. + /// Initializes a new instance of the class /// - public Dictionary DefaultHeader + public Configuration( + IDictionary defaultHeader, + IDictionary apiKey, + IDictionary apiKeyPrefix, + string basePath = "{{{basePath}}}") : this() { - get { return _defaultHeaderMap; } + 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 (apiKey == null) + throw new ArgumentNullException("apiKey"); + if (apiKeyPrefix == null) + throw new ArgumentNullException("apiKeyPrefix"); + + BasePath = basePath; + + foreach (var keyValuePair in defaultHeader) + { + DefaultHeader.Add(keyValuePair); + } - set + foreach (var keyValuePair in apiKey) { - _defaultHeaderMap = value; + ApiKey.Add(keyValuePair); + } + + foreach (var keyValuePair in apiKeyPrefix) + { + ApiKeyPrefix.Add(keyValuePair); } } /// - /// Add default header. + /// Initializes a new instance of the class with different settings /// - /// Header field name. - /// Header field value. - /// - public void AddDefaultHeader(string key, string value) + /// Api client + /// Dictionary of default HTTP header + /// Username + /// Password + /// accessToken + /// Dictionary of API key + /// Dictionary of API key prefix + /// Temp folder path + /// DateTime format string + /// HTTP connection timeout (in milliseconds) + /// HTTP user agent + [Obsolete("Use explicit object construction and setting of properties.", true)] + public Configuration( + // ReSharper disable UnusedParameter.Local + ApiClient apiClient = null, + IDictionary defaultHeader = null, + string username = null, + string password = null, + string accessToken = null, + IDictionary apiKey = null, + IDictionary apiKeyPrefix = null, + string tempFolderPath = null, + string dateTimeFormat = null, + int timeout = 100000, + string userAgent = "{{#httpUserAgent}}{{.}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{packageVersion}}/csharp{{/httpUserAgent}}" + // ReSharper restore UnusedParameter.Local + ) { - _defaultHeaderMap[key] = value; + } /// - /// Add Api Key Header. + /// Initializes a new instance of the Configuration class. /// - /// Api Key name. - /// Api Key value. - /// - public void AddApiKey(string key, string value) + /// Api client. + [Obsolete("This constructor caused unexpected sharing of static data. It is no longer supported.", true)] + // ReSharper disable once UnusedParameter.Local + public Configuration(ApiClient apiClient) { - ApiKey[key] = value; + } + #endregion Constructors + + + #region Properties + + private ApiClient _apiClient = null; /// - /// Sets the API key prefix. + /// Gets an instance of an ApiClient for this configuration /// - /// Api Key name. - /// Api Key value. - public void AddApiKeyPrefix(string key, string value) + public virtual ApiClient ApiClient { - ApiKeyPrefix[key] = value; + get + { + if (_apiClient == null) _apiClient = CreateApiClient(); + return _apiClient; + } } + private String _basePath = null; /// - /// Gets or sets the HTTP user agent. + /// Gets or sets the base path for API access. /// - /// Http user agent. - public String UserAgent { get; set; } + public virtual string BasePath { + get { return _basePath; } + set { + _basePath = value; + // pass-through to ApiClient if it's set. + if(_apiClient != null) { + _apiClient.RestClient.BaseUrl = new Uri(_basePath); + } + } + } /// - /// Gets or sets the username (HTTP basic authentication). + /// Gets or sets the default header. /// - /// The username. - public String Username { get; set; } + public virtual IDictionary DefaultHeader { get; set; } /// - /// Gets or sets the password (HTTP basic authentication). + /// Gets or sets the HTTP timeout (milliseconds) of ApiClient. Default to 100000 milliseconds. /// - /// The password. - public String Password { get; set; } + public virtual int Timeout + { + {{#netStandard}}get { return (int)ApiClient.RestClient.Timeout.GetValueOrDefault(TimeSpan.FromSeconds(0)).TotalMilliseconds; } + set { ApiClient.RestClient.Timeout = TimeSpan.FromMilliseconds(value); }{{/netStandard}}{{^netStandard}} + get { return ApiClient.RestClient.Timeout; } + set { ApiClient.RestClient.Timeout = value; }{{/netStandard}} + } /// - /// Gets or sets the access token for OAuth2 authentication. + /// Gets or sets the HTTP user agent. /// - /// The access token. - public String AccessToken { get; set; } + /// Http user agent. + public virtual string UserAgent { get; set; } /// - /// Gets or sets the API key based on the authentication name. + /// Gets or sets the username (HTTP basic authentication). /// - /// The API key. - public Dictionary ApiKey = new Dictionary(); + /// The username. + public virtual string Username { get; set; } /// - /// Gets or sets the prefix (e.g. Token) of the API key based on the authentication name. + /// Gets or sets the password (HTTP basic authentication). /// - /// The prefix of the API key. - public Dictionary ApiKeyPrefix = new Dictionary(); + /// The password. + public virtual string Password { get; set; } /// - /// Get the API key with prefix. + /// Gets the API key with prefix. /// /// API key identifier (authentication scheme). /// API key with prefix. - public string GetApiKeyWithPrefix (string apiKeyIdentifier) + public string GetApiKeyWithPrefix(string apiKeyIdentifier) { var apiKeyValue = ""; ApiKey.TryGetValue (apiKeyIdentifier, out apiKeyValue); @@ -236,48 +281,47 @@ namespace {{packageName}}.Client return apiKeyValue; } - private string _tempFolderPath; + /// + /// Gets or sets the access token for OAuth2 authentication. + /// + /// The access token. + public virtual string AccessToken { get; set; } /// /// Gets or sets the temporary folder path to store the files downloaded from the server. /// /// Folder path. - public String TempFolderPath + public virtual string TempFolderPath { - get - { - // default to Path.GetTempPath() if _tempFolderPath is not set - if (String.IsNullOrEmpty(_tempFolderPath)) - { - _tempFolderPath = Path.GetTempPath(); - } - return _tempFolderPath; - } + get { return _tempFolderPath; } set { - if (String.IsNullOrEmpty(value)) + if (string.IsNullOrEmpty(value)) { - _tempFolderPath = value; + // Possible breaking change since swagger-codegen 2.2.1, enforce a valid temporary path on set. + _tempFolderPath = Path.GetTempPath(); return; } // create the directory if it does not exist if (!Directory.Exists(value)) + { Directory.CreateDirectory(value); + } // check if the path contains directory separator at the end if (value[value.Length - 1] == Path.DirectorySeparatorChar) + { _tempFolderPath = value; + } else - _tempFolderPath = value + Path.DirectorySeparatorChar; + { + _tempFolderPath = value + Path.DirectorySeparatorChar; + } } } - private const string ISO8601_DATETIME_FORMAT = "o"; - - private string _dateTimeFormat = ISO8601_DATETIME_FORMAT; - /// /// Gets or sets the the date time format used when serializing in the ApiClient /// By default, it's set to ISO 8601 - "o", for others see: @@ -286,12 +330,9 @@ namespace {{packageName}}.Client /// No validation is done to ensure that the string you're providing is valid /// /// The DateTimeFormat string - public String DateTimeFormat + public virtual string DateTimeFormat { - get - { - return _dateTimeFormat; - } + get { return _dateTimeFormat; } set { if (string.IsNullOrEmpty(value)) @@ -307,6 +348,65 @@ namespace {{packageName}}.Client } } + /// + /// Gets or sets the prefix (e.g. Token) of the API key based on the authentication name. + /// + /// The prefix of the API key. + public virtual IDictionary ApiKeyPrefix + { + get { return _apiKeyPrefix; } + set + { + if (value == null) + { + throw new InvalidOperationException("ApiKeyPrefix collection may not be null."); + } + _apiKeyPrefix = value; + } + } + + /// + /// Gets or sets the API key based on the authentication name. + /// + /// The API key. + public virtual IDictionary ApiKey + { + get { return _apiKey; } + set + { + if (value == null) + { + throw new InvalidOperationException("ApiKey collection may not be null."); + } + _apiKey = value; + } + } + + #endregion Properties + + #region Methods + + /// + /// Add default header. + /// + /// Header field name. + /// Header field value. + /// + public void AddDefaultHeader(string key, string value) + { + DefaultHeader[key] = value; + } + + /// + /// Creates a new based on this instance. + /// + /// + public ApiClient CreateApiClient() + { + return new ApiClient(BasePath) { Configuration = this }; + } + + /// /// Returns a string with essential information for debugging. /// @@ -315,11 +415,8 @@ namespace {{packageName}}.Client String report = "C# SDK ({{{packageName}}}) Debug Report:\n"; {{^netStandard}} {{^supportsUWP}} - report += " OS: " + Environment.OSVersion + "\n"; - report += " .NET Framework Version: " + Assembly - .GetExecutingAssembly() - .GetReferencedAssemblies() - .Where(x => x.Name == "System.Core").First().Version.ToString() + "\n"; + report += " OS: " + System.Environment.OSVersion + "\n"; + report += " .NET Framework Version: " + System.Environment.Version + "\n"; {{/supportsUWP}} {{/netStandard}} {{#netStandard}} @@ -330,5 +427,28 @@ namespace {{packageName}}.Client return report; } + + /// + /// Add Api Key Header. + /// + /// Api Key name. + /// Api Key value. + /// + public void AddApiKey(string key, string value) + { + ApiKey[key] = value; + } + + /// + /// Sets the API key prefix. + /// + /// Api Key name. + /// Api Key value. + public void AddApiKeyPrefix(string key, string value) + { + ApiKeyPrefix[key] = value; + } + + #endregion Methods } } diff --git a/modules/swagger-codegen/src/main/resources/csharp/GlobalConfiguration.mustache b/modules/swagger-codegen/src/main/resources/csharp/GlobalConfiguration.mustache new file mode 100644 index 00000000000..b218d5cc349 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/csharp/GlobalConfiguration.mustache @@ -0,0 +1,25 @@ +{{>partial_header}} + +using System; +using System.Reflection; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; + +namespace {{packageName}}.Client +{ + /// + /// provides a compile-time extension point for globally configuring + /// API Clients. + /// + /// + /// A customized implementation via partial class may reside in another file and may + /// be excluded from automatic generation via a .swagger-codegen-ignore file. + /// + public partial class GlobalConfiguration : Configuration + { + + } +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/csharp/IReadableConfiguration.mustache b/modules/swagger-codegen/src/main/resources/csharp/IReadableConfiguration.mustache new file mode 100644 index 00000000000..ce165e0c81d --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/csharp/IReadableConfiguration.mustache @@ -0,0 +1,85 @@ +{{>partial_header}} + +using System.Collections.Generic; + +namespace {{packageName}}.Client +{ + /// + /// Represents a readable-only configuration contract. + /// + public interface IReadableConfiguration + { + /// + /// Gets the access token. + /// + /// Access token. + string AccessToken { get; } + + /// + /// Gets the API key. + /// + /// API key. + IDictionary ApiKey { get; } + + /// + /// Gets the API key prefix. + /// + /// API key prefix. + IDictionary ApiKeyPrefix { get; } + + /// + /// Gets the base path. + /// + /// Base path. + string BasePath { get; } + + /// + /// Gets the date time format. + /// + /// Date time foramt. + string DateTimeFormat { get; } + + /// + /// Gets the default header. + /// + /// Default header. + IDictionary DefaultHeader { get; } + + /// + /// Gets the temp folder path. + /// + /// Temp folder path. + string TempFolderPath { get; } + + /// + /// Gets the HTTP connection timeout (in milliseconds) + /// + /// HTTP connection timeout. + int Timeout { get; } + + /// + /// Gets the user agent. + /// + /// User agent. + string UserAgent { get; } + + /// + /// Gets the username. + /// + /// Username. + string Username { get; } + + /// + /// Gets the password. + /// + /// Password. + string Password { get; } + + /// + /// Gets the API key with prefix. + /// + /// API key identifier (authentication scheme). + /// API key with prefix. + string GetApiKeyWithPrefix(string apiKeyIdentifier); + } +} diff --git a/modules/swagger-codegen/src/main/resources/csharp/JsonSubTypesTests.mustache b/modules/swagger-codegen/src/main/resources/csharp/JsonSubTypesTests.mustache new file mode 100644 index 00000000000..9b1f66624d7 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/csharp/JsonSubTypesTests.mustache @@ -0,0 +1,125 @@ +{{>partial_header}} + +using System.Collections.Generic; +using System.Linq; +using JsonSubTypes; +using Newtonsoft.Json; +using NUnit.Framework; + +using {{packageName}}.{{apiPackage}}; +using {{packageName}}.{{modelPackage}}; +using {{packageName}}.Client; + +namespace {{packageName}}.Test.Client +{ + public class JsonSubTypesTests + { + [Test] + public void TestSimpleJsonSubTypesExample() + { + var annimal = + JsonConvert.DeserializeObject("{\"Kind\":\"Dog\",\"Breed\":\"Jack Russell Terrier\"}"); + Assert.AreEqual("Jack Russell Terrier", (annimal as Dog)?.Breed); + } + + [Test] + public void DeserializeObjectWithCustomMapping() + { + var annimal = + JsonConvert.DeserializeObject("{\"Sound\":\"Bark\",\"Breed\":\"Jack Russell Terrier\"}"); + Assert.AreEqual("Jack Russell Terrier", (annimal as Dog2)?.Breed); + } + + [Test] + public void DeserializeObjectMappingByPropertyPresence() + { + string json = + "[{\"Department\":\"Department1\",\"JobTitle\":\"JobTitle1\",\"FirstName\":\"FirstName1\",\"LastName\":\"LastName1\"}," + + "{\"Department\":\"Department1\",\"JobTitle\":\"JobTitle1\",\"FirstName\":\"FirstName1\",\"LastName\":\"LastName1\"}," + + "{\"Skill\":\"Painter\",\"FirstName\":\"FirstName1\",\"LastName\":\"LastName1\"}]"; + + + var persons = JsonConvert.DeserializeObject>(json); + Assert.AreEqual("Painter", (persons.Last() as Artist)?.Skill); + } + } + + [JsonConverter(typeof(JsonSubtypes), "Kind")] + public interface IAnimal + { + string Kind { get; } + } + + public class Dog : IAnimal + { + public Dog() + { + Kind = "Dog"; + } + + public string Kind { get; } + public string Breed { get; set; } + } + + class Cat : IAnimal + { + public Cat() + { + Kind = "Cat"; + } + + public string Kind { get; } + bool Declawed { get; set; } + } + + [JsonConverter(typeof(JsonSubtypes), "Sound")] + [JsonSubtypes.KnownSubType(typeof(Dog2), "Bark")] + [JsonSubtypes.KnownSubType(typeof(Cat2), "Meow")] + public class Animal2 + { + public virtual string Sound { get; } + public string Color { get; set; } + } + + public class Dog2 : Animal2 + { + public Dog2() + { + Sound = "Bark"; + } + + public override string Sound { get; } + public string Breed { get; set; } + } + + public class Cat2 : Animal2 + { + public Cat2() + { + Sound = "Meow"; + } + + public override string Sound { get; } + public bool Declawed { get; set; } + } + + [JsonConverter(typeof(JsonSubtypes))] + [JsonSubtypes.KnownSubTypeWithProperty(typeof(Employee), "JobTitle")] + [JsonSubtypes.KnownSubTypeWithProperty(typeof(Artist), "Skill")] + public class Person + { + public string FirstName { get; set; } + public string LastName { get; set; } + } + + public class Employee : Person + { + public string Department { get; set; } + public string JobTitle { get; set; } + } + + public class Artist : Person + { + public string Skill { get; set; } + } +} diff --git a/modules/swagger-codegen/src/main/resources/csharp/Project.mustache b/modules/swagger-codegen/src/main/resources/csharp/Project.mustache index d1271b9d515..d9fde4d8432 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/Project.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/Project.mustache @@ -72,11 +72,17 @@ ..\..\packages\Newtonsoft.Json.10.0.3\lib\{{targetFrameworkNuget}}\Newtonsoft.Json.dll {{binRelativePath}}\Newtonsoft.Json.10.0.3\lib\{{targetFrameworkNuget}}\Newtonsoft.Json.dll + + $(SolutionDir)\packages\JsonSubTypes.1.1.3\lib\{{targetFrameworkNuget}}\JsonSubTypes.dll + ..\packages\JsonSubTypes.1.1.3\lib\{{targetFrameworkNuget}}\JsonSubTypes.dll + ..\..\packages\JsonSubTypes.1.1.3\lib\{{targetFrameworkNuget}}\JsonSubTypes.dll + {{binRelativePath}}\JsonSubTypes.1.1.3\lib\{{targetFrameworkNuget}}\JsonSubTypes.dll + - $(SolutionDir)\packages\RestSharp.105.1.0\lib\{{targetFrameworkNuget}}\RestSharp.dll - ..\packages\RestSharp.105.1.0\lib\{{targetFrameworkNuget}}\RestSharp.dll - ..\..\packages\RestSharp.105.1.0\lib\{{targetFrameworkNuget}}\RestSharp.dll - {{binRelativePath}}\RestSharp.105.1.0\lib\{{targetFrameworkNuget}}\RestSharp.dll + $(SolutionDir)\packages\RestSharp.105.1.0\lib\{{#isNet40}}net4{{/isNet40}}{{^isNet40}}{{targetFrameworkNuget}}{{/isNet40}}\RestSharp.dll + ..\packages\RestSharp.105.1.0\lib\{{#isNet40}}net4{{/isNet40}}{{^isNet40}}{{targetFrameworkNuget}}{{/isNet40}}\RestSharp.dll + ..\..\packages\RestSharp.105.1.0\lib\{{#isNet40}}net4{{/isNet40}}{{^isNet40}}{{targetFrameworkNuget}}{{/isNet40}}\RestSharp.dll + {{binRelativePath}}\RestSharp.105.1.0\lib\{{#isNet40}}net4{{/isNet40}}{{^isNet40}}{{targetFrameworkNuget}}{{/isNet40}}\RestSharp.dll {{#generatePropertyChanged}} diff --git a/modules/swagger-codegen/src/main/resources/csharp/README.mustache b/modules/swagger-codegen/src/main/resources/csharp/README.mustache index 80e24376947..be9091f80ad 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/README.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/README.mustache @@ -39,7 +39,7 @@ This C# SDK is automatically generated by the [Swagger Codegen](https://github.c {{#netStandard}} - FubarCoder.RestSharp.Portable.Core >=4.0.7 - FubarCoder.RestSharp.Portable.HttpClient >=4.0.7 -- Newtonsoft.Json >=9.0.1 +- Newtonsoft.Json >=10.0.3 {{/netStandard}} {{^netStandard}} - [RestSharp](https://www.nuget.org/packages/RestSharp) - 105.1.0 or later diff --git a/modules/swagger-codegen/src/main/resources/csharp/ReadOnlyDictionary.mustache b/modules/swagger-codegen/src/main/resources/csharp/ReadOnlyDictionary.mustache new file mode 100644 index 00000000000..1299b2436be --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/csharp/ReadOnlyDictionary.mustache @@ -0,0 +1,137 @@ +{{>partial_header}} +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace {{packageName}}.Client +{ + public class ReadOnlyDictionary : IDictionary + { + private IDictionary _dictionaryImplementation; + public IEnumerator> GetEnumerator() + { + return _dictionaryImplementation.GetEnumerator(); + } + + public ReadOnlyDictionary() + { + _dictionaryImplementation = new Dictionary(); + } + + public ReadOnlyDictionary(IDictionary dictionaryImplementation) + { + if (dictionaryImplementation == null) throw new ArgumentNullException("dictionaryImplementation"); + _dictionaryImplementation = dictionaryImplementation; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable) _dictionaryImplementation).GetEnumerator(); + } + + public void Add(KeyValuePair item) + { + throw new ReadonlyOperationException("This instance is readonly."); + } + + public void Clear() + { + throw new ReadonlyOperationException("This instance is readonly."); + } + + public bool Contains(KeyValuePair item) + { + return _dictionaryImplementation.Contains(item); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + _dictionaryImplementation.CopyTo(array, arrayIndex); + } + + public bool Remove(KeyValuePair item) + { + throw new ReadonlyOperationException("This instance is readonly."); + } + + public int Count + { + get { return _dictionaryImplementation.Count; } + } + + public bool IsReadOnly + { + get { return true; } + } + + public void Add(T key, K value) + { + throw new ReadonlyOperationException("This instance is readonly."); + } + + public bool ContainsKey(T key) + { + return _dictionaryImplementation.ContainsKey(key); + } + + public bool Remove(T key) + { + throw new ReadonlyOperationException("This instance is readonly."); + } + + public bool TryGetValue(T key, out K value) + { + return _dictionaryImplementation.TryGetValue(key, out value); + } + + public K this[T key] + { + get { return _dictionaryImplementation[key]; } + set + { + throw new ReadonlyOperationException("This instance is readonly."); + + } + } + + public ICollection Keys + { + get { return _dictionaryImplementation.Keys; } + } + + public ICollection Values + { + get { return _dictionaryImplementation.Values; } + } + } + + [Serializable] + public class ReadonlyOperationException : Exception + { + // + // For guidelines regarding the creation of new exception types, see + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp + // and + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp + // + + public ReadonlyOperationException() + { + } + + public ReadonlyOperationException(string message) : base(message) + { + } + + public ReadonlyOperationException(string message, Exception inner) : base(message, inner) + { + } + + protected ReadonlyOperationException( + SerializationInfo info, + StreamingContext context) : base(info, context) + { + } + } +} diff --git a/modules/swagger-codegen/src/main/resources/csharp/Solution.mustache b/modules/swagger-codegen/src/main/resources/csharp/Solution.mustache index 183dbe21c8d..112cc3dc405 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/Solution.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/Solution.mustache @@ -7,21 +7,21 @@ EndProject {{^excludeTests}}Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "{{testPackageName}}", "src\{{testPackageName}}\{{testPackageName}}.csproj", "{19F1DEBC-DE5E-4517-8062-F000CD499087}" EndProject {{/excludeTests}}Global -GlobalSection(SolutionConfigurationPlatforms) = preSolution -Debug|Any CPU = Debug|Any CPU -Release|Any CPU = Release|Any CPU -EndGlobalSection -GlobalSection(ProjectConfigurationPlatforms) = postSolution -{{packageGuid}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU -{{packageGuid}}.Debug|Any CPU.Build.0 = Debug|Any CPU -{{packageGuid}}.Release|Any CPU.ActiveCfg = Release|Any CPU -{{packageGuid}}.Release|Any CPU.Build.0 = Release|Any CPU -{19F1DEBC-DE5E-4517-8062-F000CD499087}.Debug|Any CPU.ActiveCfg = Debug|Any CPU -{19F1DEBC-DE5E-4517-8062-F000CD499087}.Debug|Any CPU.Build.0 = Debug|Any CPU -{19F1DEBC-DE5E-4517-8062-F000CD499087}.Release|Any CPU.ActiveCfg = Release|Any CPU -{19F1DEBC-DE5E-4517-8062-F000CD499087}.Release|Any CPU.Build.0 = Release|Any CPU -EndGlobalSection -GlobalSection(SolutionProperties) = preSolution -HideSolutionNode = FALSE -EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {{packageGuid}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {{packageGuid}}.Debug|Any CPU.Build.0 = Debug|Any CPU + {{packageGuid}}.Release|Any CPU.ActiveCfg = Release|Any CPU + {{packageGuid}}.Release|Any CPU.Build.0 = Release|Any CPU + {19F1DEBC-DE5E-4517-8062-F000CD499087}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {19F1DEBC-DE5E-4517-8062-F000CD499087}.Debug|Any CPU.Build.0 = Debug|Any CPU + {19F1DEBC-DE5E-4517-8062-F000CD499087}.Release|Any CPU.ActiveCfg = Release|Any CPU + {19F1DEBC-DE5E-4517-8062-F000CD499087}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection EndGlobal \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/csharp/TestProject.mustache b/modules/swagger-codegen/src/main/resources/csharp/TestProject.mustache index ee8897d2a62..c88a8905c48 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/TestProject.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/TestProject.mustache @@ -64,11 +64,17 @@ ..\..\packages\Newtonsoft.Json.10.0.3\lib\{{targetFrameworkNuget}}\Newtonsoft.Json.dll {{binRelativePath}}\Newtonsoft.Json.10.0.3\lib\{{targetFrameworkNuget}}\Newtonsoft.Json.dll + + $(SolutionDir)\packages\JsonSubTypes.1.1.3\lib\{{targetFrameworkNuget}}\JsonSubTypes.dll + ..\packages\JsonSubTypes.1.1.3\lib\{{targetFrameworkNuget}}\JsonSubTypes.dll + ..\..\packages\JsonSubTypes.1.1.3\lib\{{targetFrameworkNuget}}\JsonSubTypes.dll + {{binRelativePath}}\JsonSubTypes.1.1.3\lib\{{targetFrameworkNuget}}\JsonSubTypes.dll + - $(SolutionDir)\packages\RestSharp.105.1.0\lib\{{targetFrameworkNuget}}\RestSharp.dll - ..\packages\RestSharp.105.1.0\lib\{{targetFrameworkNuget}}\RestSharp.dll - ..\..\packages\RestSharp.105.1.0\lib\{{targetFrameworkNuget}}\RestSharp.dll - {{binRelativePath}}\RestSharp.105.1.0\lib\{{targetFrameworkNuget}}\RestSharp.dll + $(SolutionDir)\packages\RestSharp.105.1.0\lib\{{#isNet40}}net4{{/isNet40}}{{^isNet40}}{{targetFrameworkNuget}}{{/isNet40}}\RestSharp.dll + ..\packages\RestSharp.105.1.0\lib\{{#isNet40}}net4{{/isNet40}}{{^isNet40}}{{targetFrameworkNuget}}{{/isNet40}}\RestSharp.dll + ..\..\packages\RestSharp.105.1.0\lib\{{#isNet40}}net4{{/isNet40}}{{^isNet40}}{{targetFrameworkNuget}}{{/isNet40}}\RestSharp.dll + {{binRelativePath}}\RestSharp.105.1.0\lib\{{#isNet40}}net4{{/isNet40}}{{^isNet40}}{{targetFrameworkNuget}}{{/isNet40}}\RestSharp.dll $(SolutionDir)\packages\NUnit.2.6.4\lib\nunit.framework.dll diff --git a/modules/swagger-codegen/src/main/resources/csharp/api.mustache b/modules/swagger-codegen/src/main/resources/csharp/api.mustache index a16b99c9f68..d97abd81589 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/api.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/api.mustache @@ -88,15 +88,9 @@ namespace {{packageName}}.{{apiPackage}} /// public {{classname}}(String basePath) { - this.Configuration = new Configuration(new ApiClient(basePath)); + this.Configuration = new Configuration { BasePath = basePath }; ExceptionFactory = {{packageName}}.Client.Configuration.DefaultExceptionFactory; - - // ensure API client has configuration ready - if (Configuration.ApiClient.Configuration == null) - { - this.Configuration.ApiClient.Configuration = this.Configuration; - } } /// @@ -113,12 +107,6 @@ namespace {{packageName}}.{{apiPackage}} this.Configuration = configuration; ExceptionFactory = {{packageName}}.Client.Configuration.DefaultExceptionFactory; - - // ensure API client has configuration ready - if (Configuration.ApiClient.Configuration == null) - { - this.Configuration.ApiClient.Configuration = this.Configuration; - } } /// @@ -167,9 +155,9 @@ namespace {{packageName}}.{{apiPackage}} /// /// Dictionary of HTTP header [Obsolete("DefaultHeader is deprecated, please use Configuration.DefaultHeader instead.")] - public Dictionary DefaultHeader() + public IDictionary DefaultHeader() { - return this.Configuration.DefaultHeader; + return new {{^net35}}ReadOnly{{/net35}}Dictionary(this.Configuration.DefaultHeader); } /// @@ -215,7 +203,7 @@ namespace {{packageName}}.{{apiPackage}} var localVarPath = "{{#netStandard}}.{{/netStandard}}{{{path}}}"; var localVarPathParams = new Dictionary(); - var localVarQueryParams = new Dictionary(); + var localVarQueryParams = new List>(); var localVarHeaderParams = new Dictionary(Configuration.DefaultHeader); var localVarFormParams = new Dictionary(); var localVarFileParams = new Dictionary(); @@ -243,7 +231,7 @@ namespace {{packageName}}.{{apiPackage}} if ({{paramName}} != null) localVarPathParams.Add("{{baseName}}", Configuration.ApiClient.ParameterToString({{paramName}})); // path parameter {{/pathParams}} {{#queryParams}} - if ({{paramName}} != null) localVarQueryParams.Add("{{baseName}}", Configuration.ApiClient.ParameterToString({{paramName}})); // query parameter + if ({{paramName}} != null) localVarQueryParams.AddRange(Configuration.ApiClient.ParameterToKeyValuePairs("{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}", "{{baseName}}", {{paramName}})); // query parameter {{/queryParams}} {{#headerParams}} if ({{paramName}} != null) localVarHeaderParams.Add("{{baseName}}", Configuration.ApiClient.ParameterToString({{paramName}})); // header parameter @@ -274,7 +262,7 @@ namespace {{packageName}}.{{apiPackage}} {{#isKeyInQuery}} if (!String.IsNullOrEmpty(Configuration.GetApiKeyWithPrefix("{{keyParamName}}"))) { - localVarQueryParams["{{keyParamName}}"] = Configuration.GetApiKeyWithPrefix("{{keyParamName}}"); + localVarQueryParams.AddRange(Configuration.ApiClient.ParameterToKeyValuePairs("", "{{keyParamName}}", Configuration.GetApiKeyWithPrefix("{{keyParamName}}"))); } {{/isKeyInQuery}} {{/isApiKey}} @@ -290,7 +278,8 @@ namespace {{packageName}}.{{apiPackage}} if (!String.IsNullOrEmpty(Configuration.AccessToken)) { localVarHeaderParams["Authorization"] = "Bearer " + Configuration.AccessToken; - }{{/isOAuth}} + } + {{/isOAuth}} {{/authMethods}} // make the HTTP request @@ -350,7 +339,7 @@ namespace {{packageName}}.{{apiPackage}} var localVarPath = "{{#netStandard}}.{{/netStandard}}{{{path}}}"; var localVarPathParams = new Dictionary(); - var localVarQueryParams = new Dictionary(); + var localVarQueryParams = new List>(); var localVarHeaderParams = new Dictionary(Configuration.DefaultHeader); var localVarFormParams = new Dictionary(); var localVarFileParams = new Dictionary(); @@ -378,7 +367,7 @@ namespace {{packageName}}.{{apiPackage}} if ({{paramName}} != null) localVarPathParams.Add("{{baseName}}", Configuration.ApiClient.ParameterToString({{paramName}})); // path parameter {{/pathParams}} {{#queryParams}} - if ({{paramName}} != null) localVarQueryParams.Add("{{baseName}}", Configuration.ApiClient.ParameterToString({{paramName}})); // query parameter + if ({{paramName}} != null) localVarQueryParams.AddRange(Configuration.ApiClient.ParameterToKeyValuePairs("{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}", "{{baseName}}", {{paramName}})); // query parameter {{/queryParams}} {{#headerParams}} if ({{paramName}} != null) localVarHeaderParams.Add("{{baseName}}", Configuration.ApiClient.ParameterToString({{paramName}})); // header parameter @@ -409,7 +398,7 @@ namespace {{packageName}}.{{apiPackage}} {{#isKeyInQuery}} if (!String.IsNullOrEmpty(Configuration.GetApiKeyWithPrefix("{{keyParamName}}"))) { - localVarQueryParams["{{keyParamName}}"] = Configuration.GetApiKeyWithPrefix("{{keyParamName}}"); + localVarQueryParams.AddRange(Configuration.ApiClient.ParameterToKeyValuePairs("", "{{keyParamName}}", Configuration.GetApiKeyWithPrefix("{{keyParamName}}"))); } {{/isKeyInQuery}} {{/isApiKey}} diff --git a/modules/swagger-codegen/src/main/resources/csharp/api_doc.mustache b/modules/swagger-codegen/src/main/resources/csharp/api_doc.mustache index ff3cf4fc60a..60bde9985e6 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/api_doc.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/api_doc.mustache @@ -41,9 +41,9 @@ namespace Example {{/isBasic}} {{#isApiKey}} // Configure API key authorization: {{{name}}} - Configuration.Default.ApiKey.Add("{{{keyParamName}}}", "YOUR_API_KEY"); + Configuration.Default.AddApiKey("{{{keyParamName}}}", "YOUR_API_KEY"); // Uncomment below to setup prefix (e.g. Bearer) for API key, if needed - // Configuration.Default.ApiKeyPrefix.Add("{{{keyParamName}}}", "Bearer"); + // Configuration.Default.AddApiKeyPrefix("{{{keyParamName}}}", "Bearer"); {{/isApiKey}} {{#isOAuth}} // Configure OAuth2 access token for authorization: {{{name}}} diff --git a/modules/swagger-codegen/src/main/resources/csharp/compile-mono.sh.mustache b/modules/swagger-codegen/src/main/resources/csharp/compile-mono.sh.mustache index bc5905954eb..7f7ab50e5cc 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/compile-mono.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/compile-mono.sh.mustache @@ -4,19 +4,48 @@ # frameworkVersion={{targetFrameworkNuget}} -netfx=${frameworkVersion#net} + +# sdk must match installed framworks under PREFIX/lib/mono/[value] +sdk={{x-mcs-sdk}} + +# langversion refers to C# language features. see man mcs for details. +langversion=${sdk} +nuget_cmd=nuget + +# Match against our known SDK possibilities +case "${sdk}" in + 4) + langversion=4 + ;; + 4.5*) + langversion=5 + ;; + 4.6*) + langversion=6 + ;; + 4.7*) + langversion=7 # ignoring 7.1 for now. + ;; + *) + langversion=6 + ;; +esac echo "[INFO] Target framework: ${frameworkVersion}" -echo "[INFO] Download nuget and packages" -wget -nc https://dist.nuget.org/win-x86-commandline/latest/nuget.exe; +if [ ! type nuget &>/dev/null ]; then + echo "[INFO] Download nuget and packages" + wget -nc https://dist.nuget.org/win-x86-commandline/latest/nuget.exe; + nuget_cmd="mono nuget" +fi + mozroots --import --sync -mono nuget.exe install src/{{packageName}}/packages.config -o packages; +${nuget_cmd} install src/{{packageName}}/packages.config -o packages; echo "[INFO] Copy DLLs to the 'bin' folder" mkdir -p bin; cp packages/Newtonsoft.Json.10.0.3/lib/{{targetFrameworkNuget}}/Newtonsoft.Json.dll bin/Newtonsoft.Json.dll; -cp packages/RestSharp.105.1.0/lib/{{targetFrameworkNuget}}/RestSharp.dll bin/RestSharp.dll; +cp packages/RestSharp.105.1.0/lib/{{#isNet40}}net4{{/isNet40}}{{^isNet40}}{{targetFrameworkNuget}}{{/isNet40}}/RestSharp.dll bin/RestSharp.dll; {{#generatePropertyChanged}} cp packages/Fody.1.29.4/Fody.dll bin/Fody.dll cp packages/PropertyChanged.Fody.1.51.3/PropertyChanged.Fody.dll bin/PropertyChanged.Fody.dll @@ -24,7 +53,7 @@ cp packages/PropertyChanged.Fody.1.51.3/Lib/dotnet/PropertyChanged.dll bin/Prope {{/generatePropertyChanged}} echo "[INFO] Run 'mcs' to build bin/{{{packageName}}}.dll" -mcs -sdk:${netfx} -r:bin/Newtonsoft.Json.dll,\ +mcs -langversion:${langversion} -sdk:${sdk} -r:bin/Newtonsoft.Json.dll,\ {{#generatePropertyChanged}} bin/Fody.dll,\ bin/PropertyChanged.Fody.dll,\ diff --git a/modules/swagger-codegen/src/main/resources/csharp/compile.mustache b/modules/swagger-codegen/src/main/resources/csharp/compile.mustache index 2ef0e06e902..dab5acb5869 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/compile.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/compile.mustache @@ -15,8 +15,8 @@ if not exist ".\nuget.exe" powershell -Command "(new-object System.Net.WebClient if not exist ".\bin" mkdir bin -copy packages\Newtonsoft.Json.10.0.3\lib\{{targetFrameworkNuget}}\Newtonsoft.Json.dll bin\Newtonsoft.Json.dll -copy packages\RestSharp.105.1.0\lib\{{targetFrameworkNuget}}\RestSharp.dll bin\RestSharp.dll +copy packages\Newtonsoft.Json.{{#isNet40}}4.5.11{{/isNet40}}{{^isNet40}}10.0.3{{/isNet40}}\lib\{{targetFrameworkNuget}}\Newtonsoft.Json.dll bin\Newtonsoft.Json.dll +copy packages\RestSharp.105.1.0\lib\{{#isNet40}}net4{{/isNet40}}{{^isNet40}}{{targetFrameworkNuget}}{{/isNet40}}\RestSharp.dll bin\RestSharp.dll {{#generatePropertyChanged}} copy packages\Fody.1.29.4\Fody.dll bin\Fody.dll copy packages\PropertyChanged.Fody.1.51.3\PropertyChanged.Fody.dll bin\PropertyChanged.Fody.dll diff --git a/modules/swagger-codegen/src/main/resources/csharp/enumClass.mustache b/modules/swagger-codegen/src/main/resources/csharp/enumClass.mustache index d8d5d4185dd..015e7142dbc 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/enumClass.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/enumClass.mustache @@ -12,6 +12,6 @@ /// Enum {{name}} for {{{value}}} /// [EnumMember(Value = {{#isLong}}"{{/isLong}}{{#isInteger}}"{{/isInteger}}{{#isFloat}}"{{/isFloat}}{{#isDouble}}"{{/isDouble}}{{{value}}}{{#isLong}}"{{/isLong}}{{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isFloat}}"{{/isFloat}})] - {{name}}{{#isLong}} = {{{value}}}{{/isLong}}{{#isInteger}} = {{{value}}}{{/isInteger}}{{^-last}}, + {{name}}{{#isLong}} = {{{value}}}{{/isLong}}{{#isInteger}} = {{{value}}}{{/isInteger}}{{^isInteger}} = {{-index}}{{/isInteger}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}} } diff --git a/modules/swagger-codegen/src/main/resources/csharp/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/csharp/git_push.sh.mustache index a9b0f28edfb..f65b794638f 100755 --- a/modules/swagger-codegen/src/main/resources/csharp/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/csharp/gitignore.mustache b/modules/swagger-codegen/src/main/resources/csharp/gitignore.mustache index d3f4f7b6f55..17302c93bf0 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/gitignore.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/gitignore.mustache @@ -6,6 +6,7 @@ *.suo *.user *.sln.docstates +./nuget # Build results diff --git a/modules/swagger-codegen/src/main/resources/csharp/model.mustache b/modules/swagger-codegen/src/main/resources/csharp/model.mustache index ac36ce6763d..9f49989303c 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/model.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/model.mustache @@ -12,6 +12,13 @@ using System.Collections.ObjectModel; using System.Runtime.Serialization; using Newtonsoft.Json; using Newtonsoft.Json.Converters; +{{#models}} +{{#model}} +{{#discriminator}} +using JsonSubTypes; +{{/discriminator}} +{{/model}} +{{/models}} {{^netStandard}} {{#generatePropertyChanged}} using PropertyChanged; diff --git a/modules/swagger-codegen/src/main/resources/csharp/modelEnum.mustache b/modules/swagger-codegen/src/main/resources/csharp/modelEnum.mustache index 64e50883f8d..23f575bb06f 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/modelEnum.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/modelEnum.mustache @@ -12,6 +12,6 @@ /// Enum {{name}} for {{{value}}} /// [EnumMember(Value = {{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{{value}}}{{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}})] - {{name}}{{#isInteger}} = {{{value}}}{{/isInteger}}{{^-last}}, + {{name}}{{#isInteger}} = {{{value}}}{{/isInteger}}{{^isInteger}} = {{-index}}{{/isInteger}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}} } diff --git a/modules/swagger-codegen/src/main/resources/csharp/modelGeneric.mustache b/modules/swagger-codegen/src/main/resources/csharp/modelGeneric.mustache index a05b84c3ee0..74d2bddc1e8 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/modelGeneric.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/modelGeneric.mustache @@ -2,22 +2,28 @@ /// {{#description}}{{.}}{{/description}}{{^description}}{{classname}}{{/description}} /// [DataContract] + {{#discriminator}} + [JsonConverter(typeof(JsonSubtypes), "{{discriminator}}")]{{#children}} + [JsonSubtypes.KnownSubType(typeof({{classname}}), "{{^vendorExtensions.x-discriminator-value}}{{name}}{{/vendorExtensions.x-discriminator-value}}{{#vendorExtensions.x-discriminator-value}}{{{vendorExtensions.x-discriminator-value}}}{{/vendorExtensions.x-discriminator-value}}")]{{/children}} + {{/discriminator}} {{#generatePropertyChanged}} [ImplementPropertyChanged] {{/generatePropertyChanged}} {{>visibility}} partial class {{classname}} : {{#parent}}{{{parent}}}, {{/parent}} IEquatable<{{classname}}>{{^netStandard}}{{#validatable}}, IValidatableObject{{/validatable}}{{/netStandard}} { {{#vars}} - {{#isEnum}} -{{>modelInnerEnum}} - {{/isEnum}} {{#items.isEnum}} {{#items}} + {{^complexType}} {{>modelInnerEnum}} + {{/complexType}} {{/items}} {{/items.isEnum}} - {{/vars}} - {{#vars}} + {{#isEnum}} + {{^complexType}} +{{>modelInnerEnum}} + {{/complexType}} + {{/isEnum}} {{#isEnum}} /// /// {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{description}}{{/description}} @@ -26,7 +32,7 @@ /// {{description}} {{/description}} [DataMember(Name="{{baseName}}", EmitDefaultValue={{emitDefaultValue}})] - public {{{datatypeWithEnum}}}{{#isEnum}}{{^isContainer}}?{{/isContainer}}{{/isEnum}} {{name}} { get; set; } + public {{#complexType}}{{{complexType}}}{{/complexType}}{{^complexType}}{{{datatypeWithEnum}}}{{/complexType}}{{^isContainer}}{{^required}}?{{/required}}{{/isContainer}} {{name}} { get; set; } {{/isEnum}} {{/vars}} {{#hasRequired}} @@ -49,9 +55,10 @@ {{#hasOnlyReadOnly}} [JsonConstructorAttribute] {{/hasOnlyReadOnly}} - public {{classname}}({{#readWriteVars}}{{{datatypeWithEnum}}}{{#isEnum}}{{^isContainer}}?{{/isContainer}}{{/isEnum}} {{name}} = {{#defaultValue}}{{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}default({{{datatypeWithEnum}}}{{#isEnum}}{{^isContainer}}?{{/isContainer}}{{/isEnum}}){{/defaultValue}}{{^-last}}, {{/-last}}{{/readWriteVars}}) + public {{classname}}({{#readWriteVars}}{{{datatypeWithEnum}}}{{#isEnum}}{{^isContainer}}{{^required}}?{{/required}}{{/isContainer}}{{/isEnum}} {{name}} = {{#defaultValue}}{{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}default({{{datatypeWithEnum}}}{{#isEnum}}{{^isContainer}}{{^required}}?{{/required}}{{/isContainer}}{{/isEnum}}){{/defaultValue}}{{^-last}}, {{/-last}}{{/readWriteVars}}){{#parent}} : base({{#parentVars}}{{name}}{{#hasMore}}, {{/hasMore}}{{/parentVars}}){{/parent}} { {{#vars}} + {{^isInherited}} {{^isReadOnly}} {{#required}} // to ensure "{{name}}" is required (not null) @@ -65,8 +72,10 @@ } {{/required}} {{/isReadOnly}} + {{/isInherited}} {{/vars}} {{#vars}} + {{^isInherited}} {{^isReadOnly}} {{^required}} {{#defaultValue}}// use default value if no "{{name}}" provided @@ -84,10 +93,12 @@ this.{{name}} = {{name}}; {{/defaultValue}} {{/required}} {{/isReadOnly}} + {{/isInherited}} {{/vars}} } {{#vars}} + {{^isInherited}} {{^isEnum}} /// /// {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{description}}{{/description}} @@ -97,6 +108,7 @@ this.{{name}} = {{name}}; [JsonConverter(typeof(SwaggerDateConverter))]{{/isDate}} public {{{datatype}}} {{name}} { get; {{#isReadOnly}}private {{/isReadOnly}}set; } {{/isEnum}} + {{/isInherited}} {{/vars}} /// @@ -107,6 +119,9 @@ this.{{name}} = {{name}}; { var sb = new StringBuilder(); sb.Append("class {{classname}} {\n"); + {{#parent}} + sb.Append(" ").Append(base.ToString().Replace("\n", "\n ")).Append("\n"); + {{/parent}} {{#vars}} sb.Append(" {{name}}: ").Append({{name}}).Append("\n"); {{/vars}} @@ -118,7 +133,7 @@ this.{{name}} = {{name}}; /// Returns the JSON string presentation of the object /// /// JSON string presentation of the object - public {{#parent}} new {{/parent}}string ToJson() + public {{#parent}}{{^isArrayModel}}override {{/isArrayModel}}{{/parent}}{{^parent}}{{#discriminator}}virtual {{/discriminator}}{{/parent}}string ToJson() { return JsonConvert.SerializeObject(this, Formatting.Indented); } @@ -126,36 +141,34 @@ this.{{name}} = {{name}}; /// /// Returns true if objects are equal /// - /// Object to be compared + /// Object to be compared /// Boolean - public override bool Equals(object obj) + public override bool Equals(object input) { - // credit: http://stackoverflow.com/a/10454552/677735 - return this.Equals(obj as {{classname}}); + return this.Equals(input as {{classname}}); } /// /// Returns true if {{classname}} instances are equal /// - /// Instance of {{classname}} to be compared + /// Instance of {{classname}} to be compared /// Boolean - public bool Equals({{classname}} other) + public bool Equals({{classname}} input) { - // credit: http://stackoverflow.com/a/10454552/677735 - if (other == null) + if (input == null) return false; - return {{#vars}}{{#isNotContainer}} + return {{#vars}}{{#parent}}base.Equals(input) && {{/parent}}{{#isNotContainer}} ( - this.{{name}} == other.{{name}} || - this.{{name}} != null && - this.{{name}}.Equals(other.{{name}}) + this.{{name}} == input.{{name}} || + (this.{{name}} != null && + this.{{name}}.Equals(input.{{name}})) ){{#hasMore}} && {{/hasMore}}{{/isNotContainer}}{{^isNotContainer}} ( - this.{{name}} == other.{{name}} || + this.{{name}} == input.{{name}} || this.{{name}} != null && - this.{{name}}.SequenceEqual(other.{{name}}) - ){{#hasMore}} && {{/hasMore}}{{/isNotContainer}}{{/vars}}{{^vars}}false{{/vars}}; + this.{{name}}.SequenceEqual(input.{{name}}) + ){{#hasMore}} && {{/hasMore}}{{/isNotContainer}}{{/vars}}{{^vars}}{{#parent}}base.Equals(input){{/parent}}{{^parent}}false{{/parent}}{{/vars}}; } /// @@ -164,16 +177,19 @@ this.{{name}} = {{name}}; /// Hash code public override int GetHashCode() { - // credit: http://stackoverflow.com/a/263416/677735 unchecked // Overflow is fine, just wrap { - int hash = 41; - // Suitable nullity checks etc, of course :) + {{#parent}} + int hashCode = base.GetHashCode(); + {{/parent}} + {{^parent}} + int hashCode = 41; + {{/parent}} {{#vars}} if (this.{{name}} != null) - hash = hash * 59 + this.{{name}}.GetHashCode(); + hashCode = hashCode * 59 + this.{{name}}.GetHashCode(); {{/vars}} - return hash; + return hashCode; } } {{^netStandard}} @@ -201,6 +217,26 @@ this.{{name}} = {{name}}; {{/generatePropertyChanged}} {{#validatable}} +{{#discriminator}} + /// + /// To validate all properties of the instance + /// + /// Validation context + /// Validation Result + IEnumerable IValidatableObject.Validate(ValidationContext validationContext) + { + return this.BaseValidate(validationContext); + } + + /// + /// To validate all properties of the instance + /// + /// Validation context + /// Validation Result + protected IEnumerable BaseValidate(ValidationContext validationContext) + { +{{/discriminator}} +{{^discriminator}} /// /// To validate all properties of the instance /// @@ -208,6 +244,12 @@ this.{{name}} = {{name}}; /// Validation Result IEnumerable IValidatableObject.Validate(ValidationContext validationContext) { +{{/discriminator}} + {{#parent}} + {{^isArrayModel}} + foreach(var x in BaseValidate(validationContext)) yield return x; + {{/isArrayModel}} + {{/parent}} {{#vars}} {{#hasValidation}} {{#maxLength}} diff --git a/modules/swagger-codegen/src/main/resources/csharp/modelInnerEnum.mustache b/modules/swagger-codegen/src/main/resources/csharp/modelInnerEnum.mustache index 7af083a925f..674ab033d01 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/modelInnerEnum.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/modelInnerEnum.mustache @@ -13,7 +13,7 @@ /// Enum {{name}} for {{{value}}} /// [EnumMember(Value = {{#isLong}}"{{/isLong}}{{#isInteger}}"{{/isInteger}}{{#isFloat}}"{{/isFloat}}{{#isDouble}}"{{/isDouble}}{{{value}}}{{#isLong}}"{{/isLong}}{{#isInteger}}"{{/isInteger}}{{#isDouble}}"{{/isDouble}}{{#isFloat}}"{{/isFloat}})] - {{name}}{{#isLong}} = {{{value}}}{{/isLong}}{{#isInteger}} = {{{value}}}{{/isInteger}}{{^-last}}, + {{name}}{{#isLong}} = {{{value}}}{{/isLong}}{{#isInteger}} = {{{value}}}{{/isInteger}}{{^isInteger}} = {{-index}}{{/isInteger}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}} } {{/isContainer}} diff --git a/modules/swagger-codegen/src/main/resources/csharp/model_doc.mustache b/modules/swagger-codegen/src/main/resources/csharp/model_doc.mustache index 112d4725495..0b62dadc6da 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/model_doc.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/model_doc.mustache @@ -5,6 +5,11 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- +{{#parent}} +{{#parentVars}} +**{{name}}** | {{#isPrimitiveType}}**{{datatype}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{datatype}}**]({{complexType}}.md){{/isPrimitiveType}} | {{description}} | {{^required}}[optional] {{/required}}{{#readOnly}}[readonly] {{/readOnly}}{{#defaultValue}}[default to {{{.}}}]{{/defaultValue}} +{{/parentVars}} +{{/parent}} {{#vars}}**{{name}}** | {{#isPrimitiveType}}**{{datatype}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{datatype}}**]({{complexType}}.md){{/isPrimitiveType}} | {{description}} | {{^required}}[optional] {{/required}}{{#readOnly}}[readonly] {{/readOnly}}{{#defaultValue}}[default to {{{.}}}]{{/defaultValue}} {{/vars}} diff --git a/modules/swagger-codegen/src/main/resources/csharp/model_test.mustache b/modules/swagger-codegen/src/main/resources/csharp/model_test.mustache index f059706af14..3a433b5f301 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/model_test.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/model_test.mustache @@ -10,6 +10,7 @@ using {{packageName}}.{{apiPackage}}; using {{packageName}}.{{modelPackage}}; using {{packageName}}.Client; using System.Reflection; +using Newtonsoft.Json; {{#models}} {{#model}} @@ -57,6 +58,20 @@ namespace {{packageName}}.Test //Assert.IsInstanceOfType<{{classname}}> (instance, "variable 'instance' is a {{classname}}"); } + {{#discriminator}} + {{#children}} + /// + /// Test deserialize a {{classname}} from type {{parent}} + /// + [Test] + public void {{classname}}DeserializeFrom{{parent}}Test() + { + // TODO uncomment below to test deserialize a {{classname}} from type {{parent}} + //Assert.IsInstanceOf<{{parent}}>(JsonConvert.DeserializeObject<{{parent}}>(new {{classname}}().ToJson())); + } + {{/children}} + {{/discriminator}} + {{#vars}} /// /// Test the property '{{name}}' diff --git a/modules/swagger-codegen/src/main/resources/csharp/netcore_project.mustache b/modules/swagger-codegen/src/main/resources/csharp/netcore_project.mustache index 85b6fe000de..9fa3461bade 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/netcore_project.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/netcore_project.mustache @@ -25,7 +25,8 @@ {{^netStandard}} {{/netStandard}} - + + {{^netStandard}} diff --git a/modules/swagger-codegen/src/main/resources/csharp/netcore_testproject.mustache b/modules/swagger-codegen/src/main/resources/csharp/netcore_testproject.mustache index c365a217ffa..33d4a61b943 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/netcore_testproject.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/netcore_testproject.mustache @@ -24,7 +24,8 @@ {{^netStandard}} {{/netStandard}} - + + {{^netStandard}} diff --git a/modules/swagger-codegen/src/main/resources/csharp/packages.config.mustache b/modules/swagger-codegen/src/main/resources/csharp/packages.config.mustache index 10218bf15c5..9f03903dce6 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/packages.config.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/packages.config.mustache @@ -1,7 +1,8 @@ - + + {{#generatePropertyChanged}} diff --git a/modules/swagger-codegen/src/main/resources/csharp/packages_test.config.mustache b/modules/swagger-codegen/src/main/resources/csharp/packages_test.config.mustache index 1403975d651..b401079b1d6 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/packages_test.config.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/packages_test.config.mustache @@ -1,6 +1,7 @@ - + + diff --git a/modules/swagger-codegen/src/main/resources/csharp/project.json.mustache b/modules/swagger-codegen/src/main/resources/csharp/project.json.mustache index 0107fc3a621..dea5442ac02 100644 --- a/modules/swagger-codegen/src/main/resources/csharp/project.json.mustache +++ b/modules/swagger-codegen/src/main/resources/csharp/project.json.mustache @@ -3,7 +3,8 @@ "dependencies": { "FubarCoder.RestSharp.Portable.Core": "4.0.7", "FubarCoder.RestSharp.Portable.HttpClient": "4.0.7", - "Newtonsoft.Json": "9.0.1" + "Newtonsoft.Json": "10.0.3", + "JsonSubTypes": "1.1.3" }, "frameworks": { "{{targetFrameworkNuget}}": {} diff --git a/modules/swagger-codegen/src/main/resources/dart/api_client.mustache b/modules/swagger-codegen/src/main/resources/dart/api_client.mustache index 86e698eb836..f35db7dbaed 100644 --- a/modules/swagger-codegen/src/main/resources/dart/api_client.mustache +++ b/modules/swagger-codegen/src/main/resources/dart/api_client.mustache @@ -16,7 +16,14 @@ class ApiClient { Map _authentications = {}; final dson = new Dartson.JSON() - ..addTransformer(new DateTimeParser(), DateTime); + {{#models}} + {{#model}} + {{#isEnum}} + ..addTransformer(new {{classname}}TypeTransformer(), {{classname}}) + {{/isEnum}} + {{/model}} + {{/models}} + ..addTransformer(new DateTimeParser(), DateTime); final _RegList = new RegExp(r'^List<(.*)>$'); final _RegMap = new RegExp(r'^Map$'); @@ -46,7 +53,16 @@ class ApiClient { {{#models}} {{#model}} case '{{classname}}': + {{#isEnum}} + // Enclose the value in a list so that Dartson can use a transformer + // to decode it. + final listValue = [value]; + final List listResult = dson.map(listValue, []); + return listResult[0]; + {{/isEnum}} + {{^isEnum}} return dson.map(value, new {{classname}}()); + {{/isEnum}} {{/model}} {{/models}} default: @@ -116,7 +132,7 @@ class ApiClient { headerParams['Content-Type'] = contentType; if(body is MultipartRequest) { - var request = new MultipartRequest(method, Uri.parse(url)); + var request = new MultipartRequest(method, Uri.parse(url)); request.fields.addAll(body.fields); request.files.addAll(body.files); request.headers.addAll(body.headers); @@ -141,7 +157,7 @@ class ApiClient { } /// Update query and header parameters based on authentication settings. - /// @param authNames The authentications to apply + /// @param authNames The authentications to apply void _updateParamsForAuth(List authNames, List queryParams, Map headerParams) { authNames.forEach((authName) { Authentication auth = _authentications[authName]; diff --git a/modules/swagger-codegen/src/main/resources/dart/api_helper.mustache b/modules/swagger-codegen/src/main/resources/dart/api_helper.mustache index 15798ef3466..48de5ab24b0 100644 --- a/modules/swagger-codegen/src/main/resources/dart/api_helper.mustache +++ b/modules/swagger-codegen/src/main/resources/dart/api_helper.mustache @@ -38,7 +38,15 @@ String _parameterToString(dynamic value) { return ''; } else if (value is DateTime) { return value.toUtc().toIso8601String(); + {{#models}} + {{#model}} + {{#isEnum}} + } else if (value is {{classname}}) { + return new {{classname}}TypeTransformer().encode(value).toString(); + {{/isEnum}} + {{/model}} + {{/models}} } else { return value.toString(); } -} \ No newline at end of file +} diff --git a/modules/swagger-codegen/src/main/resources/dart/apilib.mustache b/modules/swagger-codegen/src/main/resources/dart/apilib.mustache index 8f131054f25..8782ce2236d 100644 --- a/modules/swagger-codegen/src/main/resources/dart/apilib.mustache +++ b/modules/swagger-codegen/src/main/resources/dart/apilib.mustache @@ -6,6 +6,7 @@ import 'package:http/browser_client.dart';{{/browserClient}} import 'package:http/http.dart'; import 'package:dartson/dartson.dart'; import 'package:dartson/transformers/date_time.dart'; +import 'package:dartson/type_transformer.dart'; part 'api_client.dart'; part 'api_helper.dart'; @@ -21,4 +22,3 @@ part 'auth/http_basic_auth.dart'; {{/model}}{{/models}} ApiClient defaultApiClient = new ApiClient(); - diff --git a/modules/swagger-codegen/src/main/resources/dart/class.mustache b/modules/swagger-codegen/src/main/resources/dart/class.mustache new file mode 100644 index 00000000000..d92258c484f --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/dart/class.mustache @@ -0,0 +1,14 @@ +@Entity() +class {{classname}} { + {{#vars}}{{#description}}/* {{{description}}} */{{/description}} + @Property(name: '{{baseName}}') + {{{datatype}}} {{name}} = {{{defaultValue}}}; + {{#allowableValues}}{{#min}} // range from {{min}} to {{max}}{{/min}}//{{^min}}enum {{name}}Enum { {{#values}} {{.}}, {{/values}} };{{/min}}{{/allowableValues}} + {{/vars}} + {{classname}}(); + + @override + String toString() { + return '{{classname}}[{{#vars}}{{name}}=${{name}}, {{/vars}}]'; + } +} diff --git a/modules/swagger-codegen/src/main/resources/dart/enum.mustache b/modules/swagger-codegen/src/main/resources/dart/enum.mustache new file mode 100644 index 00000000000..debb73bbcec --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/dart/enum.mustache @@ -0,0 +1,36 @@ +@Entity() +class {{classname}} { + /// The underlying value of this enum member. + final {{dataType}} value; + + const {{classname}}._internal(this.value); + + {{#allowableValues}} + {{#enumVars}} + {{#description}} + /// {{description}} + {{/description}} + static const {{classname}} {{name}} = const {{classname}}._internal({{value}}); + {{/enumVars}} + {{/allowableValues}} +} + +class {{classname}}TypeTransformer extends TypeTransformer<{{classname}}> { + + @override + dynamic encode({{classname}} data) { + return data.value; + } + + @override + {{classname}} decode(dynamic data) { + switch (data) { + {{#allowableValues}} + {{#enumVars}} + case {{value}}: return {{classname}}.{{name}}; + {{/enumVars}} + {{/allowableValues}} + default: throw('Unknown enum value to decode: $data'); + } + } +} diff --git a/modules/swagger-codegen/src/main/resources/dart/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/dart/git_push.sh.mustache index e153ce23ecf..a2d75234837 100755 --- a/modules/swagger-codegen/src/main/resources/dart/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/dart/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/dart/model.mustache b/modules/swagger-codegen/src/main/resources/dart/model.mustache index 5fe107c7849..1fa14e179a7 100644 --- a/modules/swagger-codegen/src/main/resources/dart/model.mustache +++ b/modules/swagger-codegen/src/main/resources/dart/model.mustache @@ -1,19 +1,7 @@ part of {{pubName}}.api; -{{#models}}{{#model}} -@Entity() -class {{classname}} { - {{#vars}}{{#description}}/* {{{description}}} */{{/description}} - @Property(name: '{{baseName}}') - {{{datatype}}} {{name}} = {{{defaultValue}}}; - {{#allowableValues}}{{#min}} // range from {{min}} to {{max}}{{/min}}//{{^min}}enum {{name}}Enum { {{#values}} {{.}}, {{/values}} };{{/min}}{{/allowableValues}} - {{/vars}} - {{classname}}(); - - @override - String toString() { - return '{{classname}}[{{#vars}}{{name}}=${{name}}, {{/vars}}]'; - } - -} -{{/model}}{{/models}} +{{#models}} +{{#model}} +{{#isEnum}}{{>enum}}{{/isEnum}}{{^isEnum}}{{>class}}{{/isEnum}} +{{/model}} +{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/elixir/README.md.mustache b/modules/swagger-codegen/src/main/resources/elixir/README.md.mustache index 65c56e0ca61..fbf6231983a 100644 --- a/modules/swagger-codegen/src/main/resources/elixir/README.md.mustache +++ b/modules/swagger-codegen/src/main/resources/elixir/README.md.mustache @@ -1,18 +1,18 @@ -# {{#modulized}}{{appName}}{{/modulized}} +# {{moduleName}} -**TODO: Add description** +{{appDescription}} ## Installation If [available in Hex](https://hex.pm/docs/publish), the package can be installed -by adding `{{#underscored}}{{appName}}{{/underscored}}` to your list of dependencies in `mix.exs`: +by adding `{{#underscored}}{{packageName}}{{/underscored}}` to your list of dependencies in `mix.exs`: ```elixir def deps do - [{:{{#underscored}}{{appName}}{{/underscored}}, "~> 0.1.0"}] + [{:{{#underscored}}{{packageName}}{{/underscored}}, "~> 0.1.0"}] end ``` Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) and published on [HexDocs](https://hexdocs.pm). Once published, the docs can -be found at [https://hexdocs.pm/{{#underscored}}{{appName}}{{/underscored}}](https://hexdocs.pm/{{#underscored}}{{appName}}{{/underscored}}). +be found at [https://hexdocs.pm/{{#underscored}}{{packageName}}{{/underscored}}](https://hexdocs.pm/{{#underscored}}{{packageName}}{{/underscored}}). diff --git a/modules/swagger-codegen/src/main/resources/elixir/api.mustache b/modules/swagger-codegen/src/main/resources/elixir/api.mustache index 799a4c31c08..ece4d62da9a 100644 --- a/modules/swagger-codegen/src/main/resources/elixir/api.mustache +++ b/modules/swagger-codegen/src/main/resources/elixir/api.mustache @@ -1,35 +1,59 @@ -defmodule {{#modulized}}{{appName}}{{/modulized}}.Api.{{classname}} do +{{>licenseInfo}} +defmodule {{moduleName}}.Api.{{classname}} do @moduledoc """ - Documentation for {{#modulized}}{{appName}}{{/modulized}}.Api.{{classname}}. + API calls for all endpoints tagged `{{baseName}}`. """ - use Tesla + alias {{moduleName}}.Connection + import {{moduleName}}.RequestBuilder - plug Tesla.Middleware.BaseUrl, "{{{basePath}}}" - plug Tesla.Middleware.JSON {{#operations}} - {{#operation}} + {{#operation}} @doc """ + {{#summary}} {{summary}} - {{^notes.isEmpty}} - + {{/summary}} + {{#notes}} {{notes}} - {{/notes.isEmpty}} - """ - def {{#underscored}}{{operationId}}{{/underscored}}({{#allParams}}{{^-first}}, {{/-first}}{{#underscored}}{{paramName}}{{/underscored}}{{/allParams}}) do - method = [method: :{{#underscored}}{{httpMethod}}{{/underscored}}] - url = [url: "{{replacedPathName}}"] - query_params = [{{^queryParams.isEmpty}}query: [{{#queryParams}}{{^-first}}, {{/-first}}{:"{{baseName}}", {{#underscored}}{{paramName}}{{/underscored}}}{{/queryParams}}]{{/queryParams.isEmpty}}] - header_params = [{{^headerParams.isEmpty}}header: [{{#headerParams}}{{^-first}}, {{/-first}}{:"{{baseName}}", {{#underscored}}{{paramName}}{{/underscored}}}{{/headerParams}}]{{/headerParams.isEmpty}}] - body_params = [{{^bodyParams.isEmpty}}body: {{#bodyParams}}{{#underscored}}{{paramName}}{{/underscored}}{{/bodyParams}}{{/bodyParams.isEmpty}}] - form_params = [{{^formParams.isEmpty}}body: Enum.map_join([{{#formParams}}{{^-first}}, {{/-first}}{:"{{baseName}}", {{#underscored}}{{paramName}}{{/underscored}}}{{/formParams}}], "&", &("#{elem(&1, 0)}=#{elem(&1, 1)}")){{/formParams.isEmpty}}] - params = query_params ++ header_params ++ body_params ++ form_params - opts = [] - options = method ++ url ++ params ++ opts + {{/notes}} + + ## Parameters - request(options) + - connection ({{moduleName}}.Connection): Connection to server +{{#allParams}}{{#required}} - {{#underscored}}{{paramName}}{{/underscored}} ({{dataType}}): {{description}} +{{/required}}{{/allParams}} - opts (KeywordList): [optional] Optional parameters +{{#allParams}}{{^required}} - {{#underscored}}:{{paramName}}{{/underscored}} ({{dataType}}): {{description}} +{{/required}}{{/allParams}} + ## Returns + + {:ok, {{#isListContainer}}[%{{returnBaseType}}{}, ...]{{/isListContainer}}{{#isMapContainer}}%{}{{/isMapContainer}}{{^returnType}}%{}{{/returnType}}{{#returnSimpleType}}%{{#returnType}}{{#isMapContainer}}{{/isMapContainer}}{{moduleName}}.Model.{{{returnType}}}{{/returnType}}{}{{/returnSimpleType}}} on success + {:error, info} on failure + """ + {{typespec}} + def {{#underscored}}{{operationId}}{{/underscored}}(connection, {{#allParams}}{{#required}}{{#underscored}}{{paramName}}{{/underscored}}, {{/required}}{{/allParams}}{{^hasOptionalParams}}_{{/hasOptionalParams}}opts \\ []) do +{{#hasOptionalParams}} + optional_params = %{ + {{#allParams}}{{^required}}{{^isPathParam}}:"{{baseName}}" => {{#isBodyParam}}:body{{/isBodyParam}}{{#isFormParam}}:form{{/isFormParam}}{{#isQueryParam}}:query{{/isQueryParam}}{{#isHeaderParam}}:headers{{/isHeaderParam}}{{/isPathParam}}{{#hasMore}}, + {{/hasMore}}{{/required}}{{/allParams}} + } +{{/hasOptionalParams}} + %{} + |> method(:{{#underscored}}{{httpMethod}}{{/underscored}}) + |> url("{{replacedPathName}}") +{{#allParams}} +{{#required}} +{{^isPathParam}} |> add_param({{#isBodyParam}}:body{{/isBodyParam}}{{#isFormParam}}{{#isMultipart}}{{#isFile}}:file{{/isFile}}{{^isFile}}:form{{/isFile}}{{/isMultipart}}{{^isMultipart}}:form{{/isMultipart}}{{/isFormParam}}{{#isQueryParam}}:query{{/isQueryParam}}{{#isHeaderParam}}:headers{{/isHeaderParam}}, :"{{baseName}}", {{#underscored}}{{paramName}}{{/underscored}}) +{{/isPathParam}} +{{/required}} +{{/allParams}} +{{#hasOptionalParams}} + |> add_optional_params(optional_params, opts) +{{/hasOptionalParams}} + |> Enum.into([]) + |> (&Connection.request(connection, &1)).() + |> decode({{decodedStruct}}) end - {{/operation}} + {{/operation}} {{/operations}} end diff --git a/modules/swagger-codegen/src/main/resources/elixir/connection.ex.mustache b/modules/swagger-codegen/src/main/resources/elixir/connection.ex.mustache new file mode 100644 index 00000000000..9e10634e285 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/elixir/connection.ex.mustache @@ -0,0 +1,92 @@ +{{>licenseInfo}} +defmodule {{moduleName}}.Connection do + @moduledoc """ + Handle Tesla connections for {{moduleName}}. + """ + + use Tesla + + # Add any middleware here (authentication) + plug Tesla.Middleware.BaseUrl, "{{{basePath}}}" + plug Tesla.Middleware.Headers, %{"User-Agent" => "Elixir"} + plug Tesla.Middleware.EncodeJson + + {{#hasAuthMethods}} + {{#authMethods}} + {{#isOAuth}} + @scopes [ + {{#scopes}} + "{{scope}}"{{#hasMore}},{{/hasMore}} {{#description}}# {{description}}{{/description}} + {{/scopes}} + ] + + @doc """ + Configure a client connection using a provided OAuth2 token as a Bearer token + + ## Parameters + + - token (String): Bearer token + + ## Returns + + Tesla.Env.client + """ + @spec new(String.t) :: Tesla.Env.client + def new(token) when is_binary(token) do + Tesla.build_client([ + {Tesla.Middleware.Headers, %{"Authorization" => "Bearer #{token}"}} + ]) + end + + @doc """ + Configure a client connection using a function which yields a Bearer token. + + ## Parameters + + - token_fetcher (function arity of 1): Callback which provides an OAuth2 token + given a list of scopes + + ## Returns + + Tesla.Env.client + """ + @spec new(((list(String.t)) -> String.t)) :: Tesla.Env.client + def new(token_fetcher) when is_function(token_fetcher) do + token_fetcher.(@scopes) + |> new + end + {{/isOAuth}} + {{#isBasic}} + @doc """ + Configure an client connection using Basic authentication. + + ## Parameters + + - username (String): Username used for authentication + - password (String): Password used for authentication + + # Returns + + Tesla.Env.client + """ + @spec new(String.t, String.t) :: Tesla.Env.client + def new(username, password) do + Tesla.build_client([ + {Tesla.Middleware.BasicAuth, %{username: username, password: password}} + ]) + end + {{/isBasic}} + {{/authMethods}} + {{/hasAuthMethods}} + @doc """ + Configure an authless client connection + + # Returns + + Tesla.Env.client + """ + @spec new() :: Tesla.Env.client + def new do + Tesla.build_client([]) + end +end diff --git a/modules/swagger-codegen/src/main/resources/elixir/deserializer.ex.mustache b/modules/swagger-codegen/src/main/resources/elixir/deserializer.ex.mustache new file mode 100644 index 00000000000..931ba8f7b9c --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/elixir/deserializer.ex.mustache @@ -0,0 +1,31 @@ +{{>licenseInfo}} +defmodule {{moduleName}}.Deserializer do + @moduledoc """ + Helper functions for deserializing responses into models + """ + + @doc """ + Update the provided model with a deserialization of a nested value + """ + @spec deserialize(struct(), :atom, :atom, struct(), keyword()) :: struct() + def deserialize(model, field, :list, mod, options) do + model + |> Map.update!(field, &(Poison.Decode.decode(&1, Keyword.merge(options, [as: [struct(mod)]])))) + end + def deserialize(model, field, :struct, mod, options) do + model + |> Map.update!(field, &(Poison.Decode.decode(&1, Keyword.merge(options, [as: struct(mod)])))) + end + def deserialize(model, field, :map, mod, options) do + model + |> Map.update!(field, &(Map.new(&1, fn {key, val} -> {key, Poison.Decode.decode(val, Keyword.merge(options, [as: struct(mod)]))} end))) + end + def deserialize(model, field, :date, _, _options) do + case DateTime.from_iso8601(Map.get(model, field)) do + {:ok, datetime} -> + Map.put(model, field, datetime) + _ -> + model + end + end +end diff --git a/modules/swagger-codegen/src/main/resources/elixir/gitignore.mustache b/modules/swagger-codegen/src/main/resources/elixir/gitignore.mustache new file mode 100644 index 00000000000..b6012c77a3a --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/elixir/gitignore.mustache @@ -0,0 +1,20 @@ +# The directory Mix will write compiled artifacts to. +/_build + +# If you run "mix test --cover", coverage assets end up here. +/cover + +# The directory Mix downloads your dependencies sources to. +/deps + +# Where 3rd-party dependencies like ExDoc output generated docs. +/doc + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez diff --git a/modules/swagger-codegen/src/main/resources/elixir/licenseInfo.mustache b/modules/swagger-codegen/src/main/resources/elixir/licenseInfo.mustache new file mode 100644 index 00000000000..fea09ae1ed6 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/elixir/licenseInfo.mustache @@ -0,0 +1,6 @@ +{{#licenseHeader}}{{licenseHeader}} + +{{/licenseHeader}} +# NOTE: This class is auto generated by the swagger code generator program. +# https://github.com/swagger-api/swagger-codegen.git +# Do not edit the class manually. diff --git a/modules/swagger-codegen/src/main/resources/elixir/mix.exs.mustache b/modules/swagger-codegen/src/main/resources/elixir/mix.exs.mustache index f2b467f77cc..31b458cf49f 100644 --- a/modules/swagger-codegen/src/main/resources/elixir/mix.exs.mustache +++ b/modules/swagger-codegen/src/main/resources/elixir/mix.exs.mustache @@ -1,8 +1,8 @@ -defmodule {{#modulized}}{{appName}}{{/modulized}}.Mixfile do +defmodule {{moduleName}}.Mixfile do use Mix.Project def project do - [app: :{{#underscored}}{{appName}}{{/underscored}}, + [app: :{{#underscored}}{{packageName}}{{/underscored}}, version: "0.1.0", elixir: "~> {{supportedElixirVersion}}", build_embedded: Mix.env == :prod, diff --git a/modules/swagger-codegen/src/main/resources/elixir/model.mustache b/modules/swagger-codegen/src/main/resources/elixir/model.mustache index e69de29bb2d..ea43f78e0fc 100644 --- a/modules/swagger-codegen/src/main/resources/elixir/model.mustache +++ b/modules/swagger-codegen/src/main/resources/elixir/model.mustache @@ -0,0 +1,37 @@ +{{>licenseInfo}} +{{#models}}{{#model}}defmodule {{moduleName}}.Model.{{classname}} do + @moduledoc """ + {{description}} + """ + + @derive [Poison.Encoder] + defstruct [ + {{#vars}}:"{{baseName}}"{{#hasMore}}, + {{/hasMore}}{{/vars}} + ] + + @type t :: %__MODULE__{ + {{#vars}}:"{{baseName}}" => {{{datatype}}}{{#hasMore}}, + {{/hasMore}}{{/vars}} + } +end + +defimpl Poison.Decoder, for: {{moduleName}}.Model.{{classname}} do +{{#hasComplexVars}} + import {{moduleName}}.Deserializer + def decode(value, options) do + value + {{#vars}} + {{^isPrimitiveType}} + {{#datatype}}|> deserialize(:"{{baseName}}", {{#isListContainer}}:list, {{moduleName}}.Model.{{items.datatype}}{{/isListContainer}}{{#isMapContainer}}:map, {{moduleName}}.Model.{{items.datatype}}{{/isMapContainer}}{{#isDate}}:date, nil{{/isDate}}{{#isDateTime}}:date, nil{{/isDateTime}}{{^isDate}}{{^isDateTime}}{{^isMapContainer}}{{^isListContainer}}:struct, {{moduleName}}.Model.{{datatype}}{{/isListContainer}}{{/isMapContainer}}{{/isDateTime}}{{/isDate}}, options) + {{/datatype}} + {{/isPrimitiveType}} + {{/vars}} +{{/hasComplexVars}} +{{^hasComplexVars}} + def decode(value, _options) do + value +{{/hasComplexVars}} + end +end +{{/model}}{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/elixir/request_builder.ex.mustache b/modules/swagger-codegen/src/main/resources/elixir/request_builder.ex.mustache new file mode 100644 index 00000000000..191832e9bdb --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/elixir/request_builder.ex.mustache @@ -0,0 +1,122 @@ +{{>licenseInfo}} +defmodule {{moduleName}}.RequestBuilder do + @moduledoc """ + Helper functions for building Tesla requests + """ + + @doc """ + Specify the request method when building a request + + ## Parameters + + - request (Map) - Collected request options + - m (atom) - Request method + + ## Returns + + Map + """ + @spec method(map(), atom) :: map() + def method(request, m) do + Map.put_new(request, :method, m) + end + + @doc """ + Specify the request method when building a request + + ## Parameters + + - request (Map) - Collected request options + - u (String) - Request URL + + ## Returns + + Map + """ + @spec url(map(), String.t) :: map() + def url(request, u) do + Map.put_new(request, :url, u) + end + + @doc """ + Add optional parameters to the request + + ## Parameters + + - request (Map) - Collected request options + - definitions (Map) - Map of parameter name to parameter location. + - options (KeywordList) - The provided optional parameters + + ## Returns + + Map + """ + @spec add_optional_params(map(), %{optional(atom) => atom}, keyword()) :: map() + def add_optional_params(request, _, []), do: request + def add_optional_params(request, definitions, [{key, value} | tail]) do + case definitions do + %{^key => location} -> + request + |> add_param(location, key, value) + |> add_optional_params(definitions, tail) + _ -> + add_optional_params(request, definitions, tail) + end + end + + @doc """ + Add optional parameters to the request + + ## Parameters + + - request (Map) - Collected request options + - location (atom) - Where to put the parameter + - key (atom) - The name of the parameter + - value (any) - The value of the parameter + + ## Returns + + Map + """ + @spec add_param(map(), atom, atom, any()) :: map() + def add_param(request, :body, :body, value), do: Map.put(request, :body, value) + def add_param(request, :body, key, value) do + request + |> Map.put_new_lazy(:body, &Tesla.Multipart.new/0) + |> Map.update!(:body, &(Tesla.Multipart.add_field(&1, key, Poison.encode!(value), headers: [{:"Content-Type", "application/json"}]))) + end + def add_param(request, :file, name, path) do + request + |> Map.put_new_lazy(:body, &Tesla.Multipart.new/0) + |> Map.update!(:body, &(Tesla.Multipart.add_file(&1, path, name: name))) + end + def add_param(request, :form, name, value) do + request + |> Map.update(:body, %{name => value}, &(Map.put(&1, name, value))) + end + def add_param(request, location, key, value) do + Map.update(request, location, [{key, value}], &(&1 ++ [{key, value}])) + end + + @doc """ + Handle the response for a Tesla request + + ## Parameters + + - arg1 (Tesla.Env.t | term) - The response object + - arg2 (:false | struct | [struct]) - The shape of the struct to deserialize into + + ## Returns + + {:ok, struct} on success + {:error, term} on failure + """ + @spec decode(Tesla.Env.t | term()) :: {:ok, struct()} | {:error, Tesla.Env.t} | {:error, term()} + def decode(%Tesla.Env{status: 200, body: body}), do: Poison.decode(body) + def decode(response), do: {:error, response} + + @spec decode(Tesla.Env.t | term(), :false | struct() | [struct()]) :: {:ok, struct()} | {:error, Tesla.Env.t} | {:error, term()} + def decode(%Tesla.Env{status: 200} = env, false), do: {:ok, env} + def decode(%Tesla.Env{status: 200, body: body}, struct), do: Poison.decode(body, as: struct) + def decode(response, _struct), do: {:error, response} +end diff --git a/modules/swagger-codegen/src/main/resources/erlang-client/README.mustache b/modules/swagger-codegen/src/main/resources/erlang-client/README.mustache new file mode 100644 index 00000000000..f3eab0b5528 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/erlang-client/README.mustache @@ -0,0 +1,5 @@ +# Swagger client server library for Erlang + +## Overview + +An Erlang client stub generated by [Swagger Codegen](https://github.com/swagger-api/swagger-codegen) given an OpenAPI/Swagger spec. diff --git a/modules/swagger-codegen/src/main/resources/erlang-client/api.mustache b/modules/swagger-codegen/src/main/resources/erlang-client/api.mustache new file mode 100644 index 00000000000..21f040192f0 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/erlang-client/api.mustache @@ -0,0 +1,52 @@ +-module({{classname}}_api). + +-export([{{#operations}}{{#operation}}{{^-first}}, + {{/-first}}{{operationId}}/{{arityRequired}}, {{operationId}}/{{arityOptional}}{{/operation}}{{/operations}}]). + +-define(BASE_URL, <<"{{{basePath}}}">>). + +{{#operations}} + {{#operation}} +%% @doc {{{summary}}} +{{^notes.isEmpty}} +%% {{{notes}}} +{{/notes.isEmpty}} +-spec {{operationId}}({{#allParams}}{{#required}}{{^-first}}, {{/-first}}{{{dataType}}}{{/required}}{{/allParams}}{{^bodyParams.isEmpty}}{{#bodyParams}}, term(){{/bodyParams}}{{/bodyParams.isEmpty}}) -> {{#returnType}}{ok, list(), {{{returnType}}}} | {error, string()}{{/returnType}}{{^returnType}}ok | {error, integer()}{{/returnType}}. +{{operationId}}({{#allParams}}{{#required}}{{^-first}}, {{/-first}}{{paramName}}{{/required}}{{/allParams}}{{^bodyParams.isEmpty}}{{#bodyParams}}, {{paramName}}{{/bodyParams}}{{/bodyParams.isEmpty}}) -> + {{operationId}}({{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}{{^bodyParams.isEmpty}}{{#bodyParams}}{{paramName}}, {{/bodyParams}}{{/bodyParams.isEmpty}}#{}). + +-spec {{operationId}}({{#allParams}}{{#required}}{{{dataType}}}, {{/required}}{{/allParams}}{{^bodyParams.isEmpty}}{{#bodyParams}}term(), {{/bodyParams}}{{/bodyParams.isEmpty}}maps:map()) -> {{#returnType}}{ok, list(), {{{returnType}}}} | {error, string()}{{/returnType}}{{^returnType}}ok | {error, integer()}{{/returnType}}. +{{operationId}}({{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}{{^bodyParams.isEmpty}}{{#bodyParams}}{{paramName}}{{/bodyParams}}, {{/bodyParams.isEmpty}}_Optional) -> + Method = {{httpMethod}}, + Path = ["{{{replacedPathName}}}"], + QS = {{#queryParams.isEmpty}}[]{{/queryParams.isEmpty}}{{^queryParams.isEmpty}}lists:flatten([{{#queryParams}}{{#required}}{{^-first}}, {{/-first}}{{#qsEncode}}{{this}}{{/qsEncode}}{{/required}}{{/queryParams}}])++[{X, maps:get(X, _Optional)} || X <- [{{#queryParams}}{{^required}}{{^-first}}, {{/-first}}'{{baseName}}'{{/required}}{{/queryParams}}], maps:is_key(X, _Optional)]{{/queryParams.isEmpty}}, + Headers = {{#headerParams.isEmpty}}[]{{/headerParams.isEmpty}}{{^headerParams.isEmpty}}[{{#headerParams}}{{#required}}{{^-first}}, {{/-first}}{<<"{{baseName}}">>, {{paramName}}}{{/required}}{{/headerParams}}]++[{X, maps:get(X, _Optional)} || X <- [{{#headerParams}}{{^required}}{{^-first}}, {{/-first}}'{{baseName}}'{{/required}}{{/headerParams}}], maps:is_key(X, _Optional)]{{/headerParams.isEmpty}}, + Body1 = {{^formParams.isEmpty}}{form, [{{#formParams}}{{#required}}{{^-first}}, {{/-first}}{<<"{{baseName}}">>, {{paramName}}}{{/required}}{{/formParams}}]++[{X, maps:get(X, _Optional)} || X <- [{{#formParams}}{{^required}}{{^-first}}, {{/-first}}'{{baseName}}'{{/required}}{{/formParams}}], maps:is_key(X, _Optional)]}{{/formParams.isEmpty}}{{#formParams.isEmpty}}{{#bodyParams.isEmpty}}[]{{/bodyParams.isEmpty}}{{^bodyParams.isEmpty}}{{#bodyParams}}{{paramName}}{{/bodyParams}}{{/bodyParams.isEmpty}}{{/formParams.isEmpty}}, + Opts = [], + Url = hackney_url:make_url(?BASE_URL, Path, QS), + + case hackney:request(Method, Url, Headers, Body1, Opts) of +{{#returnType}} + {{#responses}} +{{#isDefault}} + {ok, {{code}}, RespHeaders, ClientRef} -> + {ok, ResponseBody} = hackney:body(ClientRef), + {ok, RespHeaders, jsx:decode(ResponseBody, [return_maps])}{{#hasMore}}; {{/hasMore}} +{{/isDefault}} +{{^isDefault}} + {ok, {{code}}, _RespHeaders, _ClientRef} -> + {error, "{{message}}"}{{#hasMore}}; {{/hasMore}} +{{/isDefault}} + {{/responses}} +{{/returnType}} +{{^returnType}} + {ok, 200, _RespHeaders, _ClientRef} -> + ok; + {ok, Status, _RespHeaders, _ClientRef} -> + {error, Status} +{{/returnType}} + end. + + {{/operation}} + +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/erlang-client/app.src.mustache b/modules/swagger-codegen/src/main/resources/erlang-client/app.src.mustache new file mode 100644 index 00000000000..8cf0543467a --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/erlang-client/app.src.mustache @@ -0,0 +1,17 @@ +{application, {{packageName}}, + [{description, {{#appDescription}}"{{appDescription}}"{{/appDescription}}{{^appDescription}}"Swagger client library"{{/appDescription}}}, + {vsn, "{{apiVersion}}"}, + {registered, []}, + {applications, + [kernel, + stdlib, + ssl, + hackney + ]}, + {env, []}, + {modules, []}, + + {maintainers, []}, + {licenses, [{{#licenseInfo}}"{{licenseInfo}}"{{/licenseInfo}}]}, + {links, [{{#infoUrl}}"{{infoUrl}}"{{/infoUrl}}]} +]}. diff --git a/modules/swagger-codegen/src/main/resources/erlang-client/model.mustache b/modules/swagger-codegen/src/main/resources/erlang-client/model.mustache new file mode 100644 index 00000000000..79db4433316 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/erlang-client/model.mustache @@ -0,0 +1,21 @@ +{{#models}} +{{#model}} +-module({{classname}}). + +-export([encode/1]). + +-export_type([{{classname}}/0]). + +-type {{classname}}() :: + #{ {{#vars}}'{{name}}' {{#required}}:={{/required}}{{^required}}=>{{/required}} {{{datatype}}}{{#hasMore}}, + {{/hasMore}}{{/vars}} + }. + +encode(#{ {{#vars}}'{{name}}' := {{{nameInCamelCase}}}{{#hasMore}}, + {{/hasMore}}{{/vars}} + }) -> + #{ {{#vars}}'{{baseName}}' => {{{nameInCamelCase}}}{{#hasMore}}, + {{/hasMore}}{{/vars}} + }. +{{/model}} +{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/erlang-client/rebar.config.mustache b/modules/swagger-codegen/src/main/resources/erlang-client/rebar.config.mustache new file mode 100644 index 00000000000..abe84a1a9f3 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/erlang-client/rebar.config.mustache @@ -0,0 +1,3 @@ +{erl_opts, [debug_info, warnings_as_errors, warn_untyped_record]}. + +{deps, [jsx, hackney]}. diff --git a/modules/swagger-codegen/src/main/resources/erlang-server/api.mustache b/modules/swagger-codegen/src/main/resources/erlang-server/api.mustache index d948bbae4b2..ed49d5a0a78 100644 --- a/modules/swagger-codegen/src/main/resources/erlang-server/api.mustache +++ b/modules/swagger-codegen/src/main/resources/erlang-server/api.mustache @@ -4,6 +4,8 @@ -export([request_param_info/2]). -export([populate_request/3]). -export([validate_response/4]). +%% exported to silence swagger complains +-export([get_value/3, validate_response_body/4]). -type operation_id() :: atom(). -type request_param() :: atom(). @@ -212,7 +214,7 @@ validate(Rule = {enum, Values}, Name, Value, _ValidatorState) -> end; validate(Rule = {max, Max}, Name, Value, _ValidatorState) -> - case Value >= Max of + case Value =< Max of true -> ok; false -> validation_error(Rule, Name) end; @@ -224,7 +226,7 @@ validate(Rule = {exclusive_max, ExclusiveMax}, Name, Value, _ValidatorState) -> end; validate(Rule = {min, Min}, Name, Value, _ValidatorState) -> - case Value =< Min of + case Value >= Min of true -> ok; false -> validation_error(Rule, Name) end; @@ -290,6 +292,8 @@ validation_error(ViolatedRule, Name) -> validation_error(ViolatedRule, Name, Info) -> throw({wrong_param, Name, ViolatedRule, Info}). +-spec get_value(body | qs_val | header | binding, Name :: any(), Req0 :: cowboy_req:req()) -> + {Value :: any(), Req :: cowboy_req:req()}. get_value(body, _Name, Req0) -> {ok, Body, Req} = cowboy_req:body(Req0), Value = prepare_body(Body), diff --git a/modules/swagger-codegen/src/main/resources/erlang-server/auth.mustache b/modules/swagger-codegen/src/main/resources/erlang-server/auth.mustache index bf988fac348..c81f989fd38 100644 --- a/modules/swagger-codegen/src/main/resources/erlang-server/auth.mustache +++ b/modules/swagger-codegen/src/main/resources/erlang-server/auth.mustache @@ -24,8 +24,12 @@ authorize_api_key(LogicHandler, OperationID, From, KeyParam, Req0) -> ApiKey ), case Result of + {{#authMethods}} + {{#isApiKey}} {true, Context} -> {true, Context, Req}; + {{/isApiKey}} + {{/authMethods}} false -> AuthHeader = <<"">>, {false, AuthHeader, Req} diff --git a/modules/swagger-codegen/src/main/resources/erlang-server/handler.mustache b/modules/swagger-codegen/src/main/resources/erlang-server/handler.mustache index 4c0bebc5ff4..4ed2e21a2ae 100644 --- a/modules/swagger-codegen/src/main/resources/erlang-server/handler.mustache +++ b/modules/swagger-codegen/src/main/resources/erlang-server/handler.mustache @@ -94,13 +94,15 @@ is_authorized( end; {{/isApiKey}} {{/authMethods}} +{{/operation}}{{/operations}} {{^authMethods}} is_authorized(Req, State) -> - {true, Req, State}; + {true, Req, State}. {{/authMethods}} -{{/operation}}{{/operations}} +{{#authMethods}} is_authorized(Req, State) -> {{false, <<"">>}, Req, State}. +{{/authMethods}} -spec content_types_accepted(Req :: cowboy_req:req(), State :: state()) -> { diff --git a/modules/swagger-codegen/src/main/resources/erlang-server/logic_handler.mustache b/modules/swagger-codegen/src/main/resources/erlang-server/logic_handler.mustache index cb4ba201bf4..8baf9b81c20 100644 --- a/modules/swagger-codegen/src/main/resources/erlang-server/logic_handler.mustache +++ b/modules/swagger-codegen/src/main/resources/erlang-server/logic_handler.mustache @@ -1,11 +1,7 @@ -module({{packageName}}_logic_handler). -export([handle_request/4]). -{{#authMethods}} - {{#isApiKey}} -export([authorize_api_key/3]). - {{/isApiKey}} -{{/authMethods}} -type context() :: #{binary() => any()}. -type handler_response() ::{ Status :: cowboy:http_status(), @@ -48,3 +44,9 @@ authorize_api_key(Handler, OperationID, ApiKey) -> Handler:authorize_api_key(OperationID, ApiKey). {{/isApiKey}} {{/authMethods}} +{{^authMethods}} +-spec authorize_api_key(Handler :: atom(), OperationID :: {{packageName}}_api:operation_id(), ApiKey :: binary()) -> + Result :: false. +authorize_api_key(_Handler, _OperationID, _ApiKey) -> + false. +{{/authMethods}} diff --git a/modules/swagger-codegen/src/main/resources/erlang-server/server.mustache b/modules/swagger-codegen/src/main/resources/erlang-server/server.mustache index 90a6388ea5d..348a473e9e3 100644 --- a/modules/swagger-codegen/src/main/resources/erlang-server/server.mustache +++ b/modules/swagger-codegen/src/main/resources/erlang-server/server.mustache @@ -9,6 +9,7 @@ -spec child_spec( ID :: any(), #{ ip => inet:ip_address(), port => inet:port_number(), + logic_handler => module(), net_opts => [] }) -> supervisor:child_spec(). diff --git a/modules/swagger-codegen/src/main/resources/finch/DataAccessor.mustache b/modules/swagger-codegen/src/main/resources/finch/DataAccessor.mustache index e9700efc726..ca4e3a8f851 100644 --- a/modules/swagger-codegen/src/main/resources/finch/DataAccessor.mustache +++ b/modules/swagger-codegen/src/main/resources/finch/DataAccessor.mustache @@ -2,7 +2,9 @@ package {{packageName}} // TODO: properly handle custom imports import java.io._ -import java.util.Date +import java.util.UUID +import java.time._ + import {{modelPackage}}._ @@ -18,7 +20,7 @@ trait DataAccessor { * {{{description}}} * @return A {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}} */ - def {{baseName}}_{{operationId}}({{#allParams}}{{paramName}}: {{{dataType}}}{{^-last}}, {{/-last}}{{/allParams}}): {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}} = ??? + def {{baseName}}_{{operationId}}({{{vendorExtensions.x-codegen-typedParams}}}): Either[CommonError,{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}] = ??? {{/operation}} {{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/finch/README.mustache b/modules/swagger-codegen/src/main/resources/finch/README.mustache index 327870827fd..6c9bf19fe7b 100644 --- a/modules/swagger-codegen/src/main/resources/finch/README.mustache +++ b/modules/swagger-codegen/src/main/resources/finch/README.mustache @@ -5,6 +5,11 @@ This server was generated by the [swagger-codegen](https://github.com/swagger-ap [OpenAPI-Spec](https://github.com/swagger-api/swagger-core/wiki) from a remote server, you can easily generate a server stub. This is an example of building a swagger-enabled scalatra server. -This example uses the [scalatra](http://scalatra.org/) framework. To see how to make this your own, look here: +This example uses the [finch](http://github.com/finagle/finch/) framework. To see how to make this your own, look here: -[README](https://github.com/swagger-api/swagger-codegen/tree/master/samples/server-generator/scalatra) \ No newline at end of file +[README](https://github.com/swagger-api/swagger-codegen/tree/master/samples/server-generator/finch) + + +### After generation + +Run `scalafix RemoveUnusedImports` to cleanup unused imports. \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/finch/Server.mustache b/modules/swagger-codegen/src/main/resources/finch/Server.mustache index 97f0fb79a09..dc051c48652 100644 --- a/modules/swagger-codegen/src/main/resources/finch/Server.mustache +++ b/modules/swagger-codegen/src/main/resources/finch/Server.mustache @@ -17,10 +17,8 @@ import com.twitter.util.{Await, Future} class Server { // Loads implementation defined in resources/META-INF/services/{{packageName}}.DataAccessor - val db = LoadService[DataAccessor]() match { - case accessor :: _ => accessor - case _ => new DataAccessor { } - } + val impls: Seq[DataAccessor] = LoadService[DataAccessor]() + val db = if (impls.isEmpty) new DataAccessor { } else impls.head val service = endpoint.makeService(db) @@ -32,7 +30,7 @@ class Server { } /** - * Launches the PetstoreAPI service when the system is ready. + * Launches the API service when the system is ready. */ object Server extends Server with App { Await.ready(server) diff --git a/modules/swagger-codegen/src/main/resources/finch/api.mustache b/modules/swagger-codegen/src/main/resources/finch/api.mustache index 8758174f6d3..8365f59b5bd 100644 --- a/modules/swagger-codegen/src/main/resources/finch/api.mustache +++ b/modules/swagger-codegen/src/main/resources/finch/api.mustache @@ -1,7 +1,6 @@ package {{apiPackage}} import java.io._ -import java.util.Date import {{packageName}}._ import {{modelPackage}}._ {{#imports}}import {{import}} @@ -17,6 +16,7 @@ import com.twitter.util.Future import com.twitter.io.Buf import io.finch._, items._ import java.io.File +import java.time._ object {{classname}} { /** @@ -26,25 +26,43 @@ object {{classname}} { def endpoints(da: DataAccessor) = {{#operations}} {{#operation}} - {{{operationId}}}(da){{^-last}} :+:{{/-last}} + {{{operationId}}}(da){{^-last}} :+:{{/-last}} {{/operation}} {{/operations}} + + private def checkError(e: CommonError) = e match { + case InvalidInput(_) => BadRequest(e) + case MissingIdentifier(_) => BadRequest(e) + case RecordNotFound(_) => NotFound(e) + case _ => InternalServerError(e) + } + + implicit class StringOps(s: String) { + + import java.time.format.DateTimeFormatter + + lazy val localformatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") + lazy val datetimeformatter: DateTimeFormatter = + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ") + + def toLocalDateTime: LocalDateTime = LocalDateTime.parse(s,localformatter) + def toZonedDateTime: ZonedDateTime = ZonedDateTime.parse(s, datetimeformatter) + + } + {{#operations}} {{#operation}} /** * {{{description}}} - * @return And endpoint representing a {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}} + * @return An endpoint representing a {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}} */ private def {{operationId}}(da: DataAccessor): Endpoint[{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}] = - {{httpMethod}}({{{vendorExtensions.x-codegen-path}}} {{#allParams}}{{^isPathParam}} :: {{& vendorExtensions.x-codegen-normalized-path-type}}{{/isPathParam}}{{/allParams}}) { {{#hasParams}}({{#allParams}}{{paramName}}: {{{vendorExtensions.x-codegen-normalized-input-type}}}{{^-last}}, {{/-last}}{{/allParams}}) => {{/hasParams}} - {{#returnType}} - Ok(da.{{baseName}}_{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}})) - {{/returnType}} - {{^returnType}} - da.{{baseName}}_{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) - NoContent[Unit] - {{/returnType}} + {{httpMethod}}({{{vendorExtensions.x-codegen-paths}}}) { {{#hasParams}}({{{vendorExtensions.x-codegen-typedParams}}}) => {{/hasParams}} + da.{{baseName}}_{{operationId}}({{{vendorExtensions.x-codegen-params}}}) match { + case Left(error) => checkError(error) + case Right(data) => Ok(data) + } } handle { case e: Exception => BadRequest(e) } @@ -53,20 +71,20 @@ object {{classname}} { {{/operations}} implicit private def fileUploadToFile(fileUpload: FileUpload) : File = { - fileUpload match { - case upload: InMemoryFileUpload => - bytesToFile(Buf.ByteArray.Owned.extract(upload.content)) - case upload: OnDiskFileUpload => - upload.content - case _ => null - } + fileUpload match { + case upload: InMemoryFileUpload => + bytesToFile(Buf.ByteArray.Owned.extract(upload.content)) + case upload: OnDiskFileUpload => + upload.content + case _ => null + } } private def bytesToFile(input: Array[Byte]): java.io.File = { - val file = File.createTempFile("tmp{{classname}}", null) - val output = new FileOutputStream(file) - output.write(input) - file + val file = File.createTempFile("tmp{{classname}}", null) + val output = new FileOutputStream(file) + output.write(input) + file } // This assists in params(string) application (which must be Seq[A] in parameter list) when the param is used as a List[A] elsewhere. diff --git a/modules/swagger-codegen/src/main/resources/finch/build.sbt b/modules/swagger-codegen/src/main/resources/finch/build.sbt index b58544b46bd..95f655766be 100644 --- a/modules/swagger-codegen/src/main/resources/finch/build.sbt +++ b/modules/swagger-codegen/src/main/resources/finch/build.sbt @@ -6,7 +6,7 @@ name := "finch-sample" version := "0.1.0-SNAPSHOT" -scalaVersion := "2.11.8" +scalaVersion := "2.11.11" resolvers += Resolver.sonatypeRepo("snapshots") @@ -20,6 +20,11 @@ resolvers += "Sonatype OSS Releases" at "http://oss.sonatype.org/content/reposit Defaults.itSettings +lazy val circeVersion = "0.8.0" +lazy val finagleVersion = "6.45.0" +lazy val finchVersion = "0.15.1" +lazy val scalaTestVersion = "3.0.0" + scalacOptions ++= Seq( "-deprecation", "-encoding", "UTF-8", @@ -33,21 +38,21 @@ scalacOptions ++= Seq( "-Ywarn-numeric-widen", "-Xfuture", "-Xlint", -// "-Ywarn-unused-import", + "-Ywarn-unused-import", "-language:postfixOps" ) lazy val `it-config-sbt-project` = project.in(file(".")).configs(IntegrationTest) libraryDependencies ++= Seq( - "com.github.finagle" %% "finch-core" % "0.12.0", - "com.github.finagle" %% "finch-circe" % "0.12.0", - "io.circe" %% "circe-generic" % "0.7.0", - "io.circe" %% "circe-java8" % "0.7.0", - "com.twitter" %% "util-core" % "6.40.0", - "com.github.finagle" %% "finch-test" % "0.12.0" % "test", - "org.scalacheck" %% "scalacheck" % "1.13.4" % "test", - "org.scalatest" %% "scalatest" % "3.0.0" % "test" + "com.github.finagle" %% "finch-core" % finchVersion, + "com.github.finagle" %% "finch-circe" % finchVersion, + "io.circe" %% "circe-generic" % circeVersion, + "io.circe" %% "circe-java8" % circeVersion, + "com.twitter" %% "util-core" % finagleVersion, + "com.github.finagle" %% "finch-test" % finchVersion % "test", + "org.scalacheck" %% "scalacheck" % "1.13.4" % "test", + "org.scalatest" %% "scalatest" % scalaTestVersion % "test" ) assemblyMergeStrategy in assembly := { diff --git a/modules/swagger-codegen/src/main/resources/finch/endpoint.mustache b/modules/swagger-codegen/src/main/resources/finch/endpoint.mustache index bb66defbe4f..31763220d76 100644 --- a/modules/swagger-codegen/src/main/resources/finch/endpoint.mustache +++ b/modules/swagger-codegen/src/main/resources/finch/endpoint.mustache @@ -23,7 +23,7 @@ object endpoint { Json.obj("error" -> Json.fromString("something_not_parsed")) case Error.NotValid(_, _) => Json.obj("error" -> Json.fromString("something_not_valid")) - case error: PetstoreError => + case error: CommonError => Json.obj("error" -> Json.fromString(error.message)) } @@ -44,7 +44,7 @@ object endpoint { {{/apis}} {{/apiInfo}} ).handle({ - case e: PetstoreError => NotFound(e) + case e: CommonError => NotFound(e) }).toService } \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/finch/errors.mustache b/modules/swagger-codegen/src/main/resources/finch/errors.mustache index eb90b48052a..e4d8dbce8fd 100644 --- a/modules/swagger-codegen/src/main/resources/finch/errors.mustache +++ b/modules/swagger-codegen/src/main/resources/finch/errors.mustache @@ -1,9 +1,9 @@ package {{packageName}} /** - * The parent error from which most PetstoreAPI errors extend. Thrown whenever something in the api goes wrong. + * The parent error from which most API errors extend. Thrown whenever something in the api goes wrong. */ -abstract class PetstoreError(msg: String) extends Exception(msg) { +abstract class CommonError(msg: String) extends Exception(msg) { def message: String } @@ -11,17 +11,17 @@ abstract class PetstoreError(msg: String) extends Exception(msg) { * Thrown when the object given is invalid * @param message An error message */ -case class InvalidInput(message: String) extends PetstoreError(message) +case class InvalidInput(message: String) extends CommonError(message) /** * Thrown when the given object is missing a unique ID. * @param message An error message */ -case class MissingIdentifier(message: String) extends PetstoreError(message) +case class MissingIdentifier(message: String) extends CommonError(message) /** * Thrown when the given record does not exist in the database. * @param message An error message */ -case class RecordNotFound(message: String) extends PetstoreError(message) +case class RecordNotFound(message: String) extends CommonError(message) diff --git a/modules/swagger-codegen/src/main/resources/finch/project/plugins.sbt b/modules/swagger-codegen/src/main/resources/finch/project/plugins.sbt index 761afa5688e..d005ee813b4 100644 --- a/modules/swagger-codegen/src/main/resources/finch/project/plugins.sbt +++ b/modules/swagger-codegen/src/main/resources/finch/project/plugins.sbt @@ -2,6 +2,6 @@ resolvers += Resolver.typesafeRepo("releases") addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.3") -// addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.1.4") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.5.3") addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.6.0") diff --git a/modules/swagger-codegen/src/main/resources/flash/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/flash/git_push.sh.mustache index e153ce23ecf..a2d75234837 100755 --- a/modules/swagger-codegen/src/main/resources/flash/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/flash/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/flaskConnexion/__init__model.mustache b/modules/swagger-codegen/src/main/resources/flaskConnexion/__init__model.mustache index 764211e2120..98b56e3d154 100644 --- a/modules/swagger-codegen/src/main/resources/flaskConnexion/__init__model.mustache +++ b/modules/swagger-codegen/src/main/resources/flaskConnexion/__init__model.mustache @@ -1,6 +1,7 @@ # coding: utf-8 +# flake8: noqa from __future__ import absolute_import # import models into model package -{{#models}}{{#model}}from .{{classFilename}} import {{classname}}{{/model}} +{{#models}}{{#model}}from {{modelPackage}}.{{classFilename}} import {{classname}}{{/model}} {{/models}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/flaskConnexion/__init__test.mustache b/modules/swagger-codegen/src/main/resources/flaskConnexion/__init__test.mustache index 77b10c3a012..9cea80bc630 100644 --- a/modules/swagger-codegen/src/main/resources/flaskConnexion/__init__test.mustache +++ b/modules/swagger-codegen/src/main/resources/flaskConnexion/__init__test.mustache @@ -1,8 +1,10 @@ -from flask_testing import TestCase -from ..encoder import JSONEncoder -import connexion import logging +import connexion +from flask_testing import TestCase + +from {{packageName}}.encoder import JSONEncoder + class BaseTestCase(TestCase): diff --git a/modules/swagger-codegen/src/main/resources/flaskConnexion/__main__.mustache b/modules/swagger-codegen/src/main/resources/flaskConnexion/__main__.mustache index efbea8409c5..10756347223 100644 --- a/modules/swagger-codegen/src/main/resources/flaskConnexion/__main__.mustache +++ b/modules/swagger-codegen/src/main/resources/flaskConnexion/__main__.mustache @@ -6,11 +6,16 @@ {{/supportPython2}} import connexion -from .encoder import JSONEncoder +from {{packageName}} import encoder -if __name__ == '__main__': + +def main(): app = connexion.App(__name__, specification_dir='./swagger/') - app.app.json_encoder = JSONEncoder - app.add_api('swagger.yaml', arguments={'title': '{{appDescription}}'}) + app.app.json_encoder = encoder.JSONEncoder + app.add_api('swagger.yaml', arguments={'title': '{{appName}}'}) app.run(port={{serverPort}}) + + +if __name__ == '__main__': + main() diff --git a/modules/swagger-codegen/src/main/resources/flaskConnexion/base_model_.mustache b/modules/swagger-codegen/src/main/resources/flaskConnexion/base_model_.mustache index 6e3464c7019..54517a06d53 100644 --- a/modules/swagger-codegen/src/main/resources/flaskConnexion/base_model_.mustache +++ b/modules/swagger-codegen/src/main/resources/flaskConnexion/base_model_.mustache @@ -1,38 +1,39 @@ -from pprint import pformat +import pprint + +import six {{^supportPython2}} -from typing import TypeVar, Type +import typing {{/supportPython2}} -from six import iteritems -from ..util import deserialize_model + +from {{packageName}} import util {{^supportPython2}} -T = TypeVar('T') +T = typing.TypeVar('T') {{/supportPython2}} class Model(object): - # swaggerTypes: The key is attribute name and the value is attribute type. + # swaggerTypes: The key is attribute name and the + # value is attribute type. swagger_types = {} - # attributeMap: The key is attribute name and the value is json key in definition. + # attributeMap: The key is attribute name and the + # value is json key in definition. attribute_map = {} @classmethod - def from_dict(cls{{^supportPython2}}: Type[T]{{/supportPython2}}, dikt){{^supportPython2}} -> T{{/supportPython2}}: - """ - Returns the dict as a model - """ - return deserialize_model(dikt, cls) + def from_dict(cls{{^supportPython2}}: typing.Type[T]{{/supportPython2}}, dikt){{^supportPython2}} -> T{{/supportPython2}}: + """Returns the dict as a model""" + return util.deserialize_model(dikt, cls) def to_dict(self): - """ - Returns the model properties as a dict + """Returns the model properties as a dict :rtype: dict """ result = {} - for attr, _ in iteritems(self.swagger_types): + for attr, _ in six.iteritems(self.swagger_types): value = getattr(self, attr) if isinstance(value, list): result[attr] = list(map( @@ -53,27 +54,20 @@ class Model(object): return result def to_str(self): - """ - Returns the string representation of the model + """Returns the string representation of the model :rtype: str """ - return pformat(self.to_dict()) + return pprint.pformat(self.to_dict()) def __repr__(self): - """ - For `print` and `pprint` - """ + """For `print` and `pprint`""" return self.to_str() def __eq__(self, other): - """ - Returns true if both objects are equal - """ + """Returns true if both objects are equal""" return self.__dict__ == other.__dict__ def __ne__(self, other): - """ - Returns true if both objects are not equal - """ - return not self == other \ No newline at end of file + """Returns true if both objects are not equal""" + return not self == other diff --git a/modules/swagger-codegen/src/main/resources/flaskConnexion/controller.mustache b/modules/swagger-codegen/src/main/resources/flaskConnexion/controller.mustache index 8fc01dbd7ac..b865222c7d2 100644 --- a/modules/swagger-codegen/src/main/resources/flaskConnexion/controller.mustache +++ b/modules/swagger-codegen/src/main/resources/flaskConnexion/controller.mustache @@ -1,18 +1,18 @@ import connexion -{{#imports}}{{import}} +import six + +{{#imports}}{{import}} # noqa: E501 {{/imports}} -from datetime import date, datetime -from typing import List, Dict -from six import iteritems -from ..util import deserialize_date, deserialize_datetime +from {{packageName}} import util {{#operations}} {{#operation}} -def {{operationId}}({{#allParams}}{{paramName}}{{^required}}=None{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}): - """ - {{#summary}}{{.}}{{/summary}}{{^summary}}{{operationId}}{{/summary}} - {{#notes}}{{.}}{{/notes}} +def {{operationId}}({{#allParams}}{{paramName}}{{^required}}=None{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}): # noqa: E501 + """{{#summary}}{{.}}{{/summary}}{{^summary}}{{operationId}}{{/summary}} + + {{#notes}}{{.}}{{/notes}} # noqa: E501 + {{#allParams}} :param {{paramName}}: {{description}} {{^isContainer}} @@ -55,15 +55,15 @@ def {{operationId}}({{#allParams}}{{paramName}}{{^required}}=None{{/required}}{{ {{#allParams}} {{^isContainer}} {{#isDate}} - {{paramName}} = deserialize_date({{paramName}}) + {{paramName}} = util.deserialize_date({{paramName}}) {{/isDate}} {{#isDateTime}} - {{paramName}} = deserialize_datetime({{paramName}}) + {{paramName}} = util.deserialize_datetime({{paramName}}) {{/isDateTime}} {{^isPrimitiveType}} {{^isFile}} if connexion.request.is_json: - {{paramName}} = {{baseType}}.from_dict(connexion.request.get_json()) + {{paramName}} = {{baseType}}.from_dict(connexion.request.get_json()) # noqa: E501 {{/isFile}} {{/isPrimitiveType}} {{/isContainer}} @@ -71,15 +71,15 @@ def {{operationId}}({{#allParams}}{{paramName}}{{^required}}=None{{/required}}{{ {{#items}} {{#isDate}} if connexion.request.is_json: - {{paramName}} = [deserialize_date(s) for s in connexion.request.get_json()] + {{paramName}} = [util.deserialize_date(s) for s in connexion.request.get_json()] # noqa: E501 {{/isDate}} {{#isDateTime}} if connexion.request.is_json: - {{paramName}} = [deserialize_datetime(s) for s in connexion.request.get_json()] + {{paramName}} = [util.deserialize_datetime(s) for s in connexion.request.get_json()] # noqa: E501 {{/isDateTime}} {{#complexType}} if connexion.request.is_json: - {{paramName}} = [{{complexType}}.from_dict(d) for d in connexion.request.get_json()] + {{paramName}} = [{{complexType}}.from_dict(d) for d in connexion.request.get_json()] # noqa: E501 {{/complexType}} {{/items}} {{/isListContainer}} @@ -87,15 +87,15 @@ def {{operationId}}({{#allParams}}{{paramName}}{{^required}}=None{{/required}}{{ {{#items}} {{#isDate}} if connexion.request.is_json: - {{paramName}} = {k: deserialize_date(v) for k, v in iteritems(connexion.request.get_json())} + {{paramName}} = {k: util.deserialize_date(v) for k, v in six.iteritems(connexion.request.get_json())} # noqa: E501 {{/isDate}} {{#isDateTime}} if connexion.request.is_json: - {{paramName}} = {k: deserialize_datetime(v) for k, v in iteritems(connexion.request.get_json())} + {{paramName}} = {k: util.deserialize_datetime(v) for k, v in six.iteritems(connexion.request.get_json())} # noqa: E501 {{/isDateTime}} {{#complexType}} if connexion.request.is_json: - {{paramName}} = {k: {{baseType}}.from_dict(v) for k, v in iteritems(connexion.request.get_json())} + {{paramName}} = {k: {{baseType}}.from_dict(v) for k, v in six.iteritems(connexion.request.get_json())} # noqa: E501 {{/complexType}} {{/items}} {{/isMapContainer}} diff --git a/modules/swagger-codegen/src/main/resources/flaskConnexion/controller_test.mustache b/modules/swagger-codegen/src/main/resources/flaskConnexion/controller_test.mustache index 7b202bb9126..a41b12f2c21 100644 --- a/modules/swagger-codegen/src/main/resources/flaskConnexion/controller_test.mustache +++ b/modules/swagger-codegen/src/main/resources/flaskConnexion/controller_test.mustache @@ -2,20 +2,20 @@ from __future__ import absolute_import -{{#imports}}{{import}} -{{/imports}} -from . import BaseTestCase -from six import BytesIO from flask import json +from six import BytesIO + +{{#imports}}{{import}} # noqa: E501 +{{/imports}} +from {{packageName}}.test import BaseTestCase class {{#operations}}Test{{classname}}(BaseTestCase): - """ {{classname}} integration test stubs """ + """{{classname}} integration test stubs""" {{#operation}} def test_{{operationId}}(self): - """ - Test case for {{{operationId}}} + """Test case for {{{operationId}}} {{{summary}}} """ @@ -31,15 +31,17 @@ class {{#operations}}Test{{classname}}(BaseTestCase): {{#formParams}} {{#-first}}data = dict({{/-first}}{{^-first}} {{/-first}}{{paramName}}={{{example}}}{{#hasMore}},{{/hasMore}}{{#-last}}){{/-last}} {{/formParams}} - response = self.client.open('{{#contextPath}}{{{.}}}{{/contextPath}}{{{path}}}'{{#pathParams}}{{#-first}}.format({{/-first}}{{paramName}}={{{example}}}{{#hasMore}}, {{/hasMore}}{{^hasMore}}){{/hasMore}}{{/pathParams}}, - method='{{httpMethod}}'{{#bodyParam}}, - data=json.dumps({{paramName}}){{^consumes}}, - content_type='application/json'{{/consumes}}{{/bodyParam}}{{#headerParams}}{{#-first}}, - headers=headers{{/-first}}{{/headerParams}}{{#formParams}}{{#-first}}, - data=data{{/-first}}{{/formParams}}{{#consumes}}{{#-first}}, - content_type='{{{mediaType}}}'{{/-first}}{{/consumes}}{{#queryParams}}{{#-first}}, - query_string=query_string{{/-first}}{{/queryParams}}) - self.assert200(response, "Response body is : " + response.data.decode('utf-8')) + response = self.client.open( + '{{#contextPath}}{{{.}}}{{/contextPath}}{{{path}}}'{{#pathParams}}{{#-first}}.format({{/-first}}{{paramName}}={{{example}}}{{#hasMore}}, {{/hasMore}}{{^hasMore}}){{/hasMore}}{{/pathParams}}, + method='{{httpMethod}}'{{#bodyParam}}, + data=json.dumps({{paramName}}){{^consumes}}, + content_type='application/json'{{/consumes}}{{/bodyParam}}{{#headerParams}}{{#-first}}, + headers=headers{{/-first}}{{/headerParams}}{{#formParams}}{{#-first}}, + data=data{{/-first}}{{/formParams}}{{#consumes}}{{#-first}}, + content_type='{{{mediaType}}}'{{/-first}}{{/consumes}}{{#queryParams}}{{#-first}}, + query_string=query_string{{/-first}}{{/queryParams}}) + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) {{/operation}} {{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/flaskConnexion/encoder.mustache b/modules/swagger-codegen/src/main/resources/flaskConnexion/encoder.mustache index 996d74f21e7..e303a0e41a7 100644 --- a/modules/swagger-codegen/src/main/resources/flaskConnexion/encoder.mustache +++ b/modules/swagger-codegen/src/main/resources/flaskConnexion/encoder.mustache @@ -1,6 +1,8 @@ -from six import iteritems -from {{modelPackage}}.base_model_ import Model from connexion.apps.flask_app import FlaskJSONEncoder +import six + +from {{modelPackage}}.base_model_ import Model + class JSONEncoder(FlaskJSONEncoder): include_nulls = False @@ -8,11 +10,11 @@ class JSONEncoder(FlaskJSONEncoder): def default(self, o): if isinstance(o, Model): dikt = {} - for attr, _ in iteritems(o.swagger_types): + for attr, _ in six.iteritems(o.swagger_types): value = getattr(o, attr) if value is None and not self.include_nulls: continue attr = o.attribute_map[attr] dikt[attr] = value return dikt - return FlaskJSONEncoder.default(self, o) \ No newline at end of file + return FlaskJSONEncoder.default(self, o) diff --git a/modules/swagger-codegen/src/main/resources/flaskConnexion/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/flaskConnexion/git_push.sh.mustache index e153ce23ecf..f65b794638f 100755 --- a/modules/swagger-codegen/src/main/resources/flaskConnexion/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/flaskConnexion/git_push.sh.mustache @@ -28,7 +28,7 @@ git init # Adds the files in the local repository and stages them for commit. git add . -# Commits the tracked changes and prepares them to be pushed to a remote repository. +# Commits the tracked changes and prepares them to be pushed to a remote repository. git commit -m "$release_note" # Sets the new remote @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/flaskConnexion/model.mustache b/modules/swagger-codegen/src/main/resources/flaskConnexion/model.mustache index 9a0b4f89247..ba289ef4f8c 100644 --- a/modules/swagger-codegen/src/main/resources/flaskConnexion/model.mustache +++ b/modules/swagger-codegen/src/main/resources/flaskConnexion/model.mustache @@ -1,63 +1,74 @@ # coding: utf-8 from __future__ import absolute_import -{{#imports}}{{import}} +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from {{modelPackage}}.base_model_ import Model +{{#imports}}{{import}} # noqa: F401,E501 {{/imports}} -from .base_model_ import Model -from datetime import date, datetime -from typing import List, Dict -from ..util import deserialize_model +from {{packageName}} import util {{#models}} {{#model}} class {{classname}}(Model): - """ - NOTE: This class is auto generated by the swagger code generator program. + """NOTE: This class is auto generated by the swagger code generator program. + Do not edit the class manually. + """{{#allowableValues}} + """ - def __init__(self{{#vars}}, {{name}}{{^supportPython2}}: {{datatype}}{{/supportPython2}}={{#defaultValue}}{{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}None{{/defaultValue}}{{/vars}}): - """ - {{classname}} - a model defined in Swagger + allowed enum values + """ +{{#enumVars}} + {{name}} = {{{value}}}{{^-last}} +{{/-last}} +{{/enumVars}}{{/allowableValues}} + + def __init__(self{{#vars}}, {{name}}{{^supportPython2}}: {{datatype}}{{/supportPython2}}={{#defaultValue}}{{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}None{{/defaultValue}}{{/vars}}): # noqa: E501 + """{{classname}} - a model defined in Swagger {{#vars}} - :param {{name}}: The {{name}} of this {{classname}}. + :param {{name}}: The {{name}} of this {{classname}}. # noqa: E501 :type {{name}}: {{datatype}} {{/vars}} """ self.swagger_types = { - {{#vars}}'{{name}}': {{{datatype}}}{{#hasMore}}, - {{/hasMore}}{{/vars}} +{{#vars}} + '{{name}}': {{{datatype}}}{{#hasMore}},{{/hasMore}} +{{/vars}} } self.attribute_map = { - {{#vars}}'{{name}}': '{{baseName}}'{{#hasMore}}, - {{/hasMore}}{{/vars}} - } - {{#vars}} + '{{name}}': '{{baseName}}'{{#hasMore}},{{/hasMore}} +{{/vars}} + } +{{#vars}}{{#-first}} +{{/-first}} self._{{name}} = {{name}} {{/vars}} @classmethod def from_dict(cls, dikt){{^supportPython2}} -> '{{classname}}'{{/supportPython2}}: - """ - Returns the dict as a model + """Returns the dict as a model :param dikt: A dict. :type: dict - :return: The {{name}} of this {{classname}}. + :return: The {{name}} of this {{classname}}. # noqa: E501 :rtype: {{classname}} """ - return deserialize_model(dikt, cls) -{{#vars}}{{#-first}} + return util.deserialize_model(dikt, cls){{#vars}}{{#-first}} + {{/-first}} @property def {{name}}(self){{^supportPython2}} -> {{datatype}}{{/supportPython2}}: - """ - Gets the {{name}} of this {{classname}}. + """Gets the {{name}} of this {{classname}}. + {{#description}} - {{{description}}} + {{{description}}} # noqa: E501 {{/description}} :return: The {{name}} of this {{classname}}. @@ -67,31 +78,31 @@ class {{classname}}(Model): @{{name}}.setter def {{name}}(self, {{name}}{{^supportPython2}}: {{datatype}}{{/supportPython2}}): - """ - Sets the {{name}} of this {{classname}}. + """Sets the {{name}} of this {{classname}}. + {{#description}} - {{{description}}} + {{{description}}} # noqa: E501 {{/description}} :param {{name}}: The {{name}} of this {{classname}}. :type {{name}}: {{datatype}} """ {{#isEnum}} - allowed_values = [{{#allowableValues}}{{#values}}"{{{this}}}"{{^-last}}, {{/-last}}{{/values}}{{/allowableValues}}] + allowed_values = [{{#allowableValues}}{{#values}}"{{{this}}}"{{^-last}}, {{/-last}}{{/values}}{{/allowableValues}}] # noqa: E501 {{#isContainer}} {{#isListContainer}} if not set({{{name}}}).issubset(set(allowed_values)): raise ValueError( - "Invalid values for `{{{name}}}` [{0}], must be a subset of [{1}]" - .format(", ".join(map(str, set({{{name}}})-set(allowed_values))), + "Invalid values for `{{{name}}}` [{0}], must be a subset of [{1}]" # noqa: E501 + .format(", ".join(map(str, set({{{name}}}) - set(allowed_values))), # noqa: E501 ", ".join(map(str, allowed_values))) ) {{/isListContainer}} {{#isMapContainer}} if not set({{{name}}}.keys()).issubset(set(allowed_values)): raise ValueError( - "Invalid keys in `{{{name}}}` [{0}], must be a subset of [{1}]" - .format(", ".join(map(str, set({{{name}}}.keys())-set(allowed_values))), + "Invalid keys in `{{{name}}}` [{0}], must be a subset of [{1}]" # noqa: E501 + .format(", ".join(map(str, set({{{name}}}.keys()) - set(allowed_values))), # noqa: E501 ", ".join(map(str, allowed_values))) ) {{/isMapContainer}} @@ -107,42 +118,44 @@ class {{classname}}(Model): {{^isEnum}} {{#required}} if {{name}} is None: - raise ValueError("Invalid value for `{{name}}`, must not be `None`") + raise ValueError("Invalid value for `{{name}}`, must not be `None`") # noqa: E501 {{/required}} {{#hasValidation}} {{#maxLength}} if {{name}} is not None and len({{name}}) > {{maxLength}}: - raise ValueError("Invalid value for `{{name}}`, length must be less than or equal to `{{maxLength}}`") + raise ValueError("Invalid value for `{{name}}`, length must be less than or equal to `{{maxLength}}`") # noqa: E501 {{/maxLength}} {{#minLength}} if {{name}} is not None and len({{name}}) < {{minLength}}: - raise ValueError("Invalid value for `{{name}}`, length must be greater than or equal to `{{minLength}}`") + raise ValueError("Invalid value for `{{name}}`, length must be greater than or equal to `{{minLength}}`") # noqa: E501 {{/minLength}} {{#maximum}} - if {{name}} is not None and {{name}} >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}}: - raise ValueError("Invalid value for `{{name}}`, must be a value less than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}`{{maximum}}`") + if {{name}} is not None and {{name}} >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}}: # noqa: E501 + raise ValueError("Invalid value for `{{name}}`, must be a value less than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}`{{maximum}}`") # noqa: E501 {{/maximum}} {{#minimum}} - if {{name}} is not None and {{name}} <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}}: - raise ValueError("Invalid value for `{{name}}`, must be a value greater than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}`{{minimum}}`") + if {{name}} is not None and {{name}} <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}}: # noqa: E501 + raise ValueError("Invalid value for `{{name}}`, must be a value greater than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}`{{minimum}}`") # noqa: E501 {{/minimum}} {{#pattern}} - if {{name}} is not None and not re.search('{{{vendorExtensions.x-regex}}}', {{name}}{{#vendorExtensions.x-modifiers}}{{#-first}}, flags={{/-first}}re.{{.}}{{^-last}} | {{/-last}}{{/vendorExtensions.x-modifiers}}): - raise ValueError("Invalid value for `{{name}}`, must be a follow pattern or equal to `{{{pattern}}}`") + if {{name}} is not None and not re.search('{{{vendorExtensions.x-regex}}}', {{name}}{{#vendorExtensions.x-modifiers}}{{#-first}}, flags={{/-first}}re.{{.}}{{^-last}} | {{/-last}}{{/vendorExtensions.x-modifiers}}): # noqa: E501 + raise ValueError("Invalid value for `{{name}}`, must be a follow pattern or equal to `{{{pattern}}}`") # noqa: E501 {{/pattern}} {{#maxItems}} if {{name}} is not None and len({{name}}) > {{maxItems}}: - raise ValueError("Invalid value for `{{name}}`, number of items must be less than or equal to `{{maxItems}}`") + raise ValueError("Invalid value for `{{name}}`, number of items must be less than or equal to `{{maxItems}}`") # noqa: E501 {{/maxItems}} {{#minItems}} if {{name}} is not None and len({{name}}) < {{minItems}}: - raise ValueError("Invalid value for `{{name}}`, number of items must be greater than or equal to `{{minItems}}`") + raise ValueError("Invalid value for `{{name}}`, number of items must be greater than or equal to `{{minItems}}`") # noqa: E501 {{/minItems}} {{/hasValidation}} {{/isEnum}} - self._{{name}} = {{name}} + self._{{name}} = {{name}}{{^-last}} +{{/-last}} {{/vars}} + {{/model}} -{{/models}} +{{/models}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/flaskConnexion/requirements.mustache b/modules/swagger-codegen/src/main/resources/flaskConnexion/requirements.mustache index 30ec45ba153..04042d4b1d4 100644 --- a/modules/swagger-codegen/src/main/resources/flaskConnexion/requirements.mustache +++ b/modules/swagger-codegen/src/main/resources/flaskConnexion/requirements.mustache @@ -1,4 +1,4 @@ -connexion == 1.1.9 +connexion == 1.1.15 python_dateutil == 2.6.0 {{#supportPython2}} typing == 3.5.2.2 diff --git a/modules/swagger-codegen/src/main/resources/flaskConnexion/setup.mustache b/modules/swagger-codegen/src/main/resources/flaskConnexion/setup.mustache index dd2dea6aeed..56f8bc1ec17 100644 --- a/modules/swagger-codegen/src/main/resources/flaskConnexion/setup.mustache +++ b/modules/swagger-codegen/src/main/resources/flaskConnexion/setup.mustache @@ -26,6 +26,8 @@ setup( packages=find_packages(), package_data={'': ['swagger/swagger.yaml']}, include_package_data=True, + entry_points={ + 'console_scripts': ['{{packageName}}={{packageName}}.__main__:main']}, long_description="""\ {{appDescription}} """ diff --git a/modules/swagger-codegen/src/main/resources/flaskConnexion/tox.mustache b/modules/swagger-codegen/src/main/resources/flaskConnexion/tox.mustache index 5ad8fe6c031..3efa994317d 100644 --- a/modules/swagger-codegen/src/main/resources/flaskConnexion/tox.mustache +++ b/modules/swagger-codegen/src/main/resources/flaskConnexion/tox.mustache @@ -4,7 +4,7 @@ envlist = {{#supportPython2}}py27, {{/supportPython2}}py35 [testenv] deps=-r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt - + commands= nosetests \ [] \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/flaskConnexion/util.mustache b/modules/swagger-codegen/src/main/resources/flaskConnexion/util.mustache index 40c72d43ebd..527d1424c3d 100644 --- a/modules/swagger-codegen/src/main/resources/flaskConnexion/util.mustache +++ b/modules/swagger-codegen/src/main/resources/flaskConnexion/util.mustache @@ -1,11 +1,11 @@ -from typing import GenericMeta -from datetime import datetime, date -from six import integer_types, iteritems +import datetime + +import six +import typing def _deserialize(data, klass): - """ - Deserializes dict, list, str into an object. + """Deserializes dict, list, str into an object. :param data: dict, list or str. :param klass: class literal, or string of class name. @@ -15,15 +15,15 @@ def _deserialize(data, klass): if data is None: return None - if klass in integer_types or klass in (float, str, bool): + if klass in six.integer_types or klass in (float, str, bool): return _deserialize_primitive(data, klass) elif klass == object: return _deserialize_object(data) - elif klass == date: + elif klass == datetime.date: return deserialize_date(data) - elif klass == datetime: + elif klass == datetime.datetime: return deserialize_datetime(data) - elif type(klass) == GenericMeta: + elif type(klass) == typing.GenericMeta: if klass.__extra__ == list: return _deserialize_list(data, klass.__args__[0]) if klass.__extra__ == dict: @@ -33,8 +33,7 @@ def _deserialize(data, klass): def _deserialize_primitive(data, klass): - """ - Deserializes to primitive type. + """Deserializes to primitive type. :param data: data to deserialize. :param klass: class literal. @@ -45,15 +44,14 @@ def _deserialize_primitive(data, klass): try: value = klass(data) except UnicodeEncodeError: - value = unicode(data) + value = six.u(data) except TypeError: value = data return value def _deserialize_object(value): - """ - Return a original value. + """Return a original value. :return: object. """ @@ -61,8 +59,7 @@ def _deserialize_object(value): def deserialize_date(string): - """ - Deserializes string to date. + """Deserializes string to date. :param string: str. :type string: str @@ -77,8 +74,7 @@ def deserialize_date(string): def deserialize_datetime(string): - """ - Deserializes string to datetime. + """Deserializes string to datetime. The string should be in iso8601 datetime format. @@ -95,8 +91,7 @@ def deserialize_datetime(string): def deserialize_model(data, klass): - """ - Deserializes list or dict to model. + """Deserializes list or dict to model. :param data: dict, list. :type data: dict | list @@ -108,7 +103,7 @@ def deserialize_model(data, klass): if not instance.swagger_types: return data - for attr, attr_type in iteritems(instance.swagger_types): + for attr, attr_type in six.iteritems(instance.swagger_types): if data is not None \ and instance.attribute_map[attr] in data \ and isinstance(data, (list, dict)): @@ -119,8 +114,7 @@ def deserialize_model(data, klass): def _deserialize_list(data, boxed_type): - """ - Deserializes a list and its elements. + """Deserializes a list and its elements. :param data: list to deserialize. :type data: list @@ -133,10 +127,8 @@ def _deserialize_list(data, boxed_type): for sub_data in data] - def _deserialize_dict(data, boxed_type): - """ - Deserializes a dict and its elements. + """Deserializes a dict and its elements. :param data: dict to deserialize. :type data: dict @@ -146,4 +138,4 @@ def _deserialize_dict(data, boxed_type): :rtype: dict """ return {k: _deserialize(v, boxed_type) - for k, v in iteritems(data)} + for k, v in six.iteritems(data)} diff --git a/modules/swagger-codegen/src/main/resources/go/README.mustache b/modules/swagger-codegen/src/main/resources/go/README.mustache index 190beb68810..edca48eed03 100644 --- a/modules/swagger-codegen/src/main/resources/go/README.mustache +++ b/modules/swagger-codegen/src/main/resources/go/README.mustache @@ -38,16 +38,31 @@ Class | Method | HTTP request | Description {{/model}}{{/models}} ## Documentation For Authorization - -{{^authMethods}} All endpoints do not require authorization. +{{^authMethods}} Endpoints do not require authorization. {{/authMethods}}{{#authMethods}}{{#last}} Authentication schemes defined for the API:{{/last}}{{/authMethods}} -{{#authMethods}}## {{{name}}} - +{{#authMethods}} +## {{{name}}} {{#isApiKey}}- **Type**: API key -- **API key parameter name**: {{{keyParamName}}} -- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}} + +Example +``` + auth := context.WithValue(context.Background(), sw.ContextAPIKey, sw.APIKey{ + Key: "APIKEY", + Prefix: "Bearer", // Omit if not necessary. + }) + r, err := client.Service.Operation(auth, args) +``` {{/isApiKey}} {{#isBasic}}- **Type**: HTTP basic authentication + +Example +``` + auth := context.WithValue(context.Background(), sw.ContextBasicAuth, sw.BasicAuth{ + UserName: "username", + Password: "password", + }) + r, err := client.Service.Operation(auth, args) +``` {{/isBasic}} {{#isOAuth}}- **Type**: OAuth - **Flow**: {{{flow}}} @@ -55,8 +70,24 @@ Class | Method | HTTP request | Description - **Scopes**: {{^scopes}}N/A{{/scopes}} {{#scopes}} - **{{{scope}}}**: {{{description}}} {{/scopes}} -{{/isOAuth}} +Example +``` + auth := context.WithValue(context.Background(), sw.ContextAccessToken, "ACCESSTOKENSTRING") + r, err := client.Service.Operation(auth, args) +``` + +Or via OAuth2 module to automaticly refresh tokens and perform user authentication. +``` + import "golang.org/x/oauth2" + + / .. Perform OAuth2 round trip request and obtain a token .. // + + tokenSource := oauth2cfg.TokenSource(createContext(httpClient), &token) + auth := context.WithValue(oauth2.NoContext, sw.ContextOAuth2, tokenSource) + r, err := client.Service.Operation(auth, args) +``` +{{/isOAuth}} {{/authMethods}} ## Author diff --git a/modules/swagger-codegen/src/main/resources/go/api.mustache b/modules/swagger-codegen/src/main/resources/go/api.mustache index 7c2e31ecd41..9a3a91fb41e 100644 --- a/modules/swagger-codegen/src/main/resources/go/api.mustache +++ b/modules/swagger-codegen/src/main/resources/go/api.mustache @@ -4,102 +4,123 @@ package {{packageName}} {{#operations}} import ( "net/url" + "net/http" "strings" + "golang.org/x/net/context" {{#imports}} "{{import}}" {{/imports}} ) -type {{classname}} struct { - Configuration *Configuration -} - -func New{{classname}}() *{{classname}} { - configuration := NewConfiguration() - return &{{classname}}{ - Configuration: configuration, - } -} +// Linger please +var ( + _ context.Context +) -func New{{classname}}WithBasePath(basePath string) *{{classname}} { - configuration := NewConfiguration() - configuration.BasePath = basePath +type {{classname}}Service service - return &{{classname}}{ - Configuration: configuration, - } -} {{#operation}} -/** - * {{summary}}{{#notes}} - * {{notes}}{{/notes}} - * -{{#allParams}} * @param {{paramName}} {{description}} -{{/allParams}} * @return {{#returnType}}{{^isListContainer}}*{{/isListContainer}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} - */ -func (a {{{classname}}}) {{{nickname}}}({{#allParams}}{{paramName}} {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) ({{#returnType}}{{^isListContainer}}*{{/isListContainer}}{{{returnType}}}, {{/returnType}}*APIResponse, error) { +/* {{{classname}}}Service {{summary}}{{#notes}} + {{notes}}{{/notes}} + * @param ctx context.Context for authentication, logging, tracing, etc. +{{#allParams}}{{#required}} @param {{paramName}} {{description}} +{{/required}}{{/allParams}}{{#hasOptionalParams}} @param optional (nil or map[string]interface{}) with one or more of: +{{#allParams}}{{^required}} @param "{{paramName}}" ({{dataType}}) {{description}} +{{/required}}{{/allParams}}{{/hasOptionalParams}} @return {{#returnType}}{{{returnType}}}{{/returnType}}*/ +func (a *{{{classname}}}Service) {{{nickname}}}(ctx context.Context{{#hasParams}}, {{/hasParams}}{{#allParams}}{{#required}}{{paramName}} {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/required}}{{/allParams}}{{#hasOptionalParams}}localVarOptionals map[string]interface{}{{/hasOptionalParams}}) ({{#returnType}}{{{returnType}}}, {{/returnType}} *http.Response, error) { + var ( + localVarHttpMethod = strings.ToUpper("{{httpMethod}}") + localVarPostBody interface{} + localVarFileName string + localVarFileBytes []byte +{{#returnType}} + successPayload {{returnType}} +{{/returnType}} + ) - var localVarHttpMethod = strings.ToUpper("{{httpMethod}}") // create path and map variables - localVarPath := a.Configuration.BasePath + "{{{path}}}"{{#pathParams}} + localVarPath := a.client.cfg.BasePath + "{{{path}}}"{{#pathParams}} localVarPath = strings.Replace(localVarPath, "{"+"{{baseName}}"+"}", fmt.Sprintf("%v", {{paramName}}), -1){{/pathParams}} localVarHeaderParams := make(map[string]string) localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte -{{#authMethods}} - // authentication '({{name}})' required -{{#isApiKey}} -{{#isKeyInHeader}} - // set key with prefix in header - localVarHeaderParams["{{keyParamName}}"] = a.Configuration.GetAPIKeyWithPrefix("{{keyParamName}}") -{{/isKeyInHeader}} -{{#isKeyInQuery}} - // set key with prefix in query string - localVarQueryParams["{{keyParamName}}"] = a.Configuration.GetAPIKeyWithPrefix("{{keyParamName}}") -{{/isKeyInQuery}} -{{/isApiKey}} -{{#isBasic}} - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } -{{/isBasic}} -{{#isOAuth}} - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } -{{/isOAuth}} -{{/authMethods}} - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] + localVarFormParams := url.Values{} + + {{#allParams}} + {{^required}} + {{#isPrimitiveType}} + if err := typeCheckParameter(localVarOptionals["{{paramName}}"], "{{{dataType}}}", "{{paramName}}"); err != nil { + return {{#returnType}}successPayload, {{/returnType}}nil, err + } + {{/isPrimitiveType}} + {{/required}} + {{#required}} + {{#minItems}} + if len({{paramName}}) < {{minItems}} { + return {{#returnType}}successPayload, {{/returnType}}nil, reportError("{{paramName}} must have at least {{minItems}} elements") + } + {{/minItems}} + {{#maxItems}} + if len({{paramName}}) > {{maxItems}} { + return {{#returnType}}successPayload, {{/returnType}}nil, reportError("{{paramName}} must have less than {{maxItems}} elements") + } + {{/maxItems}} + {{#minLength}} + if strlen({{paramName}}) < {{minLength}} { + return {{#returnType}}successPayload, {{/returnType}}nil, reportError("{{paramName}} must have at least {{minLength}} elements") + } + {{/minLength}} + {{#maxLength}} + if strlen({{paramName}}) > {{maxLength}} { + return {{#returnType}}successPayload, {{/returnType}}nil, reportError("{{paramName}} must have less than {{maxLength}} elements") } + {{/maxLength}} + {{#minimum}} + {{#isString}} + {{paramName}}Txt, err := atoi({{paramName}}) + if {{paramName}}Txt < {{minimum}} { + {{/isString}} + {{^isString}} + if {{paramName}} < {{minimum}} { + {{/isString}} + return {{#returnType}}successPayload, {{/returnType}}nil, reportError("{{paramName}} must be greater than {{minimum}}") + } + {{/minimum}} + {{#maximum}} + {{#isString}} + {{paramName}}Txt, err := atoi({{paramName}}) + if {{paramName}}Txt > {{maximum}} { + {{/isString}} + {{^isString}} + if {{paramName}} > {{maximum}} { + {{/isString}} + return {{#returnType}}successPayload, {{/returnType}}nil, reportError("{{paramName}} must be less than {{maximum}}") + } + {{/maximum}} + {{/required}} + {{/allParams}} + {{#hasQueryParams}} {{#queryParams}} - {{#isListContainer}} - var {{paramName}}CollectionFormat = "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}" - localVarQueryParams.Add("{{baseName}}", a.Configuration.APIClient.ParameterToString({{paramName}}, {{paramName}}CollectionFormat)) - - {{/isListContainer}} - {{^isListContainer}} - localVarQueryParams.Add("{{baseName}}", a.Configuration.APIClient.ParameterToString({{paramName}}, "")) - {{/isListContainer}} + {{#required}} + localVarQueryParams.Add("{{baseName}}", parameterToString({{paramName}}, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}")) + {{/required}} + {{^required}} + if localVarTempParam, localVarOk := localVarOptionals["{{paramName}}"].({{dataType}}); localVarOk { + localVarQueryParams.Add("{{baseName}}", parameterToString(localVarTempParam, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}")) + } + {{/required}} {{/queryParams}} {{/hasQueryParams}} - // to determine the Content-Type header localVarHttpContentTypes := []string{ {{#consumes}}"{{{mediaType}}}", {{/consumes}} } // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) + localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) if localVarHttpContentType != "" { localVarHeaderParams["Content-Type"] = localVarHttpContentType } + // to determine the Accept header localVarHttpHeaderAccepts := []string{ {{#produces}} @@ -108,52 +129,99 @@ func (a {{{classname}}}) {{{nickname}}}({{#allParams}}{{paramName}} {{{dataType} } // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) + localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) if localVarHttpHeaderAccept != "" { localVarHeaderParams["Accept"] = localVarHttpHeaderAccept } {{#hasHeaderParams}} {{#headerParams}} - // header params "{{baseName}}" - localVarHeaderParams["{{baseName}}"] = a.Configuration.APIClient.ParameterToString({{paramName}}, "") + {{#required}} + localVarHeaderParams["{{baseName}}"] = parameterToString({{paramName}}, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}") + {{/required}} + {{^required}} + if localVarTempParam, localVarOk := localVarOptionals["{{paramName}}"].({{dataType}}); localVarOk { + localVarHeaderParams["{{baseName}}"] = parameterToString(localVarTempParam, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}") + } + {{/required}} {{/headerParams}} {{/hasHeaderParams}} {{#hasFormParams}} {{#formParams}} {{#isFile}} - fbs, _ := ioutil.ReadAll(file) - localVarFileBytes = fbs - localVarFileName = file.Name() +{{^required}} + var file ({{dataType}}) + if localVarTempParam, localVarOk := localVarOptionals["{{paramName}}"].({{dataType}}); localVarOk { + file = localVarTempParam + } +{{/required}} + if file != nil { + fbs, _ := ioutil.ReadAll(file) + localVarFileBytes = fbs + localVarFileName = file.Name() + file.Close() + } {{/isFile}} {{^isFile}} - localVarFormParams["{{paramName}}"] = a.Configuration.APIClient.ParameterToString({{paramName}}, "") +{{#required}} + localVarFormParams.Add("{{baseName}}", parameterToString({{paramName}}, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}")) +{{/required}} +{{^required}} + if localVarTempParam, localVarOk := localVarOptionals["{{paramName}}"].({{dataType}}); localVarOk { + localVarFormParams.Add("{{baseName}}", parameterToString(localVarTempParam, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}")) + } +{{/required}} {{/isFile}} {{/formParams}} {{/hasFormParams}} {{#hasBodyParam}} {{#bodyParams}} // body params +{{#required}} localVarPostBody = &{{paramName}} +{{/required}} +{{^required}} + if localVarTempParam, localVarOk := localVarOptionals["{{paramName}}"].({{dataType}}); localVarOk { + localVarPostBody = &localVarTempParam + } +{{/required}} {{/bodyParams}} {{/hasBodyParam}} -{{#returnType}} - var successPayload = new({{returnType}}) -{{/returnType}} - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "{{operationId}}", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() +{{#authMethods}} +{{#isApiKey}} + if ctx != nil { + // API Key Authentication + if auth, ok := ctx.Value(ContextAPIKey).(APIKey); ok { + var key string + if auth.Prefix != "" { + key = auth.Prefix + " " + auth.Key + } else { + key = auth.Key + } + {{#isKeyInHeader}}localVarHeaderParams["{{keyParamName}}"] = key{{/isKeyInHeader}}{{#isKeyInQuery}}localVarQueryParams.Add("{{keyParamName}}", key){{/isKeyInQuery}} + } } - +{{/isApiKey}} +{{/authMethods}} + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) if err != nil { - return {{#returnType}}{{#isListContainer}}*{{/isListContainer}}successPayload, {{/returnType}}localVarAPIResponse, err + return {{#returnType}}successPayload, {{/returnType}}nil, err + } + + localVarHttpResponse, err := a.client.callAPI(r) + if err != nil || localVarHttpResponse == nil { + return {{#returnType}}successPayload, {{/returnType}}localVarHttpResponse, err + } + defer localVarHttpResponse.Body.Close() + if localVarHttpResponse.StatusCode >= 300 { + return {{#returnType}}successPayload, {{/returnType}}localVarHttpResponse, reportError(localVarHttpResponse.Status) } {{#returnType}} - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) + + if err = json.NewDecoder(localVarHttpResponse.Body).Decode(&successPayload); err != nil { + return {{#returnType}}successPayload, {{/returnType}}localVarHttpResponse, err + } + {{/returnType}} - return {{#returnType}}{{#isListContainer}}*{{/isListContainer}}successPayload, {{/returnType}}localVarAPIResponse, err + + return {{#returnType}}successPayload, {{/returnType}}localVarHttpResponse, err } {{/operation}}{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/go/api_client.mustache b/modules/swagger-codegen/src/main/resources/go/api_client.mustache index 1a590bf6a1a..952ae7b0cf2 100644 --- a/modules/swagger-codegen/src/main/resources/go/api_client.mustache +++ b/modules/swagger-codegen/src/main/resources/go/api_client.mustache @@ -3,21 +3,81 @@ package {{packageName}} import ( "bytes" + "encoding/json" + "encoding/xml" "fmt" + "errors" + "io" + "mime/multipart" + "golang.org/x/oauth2" + "golang.org/x/net/context" + "net/http" + "net/url" + "time" + "os" "path/filepath" "reflect" + "regexp" "strings" - "net/url" - "io/ioutil" - "gopkg.in/go-resty/resty.v0" + "unicode/utf8" + "strconv" +) + +var ( + jsonCheck = regexp.MustCompile("(?i:[application|text]/json)") + xmlCheck = regexp.MustCompile("(?i:[application|text]/xml)") ) +// APIClient manages communication with the {{appName}} API v{{version}} +// In most cases there should be only one, shared, APIClient. type APIClient struct { - config *Configuration + cfg *Configuration + common service // Reuse a single struct instead of allocating one for each service on the heap. + + // API Services +{{#apiInfo}} +{{#apis}} +{{#operations}} + {{classname}} *{{classname}}Service +{{/operations}} +{{/apis}} +{{/apiInfo}} } -func (c *APIClient) SelectHeaderContentType(contentTypes []string) string { +type service struct { + client *APIClient +} +// NewAPIClient creates a new API client. Requires a userAgent string describing your application. +// optionally a custom http.Client to allow for advanced features such as caching. +func NewAPIClient(cfg *Configuration) *APIClient { + if cfg.HTTPClient == nil { + cfg.HTTPClient = http.DefaultClient + } + + c := &APIClient{} + c.cfg = cfg + c.common.client = c + +{{#apiInfo}} + // API Services +{{#apis}} +{{#operations}} + c.{{classname}} = (*{{classname}}Service)(&c.common) +{{/operations}} +{{/apis}} +{{/apiInfo}} + + return c +} + +func atoi(in string) (int, error) { + return strconv.Atoi(in) +} + + +// selectHeaderContentType select a content type from the available list. +func selectHeaderContentType(contentTypes []string) string { if len(contentTypes) == 0 { return "" } @@ -27,17 +87,20 @@ func (c *APIClient) SelectHeaderContentType(contentTypes []string) string { return contentTypes[0] // use the first content type specified in 'consumes' } -func (c *APIClient) SelectHeaderAccept(accepts []string) string { - +// selectHeaderAccept join all accept types and return +func selectHeaderAccept(accepts []string) string { if len(accepts) == 0 { return "" } + if contains(accepts, "application/json") { return "application/json" } + return strings.Join(accepts, ",") } +// contains is a case insenstive match, finding needle in a haystack func contains(haystack []string, needle string) bool { for _, a := range haystack { if strings.ToLower(a) == strings.ToLower(needle) { @@ -47,40 +110,24 @@ func contains(haystack []string, needle string) bool { return false } -func (c *APIClient) CallAPI(path string, method string, - postBody interface{}, - headerParams map[string]string, - queryParams url.Values, - formParams map[string]string, - fileName string, - fileBytes []byte) (*resty.Response, error) { - - rClient := c.prepareClient() - request := c.prepareRequest(rClient, postBody, headerParams, queryParams, formParams, fileName, fileBytes) - - switch strings.ToUpper(method) { - case "GET": - response, err := request.Get(path) - return response, err - case "POST": - response, err := request.Post(path) - return response, err - case "PUT": - response, err := request.Put(path) - return response, err - case "PATCH": - response, err := request.Patch(path) - return response, err - case "DELETE": - response, err := request.Delete(path) - return response, err - } - - return nil, fmt.Errorf("invalid method %v", method) -} - -func (c *APIClient) ParameterToString(obj interface{}, collectionFormat string) string { - delimiter := "" +// Verify optional parameters are of the correct type. +func typeCheckParameter(obj interface{}, expected string, name string) error { + // Make sure there is an object. + if obj == nil { + return nil + } + + // Check the type is as expected. + if reflect.TypeOf(obj).String() != expected { + return fmt.Errorf("Expected %s to be of type %s but received %s.", name, expected, reflect.TypeOf(obj).String()) + } + return nil +} + +// parameterToString convert interface{} parameters to string, using a delimiter if format is provided. +func parameterToString(obj interface{}, collectionFormat string) string { + var delimiter string + switch collectionFormat { case "pipes": delimiter = "|" @@ -99,57 +146,287 @@ func (c *APIClient) ParameterToString(obj interface{}, collectionFormat string) return fmt.Sprintf("%v", obj) } -func (c *APIClient) prepareClient() *resty.Client { - - rClient := resty.New() - - rClient.SetDebug(c.config.Debug) - if c.config.Transport != nil { - rClient.SetTransport(c.config.Transport) - } +// callAPI do the request. +func (c *APIClient) callAPI(request *http.Request) (*http.Response, error) { + return c.cfg.HTTPClient.Do(request) +} - if c.config.Timeout != nil { - rClient.SetTimeout(*c.config.Timeout) - } - rClient.SetLogger(ioutil.Discard) - return rClient +// Change base path to allow switching to mocks +func (c *APIClient) ChangeBasePath (path string) { + c.cfg.BasePath = path } -func (c *APIClient) prepareRequest( - rClient *resty.Client, +// prepareRequest build the request +func (c *APIClient) prepareRequest ( + ctx context.Context, + path string, method string, postBody interface{}, headerParams map[string]string, queryParams url.Values, - formParams map[string]string, + formParams url.Values, fileName string, - fileBytes []byte) *resty.Request { + fileBytes []byte) (localVarRequest *http.Request, err error) { + + var body *bytes.Buffer + + // Detect postBody type and post. + if postBody != nil { + contentType := headerParams["Content-Type"] + if contentType == "" { + contentType = detectContentType(postBody) + headerParams["Content-Type"] = contentType + } + + body, err = setBody(postBody, contentType) + if err != nil { + return nil, err + } + } + + // add form paramters and file if available. + if len(formParams) > 0 || (len(fileBytes) > 0 && fileName != "") { + if body != nil { + return nil, errors.New("Cannot specify postBody and multipart form at the same time.") + } + body = &bytes.Buffer{} + w := multipart.NewWriter(body) + + for k, v := range formParams { + for _, iv := range v { + if strings.HasPrefix(k, "@") { // file + err = addFile(w, k[1:], iv) + if err != nil { + return nil, err + } + } else { // form value + w.WriteField(k, iv) + } + } + } + if len(fileBytes) > 0 && fileName != "" { + w.Boundary() + //_, fileNm := filepath.Split(fileName) + part, err := w.CreateFormFile("file", filepath.Base(fileName)) + if err != nil { + return nil, err + } + _, err = part.Write(fileBytes) + if err != nil { + return nil, err + } + // Set the Boundary in the Content-Type + headerParams["Content-Type"] = w.FormDataContentType() + } + + // Set Content-Length + headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) + w.Close() + } + + // Setup path and query paramters + url, err := url.Parse(path) + if err != nil { + return nil, err + } + // Adding Query Param + query := url.Query() + for k, v := range queryParams { + for _, iv := range v { + query.Add(k, iv) + } + } - request := rClient.R() - request.SetBody(postBody) + // Encode the parameters. + url.RawQuery = query.Encode() - if c.config.UserAgent != "" { - request.SetHeader("User-Agent", c.config.UserAgent) + // Generate a new request + if body != nil { + localVarRequest, err = http.NewRequest(method, url.String(), body) + } else { + localVarRequest, err = http.NewRequest(method, url.String(), nil) + } + if err != nil { + return nil, err } - // add header parameter, if any + // add header parameters, if any if len(headerParams) > 0 { - request.SetHeaders(headerParams) + headers := http.Header{} + for h, v := range headerParams { + headers.Set(h, v) + } + localVarRequest.Header = headers } - // add query parameter, if any - if len(queryParams) > 0 { - request.SetMultiValueQueryParams(queryParams) + // Override request host, if applicable + if c.cfg.Host != "" { + localVarRequest.Host = c.cfg.Host } + + // Add the user agent to the request. + localVarRequest.Header.Add("User-Agent", c.cfg.UserAgent) + + + if ctx != nil { + // add context to the request + localVarRequest = localVarRequest.WithContext(ctx) + + // Walk through any authentication. + + // OAuth2 authentication + if tok, ok := ctx.Value(ContextOAuth2).(oauth2.TokenSource); ok { + // We were able to grab an oauth2 token from the context + var latestToken *oauth2.Token + if latestToken, err = tok.Token(); err != nil { + return nil, err + } + + latestToken.SetAuthHeader(localVarRequest) + } - // add form parameter, if any - if len(formParams) > 0 { - request.SetFormData(formParams) + // Basic HTTP Authentication + if auth, ok := ctx.Value(ContextBasicAuth).(BasicAuth); ok { + localVarRequest.SetBasicAuth(auth.UserName, auth.Password) + } + + // AccessToken Authentication + if auth, ok := ctx.Value(ContextAccessToken).(string); ok { + localVarRequest.Header.Add("Authorization", "Bearer " + auth) + } } - if len(fileBytes) > 0 && fileName != "" { - _, fileNm := filepath.Split(fileName) - request.SetFileReader("file", fileNm, bytes.NewReader(fileBytes)) + for header, value := range c.cfg.DefaultHeader { + localVarRequest.Header.Add(header, value) } - return request + + return localVarRequest, nil } + + +// Add a file to the multipart request +func addFile(w *multipart.Writer, fieldName, path string) error { + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + + part, err := w.CreateFormFile(fieldName, filepath.Base(path)) + if err != nil { + return err + } + _, err = io.Copy(part, file) + + return err +} + +// Prevent trying to import "fmt" +func reportError(format string, a ...interface{}) (error) { + return fmt.Errorf(format, a...) +} + +// Set request body from an interface{} +func setBody(body interface{}, contentType string) (bodyBuf *bytes.Buffer, err error) { + if bodyBuf == nil { + bodyBuf = &bytes.Buffer{} + } + + if reader, ok := body.(io.Reader); ok { + _, err = bodyBuf.ReadFrom(reader) + } else if b, ok := body.([]byte); ok { + _, err = bodyBuf.Write(b) + } else if s, ok := body.(string); ok { + _, err = bodyBuf.WriteString(s) + } else if jsonCheck.MatchString(contentType) { + err = json.NewEncoder(bodyBuf).Encode(body) + } else if xmlCheck.MatchString(contentType) { + xml.NewEncoder(bodyBuf).Encode(body) + } + + if err != nil { + return nil, err + } + + if bodyBuf.Len() == 0 { + err = fmt.Errorf("Invalid body type %s\n", contentType) + return nil, err + } + return bodyBuf, nil +} + +// detectContentType method is used to figure out `Request.Body` content type for request header +func detectContentType(body interface{}) string { + contentType := "text/plain; charset=utf-8" + kind := reflect.TypeOf(body).Kind() + + switch kind { + case reflect.Struct, reflect.Map, reflect.Ptr: + contentType = "application/json; charset=utf-8" + case reflect.String: + contentType = "text/plain; charset=utf-8" + default: + if b, ok := body.([]byte); ok { + contentType = http.DetectContentType(b) + } else if kind == reflect.Slice { + contentType = "application/json; charset=utf-8" + } + } + + return contentType +} + + +// Ripped from https://github.com/gregjones/httpcache/blob/master/httpcache.go +type cacheControl map[string]string + +func parseCacheControl(headers http.Header) cacheControl { + cc := cacheControl{} + ccHeader := headers.Get("Cache-Control") + for _, part := range strings.Split(ccHeader, ",") { + part = strings.Trim(part, " ") + if part == "" { + continue + } + if strings.ContainsRune(part, '=') { + keyval := strings.Split(part, "=") + cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",") + } else { + cc[part] = "" + } + } + return cc +} + +// CacheExpires helper function to determine remaining time before repeating a request. +func CacheExpires(r *http.Response) (time.Time) { + // Figure out when the cache expires. + var expires time.Time + now, err := time.Parse(time.RFC1123, r.Header.Get("date")) + if err != nil { + return time.Now() + } + respCacheControl := parseCacheControl(r.Header) + + if maxAge, ok := respCacheControl["max-age"]; ok { + lifetime, err := time.ParseDuration(maxAge + "s") + if err != nil { + expires = now + } + expires = now.Add(lifetime) + } else { + expiresHeader := r.Header.Get("Expires") + if expiresHeader != "" { + expires, err = time.Parse(time.RFC1123, expiresHeader) + if err != nil { + expires = now + } + } + } + return expires +} + +func strlen(s string) (int) { + return utf8.RuneCountInString(s) +} + diff --git a/modules/swagger-codegen/src/main/resources/go/api_doc.mustache b/modules/swagger-codegen/src/main/resources/go/api_doc.mustache index 3c3444474ee..635a91e3ed8 100644 --- a/modules/swagger-codegen/src/main/resources/go/api_doc.mustache +++ b/modules/swagger-codegen/src/main/resources/go/api_doc.mustache @@ -11,23 +11,29 @@ Method | HTTP request | Description {{#operations}} {{#operation}} # **{{{operationId}}}** -> {{#returnType}}{{{returnType}}} {{/returnType}}{{{operationId}}}({{#allParams}}${{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) - +> {{#returnType}}{{{returnType}}} {{/returnType}}{{{operationId}}}(ctx, {{#allParams}}{{#required}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/required}}{{/allParams}}{{#hasOptionalParams}}optional{{/hasOptionalParams}}) {{{summary}}}{{#notes}} {{{notes}}}{{/notes}} - -### Parameters +### Required Parameters {{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}} Name | Type | Description | Notes -------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}} -{{#allParams}} **{{paramName}}** | {{#isFile}}**{{dataType}}**{{/isFile}}{{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}[**{{dataType}}**]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} | {{^required}}[optional] {{/required}}{{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}} -{{/allParams}} +------------- | ------------- | ------------- | ------------- + **ctx** | **context.Context** | context for logging, tracing, authentication, etc.{{/-last}}{{/allParams}}{{#allParams}}{{#required}} + **{{paramName}}** | {{#isFile}}**{{dataType}}**{{/isFile}}{{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}[**{{dataType}}**]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} | {{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}}{{/required}}{{/allParams}}{{#hasOptionalParams}} + **optional** | **map[string]interface{}** | optional parameters | nil if no parameters + +### Optional Parameters +Optional parameters are passed through a map[string]interface{}. +{{#allParams}}{{#-last}} +Name | Type | Description | Notes +------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}}{{#allParams}} + **{{paramName}}** | {{#isFile}}**{{dataType}}**{{/isFile}}{{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}[**{{dataType}}**]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} | {{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}}{{/allParams}}{{/hasOptionalParams}} ### Return type -{{#returnType}}{{#returnTypeIsPrimitive}}**{{{returnType}}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{{returnType}}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}void (empty response body){{/returnType}} +{{#returnType}}{{#returnTypeIsPrimitive}}**{{{returnType}}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{{returnType}}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}} (empty response body){{/returnType}} ### Authorization diff --git a/modules/swagger-codegen/src/main/resources/go/configuration.mustache b/modules/swagger-codegen/src/main/resources/go/configuration.mustache index bd3ce67afca..3a13d41ffdd 100644 --- a/modules/swagger-codegen/src/main/resources/go/configuration.mustache +++ b/modules/swagger-codegen/src/main/resources/go/configuration.mustache @@ -2,57 +2,63 @@ package {{packageName}} import ( - "encoding/base64" "net/http" - "time" ) +// contextKeys are used to identify the type of value in the context. +// Since these are string, it is possible to get a short description of the +// context key for logging and debugging using key.String(). + +type contextKey string + +func (c contextKey) String() string { + return "auth " + string(c) +} + +var ( + // ContextOAuth2 takes a oauth2.TokenSource as authentication for the request. + ContextOAuth2 = contextKey("token") + + // ContextBasicAuth takes BasicAuth as authentication for the request. + ContextBasicAuth = contextKey("basic") + + // ContextAccessToken takes a string oauth2 access token as authentication for the request. + ContextAccessToken = contextKey("accesstoken") + + // ContextAPIKey takes an APIKey as authentication for the request + ContextAPIKey = contextKey("apikey") +) + +// BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth +type BasicAuth struct { + UserName string `json:"userName,omitempty"` + Password string `json:"password,omitempty"` +} + +// APIKey provides API key based authentication to a request passed via context using ContextAPIKey +type APIKey struct { + Key string + Prefix string +} type Configuration struct { - Username string `json:"userName,omitempty"` - Password string `json:"password,omitempty"` - APIKeyPrefix map[string]string `json:"APIKeyPrefix,omitempty"` - APIKey map[string]string `json:"APIKey,omitempty"` - Debug bool `json:"debug,omitempty"` - DebugFile string `json:"debugFile,omitempty"` - OAuthToken string `json:"oAuthToken,omitempty"` - BasePath string `json:"basePath,omitempty"` - Host string `json:"host,omitempty"` - Scheme string `json:"scheme,omitempty"` - AccessToken string `json:"accessToken,omitempty"` - DefaultHeader map[string]string `json:"defaultHeader,omitempty"` - UserAgent string `json:"userAgent,omitempty"` - APIClient *APIClient - Transport *http.Transport - Timeout *time.Duration `json:"timeout,omitempty"` + BasePath string `json:"basePath,omitempty"` + Host string `json:"host,omitempty"` + Scheme string `json:"scheme,omitempty"` + DefaultHeader map[string]string `json:"defaultHeader,omitempty"` + UserAgent string `json:"userAgent,omitempty"` + HTTPClient *http.Client } func NewConfiguration() *Configuration { cfg := &Configuration{ BasePath: "{{{basePath}}}", DefaultHeader: make(map[string]string), - APIKey: make(map[string]string), - APIKeyPrefix: make(map[string]string), UserAgent: "{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{{packageVersion}}}/go{{/httpUserAgent}}", - APIClient: &APIClient{}, } - - cfg.APIClient.config = cfg return cfg } -func (c *Configuration) GetBasicAuthEncodedString() string { - return base64.StdEncoding.EncodeToString([]byte(c.Username + ":" + c.Password)) -} - func (c *Configuration) AddDefaultHeader(key string, value string) { c.DefaultHeader[key] = value -} - -func (c *Configuration) GetAPIKeyWithPrefix(APIKeyIdentifier string) string { - if c.APIKeyPrefix[APIKeyIdentifier] != "" { - return c.APIKeyPrefix[APIKeyIdentifier] + " " + c.APIKey[APIKeyIdentifier] - } - - return c.APIKey[APIKeyIdentifier] -} +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/go/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/go/git_push.sh.mustache index e153ce23ecf..a2d75234837 100755 --- a/modules/swagger-codegen/src/main/resources/go/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/go/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/go/model.mustache b/modules/swagger-codegen/src/main/resources/go/model.mustache index 2ed15672ac7..7c9e5c88aab 100644 --- a/modules/swagger-codegen/src/main/resources/go/model.mustache +++ b/modules/swagger-codegen/src/main/resources/go/model.mustache @@ -4,12 +4,21 @@ package {{packageName}} import ({{/imports}}{{#imports}} "{{import}}"{{/imports}}{{#imports}} ) -{{/imports}}{{#model}}{{#description}} +{{/imports}}{{#model}}{{#isEnum}}{{#description}}// {{{classname}}} : {{{description}}}{{/description}} +type {{{name}}} {{^format}}{{dataType}}{{/format}}{{#format}}{{{format}}}{{/format}} + +// List of {{{name}}} +const ( + {{#allowableValues}} + {{#enumVars}} + {{name}} {{{classname}}} = "{{{value}}}" + {{/enumVars}} + {{/allowableValues}} +){{/isEnum}}{{^isEnum}}{{#description}} // {{{description}}}{{/description}} type {{classname}} struct { {{#vars}}{{#description}} // {{{description}}}{{/description}} - {{name}} {{{datatype}}} `json:"{{baseName}}{{^required}},omitempty{{/required}}"` + {{name}} {{^isEnum}}{{^isPrimitiveType}}{{^isContainer}}{{^isDateTime}}*{{/isDateTime}}{{/isContainer}}{{/isPrimitiveType}}{{/isEnum}}{{{datatype}}} `json:"{{baseName}}{{^required}},omitempty{{/required}}"` {{/vars}} -} -{{/model}}{{/models}} \ No newline at end of file +}{{/isEnum}}{{/model}}{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/go/model_doc.mustache b/modules/swagger-codegen/src/main/resources/go/model_doc.mustache index 569550df372..25537b2c5ed 100644 --- a/modules/swagger-codegen/src/main/resources/go/model_doc.mustache +++ b/modules/swagger-codegen/src/main/resources/go/model_doc.mustache @@ -3,7 +3,7 @@ ## Properties Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -{{#vars}}**{{name}}** | {{#isPrimitiveType}}**{{datatype}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{datatype}}**]({{complexType}}.md){{/isPrimitiveType}} | {{description}} | {{^required}}[optional] {{/required}}{{#readOnly}}[readonly] {{/readOnly}}{{#defaultValue}}[default to {{{.}}}]{{/defaultValue}} +{{#vars}}**{{name}}** | {{#isPrimitiveType}}**{{{datatype}}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{^isContainer}}{{^isDateTime}}*{{/isDateTime}}{{/isContainer}}{{{datatype}}}**]({{complexType}}.md){{/isPrimitiveType}} | {{description}} | {{^required}}[optional] {{/required}}{{#readOnly}}[readonly] {{/readOnly}}{{#defaultValue}}[default to {{{.}}}]{{/defaultValue}} {{/vars}} [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/modules/swagger-codegen/src/main/resources/go/partial_header.mustache b/modules/swagger-codegen/src/main/resources/go/partial_header.mustache index b655ebb8269..d24dfec369d 100644 --- a/modules/swagger-codegen/src/main/resources/go/partial_header.mustache +++ b/modules/swagger-codegen/src/main/resources/go/partial_header.mustache @@ -1,4 +1,4 @@ -/* +/* {{#appName}} * {{{appName}}} * @@ -7,7 +7,11 @@ * {{{appDescription}}} * {{/appDescription}} - * {{#version}}OpenAPI spec version: {{{version}}}{{/version}} - * {{#infoEmail}}Contact: {{{infoEmail}}}{{/infoEmail}} - * Generated by: https://github.com/swagger-api/swagger-codegen.git + {{#version}} + * API version: {{{version}}} + {{/version}} + {{#infoEmail}} + * Contact: {{{infoEmail}}} + {{/infoEmail}} + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) */ diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/.travis.yml b/modules/swagger-codegen/src/main/resources/haskell-http-client/.travis.yml new file mode 100644 index 00000000000..a90f612b74d --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/.travis.yml @@ -0,0 +1,16 @@ +sudo: false +language: c +addons: + apt: + packages: + - libgmp-dev +before_install: +- mkdir -p ~/.local/bin +- export PATH=$HOME/.local/bin:$PATH +- travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack' +script: +- stack --install-ghc --no-haddock-deps haddock +- stack test +cache: + directories: + - $HOME/.stack diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/API.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/API.mustache new file mode 100644 index 00000000000..d86611826c7 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/API.mustache @@ -0,0 +1,174 @@ +{{>partial_header}} +{-| +Module : {{title}}.API +-} + +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE InstanceSigs #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-unused-binds -fno-warn-unused-imports #-} + +module {{title}}.API where + +import {{title}}.Core +import {{title}}.MimeTypes +import {{title}}.Model as M + +import qualified Data.Aeson as A +import qualified Data.ByteString as B +import qualified Data.ByteString.Base64 as B64 +import qualified Data.ByteString.Char8 as BC +import qualified Data.ByteString.Lazy as BL +import qualified Data.Data as P (Typeable, TypeRep, typeOf, typeRep) +import qualified Data.Foldable as P +import qualified Data.Map as Map +import qualified Data.Maybe as P +import qualified Data.Proxy as P (Proxy(..)) +import qualified Data.Set as Set +import qualified Data.String as P +import qualified Data.Text as T +import qualified Data.Text.Encoding as T +import qualified Data.Text.Lazy as TL +import qualified Data.Text.Lazy.Encoding as TL +import qualified Data.Time as TI +import qualified GHC.Base as P (Alternative) +import qualified Lens.Micro as L +import qualified Network.HTTP.Client.MultipartFormData as NH +import qualified Network.HTTP.Media as ME +import qualified Network.HTTP.Types as NH +import qualified Web.FormUrlEncoded as WH +import qualified Web.HttpApiData as WH + +import Data.Monoid ((<>)) +import Data.Function ((&)) +import Data.Text (Text) +import GHC.Base ((<|>)) + +import Prelude ((==),(/=),($), (.),(<$>),(<*>),(>>=),Maybe(..),Bool(..),Char,Double,FilePath,Float,Int,Integer,String,fmap,undefined,mempty,maybe,pure,Monad,Applicative,Functor) +import qualified Prelude as P + +-- * Operations +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}{{#vendorExtensions.x-hasNewTag}} + +-- ** {{baseName}}{{/vendorExtensions.x-hasNewTag}} + +-- *** {{operationId}} + +-- | @{{{vendorExtensions.x-haddockPath}}}@ +-- {{#summary}} +-- {{{.}}} +-- {{/summary}}{{#notes}} +-- {{{.}}} +-- {{/notes}}{{#hasAuthMethods}} +-- AuthMethod: {{#authMethods}}'{{{name}}}'{{#hasMore}}, {{/hasMore}}{{/authMethods}} +-- {{/hasAuthMethods}}{{#vendorExtensions.x-hasUnknownReturn}} +-- Note: Has 'Produces' instances, but no response schema +-- {{/vendorExtensions.x-hasUnknownReturn}} +{{operationId}} + :: {{#vendorExtensions.x-hasBodyOrFormParam}}(Consumes {{{vendorExtensions.x-operationType}}} {{>_contentType}}{{#allParams}}{{#isBodyParam}}{{#required}}, MimeRender {{>_contentType}} {{#vendorExtensions.x-paramNameType}}{{{.}}}{{/vendorExtensions.x-paramNameType}}{{^vendorExtensions.x-paramNameType}}{{{dataType}}}{{/vendorExtensions.x-paramNameType}}{{/required}}{{/isBodyParam}}{{/allParams}}) + => {{^vendorExtensions.x-inlineContentType}}ContentType contentType -- ^ request content-type ('MimeType') + -> {{/vendorExtensions.x-inlineContentType}}{{/vendorExtensions.x-hasBodyOrFormParam}}{{^vendorExtensions.x-inlineAccept}}Accept accept -- ^ request accept ('MimeType') + -> {{/vendorExtensions.x-inlineAccept}}{{#allParams}}{{#required}}{{#vendorExtensions.x-paramNameType}}{{{.}}}{{/vendorExtensions.x-paramNameType}}{{^vendorExtensions.x-paramNameType}}{{{dataType}}}{{/vendorExtensions.x-paramNameType}} -- ^ "{{{paramName}}}"{{#description}} - {{{.}}}{{/description}}{{^required}}{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}{{/required}} + -> {{/required}}{{/allParams}}{{requestType}} {{{vendorExtensions.x-operationType}}} {{>_contentType}} {{vendorExtensions.x-returnType}} {{>_accept}} +{{operationId}} {{^vendorExtensions.x-inlineContentType}}_ {{/vendorExtensions.x-inlineContentType}}{{^vendorExtensions.x-inlineAccept}} _ {{/vendorExtensions.x-inlineAccept}}{{#allParams}}{{#required}}{{#isBodyParam}}{{{paramName}}}{{/isBodyParam}}{{^isBodyParam}}({{{vendorExtensions.x-paramNameType}}} {{{paramName}}}){{/isBodyParam}} {{/required}}{{/allParams}}= + _mkRequest "{{httpMethod}}" {{{vendorExtensions.x-path}}}{{#authMethods}} + `_hasAuthType` (P.Proxy :: P.Proxy {{name}}){{/authMethods}}{{#allParams}}{{#required}}{{#isHeaderParam}} + `setHeader` {{>_headerColl}} ("{{{baseName}}}", {{{paramName}}}){{/isHeaderParam}}{{#isQueryParam}} + `setQuery` {{>_queryColl}} ("{{{baseName}}}", Just {{{paramName}}}){{/isQueryParam}}{{#isFormParam}}{{#isFile}} + `_addMultiFormPart` NH.partFileSource "{{{baseName}}}" {{{paramName}}}{{/isFile}}{{^isFile}}{{#isMultipart}} + `_addMultiFormPart` NH.partLBS "{{{baseName}}}" (mimeRender' MimeMultipartFormData {{{paramName}}}){{/isMultipart}}{{^isMultipart}} + `addForm` {{>_formColl}} ("{{{baseName}}}", {{{paramName}}}){{/isMultipart}}{{/isFile}}{{/isFormParam}}{{#isBodyParam}} + `setBodyParam` {{{paramName}}}{{/isBodyParam}}{{/required}}{{/allParams}}{{#isDeprecated}} + +{-# DEPRECATED {{operationId}} "" #-}{{/isDeprecated}} + +data {{{vendorExtensions.x-operationType}}} {{#allParams}}{{#isBodyParam}}{{#description}} + +-- | /Body Param/ "{{{baseName}}}" - {{{description}}}{{/description}} +instance HasBodyParam {{{vendorExtensions.x-operationType}}} {{#vendorExtensions.x-paramNameType}}{{{.}}}{{/vendorExtensions.x-paramNameType}}{{^vendorExtensions.x-paramNameType}}{{{dataType}}}{{/vendorExtensions.x-paramNameType}}{{/isBodyParam}}{{/allParams}} {{#vendorExtensions.x-hasOptionalParams}}{{#allParams}}{{^isBodyParam}}{{^required}}{{#description}} + +-- | /Optional Param/ "{{{baseName}}}" - {{{description}}}{{/description}} +instance HasOptionalParam {{{vendorExtensions.x-operationType}}} {{{vendorExtensions.x-paramNameType}}} where + applyOptionalParam req ({{{vendorExtensions.x-paramNameType}}} xs) = + {{#isHeaderParam}}req `setHeader` {{>_headerColl}} ("{{{baseName}}}", xs){{/isHeaderParam}}{{#isQueryParam}}req `setQuery` {{>_queryColl}} ("{{{baseName}}}", Just xs){{/isQueryParam}}{{#isFormParam}}{{#isFile}}req `_addMultiFormPart` NH.partFileSource "{{{baseName}}}" xs{{/isFile}}{{^isFile}}{{#isMultipart}}req `_addMultiFormPart` NH.partLBS "{{{baseName}}}" (mimeRender' MimeMultipartFormData xs){{/isMultipart}}{{^isMultipart}}req `addForm` {{>_formColl}} ("{{{baseName}}}", xs){{/isMultipart}}{{/isFile}}{{/isFormParam}}{{/required}}{{/isBodyParam}}{{/allParams}}{{/vendorExtensions.x-hasOptionalParams}}{{#hasConsumes}} + +{{#consumes}}-- | @{{{mediaType}}}@ +instance Consumes {{{vendorExtensions.x-operationType}}} {{{x-mediaDataType}}} +{{/consumes}}{{/hasConsumes}}{{#hasProduces}} +{{#produces}}-- | @{{{mediaType}}}@ +instance Produces {{{vendorExtensions.x-operationType}}} {{{x-mediaDataType}}} +{{/produces}}{{/hasProduces}}{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + + +-- * Parameter newtypes +{{#x-allUniqueParams}}{{#x-newtype}} +newtype {{{x-paramNameType}}} = {{{x-paramNameType}}} { un{{{x-paramNameType}}} :: {{{x-dataType}}} } deriving (P.Eq, P.Show{{#x-isBodyParam}}, A.ToJSON{{/x-isBodyParam}}){{/x-newtype}}{{/x-allUniqueParams}} + +{{#authMethods}}{{#-first}}-- * Auth Methods + +{{/-first}}{{#isBasic}}-- ** {{name}} +data {{name}} = + {{name}} B.ByteString B.ByteString -- ^ username password + deriving (P.Eq, P.Show, P.Typeable) + +instance AuthMethod {{name}} where + applyAuthMethod _ a@({{name}} user pw) req = + P.pure $ + if (P.typeOf a `P.elem` rAuthTypes req) + then req `setHeader` toHeader ("Authorization", T.decodeUtf8 cred) + & L.over rAuthTypesL (P.filter (/= P.typeOf a)) + else req + where cred = BC.append "Basic " (B64.encode $ BC.concat [ user, ":", pw ]) + +{{/isBasic}}{{#isApiKey}}-- ** {{name}} +data {{name}} = + {{name}} Text -- ^ secret + deriving (P.Eq, P.Show, P.Typeable) + +instance AuthMethod {{name}} where + applyAuthMethod _ a@({{name}} secret) req = + P.pure $ + if (P.typeOf a `P.elem` rAuthTypes req) + then req {{#isKeyInHeader}}`setHeader` toHeader ("{{keyParamName}}", secret){{/isKeyInHeader}}{{^isKeyInHeader}}`setQuery` toQuery ("{{keyParamName}}", Just secret){{/isKeyInHeader}} + & L.over rAuthTypesL (P.filter (/= P.typeOf a)) + else req + +{{/isApiKey}}{{#isOAuth}}-- ** {{name}} +data {{name}} = + {{name}} Text -- ^ secret + deriving (P.Eq, P.Show, P.Typeable) + +instance AuthMethod {{name}} where + applyAuthMethod _ a@({{name}} secret) req = + P.pure $ + if (P.typeOf a `P.elem` rAuthTypes req) + then req `setHeader` toHeader ("Authorization", "Bearer " <> secret) + & L.over rAuthTypesL (P.filter (/= P.typeOf a)) + else req + +{{/isOAuth}}{{/authMethods}} + +{{#x-hasUnknownMimeTypes}} +-- * Custom Mime Types + +{{#x-unknownMimeTypes}}-- ** {{{x-mediaDataType}}} + +data {{{x-mediaDataType}}} = {{{x-mediaDataType}}} deriving (P.Typeable) + +-- | @{{{mediaType}}}@ +instance MimeType {{{x-mediaDataType}}} where + mimeType _ = Just $ P.fromString "{{{mediaType}}}"{{#x-mediaIsJson}} +instance A.ToJSON a => MimeRender {{{x-mediaDataType}}} a where mimeRender _ = A.encode +instance A.FromJSON a => MimeUnrender {{{x-mediaDataType}}} a where mimeUnrender _ = A.eitherDecode{{/x-mediaIsJson}} +-- instance MimeRender {{{x-mediaDataType}}} T.Text where mimeRender _ = undefined +-- instance MimeUnrender {{{x-mediaDataType}}} T.Text where mimeUnrender _ = undefined + +{{/x-unknownMimeTypes}}{{/x-hasUnknownMimeTypes}} diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/Client.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/Client.mustache new file mode 100644 index 00000000000..643b7425d3f --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/Client.mustache @@ -0,0 +1,208 @@ +{{>partial_header}} +{-| +Module : {{title}}.Client +-} + +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE DeriveFunctor #-} +{-# LANGUAGE DeriveFoldable #-} +{-# LANGUAGE DeriveTraversable #-} +{-# OPTIONS_GHC -fno-warn-unused-binds -fno-warn-unused-imports #-} + +module {{title}}.Client where + +import {{title}}.Core +import {{title}}.Logging +import {{title}}.MimeTypes + +import qualified Control.Exception.Safe as E +import qualified Control.Monad.IO.Class as P +import qualified Control.Monad as P +import qualified Data.Aeson.Types as A +import qualified Data.ByteString.Char8 as BC +import qualified Data.ByteString.Lazy as BL +import qualified Data.ByteString.Lazy.Char8 as BCL +import qualified Data.Proxy as P (Proxy(..)) +import qualified Data.Text as T +import qualified Data.Text.Encoding as T +import qualified Network.HTTP.Client as NH +import qualified Network.HTTP.Client.MultipartFormData as NH +import qualified Network.HTTP.Types as NH +import qualified Web.FormUrlEncoded as WH +import qualified Web.HttpApiData as WH + +import Data.Function ((&)) +import Data.Monoid ((<>)) +import Data.Text (Text) +import GHC.Exts (IsString(..)) + +-- * Dispatch + +-- ** Lbs + +-- | send a request returning the raw http response +dispatchLbs + :: (Produces req accept, MimeType contentType) + => NH.Manager -- ^ http-client Connection manager + -> {{configType}} -- ^ config + -> {{requestType}} req contentType res accept -- ^ request + -> IO (NH.Response BCL.ByteString) -- ^ response +dispatchLbs manager config request = do + initReq <- _toInitRequest config request + dispatchInitUnsafe manager config initReq + +-- ** Mime + +-- | pair of decoded http body and http response +data MimeResult res = + MimeResult { mimeResult :: Either MimeError res -- ^ decoded http body + , mimeResultResponse :: NH.Response BCL.ByteString -- ^ http response + } + deriving (Show, Functor, Foldable, Traversable) + +-- | pair of unrender/parser error and http response +data MimeError = + MimeError { + mimeError :: String -- ^ unrender/parser error + , mimeErrorResponse :: NH.Response BCL.ByteString -- ^ http response + } deriving (Eq, Show) + +-- | send a request returning the 'MimeResult' +dispatchMime + :: forall req contentType res accept. (Produces req accept, MimeUnrender accept res, MimeType contentType) + => NH.Manager -- ^ http-client Connection manager + -> {{configType}} -- ^ config + -> {{requestType}} req contentType res accept -- ^ request + -> IO (MimeResult res) -- ^ response +dispatchMime manager config request = do + httpResponse <- dispatchLbs manager config request + let statusCode = NH.statusCode . NH.responseStatus $ httpResponse + parsedResult <- + runConfigLogWithExceptions "Client" config $ + do if (statusCode >= 400 && statusCode < 600) + then do + let s = "error statusCode: " ++ show statusCode + _log "Client" levelError (T.pack s) + pure (Left (MimeError s httpResponse)) + else case mimeUnrender (P.Proxy :: P.Proxy accept) (NH.responseBody httpResponse) of + Left s -> do + _log "Client" levelError (T.pack s) + pure (Left (MimeError s httpResponse)) + Right r -> pure (Right r) + return (MimeResult parsedResult httpResponse) + +-- | like 'dispatchMime', but only returns the decoded http body +dispatchMime' + :: (Produces req accept, MimeUnrender accept res, MimeType contentType) + => NH.Manager -- ^ http-client Connection manager + -> {{configType}} -- ^ config + -> {{requestType}} req contentType res accept -- ^ request + -> IO (Either MimeError res) -- ^ response +dispatchMime' manager config request = do + MimeResult parsedResult _ <- dispatchMime manager config request + return parsedResult + +-- ** Unsafe + +-- | like 'dispatchReqLbs', but does not validate the operation is a 'Producer' of the "accept" 'MimeType'. (Useful if the server's response is undocumented) +dispatchLbsUnsafe + :: (MimeType accept, MimeType contentType) + => NH.Manager -- ^ http-client Connection manager + -> {{configType}} -- ^ config + -> {{requestType}} req contentType res accept -- ^ request + -> IO (NH.Response BCL.ByteString) -- ^ response +dispatchLbsUnsafe manager config request = do + initReq <- _toInitRequest config request + dispatchInitUnsafe manager config initReq + +-- | dispatch an InitRequest +dispatchInitUnsafe + :: NH.Manager -- ^ http-client Connection manager + -> {{configType}} -- ^ config + -> InitRequest req contentType res accept -- ^ init request + -> IO (NH.Response BCL.ByteString) -- ^ response +dispatchInitUnsafe manager config (InitRequest req) = do + runConfigLogWithExceptions src config $ + do _log src levelInfo requestLogMsg + _log src levelDebug requestDbgLogMsg + res <- P.liftIO $ NH.httpLbs req manager + _log src levelInfo (responseLogMsg res) + _log src levelDebug ((T.pack . show) res) + return res + where + src = "Client" + endpoint = + T.pack $ + BC.unpack $ + NH.method req <> " " <> NH.host req <> NH.path req <> NH.queryString req + requestLogMsg = "REQ:" <> endpoint + requestDbgLogMsg = + "Headers=" <> (T.pack . show) (NH.requestHeaders req) <> " Body=" <> + (case NH.requestBody req of + NH.RequestBodyLBS xs -> T.decodeUtf8 (BL.toStrict xs) + _ -> "") + responseStatusCode = (T.pack . show) . NH.statusCode . NH.responseStatus + responseLogMsg res = + "RES:statusCode=" <> responseStatusCode res <> " (" <> endpoint <> ")" + +-- * InitRequest + +-- | wraps an http-client 'Request' with request/response type parameters +newtype InitRequest req contentType res accept = InitRequest + { unInitRequest :: NH.Request + } deriving (Show) + +-- | Build an http-client 'Request' record from the supplied config and request +_toInitRequest + :: (MimeType accept, MimeType contentType) + => {{configType}} -- ^ config + -> {{requestType}} req contentType res accept -- ^ request + -> IO (InitRequest req contentType res accept) -- ^ initialized request +_toInitRequest config req0 = + runConfigLogWithExceptions "Client" config $ do + parsedReq <- P.liftIO $ NH.parseRequest $ BCL.unpack $ BCL.append (configHost config) (BCL.concat (rUrlPath req0)) + req1 <- P.liftIO $ _applyAuthMethods req0 config + P.when + (configValidateAuthMethods config && (not . null . rAuthTypes) req1) + (E.throw $ AuthMethodException $ "AuthMethod not configured: " <> (show . head . rAuthTypes) req1) + let req2 = req1 & _setContentTypeHeader & _setAcceptHeader + reqHeaders = ("User-Agent", WH.toHeader (configUserAgent config)) : paramsHeaders (rParams req2) + reqQuery = NH.renderQuery True (paramsQuery (rParams req2)) + pReq = parsedReq { NH.method = (rMethod req2) + , NH.requestHeaders = reqHeaders + , NH.queryString = reqQuery + } + outReq <- case paramsBody (rParams req2) of + ParamBodyNone -> pure (pReq { NH.requestBody = mempty }) + ParamBodyB bs -> pure (pReq { NH.requestBody = NH.RequestBodyBS bs }) + ParamBodyBL bl -> pure (pReq { NH.requestBody = NH.RequestBodyLBS bl }) + ParamBodyFormUrlEncoded form -> pure (pReq { NH.requestBody = NH.RequestBodyLBS (WH.urlEncodeForm form) }) + ParamBodyMultipartFormData parts -> NH.formDataBody parts pReq + + pure (InitRequest outReq) + +-- | modify the underlying Request +modifyInitRequest :: InitRequest req contentType res accept -> (NH.Request -> NH.Request) -> InitRequest req contentType res accept +modifyInitRequest (InitRequest req) f = InitRequest (f req) + +-- | modify the underlying Request (monadic) +modifyInitRequestM :: Monad m => InitRequest req contentType res accept -> (NH.Request -> m NH.Request) -> m (InitRequest req contentType res accept) +modifyInitRequestM (InitRequest req) f = fmap InitRequest (f req) + +-- ** Logging + +-- | Run a block using the configured logger instance +runConfigLog + :: P.MonadIO m + => {{configType}} -> LogExec m +runConfigLog config = configLogExecWithContext config (configLogContext config) + +-- | Run a block using the configured logger instance (logs exceptions) +runConfigLogWithExceptions + :: (E.MonadCatch m, P.MonadIO m) + => T.Text -> {{configType}} -> LogExec m +runConfigLogWithExceptions src config = runConfigLog config . logExceptions src diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/Core.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/Core.mustache new file mode 100644 index 00000000000..b40ec37d0d7 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/Core.mustache @@ -0,0 +1,535 @@ +{{>partial_header}} +{-| +Module : {{title}}.Core +-} + +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE TypeFamilies #-} +{-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-unused-binds #-} + +module {{title}}.Core where + +import {{title}}.MimeTypes +import {{title}}.Logging + +import qualified Control.Arrow as P (left) +import qualified Control.DeepSeq as NF +import qualified Control.Exception.Safe as E +import qualified Data.Aeson as A +import qualified Data.ByteString as B +import qualified Data.ByteString.Base64.Lazy as BL64 +import qualified Data.ByteString.Builder as BB +import qualified Data.ByteString.Char8 as BC +import qualified Data.ByteString.Lazy as BL +import qualified Data.ByteString.Lazy.Char8 as BCL +import qualified Data.CaseInsensitive as CI +import qualified Data.Data as P (Data, Typeable, TypeRep, typeRep) +import qualified Data.Foldable as P +import qualified Data.Ix as P +import qualified Data.Maybe as P +import qualified Data.Proxy as P (Proxy(..)) +import qualified Data.Text as T +import qualified Data.Text.Encoding as T +import qualified Data.Time as TI +import qualified Data.Time.ISO8601 as TI +import qualified GHC.Base as P (Alternative) +import qualified Lens.Micro as L +import qualified Network.HTTP.Client.MultipartFormData as NH +import qualified Network.HTTP.Types as NH +import qualified Prelude as P +import qualified Web.FormUrlEncoded as WH +import qualified Web.HttpApiData as WH +import qualified Text.Printf as T + +import Control.Applicative ((<|>)) +import Control.Applicative (Alternative) +import Data.Function ((&)) +import Data.Foldable(foldlM) +import Data.Monoid ((<>)) +import Data.Text (Text) +import Prelude (($), (.), (<$>), (<*>), Maybe(..), Bool(..), Char, String, fmap, mempty, pure, return, show, IO, Monad, Functor) + +-- * {{configType}} + +-- | +data {{configType}} = {{configType}} + { configHost :: BCL.ByteString -- ^ host supplied in the Request + , configUserAgent :: Text -- ^ user-agent supplied in the Request + , configLogExecWithContext :: LogExecWithContext -- ^ Run a block using a Logger instance + , configLogContext :: LogContext -- ^ Configures the logger + , configAuthMethods :: [AnyAuthMethod] -- ^ List of configured auth methods + , configValidateAuthMethods :: Bool -- ^ throw exceptions if auth methods are not configured + } + +-- | display the config +instance P.Show {{configType}} where + show c = + T.printf + "{ configHost = %v, configUserAgent = %v, ..}" + (show (configHost c)) + (show (configUserAgent c)) + +-- | constructs a default {{configType}} +-- +-- configHost: +-- +-- @{{basePath}}@ +-- +-- configUserAgent: +-- +-- @"{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}{{{artifactId}}}/{{{artifactVersion}}}{{/httpUserAgent}}"@ +-- +newConfig :: IO {{configType}} +newConfig = do + logCxt <- initLogContext + return $ {{configType}} + { configHost = "{{{basePath}}}" + , configUserAgent = "{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}{{{artifactId}}}/{{{artifactVersion}}}{{/httpUserAgent}}" + , configLogExecWithContext = runDefaultLogExecWithContext + , configLogContext = logCxt + , configAuthMethods = [] + , configValidateAuthMethods = True + } + +-- | updates config use AuthMethod on matching requests +addAuthMethod :: AuthMethod auth => {{configType}} -> auth -> {{configType}} +addAuthMethod config@{{configType}} {configAuthMethods = as} a = + config { configAuthMethods = AnyAuthMethod a : as} + +-- | updates the config to use stdout logging +withStdoutLogging :: {{configType}} -> IO {{configType}} +withStdoutLogging p = do + logCxt <- stdoutLoggingContext (configLogContext p) + return $ p { configLogExecWithContext = stdoutLoggingExec, configLogContext = logCxt } + +-- | updates the config to use stderr logging +withStderrLogging :: {{configType}} -> IO {{configType}} +withStderrLogging p = do + logCxt <- stderrLoggingContext (configLogContext p) + return $ p { configLogExecWithContext = stderrLoggingExec, configLogContext = logCxt } + +-- | updates the config to disable logging +withNoLogging :: {{configType}} -> {{configType}} +withNoLogging p = p { configLogExecWithContext = runNullLogExec} + +-- * {{requestType}} + +-- | Represents a request. +-- +-- Type Variables: +-- +-- * req - request operation +-- * contentType - 'MimeType' associated with request body +-- * res - response model +-- * accept - 'MimeType' associated with response body +data {{requestType}} req contentType res accept = {{requestType}} + { rMethod :: NH.Method -- ^ Method of {{requestType}} + , rUrlPath :: [BCL.ByteString] -- ^ Endpoint of {{requestType}} + , rParams :: Params -- ^ params of {{requestType}} + , rAuthTypes :: [P.TypeRep] -- ^ types of auth methods + } + deriving (P.Show) + +-- | 'rMethod' Lens +rMethodL :: Lens_' ({{requestType}} req contentType res accept) NH.Method +rMethodL f {{requestType}}{..} = (\rMethod -> {{requestType}} { rMethod, ..} ) <$> f rMethod +{-# INLINE rMethodL #-} + +-- | 'rUrlPath' Lens +rUrlPathL :: Lens_' ({{requestType}} req contentType res accept) [BCL.ByteString] +rUrlPathL f {{requestType}}{..} = (\rUrlPath -> {{requestType}} { rUrlPath, ..} ) <$> f rUrlPath +{-# INLINE rUrlPathL #-} + +-- | 'rParams' Lens +rParamsL :: Lens_' ({{requestType}} req contentType res accept) Params +rParamsL f {{requestType}}{..} = (\rParams -> {{requestType}} { rParams, ..} ) <$> f rParams +{-# INLINE rParamsL #-} + +-- | 'rParams' Lens +rAuthTypesL :: Lens_' ({{requestType}} req contentType res accept) [P.TypeRep] +rAuthTypesL f {{requestType}}{..} = (\rAuthTypes -> {{requestType}} { rAuthTypes, ..} ) <$> f rAuthTypes +{-# INLINE rAuthTypesL #-} + +-- * HasBodyParam + +-- | Designates the body parameter of a request +class HasBodyParam req param where + setBodyParam :: forall contentType res accept. (Consumes req contentType, MimeRender contentType param) => {{requestType}} req contentType res accept -> param -> {{requestType}} req contentType res accept + setBodyParam req xs = + req `_setBodyLBS` mimeRender (P.Proxy :: P.Proxy contentType) xs & _setContentTypeHeader + +-- * HasOptionalParam + +-- | Designates the optional parameters of a request +class HasOptionalParam req param where + {-# MINIMAL applyOptionalParam | (-&-) #-} + + -- | Apply an optional parameter to a request + applyOptionalParam :: {{requestType}} req contentType res accept -> param -> {{requestType}} req contentType res accept + applyOptionalParam = (-&-) + {-# INLINE applyOptionalParam #-} + + -- | infix operator \/ alias for 'addOptionalParam' + (-&-) :: {{requestType}} req contentType res accept -> param -> {{requestType}} req contentType res accept + (-&-) = applyOptionalParam + {-# INLINE (-&-) #-} + +infixl 2 -&- + +-- | Request Params +data Params = Params + { paramsQuery :: NH.Query + , paramsHeaders :: NH.RequestHeaders + , paramsBody :: ParamBody + } + deriving (P.Show) + +-- | 'paramsQuery' Lens +paramsQueryL :: Lens_' Params NH.Query +paramsQueryL f Params{..} = (\paramsQuery -> Params { paramsQuery, ..} ) <$> f paramsQuery +{-# INLINE paramsQueryL #-} + +-- | 'paramsHeaders' Lens +paramsHeadersL :: Lens_' Params NH.RequestHeaders +paramsHeadersL f Params{..} = (\paramsHeaders -> Params { paramsHeaders, ..} ) <$> f paramsHeaders +{-# INLINE paramsHeadersL #-} + +-- | 'paramsBody' Lens +paramsBodyL :: Lens_' Params ParamBody +paramsBodyL f Params{..} = (\paramsBody -> Params { paramsBody, ..} ) <$> f paramsBody +{-# INLINE paramsBodyL #-} + +-- | Request Body +data ParamBody + = ParamBodyNone + | ParamBodyB B.ByteString + | ParamBodyBL BL.ByteString + | ParamBodyFormUrlEncoded WH.Form + | ParamBodyMultipartFormData [NH.Part] + deriving (P.Show) + +-- ** {{requestType}} Utils + +_mkRequest :: NH.Method -- ^ Method + -> [BCL.ByteString] -- ^ Endpoint + -> {{requestType}} req contentType res accept -- ^ req: Request Type, res: Response Type +_mkRequest m u = {{requestType}} m u _mkParams [] + +_mkParams :: Params +_mkParams = Params [] [] ParamBodyNone + +setHeader :: {{requestType}} req contentType res accept -> [NH.Header] -> {{requestType}} req contentType res accept +setHeader req header = + req `removeHeader` P.fmap P.fst header & + L.over (rParamsL . paramsHeadersL) (header P.++) + +removeHeader :: {{requestType}} req contentType res accept -> [NH.HeaderName] -> {{requestType}} req contentType res accept +removeHeader req header = + req & + L.over + (rParamsL . paramsHeadersL) + (P.filter (\h -> cifst h `P.notElem` P.fmap CI.mk header)) + where + cifst = CI.mk . P.fst + + +_setContentTypeHeader :: forall req contentType res accept. MimeType contentType => {{requestType}} req contentType res accept -> {{requestType}} req contentType res accept +_setContentTypeHeader req = + case mimeType (P.Proxy :: P.Proxy contentType) of + Just m -> req `setHeader` [("content-type", BC.pack $ P.show m)] + Nothing -> req `removeHeader` ["content-type"] + +_setAcceptHeader :: forall req contentType res accept. MimeType accept => {{requestType}} req contentType res accept -> {{requestType}} req contentType res accept +_setAcceptHeader req = + case mimeType (P.Proxy :: P.Proxy accept) of + Just m -> req `setHeader` [("accept", BC.pack $ P.show m)] + Nothing -> req `removeHeader` ["accept"] + +setQuery :: {{requestType}} req contentType res accept -> [NH.QueryItem] -> {{requestType}} req contentType res accept +setQuery req query = + req & + L.over + (rParamsL . paramsQueryL) + ((query P.++) . P.filter (\q -> cifst q `P.notElem` P.fmap cifst query)) + where + cifst = CI.mk . P.fst + +addForm :: {{requestType}} req contentType res accept -> WH.Form -> {{requestType}} req contentType res accept +addForm req newform = + let form = case paramsBody (rParams req) of + ParamBodyFormUrlEncoded _form -> _form + _ -> mempty + in req & L.set (rParamsL . paramsBodyL) (ParamBodyFormUrlEncoded (newform <> form)) + +_addMultiFormPart :: {{requestType}} req contentType res accept -> NH.Part -> {{requestType}} req contentType res accept +_addMultiFormPart req newpart = + let parts = case paramsBody (rParams req) of + ParamBodyMultipartFormData _parts -> _parts + _ -> [] + in req & L.set (rParamsL . paramsBodyL) (ParamBodyMultipartFormData (newpart : parts)) + +_setBodyBS :: {{requestType}} req contentType res accept -> B.ByteString -> {{requestType}} req contentType res accept +_setBodyBS req body = + req & L.set (rParamsL . paramsBodyL) (ParamBodyB body) + +_setBodyLBS :: {{requestType}} req contentType res accept -> BL.ByteString -> {{requestType}} req contentType res accept +_setBodyLBS req body = + req & L.set (rParamsL . paramsBodyL) (ParamBodyBL body) + +_hasAuthType :: AuthMethod authMethod => {{requestType}} req contentType res accept -> P.Proxy authMethod -> {{requestType}} req contentType res accept +_hasAuthType req proxy = + req & L.over rAuthTypesL (P.typeRep proxy :) + +-- ** Params Utils + +toPath + :: WH.ToHttpApiData a + => a -> BCL.ByteString +toPath = BB.toLazyByteString . WH.toEncodedUrlPiece + +toHeader :: WH.ToHttpApiData a => (NH.HeaderName, a) -> [NH.Header] +toHeader x = [fmap WH.toHeader x] + +toForm :: WH.ToHttpApiData v => (BC.ByteString, v) -> WH.Form +toForm (k,v) = WH.toForm [(BC.unpack k,v)] + +toQuery :: WH.ToHttpApiData a => (BC.ByteString, Maybe a) -> [NH.QueryItem] +toQuery x = [(fmap . fmap) toQueryParam x] + where toQueryParam = T.encodeUtf8 . WH.toQueryParam + +-- *** Swagger `CollectionFormat` Utils + +-- | Determines the format of the array if type array is used. +data CollectionFormat + = CommaSeparated -- ^ CSV format for multiple parameters. + | SpaceSeparated -- ^ Also called "SSV" + | TabSeparated -- ^ Also called "TSV" + | PipeSeparated -- ^ `value1|value2|value2` + | MultiParamArray -- ^ Using multiple GET parameters, e.g. `foo=bar&foo=baz`. This is valid only for parameters in "query" ('NH.Query') or "formData" ('WH.Form') + +toHeaderColl :: WH.ToHttpApiData a => CollectionFormat -> (NH.HeaderName, [a]) -> [NH.Header] +toHeaderColl c xs = _toColl c toHeader xs + +toFormColl :: WH.ToHttpApiData v => CollectionFormat -> (BC.ByteString, [v]) -> WH.Form +toFormColl c xs = WH.toForm $ fmap unpack $ _toColl c toHeader $ pack xs + where + pack (k,v) = (CI.mk k, v) + unpack (k,v) = (BC.unpack (CI.original k), BC.unpack v) + +toQueryColl :: WH.ToHttpApiData a => CollectionFormat -> (BC.ByteString, Maybe [a]) -> NH.Query +toQueryColl c xs = _toCollA c toQuery xs + +_toColl :: P.Traversable f => CollectionFormat -> (f a -> [(b, BC.ByteString)]) -> f [a] -> [(b, BC.ByteString)] +_toColl c encode xs = fmap (fmap P.fromJust) (_toCollA' c fencode BC.singleton (fmap Just xs)) + where fencode = fmap (fmap Just) . encode . fmap P.fromJust + {-# INLINE fencode #-} + +_toCollA :: (P.Traversable f, P.Traversable t, P.Alternative t) => CollectionFormat -> (f (t a) -> [(b, t BC.ByteString)]) -> f (t [a]) -> [(b, t BC.ByteString)] +_toCollA c encode xs = _toCollA' c encode BC.singleton xs + +_toCollA' :: (P.Monoid c, P.Traversable f, P.Traversable t, P.Alternative t) => CollectionFormat -> (f (t a) -> [(b, t c)]) -> (Char -> c) -> f (t [a]) -> [(b, t c)] +_toCollA' c encode one xs = case c of + CommaSeparated -> go (one ',') + SpaceSeparated -> go (one ' ') + TabSeparated -> go (one '\t') + PipeSeparated -> go (one '|') + MultiParamArray -> expandList + where + go sep = + [P.foldl1 (\(sk, sv) (_, v) -> (sk, (combine sep <$> sv <*> v) <|> sv <|> v)) expandList] + combine sep x y = x <> sep <> y + expandList = (P.concatMap encode . (P.traverse . P.traverse) P.toList) xs + {-# INLINE go #-} + {-# INLINE expandList #-} + {-# INLINE combine #-} + +-- * AuthMethods + +-- | Provides a method to apply auth methods to requests +class P.Typeable a => + AuthMethod a where + applyAuthMethod + :: {{configType}} + -> a + -> {{requestType}} req contentType res accept + -> IO ({{requestType}} req contentType res accept) + +-- | An existential wrapper for any AuthMethod +data AnyAuthMethod = forall a. AuthMethod a => AnyAuthMethod a deriving (P.Typeable) + +instance AuthMethod AnyAuthMethod where applyAuthMethod config (AnyAuthMethod a) req = applyAuthMethod config a req + +-- | indicates exceptions related to AuthMethods +data AuthMethodException = AuthMethodException String deriving (P.Show, P.Typeable) + +instance E.Exception AuthMethodException + +-- | apply all matching AuthMethods in config to request +_applyAuthMethods + :: {{requestType}} req contentType res accept + -> {{configType}} + -> IO ({{requestType}} req contentType res accept) +_applyAuthMethods req config@({{configType}} {configAuthMethods = as}) = + foldlM go req as + where + go r (AnyAuthMethod a) = applyAuthMethod config a r + +-- * Utils + +-- | Removes Null fields. (OpenAPI-Specification 2.0 does not allow Null in JSON) +_omitNulls :: [(Text, A.Value)] -> A.Value +_omitNulls = A.object . P.filter notNull + where + notNull (_, A.Null) = False + notNull _ = True + +-- | Encodes fields using WH.toQueryParam +_toFormItem :: (WH.ToHttpApiData a, Functor f) => t -> f a -> f (t, [Text]) +_toFormItem name x = (name,) . (:[]) . WH.toQueryParam <$> x + +-- | Collapse (Just "") to Nothing +_emptyToNothing :: Maybe String -> Maybe String +_emptyToNothing (Just "") = Nothing +_emptyToNothing x = x +{-# INLINE _emptyToNothing #-} + +-- | Collapse (Just mempty) to Nothing +_memptyToNothing :: (P.Monoid a, P.Eq a) => Maybe a -> Maybe a +_memptyToNothing (Just x) | x P.== P.mempty = Nothing +_memptyToNothing x = x +{-# INLINE _memptyToNothing #-} + +-- * DateTime Formatting + +newtype DateTime = DateTime { unDateTime :: TI.UTCTime } + deriving (P.Eq,P.Data,P.Ord,P.Typeable,NF.NFData,TI.ParseTime,TI.FormatTime) +instance A.FromJSON DateTime where + parseJSON = A.withText "DateTime" (_readDateTime . T.unpack) +instance A.ToJSON DateTime where + toJSON (DateTime t) = A.toJSON (_showDateTime t) +instance WH.FromHttpApiData DateTime where + parseUrlPiece = P.left T.pack . _readDateTime . T.unpack +instance WH.ToHttpApiData DateTime where + toUrlPiece (DateTime t) = T.pack (_showDateTime t) +instance P.Show DateTime where + show (DateTime t) = _showDateTime t +instance MimeRender MimeMultipartFormData DateTime where + mimeRender _ = mimeRenderDefaultMultipartFormData + +-- | @{{^dateTimeFormat}}_parseISO8601{{/dateTimeFormat}}{{#dateTimeFormat}}TI.parseTimeM True TI.defaultTimeLocale "{{{dateTimeFormat}}}"{{/dateTimeFormat}}@ +_readDateTime :: (TI.ParseTime t, Monad m, {{^dateTimeFormat}}Alternative m{{/dateTimeFormat}}) => String -> m t +_readDateTime = + {{^dateTimeFormat}}_parseISO8601{{/dateTimeFormat}}{{#dateTimeFormat}}TI.parseTimeM True TI.defaultTimeLocale "{{{dateTimeFormat}}}"{{/dateTimeFormat}} +{-# INLINE _readDateTime #-} + +-- | @{{^dateTimeFormat}}TI.formatISO8601Millis{{/dateTimeFormat}}{{#dateTimeFormat}}TI.formatTime TI.defaultTimeLocale "{{{dateTimeFormat}}}"{{/dateTimeFormat}}@ +_showDateTime :: ({{^dateTimeFormat}}t ~ TI.UTCTime, {{/dateTimeFormat}}TI.FormatTime t) => t -> String +_showDateTime = + {{^dateTimeFormat}}TI.formatISO8601Millis{{/dateTimeFormat}}{{#dateTimeFormat}}TI.formatTime TI.defaultTimeLocale "{{{dateTimeFormat}}}"{{/dateTimeFormat}} +{-# INLINE _showDateTime #-} + +-- | parse an ISO8601 date-time string +_parseISO8601 :: (TI.ParseTime t, Monad m, Alternative m) => String -> m t +_parseISO8601 t = + P.asum $ + P.flip (TI.parseTimeM True TI.defaultTimeLocale) t <$> + ["%FT%T%QZ", "%FT%T%Q%z", "%FT%T%Q%Z"] +{-# INLINE _parseISO8601 #-} + +-- * Date Formatting + +newtype Date = Date { unDate :: TI.Day } + deriving (P.Enum,P.Eq,P.Data,P.Ord,P.Ix,NF.NFData,TI.ParseTime,TI.FormatTime) +instance A.FromJSON Date where + parseJSON = A.withText "Date" (_readDate . T.unpack) +instance A.ToJSON Date where + toJSON (Date t) = A.toJSON (_showDate t) +instance WH.FromHttpApiData Date where + parseUrlPiece = P.left T.pack . _readDate . T.unpack +instance WH.ToHttpApiData Date where + toUrlPiece (Date t) = T.pack (_showDate t) +instance P.Show Date where + show (Date t) = _showDate t +instance MimeRender MimeMultipartFormData Date where + mimeRender _ = mimeRenderDefaultMultipartFormData + +-- | @TI.parseTimeM True TI.defaultTimeLocale "{{{dateFormat}}}"@ +_readDate :: (TI.ParseTime t, Monad m) => String -> m t +_readDate = + TI.parseTimeM True TI.defaultTimeLocale "{{{dateFormat}}}" +{-# INLINE _readDate #-} + +-- | @TI.formatTime TI.defaultTimeLocale "{{{dateFormat}}}"@ +_showDate :: TI.FormatTime t => t -> String +_showDate = + TI.formatTime TI.defaultTimeLocale "{{{dateFormat}}}" +{-# INLINE _showDate #-} + +-- * Byte/Binary Formatting + + +-- | base64 encoded characters +newtype ByteArray = ByteArray { unByteArray :: BL.ByteString } + deriving (P.Eq,P.Data,P.Ord,P.Typeable,NF.NFData) + +instance A.FromJSON ByteArray where + parseJSON = A.withText "ByteArray" _readByteArray +instance A.ToJSON ByteArray where + toJSON = A.toJSON . _showByteArray +instance WH.FromHttpApiData ByteArray where + parseUrlPiece = P.left T.pack . _readByteArray +instance WH.ToHttpApiData ByteArray where + toUrlPiece = _showByteArray +instance P.Show ByteArray where + show = T.unpack . _showByteArray +instance MimeRender MimeMultipartFormData ByteArray where + mimeRender _ = mimeRenderDefaultMultipartFormData + +-- | read base64 encoded characters +_readByteArray :: Monad m => Text -> m ByteArray +_readByteArray = P.either P.fail (pure . ByteArray) . BL64.decode . BL.fromStrict . T.encodeUtf8 +{-# INLINE _readByteArray #-} + +-- | show base64 encoded characters +_showByteArray :: ByteArray -> Text +_showByteArray = T.decodeUtf8 . BL.toStrict . BL64.encode . unByteArray +{-# INLINE _showByteArray #-} + +-- | any sequence of octets +newtype Binary = Binary { unBinary :: BL.ByteString } + deriving (P.Eq,P.Data,P.Ord,P.Typeable,NF.NFData) + +instance A.FromJSON Binary where + parseJSON = A.withText "Binary" _readBinaryBase64 +instance A.ToJSON Binary where + toJSON = A.toJSON . _showBinaryBase64 +instance WH.FromHttpApiData Binary where + parseUrlPiece = P.left T.pack . _readBinaryBase64 +instance WH.ToHttpApiData Binary where + toUrlPiece = _showBinaryBase64 +instance P.Show Binary where + show = T.unpack . _showBinaryBase64 +instance MimeRender MimeMultipartFormData Binary where + mimeRender _ = unBinary + +_readBinaryBase64 :: Monad m => Text -> m Binary +_readBinaryBase64 = P.either P.fail (pure . Binary) . BL64.decode . BL.fromStrict . T.encodeUtf8 +{-# INLINE _readBinaryBase64 #-} + +_showBinaryBase64 :: Binary -> Text +_showBinaryBase64 = T.decodeUtf8 . BL.toStrict . BL64.encode . unBinary +{-# INLINE _showBinaryBase64 #-} + +-- * Lens Type Aliases + +type Lens_' s a = Lens_ s s a a +type Lens_ s t a b = forall (f :: * -> *). Functor f => (a -> f b) -> s -> f t diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/LoggingKatip.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/LoggingKatip.mustache new file mode 100644 index 00000000000..602775d4a3f --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/LoggingKatip.mustache @@ -0,0 +1,109 @@ +{{>partial_header}} +{-| +Module : {{title}}.Logging +Katip Logging functions +-} + +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module {{title}}.Logging where + +import qualified Control.Exception.Safe as E +import qualified Control.Monad.IO.Class as P +import qualified Control.Monad.Trans.Reader as P +import qualified Data.Text as T +import qualified Lens.Micro as L +import qualified System.IO as IO + +import Data.Text (Text) +import GHC.Exts (IsString(..)) + +import qualified Katip as LG + +-- * Type Aliases (for compatability) + +-- | Runs a Katip logging block with the Log environment +type LogExecWithContext = forall m. P.MonadIO m => + LogContext -> LogExec m + +-- | A Katip logging block +type LogExec m = forall a. LG.KatipT m a -> m a + +-- | A Katip Log environment +type LogContext = LG.LogEnv + +-- | A Katip Log severity +type LogLevel = LG.Severity + +-- * default logger + +-- | the default log environment +initLogContext :: IO LogContext +initLogContext = LG.initLogEnv "{{title}}" "dev" + +-- | Runs a Katip logging block with the Log environment +runDefaultLogExecWithContext :: LogExecWithContext +runDefaultLogExecWithContext = LG.runKatipT + +-- * stdout logger + +-- | Runs a Katip logging block with the Log environment +stdoutLoggingExec :: LogExecWithContext +stdoutLoggingExec = runDefaultLogExecWithContext + +-- | A Katip Log environment which targets stdout +stdoutLoggingContext :: LogContext -> IO LogContext +stdoutLoggingContext cxt = do + handleScribe <- LG.mkHandleScribe LG.ColorIfTerminal IO.stdout LG.InfoS LG.V2 + LG.registerScribe "stdout" handleScribe LG.defaultScribeSettings cxt + +-- * stderr logger + +-- | Runs a Katip logging block with the Log environment +stderrLoggingExec :: LogExecWithContext +stderrLoggingExec = runDefaultLogExecWithContext + +-- | A Katip Log environment which targets stderr +stderrLoggingContext :: LogContext -> IO LogContext +stderrLoggingContext cxt = do + handleScribe <- LG.mkHandleScribe LG.ColorIfTerminal IO.stderr LG.InfoS LG.V2 + LG.registerScribe "stderr" handleScribe LG.defaultScribeSettings cxt + +-- * Null logger + +-- | Disables Katip logging +runNullLogExec :: LogExecWithContext +runNullLogExec le (LG.KatipT f) = P.runReaderT f (L.set LG.logEnvScribes mempty le) + +-- * Log Msg + +-- | Log a katip message +_log :: (Applicative m, LG.Katip m) => Text -> LogLevel -> Text -> m () +_log src level msg = do + LG.logMsg (fromString $ T.unpack src) level (LG.logStr msg) + +-- * Log Exceptions + +-- | re-throws exceptions after logging them +logExceptions + :: (LG.Katip m, E.MonadCatch m, Applicative m) + => Text -> m a -> m a +logExceptions src = + E.handle + (\(e :: E.SomeException) -> do + _log src LG.ErrorS ((T.pack . show) e) + E.throw e) + +-- * Log Level + +levelInfo :: LogLevel +levelInfo = LG.InfoS + +levelError :: LogLevel +levelError = LG.ErrorS + +levelDebug :: LogLevel +levelDebug = LG.DebugS + diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/LoggingMonadLogger.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/LoggingMonadLogger.mustache new file mode 100644 index 00000000000..dccbaf0252a --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/LoggingMonadLogger.mustache @@ -0,0 +1,118 @@ +{{>partial_header}} +{-| +Module : {{title}}.Logging +monad-logger Logging functions +-} + +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module {{title}}.Logging where + +import qualified Control.Exception.Safe as E +import qualified Control.Monad.IO.Class as P +import qualified Data.Text as T +import qualified Data.Time as TI + +import Data.Monoid ((<>)) +import Data.Text (Text) + +import qualified Control.Monad.Logger as LG + +-- * Type Aliases (for compatability) + +-- | Runs a monad-logger block with the filter predicate +type LogExecWithContext = forall m. P.MonadIO m => + LogContext -> LogExec m + +-- | A monad-logger block +type LogExec m = forall a. LG.LoggingT m a -> m a + +-- | A monad-logger filter predicate +type LogContext = LG.LogSource -> LG.LogLevel -> Bool + +-- | A monad-logger log level +type LogLevel = LG.LogLevel + +-- * default logger + +-- | the default log environment +initLogContext :: IO LogContext +initLogContext = pure infoLevelFilter + +-- | Runs a monad-logger block with the filter predicate +runDefaultLogExecWithContext :: LogExecWithContext +runDefaultLogExecWithContext = runNullLogExec + +-- * stdout logger + +-- | Runs a monad-logger block targeting stdout, with the filter predicate +stdoutLoggingExec :: LogExecWithContext +stdoutLoggingExec cxt = LG.runStdoutLoggingT . LG.filterLogger cxt + +-- | @pure@ +stdoutLoggingContext :: LogContext -> IO LogContext +stdoutLoggingContext = pure + +-- * stderr logger + +-- | Runs a monad-logger block targeting stderr, with the filter predicate +stderrLoggingExec :: LogExecWithContext +stderrLoggingExec cxt = LG.runStderrLoggingT . LG.filterLogger cxt + +-- | @pure@ +stderrLoggingContext :: LogContext -> IO LogContext +stderrLoggingContext = pure + +-- * Null logger + +-- | Disables monad-logger logging +runNullLogExec :: LogExecWithContext +runNullLogExec = const (`LG.runLoggingT` nullLogger) + +-- | monad-logger which does nothing +nullLogger :: LG.Loc -> LG.LogSource -> LG.LogLevel -> LG.LogStr -> IO () +nullLogger _ _ _ _ = return () + +-- * Log Msg + +-- | Log a message using the current time +_log :: (P.MonadIO m, LG.MonadLogger m) => Text -> LG.LogLevel -> Text -> m () +_log src level msg = do + now <- P.liftIO (formatTimeLog <$> TI.getCurrentTime) + LG.logOtherNS ("{{title}}." <> src) level ("[" <> now <> "] " <> msg) + where + formatTimeLog = + T.pack . TI.formatTime TI.defaultTimeLocale "%Y-%m-%dT%H:%M:%S%Z" + +-- * Log Exceptions + +-- | re-throws exceptions after logging them +logExceptions + :: (LG.MonadLogger m, E.MonadCatch m, P.MonadIO m) + => Text -> m a -> m a +logExceptions src = + E.handle + (\(e :: E.SomeException) -> do + _log src LG.LevelError ((T.pack . show) e) + E.throw e) + +-- * Log Level + +levelInfo :: LogLevel +levelInfo = LG.LevelInfo + +levelError :: LogLevel +levelError = LG.LevelError + +levelDebug :: LogLevel +levelDebug = LG.LevelDebug + +-- * Level Filter + +minLevelFilter :: LG.LogLevel -> LG.LogSource -> LG.LogLevel -> Bool +minLevelFilter l _ l' = l' >= l + +infoLevelFilter :: LG.LogSource -> LG.LogLevel -> Bool +infoLevelFilter = minLevelFilter LG.LevelInfo diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/MimeTypes.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/MimeTypes.mustache new file mode 100644 index 00000000000..072a8634c8d --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/MimeTypes.mustache @@ -0,0 +1,192 @@ +{{>partial_header}} +{-| +Module : {{title}}.MimeTypes +-} + +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# OPTIONS_GHC -fno-warn-unused-binds -fno-warn-unused-imports #-} + +module {{title}}.MimeTypes where + +import qualified Control.Arrow as P (left) +import qualified Data.Aeson as A +import qualified Data.ByteString as B +import qualified Data.ByteString.Builder as BB +import qualified Data.ByteString.Char8 as BC +import qualified Data.ByteString.Lazy as BL +import qualified Data.ByteString.Lazy.Char8 as BCL +import qualified Data.Data as P (Typeable) +import qualified Data.Proxy as P (Proxy(..)) +import qualified Data.String as P +import qualified Data.Text as T +import qualified Data.Text.Encoding as T +import qualified Network.HTTP.Media as ME +import qualified Web.FormUrlEncoded as WH +import qualified Web.HttpApiData as WH + +import Prelude (($), (.),(<$>),(<*>),Maybe(..),Bool(..),Char,Double,FilePath,Float,Int,Integer,String,fmap,undefined,mempty) +import qualified Prelude as P + +-- * ContentType MimeType + +data ContentType a = MimeType a => ContentType { unContentType :: a } + +-- * Accept MimeType + +data Accept a = MimeType a => Accept { unAccept :: a } + +-- * Consumes Class + +class MimeType mtype => Consumes req mtype where + +-- * Produces Class + +class MimeType mtype => Produces req mtype where + +-- * Default Mime Types + +data MimeJSON = MimeJSON deriving (P.Typeable) +data MimeXML = MimeXML deriving (P.Typeable) +data MimePlainText = MimePlainText deriving (P.Typeable) +data MimeFormUrlEncoded = MimeFormUrlEncoded deriving (P.Typeable) +data MimeMultipartFormData = MimeMultipartFormData deriving (P.Typeable) +data MimeOctetStream = MimeOctetStream deriving (P.Typeable) +data MimeNoContent = MimeNoContent deriving (P.Typeable) +data MimeAny = MimeAny deriving (P.Typeable) + +-- | A type for responses without content-body. +data NoContent = NoContent + deriving (P.Show, P.Eq, P.Typeable) + + +-- * MimeType Class + +class P.Typeable mtype => MimeType mtype where + {-# MINIMAL mimeType | mimeTypes #-} + + mimeTypes :: P.Proxy mtype -> [ME.MediaType] + mimeTypes p = + case mimeType p of + Just x -> [x] + Nothing -> [] + + mimeType :: P.Proxy mtype -> Maybe ME.MediaType + mimeType p = + case mimeTypes p of + [] -> Nothing + (x:_) -> Just x + + mimeType' :: mtype -> Maybe ME.MediaType + mimeType' _ = mimeType (P.Proxy :: P.Proxy mtype) + mimeTypes' :: mtype -> [ME.MediaType] + mimeTypes' _ = mimeTypes (P.Proxy :: P.Proxy mtype) + +-- Default MimeType Instances + +-- | @application/json; charset=utf-8@ +instance MimeType MimeJSON where + mimeType _ = Just $ P.fromString "application/json" +-- | @application/xml; charset=utf-8@ +instance MimeType MimeXML where + mimeType _ = Just $ P.fromString "application/xml" +-- | @application/x-www-form-urlencoded@ +instance MimeType MimeFormUrlEncoded where + mimeType _ = Just $ P.fromString "application/x-www-form-urlencoded" +-- | @multipart/form-data@ +instance MimeType MimeMultipartFormData where + mimeType _ = Just $ P.fromString "multipart/form-data" +-- | @text/plain; charset=utf-8@ +instance MimeType MimePlainText where + mimeType _ = Just $ P.fromString "text/plain" +-- | @application/octet-stream@ +instance MimeType MimeOctetStream where + mimeType _ = Just $ P.fromString "application/octet-stream" +-- | @"*/*"@ +instance MimeType MimeAny where + mimeType _ = Just $ P.fromString "*/*" +instance MimeType MimeNoContent where + mimeType _ = Nothing + +-- * MimeRender Class + +class MimeType mtype => MimeRender mtype x where + mimeRender :: P.Proxy mtype -> x -> BL.ByteString + mimeRender' :: mtype -> x -> BL.ByteString + mimeRender' _ x = mimeRender (P.Proxy :: P.Proxy mtype) x + + +mimeRenderDefaultMultipartFormData :: WH.ToHttpApiData a => a -> BL.ByteString +mimeRenderDefaultMultipartFormData = BL.fromStrict . T.encodeUtf8 . WH.toQueryParam + +-- Default MimeRender Instances + +-- | `A.encode` +instance A.ToJSON a => MimeRender MimeJSON a where mimeRender _ = A.encode +-- | @WH.urlEncodeAsForm@ +instance WH.ToForm a => MimeRender MimeFormUrlEncoded a where mimeRender _ = WH.urlEncodeAsForm + +-- | @P.id@ +instance MimeRender MimePlainText BL.ByteString where mimeRender _ = P.id +-- | @BL.fromStrict . T.encodeUtf8@ +instance MimeRender MimePlainText T.Text where mimeRender _ = BL.fromStrict . T.encodeUtf8 +-- | @BCL.pack@ +instance MimeRender MimePlainText String where mimeRender _ = BCL.pack + +-- | @P.id@ +instance MimeRender MimeOctetStream BL.ByteString where mimeRender _ = P.id +-- | @BL.fromStrict . T.encodeUtf8@ +instance MimeRender MimeOctetStream T.Text where mimeRender _ = BL.fromStrict . T.encodeUtf8 +-- | @BCL.pack@ +instance MimeRender MimeOctetStream String where mimeRender _ = BCL.pack + +instance MimeRender MimeMultipartFormData BL.ByteString where mimeRender _ = P.id + +instance MimeRender MimeMultipartFormData Bool where mimeRender _ = mimeRenderDefaultMultipartFormData +instance MimeRender MimeMultipartFormData Char where mimeRender _ = mimeRenderDefaultMultipartFormData +instance MimeRender MimeMultipartFormData Double where mimeRender _ = mimeRenderDefaultMultipartFormData +instance MimeRender MimeMultipartFormData Float where mimeRender _ = mimeRenderDefaultMultipartFormData +instance MimeRender MimeMultipartFormData Int where mimeRender _ = mimeRenderDefaultMultipartFormData +instance MimeRender MimeMultipartFormData Integer where mimeRender _ = mimeRenderDefaultMultipartFormData +instance MimeRender MimeMultipartFormData String where mimeRender _ = mimeRenderDefaultMultipartFormData +instance MimeRender MimeMultipartFormData T.Text where mimeRender _ = mimeRenderDefaultMultipartFormData + +-- | @P.Right . P.const NoContent@ +instance MimeRender MimeNoContent NoContent where mimeRender _ = P.const BCL.empty + + +-- * MimeUnrender Class + +class MimeType mtype => MimeUnrender mtype o where + mimeUnrender :: P.Proxy mtype -> BL.ByteString -> P.Either String o + mimeUnrender' :: mtype -> BL.ByteString -> P.Either String o + mimeUnrender' _ x = mimeUnrender (P.Proxy :: P.Proxy mtype) x + +-- Default MimeUnrender Instances + +-- | @A.eitherDecode@ +instance A.FromJSON a => MimeUnrender MimeJSON a where mimeUnrender _ = A.eitherDecode +-- | @P.left T.unpack . WH.urlDecodeAsForm@ +instance WH.FromForm a => MimeUnrender MimeFormUrlEncoded a where mimeUnrender _ = P.left T.unpack . WH.urlDecodeAsForm +-- | @P.Right . P.id@ + +instance MimeUnrender MimePlainText BL.ByteString where mimeUnrender _ = P.Right . P.id +-- | @P.left P.show . TL.decodeUtf8'@ +instance MimeUnrender MimePlainText T.Text where mimeUnrender _ = P.left P.show . T.decodeUtf8' . BL.toStrict +-- | @P.Right . BCL.unpack@ +instance MimeUnrender MimePlainText String where mimeUnrender _ = P.Right . BCL.unpack + +-- | @P.Right . P.id@ +instance MimeUnrender MimeOctetStream BL.ByteString where mimeUnrender _ = P.Right . P.id +-- | @P.left P.show . T.decodeUtf8' . BL.toStrict@ +instance MimeUnrender MimeOctetStream T.Text where mimeUnrender _ = P.left P.show . T.decodeUtf8' . BL.toStrict +-- | @P.Right . BCL.unpack@ +instance MimeUnrender MimeOctetStream String where mimeUnrender _ = P.Right . BCL.unpack + +-- | @P.Right . P.const NoContent@ +instance MimeUnrender MimeNoContent NoContent where mimeUnrender _ = P.Right . P.const NoContent diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/Model.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/Model.mustache new file mode 100644 index 00000000000..3613d9588d1 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/Model.mustache @@ -0,0 +1,142 @@ +{{>partial_header}} +{-| +Module : {{title}}.Model +-} + +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE DeriveFoldable #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DeriveTraversable #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE TypeFamilies #-} +{-# OPTIONS_GHC -fno-warn-unused-matches -fno-warn-unused-binds -fno-warn-unused-imports #-} + +module {{title}}.Model where + +import {{title}}.Core +import {{title}}.MimeTypes + +import Data.Aeson ((.:),(.:!),(.:?),(.=)) + +import qualified Control.Arrow as P (left) +import qualified Data.Aeson as A +import qualified Data.ByteString as B +import qualified Data.ByteString.Lazy as BL +import qualified Data.Data as P (Data, Typeable) +import qualified Data.Foldable as P +import qualified Data.HashMap.Lazy as HM +import qualified Data.Map as Map +import qualified Data.Maybe as P +import qualified Data.Set as Set +import qualified Data.Text as T +import qualified Data.Text.Encoding as T +import qualified Data.Time as TI +import qualified Web.FormUrlEncoded as WH +import qualified Web.HttpApiData as WH + +import Control.Applicative ((<|>)) +import Control.Applicative (Alternative) +import Data.Text (Text) +import Prelude (($), (.),(<$>),(<*>),(>>=),(=<<),Maybe(..),Bool(..),Char,Double,FilePath,Float,Int,Integer,String,fmap,undefined,mempty,maybe,pure,Monad,Applicative,Functor) + +import qualified Prelude as P + + +{{#imports}}import {{import}} +{{/imports}} + +-- * Models + +{{#models}} +{{#model}}{{^isEnum}} +-- ** {{classname}} +-- | {{classname}}{{#title}} +-- {{{.}}} +-- {{/title}}{{#description}} +-- {{{.}}}{{/description}}{{#isAlias}} +newtype {{classname}} = {{classname}} + { un{{classname}} :: {{{vendorExtensions.x-dataType}}} + } deriving (P.Eq, P.Show, P.Typeable, A.ToJSON, A.FromJSON, WH.ToHttpApiData, WH.FromHttpApiData{{#modelDeriving}}, {{modelDeriving}}{{/modelDeriving}}){{/isAlias}}{{^isAlias}} +data {{classname}} = {{classname}} + { {{#vars}}{{name}} :: {{#x-strictFields}}!({{/x-strictFields}}{{^required}}Maybe {{/required}}{{{vendorExtensions.x-dataType}}}{{#x-strictFields}}){{/x-strictFields}} -- ^ {{#required}}/Required/ {{/required}}{{#readOnly}}/ReadOnly/ {{/readOnly}}"{{baseName}}"{{#description}} - {{description}}{{/description}}{{#hasMore}} + , {{/hasMore}}{{/vars}} + } deriving (P.Show, P.Eq, P.Typeable{{#modelDeriving}}, {{modelDeriving}}{{/modelDeriving}}){{/isAlias}} + +{{^isAlias}}-- | FromJSON {{classname}} +instance A.FromJSON {{classname}} where + parseJSON = A.withObject "{{classname}}" $ \o -> + {{^hasVars}}pure {{/hasVars}}{{classname}} + {{#hasVars}}<$>{{/hasVars}}{{#vars}} (o {{#required}}.: {{/required}}{{^required}}{{^allowFromJsonNulls}}.:!{{/allowFromJsonNulls}}{{#allowFromJsonNulls}}.:?{{/allowFromJsonNulls}}{{/required}} "{{baseName}}"){{#hasMore}} + <*>{{/hasMore}}{{/vars}} + +-- | ToJSON {{classname}} +instance A.ToJSON {{classname}} where + toJSON {{classname}} {{#hasVars}}{..}{{/hasVars}} = + {{^allowToJsonNulls}}_omitNulls{{/allowToJsonNulls}}{{#allowToJsonNulls}}A.object{{/allowToJsonNulls}} + [ {{#vars}}"{{baseName}}" .= {{name}}{{#hasMore}} + , {{/hasMore}}{{/vars}} + ] + +{{#vendorExtensions.x-hasMimeFormUrlEncoded}} +-- | FromForm {{classname}} +instance WH.FromForm {{classname}} where + fromForm f = + {{^hasVars}}pure {{/hasVars}}{{classname}} + {{#hasVars}}<$>{{/hasVars}}{{#vars}} ({{#required}}WH.parseUnique {{/required}}{{^required}}WH.parseMaybe {{/required}}"{{baseName}}" f){{#hasMore}} + <*>{{/hasMore}}{{/vars}} + +-- | ToForm {{classname}} +instance WH.ToForm {{classname}} where + toForm {{classname}} {{#hasVars}}{..}{{/hasVars}} = + WH.Form $ HM.fromList $ P.catMaybes $ + [ {{#vars}}_toFormItem "{{baseName}}" ({{#required}}Just {{/required}}{{name}}){{#hasMore}} + , {{/hasMore}}{{/vars}} + ] +{{/vendorExtensions.x-hasMimeFormUrlEncoded}} + +{{#generateModelConstructors}} +-- | Construct a value of type '{{classname}}' (by applying it's required fields, if any) +mk{{classname}} + :: {{#requiredVars}}{{{vendorExtensions.x-dataType}}} -- ^ '{{name}}'{{#description}}:{{/description}} {{{description}}} + -> {{/requiredVars}}{{classname}} +mk{{classname}} {{#requiredVars}}{{name}} {{/requiredVars}}= + {{classname}} + { {{#vars}}{{#required}}{{name}}{{/required}}{{^required}}{{name}} = {{#isListContainer}}Nothing{{/isListContainer}}{{#isMapContainer}}Nothing{{/isMapContainer}}{{^isContainer}}Nothing{{/isContainer}}{{/required}}{{#hasMore}} + , {{/hasMore}}{{/vars}} + } +{{/generateModelConstructors}}{{/isAlias}}{{/isEnum}}{{/model}}{{/models}} + +{{#x-hasEnumSection}}-- * Enums +{{#x-allUniqueParams}}{{#x-enum}} + +-- ** {{{x-paramNameType}}} + +-- | Enum of '{{{x-dataType}}}'{{#description}} . +-- {{{.}}}{{/description}} +data {{{x-paramNameType}}} + = {{#allowableValues}}{{#enumVars}}{{{name}}} -- ^ @{{{value}}}@ + {{^-last}}| {{/-last}}{{#-last}}deriving (P.Show, P.Eq, P.Typeable, P.Ord, P.Bounded, P.Enum){{/-last}}{{/enumVars}}{{/allowableValues}} + +instance A.ToJSON {{{x-paramNameType}}} where toJSON = A.toJSON . from{{{x-paramNameType}}} +instance A.FromJSON {{{x-paramNameType}}} where parseJSON o = P.either P.fail (pure . P.id) . to{{{x-paramNameType}}} =<< A.parseJSON o +instance WH.ToHttpApiData {{{x-paramNameType}}} where toQueryParam = WH.toQueryParam . from{{{x-paramNameType}}} +instance WH.FromHttpApiData {{{x-paramNameType}}} where parseQueryParam o = WH.parseQueryParam o >>= P.left T.pack . to{{{x-paramNameType}}} +instance MimeRender MimeMultipartFormData {{{x-paramNameType}}} where mimeRender _ = mimeRenderDefaultMultipartFormData + +-- | unwrap '{{{x-paramNameType}}}' enum +from{{{x-paramNameType}}} :: {{{x-paramNameType}}} -> {{{x-dataType}}} +from{{{x-paramNameType}}} = \case{{#allowableValues}}{{#enumVars}} + {{{name}}} -> {{{value}}}{{/enumVars}}{{/allowableValues}} + +-- | parse '{{{x-paramNameType}}}' enum +to{{{x-paramNameType}}} :: {{{x-dataType}}} -> P.Either String {{{x-paramNameType}}} +to{{{x-paramNameType}}} = \case{{#allowableValues}}{{#enumVars}} + {{{value}}} -> P.Right {{{name}}}{{/enumVars}}{{/allowableValues}} + s -> P.Left $ "to{{{x-paramNameType}}}: enum parse failure: " P.++ P.show s +{{/x-enum}}{{/x-allUniqueParams}}{{/x-hasEnumSection}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/ModelLens.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/ModelLens.mustache new file mode 100644 index 00000000000..d15a4ba8336 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/ModelLens.mustache @@ -0,0 +1,43 @@ +{{>partial_header}} +{-| +Module : {{title}}.Lens +-} + +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE RecordWildCards #-} +{-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-unused-matches -fno-warn-unused-binds -fno-warn-unused-imports #-} + +module {{title}}.ModelLens where + +import qualified Data.Aeson as A +import qualified Data.ByteString.Lazy as BL +import qualified Data.Data as P (Data, Typeable) +import qualified Data.Map as Map +import qualified Data.Set as Set +import qualified Data.Time as TI + +import Data.Text (Text) + +import Prelude (($), (.),(<$>),(<*>),(=<<),Maybe(..),Bool(..),Char,Double,FilePath,Float,Int,Integer,String,fmap,undefined,mempty,maybe,pure,Monad,Applicative,Functor) +import qualified Prelude as P + +import {{title}}.Model +import {{title}}.Core + +{{#models}} +{{#model}} + +-- * {{classname}} + +{{#vars}} +-- | '{{name}}' Lens +{{name}}L :: Lens_' {{classname}} ({{^required}}Maybe {{/required}}{{{vendorExtensions.x-dataType}}}) +{{name}}L f {{classname}}{..} = (\{{name}} -> {{classname}} { {{name}}, ..} ) <$> f {{name}} +{-# INLINE {{name}}L #-} + +{{/vars}} + +{{/model}} +{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/README.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/README.mustache new file mode 100644 index 00000000000..747f97d3f42 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/README.mustache @@ -0,0 +1,196 @@ +## Swagger Auto-Generated [http-client](https://www.stackage.org/lts-9.0/package/http-client-0.5.7.0) Bindings to `{{title}}` + +The library in `lib` provides auto-generated-from-Swagger [http-client](https://www.stackage.org/lts-9.0/package/http-client-0.5.7.0) bindings to the {{title}} API. + +Targeted swagger version: {{swaggerVersion}} + +OpenAPI-Specification: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/{{swaggerVersion}}.md + +## Installation + +Installation follows the standard approach to installing Stack-based projects. + +1. Install the [Haskell `stack` tool](http://docs.haskellstack.org/en/stable/README). +2. To build the package, and generate the documentation (recommended): +``` +stack haddock +``` +which will generate docs for this lib in the `docs` folder. + +To generate the docs in the normal location (to enable hyperlinks to external libs), remove +``` +build: + haddock-arguments: + haddock-args: + - "--odir=./docs" +``` +from the stack.yaml file and run `stack haddock` again. + +3. To run unit tests: +``` +stack test +``` + +## Swagger-Codegen + +The code generator that produced this library, and which explains how +to obtain and use the swagger-codegen cli tool lives at + +https://github.com/swagger-api/swagger-codegen + +The _language_ argument (`--lang`) passed to the cli tool used should be + +``` +haskell-http-client +``` + +### Unsupported Swagger Features + +* Model Inheritance + +This is beta software; other cases may not be supported. + +### Codegen "additional properties" parameters + +These options allow some customization of the code generation process. + +**haskell-http-client additional properties:** + +| OPTION | DESCRIPTION | DEFAULT | ACTUAL | +| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------------- | +| allowFromJsonNulls | allow JSON Null during model decoding from JSON | true | {{{allowFromJsonNulls}}} | +| allowToJsonNulls | allow emitting JSON Null during model encoding to JSON | false | {{{allowToJsonNulls}}} | +| dateFormat | format string used to parse/render a date | %Y-%m-%d | {{{dateFormat}}} | +| dateTimeFormat | format string used to parse/render a datetime. (Defaults to [formatISO8601Millis][1] when not provided) | | {{{dateTimeFormat}}} | +| generateEnums | Generate specific datatypes for swagger enums | true | {{{generateEnums}}} | +| generateFormUrlEncodedInstances | Generate FromForm/ToForm instances for models used by x-www-form-urlencoded operations (model fields must be primitive types) | true | {{{generateFormUrlEncodedInstances}}} | +| generateLenses | Generate Lens optics for Models | true | {{{generateLenses}}} | +| generateModelConstructors | Generate smart constructors (only supply required fields) for models | true | {{{generateModelConstructors}}} | +| inlineMimeTypes | Inline (hardcode) the content-type and accept parameters on operations, when there is only 1 option | false | {{{inlineMimeTypes}}} | +| modelDeriving | Additional classes to include in the deriving() clause of Models | | {{{modelDeriving}}} | +| strictFields | Add strictness annotations to all model fields | true | {{{x-strictFields}}} | +| useMonadLogger | Use the monad-logger package to provide logging (if instead false, use the katip logging package) | false | {{{x-useMonadLogger}}} | + +[1]: https://www.stackage.org/haddock/lts-9.0/iso8601-time-0.1.4/Data-Time-ISO8601.html#v:formatISO8601Millis + +An example setting _strictFields_ and _dateTimeFormat_: + +``` +java -jar swagger-codegen-cli.jar generate -i petstore.yaml -l haskell-http-client -o output/haskell-http-client -DstrictFields=true -DdateTimeFormat="%Y-%m-%dT%H:%M:%S%Q%z" +``` + +View the full list of Codegen "config option" parameters with the command: + +``` +java -jar swagger-codegen-cli.jar config-help -l haskell-http-client +``` + +## Usage Notes + +### Example SwaggerPetstore Haddock documentation + +An example of the generated haddock documentation targeting the server http://petstore.swagger.io/ (SwaggerPetstore) can be found [here][2] + +[2]: https://hackage.haskell.org/package/swagger-petstore + +### Example SwaggerPetstore App + +An example application using the auto-generated haskell-http-client bindings for the server http://petstore.swagger.io/ can be found [here][3] + +[3]: https://github.com/swagger-api/swagger-codegen/tree/master/samples/client/petstore/haskell-http-client/example-app + +This library is intended to be imported qualified. + +### Modules + +| MODULE | NOTES | +| ------------------- | --------------------------------------------------- | +| {{title}}.Client | use the "dispatch" functions to send requests | +| {{title}}.Core | core funcions, config and request types | +| {{title}}.API | construct api requests | +| {{title}}.Model | describes api models | +| {{title}}.MimeTypes | encoding/decoding MIME types (content-types/accept) | +| {{title}}.ModelLens | lenses for model fields | +| {{title}}.Logging | logging functions and utils | + + +### MimeTypes + +This library adds type safety around what swagger specifies as +Produces and Consumes for each Operation (e.g. the list of MIME types an +Operation can Produce (using 'accept' headers) and Consume (using 'content-type' headers). + +For example, if there is an Operation named _addFoo_, there will be a +data type generated named _AddFoo_ (note the capitalization), which +describes additional constraints and actions on the _addFoo_ operation +via its typeclass instances. These typeclass instances can be viewed +in GHCi or via the Haddocks. + +* requried parameters are included as function arguments to _addFoo_ +* optional non-body parameters are included by using `applyOptionalParam` +* optional body parameters are set by using `setBodyParam` + +Example code generated for pretend _addFoo_ operation: + +```haskell +data AddFoo +instance Consumes AddFoo MimeJSON +instance Produces AddFoo MimeJSON +instance Produces AddFoo MimeXML +instance HasBodyParam AddFoo FooModel +instance HasOptionalParam AddFoo FooName +instance HasOptionalParam AddFoo FooId +``` + +this would indicate that: + +* the _addFoo_ operation can consume JSON +* the _addFoo_ operation produces JSON or XML, depending on the argument passed to the dispatch function +* the _addFoo_ operation can set it's body param of _FooModel_ via `setBodyParam` +* the _addFoo_ operation can set 2 different optional parameters via `applyOptionalParam` + +If the swagger spec doesn't declare it can accept or produce a certain +MIME type for a given Operation, you should either add a Produces or +Consumes instance for the desired MIME types (assuming the server +supports it), use `dispatchLbsUnsafe` or modify the swagger spec and +run the generator again. + +New MIME type instances can be added via MimeType/MimeRender/MimeUnrender + +Only JSON instances are generated by default, and in some case +x-www-form-urlencoded instances (FromFrom, ToForm) will also be +generated if the model fields are primitive types, and there are +Operations using x-www-form-urlencoded which use those models. + +### Authentication + +A haskell data type will be generated for each swagger authentication type. + +If for example the AuthMethod `AuthOAuthFoo` is generated for OAuth operations, then +`addAuthMethod` should be used to add the AuthMethod config. + +When a request is dispatched, if a matching auth method is found in +the config, it will be applied to the request. + +### Example + +```haskell +mgr <- newManager defaultManagerSettings +config0 <- withStdoutLogging =<< newConfig +let config = config0 + `addAuthMethod` AuthOAuthFoo "secret-key" + +let addFooRequest = + addFoo + (ContentType MimeJSON) + (Accept MimeXML) + (ParamBar paramBar) + (ParamQux paramQux) + modelBaz + `applyOptionalParam` FooId 1 + `applyOptionalParam` FooName "name" + `setHeader` [("qux_header","xxyy")] +addFooResult <- dispatchMime mgr config addFooRequest +``` + +See the example app and the haddocks for details. diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/Setup.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/Setup.mustache new file mode 100644 index 00000000000..9a994af677b --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/Setup.mustache @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/TopLevel.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/TopLevel.mustache new file mode 100644 index 00000000000..485b41c504c --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/TopLevel.mustache @@ -0,0 +1,22 @@ +{{>partial_header}} +{-| +Module : {{title}} +-} + +module {{title}} + ( module {{title}}.API + , module {{title}}.Client + , module {{title}}.Core + , module {{title}}.Logging + , module {{title}}.MimeTypes + , module {{title}}.Model + , module {{title}}.ModelLens + ) where + +import {{title}}.API +import {{title}}.Client +import {{title}}.Core +import {{title}}.Logging +import {{title}}.MimeTypes +import {{title}}.Model +import {{title}}.ModelLens diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/_accept.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/_accept.mustache new file mode 100644 index 00000000000..bf410b31bdb --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/_accept.mustache @@ -0,0 +1 @@ +{{^vendorExtensions.x-inlineAccept}}accept{{/vendorExtensions.x-inlineAccept}}{{#vendorExtensions.x-inlineAccept}}{{{x-mediaDataType}}}{{/vendorExtensions.x-inlineAccept}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/_contentType.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/_contentType.mustache new file mode 100644 index 00000000000..fcdf1460ce3 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/_contentType.mustache @@ -0,0 +1 @@ +{{^vendorExtensions.x-inlineContentType}}contentType{{/vendorExtensions.x-inlineContentType}}{{#vendorExtensions.x-inlineContentType}}{{{x-mediaDataType}}}{{/vendorExtensions.x-inlineContentType}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/_formColl.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/_formColl.mustache new file mode 100644 index 00000000000..0984ad9af6f --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/_formColl.mustache @@ -0,0 +1 @@ +toForm{{#collectionFormat}}Coll {{vendorExtensions.x-collectionFormat}}{{/collectionFormat}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/_headerColl.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/_headerColl.mustache new file mode 100644 index 00000000000..857498867ff --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/_headerColl.mustache @@ -0,0 +1 @@ +toHeader{{#collectionFormat}}Coll {{vendorExtensions.x-collectionFormat}}{{/collectionFormat}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/_queryColl.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/_queryColl.mustache new file mode 100644 index 00000000000..e494efc52d1 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/_queryColl.mustache @@ -0,0 +1 @@ +toQuery{{#collectionFormat}}Coll {{vendorExtensions.x-collectionFormat}}{{/collectionFormat}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/git_push.sh.mustache new file mode 100644 index 00000000000..a2d75234837 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/git_push.sh.mustache @@ -0,0 +1,52 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 swagger-petstore-perl "minor update" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 + +if [ "$git_user_id" = "" ]; then + git_user_id="{{{gitUserId}}}" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="{{{gitRepoId}}}" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="{{{releaseNote}}}" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=`git remote` +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://github.com/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' + diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/gitignore.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/gitignore.mustache new file mode 100644 index 00000000000..aaed8f870ea --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/gitignore.mustache @@ -0,0 +1,8 @@ +.stack-work +src/highlight.js +src/style.css +dist +dist-newstyle +cabal.project.local +.cabal-sandbox +cabal.sandbox.config \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/haskell-http-client.cabal.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/haskell-http-client.cabal.mustache new file mode 100644 index 00000000000..2b0b517b845 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/haskell-http-client.cabal.mustache @@ -0,0 +1,103 @@ +name: {{package}} +version: 0.1.0.0 +synopsis: Auto-generated {{package}} API Client +description: . + Client library for calling the {{package}} API based on http-client. + . + host: {{host}} + . + base path: {{basePath}} + {{#version}} + . + {{appName}} API version: {{{.}}} + {{/version}} + . + OpenAPI spec version: {{swaggerVersion}} + . + {{^hideGenerationTimestamp}}Generated on: {{generatedDate}} + . + {{/hideGenerationTimestamp}}OpenAPI-Specification: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/{{swaggerVersion}}.md + . +category: Web +homepage: https://github.com/swagger-api/swagger-codegen#readme +author: Author Name Here +maintainer: author.name@email.com +copyright: YEAR - AUTHOR +license: UnspecifiedLicense +build-type: Simple +cabal-version: >= 1.10 + +extra-source-files: + README.md + swagger.yaml + +library + hs-source-dirs: + lib + ghc-options: -Wall -funbox-strict-fields + build-depends: + base >=4.7 && <5.0 + , transformers >=0.4.0.0 + , mtl >=2.2.1 + , unordered-containers + , aeson >=1.0 && <2.0 + , bytestring >=0.10.0 && <0.11 + , base64-bytestring >1.0 && <2.0 + , containers >=0.5.0.0 && <0.6 + , http-types >=0.8 && <0.11 + , http-client >=0.5 && <0.6 + , http-client-tls + , http-api-data >= 0.3.4 && <0.4 + , http-media >= 0.4 && < 0.8 + , text >=0.11 && <1.3 + , time >=1.5 && <1.9 + , iso8601-time >=0.1.3 && <0.2.0 + , vector >=0.10.9 && <0.13 + , network >=2.6.2 && <2.7 + , random >=1.1 + , exceptions >= 0.4 + , {{^x-useMonadLogger}}katip >=0.4 && < 0.6{{/x-useMonadLogger}}{{#x-useMonadLogger}}monad-logger >=0.3 && <0.4{{/x-useMonadLogger}} + , safe-exceptions <0.2 + , case-insensitive + , microlens >= 0.4.3 && <0.5 + , deepseq >= 1.4 && <1.6 + exposed-modules: + {{title}} + {{title}}.API + {{title}}.Client + {{title}}.Core + {{title}}.Logging + {{title}}.MimeTypes + {{title}}.Model + {{title}}.ModelLens + other-modules: + Paths_{{pathsName}} + default-language: Haskell2010 + +test-suite tests + type: exitcode-stdio-1.0 + main-is: Test.hs + hs-source-dirs: + tests + ghc-options: -Wall -fno-warn-orphans + build-depends: + base >=4.7 && <5.0 + , transformers >=0.4.0.0 + , mtl >=2.2.1 + , unordered-containers + , {{package}} + , bytestring >=0.10.0 && <0.11 + , containers + , hspec >=1.8 + , text + , time + , iso8601-time + , aeson + , vector + , semigroups + , QuickCheck + other-modules: + ApproxEq + Instances + PropMime + default-language: Haskell2010 diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/partial_header.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/partial_header.mustache new file mode 100644 index 00000000000..7c31d3f6fe1 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/partial_header.mustache @@ -0,0 +1,20 @@ +{- + {{#appName}} + {{{.}}} + + {{/appName}} + {{#appDescription}} + {{{.}}} + + {{/appDescription}} + {{#swaggerVersion}} + OpenAPI spec version: {{{.}}} + {{/swaggerVersion}} + {{#version}} + {{appName}} API version: {{{.}}} + {{/version}} + {{#infoEmail}} + Contact: {{{.}}} + {{/infoEmail}} + Generated by Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) +-} diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/stack.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/stack.mustache new file mode 100644 index 00000000000..aa4a059faa5 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/stack.mustache @@ -0,0 +1,8 @@ +resolver: lts-9.10 +build: + haddock-arguments: + haddock-args: + - "--odir=./docs" +extra-deps: [] +packages: +- '.' diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/swagger.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/swagger.mustache new file mode 100644 index 00000000000..51560926bba --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/swagger.mustache @@ -0,0 +1 @@ +{{{swagger-yaml}}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/tests/ApproxEq.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/tests/ApproxEq.mustache new file mode 100644 index 00000000000..88ca2110a06 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/tests/ApproxEq.mustache @@ -0,0 +1,81 @@ +{-# LANGUAGE DefaultSignatures #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeOperators #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module ApproxEq where + +import Data.Text (Text) +import Data.Time.Clock +import Test.QuickCheck +import GHC.Generics as G + +(==~) + :: (ApproxEq a, Show a) + => a -> a -> Property +a ==~ b = counterexample (show a ++ " !=~ " ++ show b) (a =~ b) + +class GApproxEq f where + gApproxEq :: f a -> f a -> Bool + +instance GApproxEq U1 where + gApproxEq U1 U1 = True + +instance (GApproxEq a, GApproxEq b) => + GApproxEq (a :+: b) where + gApproxEq (L1 a) (L1 b) = gApproxEq a b + gApproxEq (R1 a) (R1 b) = gApproxEq a b + gApproxEq _ _ = False + +instance (GApproxEq a, GApproxEq b) => + GApproxEq (a :*: b) where + gApproxEq (a1 :*: b1) (a2 :*: b2) = gApproxEq a1 a2 && gApproxEq b1 b2 + +instance (ApproxEq a) => + GApproxEq (K1 i a) where + gApproxEq (K1 a) (K1 b) = a =~ b + +instance (GApproxEq f) => + GApproxEq (M1 i t f) where + gApproxEq (M1 a) (M1 b) = gApproxEq a b + +class ApproxEq a where + (=~) :: a -> a -> Bool + default (=~) :: (Generic a, GApproxEq (Rep a)) => a -> a -> Bool + a =~ b = gApproxEq (G.from a) (G.from b) + +instance ApproxEq Text where + (=~) = (==) + +instance ApproxEq Char where + (=~) = (==) + +instance ApproxEq Bool where + (=~) = (==) + +instance ApproxEq Int where + (=~) = (==) + +instance ApproxEq Double where + (=~) = (==) + +instance ApproxEq a => + ApproxEq (Maybe a) + +instance ApproxEq UTCTime where + (=~) = (==) + +instance ApproxEq a => + ApproxEq [a] where + as =~ bs = and (zipWith (=~) as bs) + +instance (ApproxEq l, ApproxEq r) => + ApproxEq (Either l r) where + Left a =~ Left b = a =~ b + Right a =~ Right b = a =~ b + _ =~ _ = False + +instance (ApproxEq l, ApproxEq r) => + ApproxEq (l, r) where + (=~) (l1, r1) (l2, r2) = l1 =~ l2 && r1 =~ r2 diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/tests/Instances.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/tests/Instances.mustache new file mode 100644 index 00000000000..afaaf140c98 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/tests/Instances.mustache @@ -0,0 +1,109 @@ +{-# OPTIONS_GHC -fno-warn-unused-imports #-} + +module Instances where + +import {{title}}.Model +import {{title}}.Core + +import qualified Data.Aeson as A +import qualified Data.ByteString.Lazy as BL +import qualified Data.HashMap.Strict as HM +import qualified Data.Set as Set +import qualified Data.Text as T +import qualified Data.Time as TI +import qualified Data.Vector as V + +import Control.Monad +import Data.Char (isSpace) +import Data.List (sort) +import Test.QuickCheck + +import ApproxEq + +instance Arbitrary T.Text where + arbitrary = T.pack <$> arbitrary + +instance Arbitrary TI.Day where + arbitrary = TI.ModifiedJulianDay . (2000 +) <$> arbitrary + shrink = (TI.ModifiedJulianDay <$>) . shrink . TI.toModifiedJulianDay + +instance Arbitrary TI.UTCTime where + arbitrary = + TI.UTCTime <$> arbitrary <*> (TI.secondsToDiffTime <$> choose (0, 86401)) + +instance Arbitrary BL.ByteString where + arbitrary = BL.pack <$> arbitrary + shrink xs = BL.pack <$> shrink (BL.unpack xs) + +instance Arbitrary ByteArray where + arbitrary = ByteArray <$> arbitrary + shrink (ByteArray xs) = ByteArray <$> shrink xs + +instance Arbitrary Binary where + arbitrary = Binary <$> arbitrary + shrink (Binary xs) = Binary <$> shrink xs + +instance Arbitrary DateTime where + arbitrary = DateTime <$> arbitrary + shrink (DateTime xs) = DateTime <$> shrink xs + +instance Arbitrary Date where + arbitrary = Date <$> arbitrary + shrink (Date xs) = Date <$> shrink xs + +-- | A naive Arbitrary instance for A.Value: +instance Arbitrary A.Value where + arbitrary = frequency [(3, simpleTypes), (1, arrayTypes), (1, objectTypes)] + where + simpleTypes :: Gen A.Value + simpleTypes = + frequency + [ (1, return A.Null) + , (2, liftM A.Bool (arbitrary :: Gen Bool)) + , (2, liftM (A.Number . fromIntegral) (arbitrary :: Gen Int)) + , (2, liftM (A.String . T.pack) (arbitrary :: Gen String)) + ] + mapF (k, v) = (T.pack k, v) + simpleAndArrays = frequency [(1, sized sizedArray), (4, simpleTypes)] + arrayTypes = sized sizedArray + objectTypes = sized sizedObject + sizedArray n = liftM (A.Array . V.fromList) $ replicateM n simpleTypes + sizedObject n = + liftM (A.object . map mapF) $ + replicateM n $ (,) <$> (arbitrary :: Gen String) <*> simpleAndArrays + +-- | Checks if a given list has no duplicates in _O(n log n)_. +hasNoDups + :: (Ord a) + => [a] -> Bool +hasNoDups = go Set.empty + where + go _ [] = True + go s (x:xs) + | s' <- Set.insert x s + , Set.size s' > Set.size s = go s' xs + | otherwise = False + +instance ApproxEq TI.Day where + (=~) = (==) + +-- * Models + +{{#models}} +{{#model}} +{{^isEnum}} +instance Arbitrary {{classname}} where + arbitrary = + {{#isAlias}}{{classname}} <$> arbitrary{{/isAlias}}{{^isAlias}}{{^hasVars}} + pure {{/hasVars}}{{classname}} + {{#hasVars}} <$>{{/hasVars}} {{#vars}}arbitrary -- {{name}} :: {{^required}}Maybe {{/required}}{{datatype}} + {{#hasMore}} <*> {{/hasMore}}{{/vars}}{{/isAlias}} +{{/isEnum}} +{{/model}} +{{/models}} + + +{{#x-allUniqueParams}}{{#x-enum}} +instance Arbitrary {{{x-paramNameType}}} where + arbitrary = arbitraryBoundedEnum +{{/x-enum}}{{/x-allUniqueParams}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/tests/PropMime.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/tests/PropMime.mustache new file mode 100644 index 00000000000..f672a4d3adf --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/tests/PropMime.mustache @@ -0,0 +1,51 @@ +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ConstraintKinds #-} +{-# OPTIONS_GHC -fno-warn-unused-imports #-} + +module PropMime where + +import Data.Aeson +import Data.Aeson.Types (parseEither) +import Data.Monoid ((<>)) +import Data.Typeable (Proxy(..), typeOf, Typeable) +import qualified Data.ByteString.Lazy.Char8 as BL8 +import Test.Hspec +import Test.QuickCheck +import Test.QuickCheck.Property +import Test.Hspec.QuickCheck (prop) + +import {{title}}.MimeTypes + +import ApproxEq + +-- * Type Aliases + +type ArbitraryMime mime a = ArbitraryRoundtrip (MimeUnrender mime) (MimeRender mime) a + +type ArbitraryRoundtrip from to a = (from a, to a, Arbitrary' a) + +type Arbitrary' a = (Arbitrary a, Show a, Typeable a) + +-- * Mime + +propMime + :: forall a b mime. + (ArbitraryMime mime a, Testable b) + => String -> (a -> a -> b) -> mime -> Proxy a -> Spec +propMime eqDescr eq m _ = + prop + (show (typeOf (undefined :: a)) <> " " <> show (typeOf (undefined :: mime)) <> " roundtrip " <> eqDescr) $ + \(x :: a) -> + let rendered = mimeRender' m x + actual = mimeUnrender' m rendered + expected = Right x + failMsg = + "ACTUAL: " <> show actual <> "\nRENDERED: " <> BL8.unpack rendered + in counterexample failMsg $ + either reject property (eq <$> actual <*> expected) + where + reject = property . const rejected + +propMimeEq :: (ArbitraryMime mime a, Eq a) => mime -> Proxy a -> Spec +propMimeEq = propMime "(EQ)" (==) diff --git a/modules/swagger-codegen/src/main/resources/haskell-http-client/tests/Test.mustache b/modules/swagger-codegen/src/main/resources/haskell-http-client/tests/Test.mustache new file mode 100644 index 00000000000..bc82ad90638 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/haskell-http-client/tests/Test.mustache @@ -0,0 +1,24 @@ +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE PartialTypeSignatures #-} + +module Main where + +import Data.Typeable (Proxy(..)) +import Test.Hspec +import Test.Hspec.QuickCheck + +import PropMime +import Instances () + +import {{title}}.Model +import {{title}}.MimeTypes + +main :: IO () +main = + hspec $ modifyMaxSize (const 10) $ do + describe "JSON instances" $ do + pure () + {{#models}}{{#model}}propMimeEq MimeJSON (Proxy :: Proxy {{classname}}) + {{/model}}{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/haskell-servant/stack.mustache b/modules/swagger-codegen/src/main/resources/haskell-servant/stack.mustache index 36060148910..78ed93c5414 100644 --- a/modules/swagger-codegen/src/main/resources/haskell-servant/stack.mustache +++ b/modules/swagger-codegen/src/main/resources/haskell-servant/stack.mustache @@ -1,8 +1,8 @@ -resolver: lts-5.11 +resolver: lts-8.5 extra-deps: -- servant-0.8.1 -- servant-client-0.8.1 -- servant-server-0.8.1 -- http-api-data-0.2.4 +- servant-0.9.1.1 +- servant-client-0.9.1.1 +- servant-server-0.9.1.1 +- http-api-data-0.3.5 packages: - '.' diff --git a/modules/swagger-codegen/src/main/resources/htmlDocs2/js_jsonformatter.mustache b/modules/swagger-codegen/src/main/resources/htmlDocs2/js_jsonformatter.mustache index 565329fed19..cc105bcdcdd 100644 --- a/modules/swagger-codegen/src/main/resources/htmlDocs2/js_jsonformatter.mustache +++ b/modules/swagger-codegen/src/main/resources/htmlDocs2/js_jsonformatter.mustache @@ -825,7 +825,7 @@ return /******/ (function(modules) { // webpackBootstrap var sourceMap = obj.sourceMap; if(sourceMap) { - // http://stackoverflow.com/a/26603875 + // https://developer.mozilla.org/en/docs/Web/API/WindowBase64/Base64_encoding_and_decoding css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */"; } @@ -847,13 +847,13 @@ return /******/ (function(modules) { // webpackBootstrap "use strict"; /* * Escapes `"` charachters from string - */ + */ function escapeString(str) { return str.replace('"', '\"'); } /* * Determines if a value is an object - */ + */ function isObject(value) { var type = typeof value; return !!value && (type == 'object'); @@ -861,32 +861,27 @@ return /******/ (function(modules) { // webpackBootstrap exports.isObject = isObject; /* * Gets constructor name of an object. - * From http://stackoverflow.com/a/332429 * - */ + */ function getObjectName(object) { if (object === undefined) { return ''; } - if (object === null) { - return 'Object'; - } - if (typeof object === 'object' && !object.constructor) { + if (object === null || (typeof object === 'object' && !object.constructor)) { return 'Object'; } var funcNameRegex = /function ([^(]*)/; var results = (funcNameRegex).exec((object).constructor.toString()); if (results && results.length > 1) { return results[1]; - } - else { + } else { return ''; } } exports.getObjectName = getObjectName; /* * Gets type of an object. Returns "null" for null objects - */ + */ function getType(object) { if (object === null) { return 'null'; @@ -965,4 +960,4 @@ return /******/ (function(modules) { // webpackBootstrap ; //# sourceMappingURL=json-formatter.js.map - \ No newline at end of file + diff --git a/modules/swagger-codegen/src/main/resources/htmlDocs2/js_jsonschemaview.mustache b/modules/swagger-codegen/src/main/resources/htmlDocs2/js_jsonschemaview.mustache index 0d357673702..c93e403eaf7 100644 --- a/modules/swagger-codegen/src/main/resources/htmlDocs2/js_jsonschemaview.mustache +++ b/modules/swagger-codegen/src/main/resources/htmlDocs2/js_jsonschemaview.mustache @@ -165,9 +165,9 @@ var JSONSchemaView = (function () { (0, _helpersJs._if)(this.schema.required && !this.isCollapsed && this.options.isBodyParam != true)(_templateObject26, this.schema.required), (0, _helpersJs._if)(this.schema.required && !this.isCollapsed && this.options.isBodyParam == true)(_templateObject266, this.schema.required), - (0, _helpersJs._if)(this.schema['default'] && !this.isCollapsed)(_templateObject27, this.schema['default']), + (0, _helpersJs._if)(this.schema['default'] && !this.isCollapsed)(_templateObject27, this.schema['default']),(0, _helpersJs._if)(!this.isCollapsed)(_templateObject29), (0, _helpersJs._if)(!this.isCollapsed && this.schema.pattern)(_templateObject28, this.schema.pattern), - (0, _helpersJs._if)(!this.isCollapsed && this.schema['enum'])(_templateObject16, this['enum'](this.schema, this.isCollapsed, this.open)), (0, _helpersJs._if)(this.schema.allOf && !this.isCollapsed)(_templateObject17, this.xOf(this.schema, 'allOf')), (0, _helpersJs._if)(this.schema.oneOf && !this.isCollapsed)(_templateObject17, this.xOf(this.schema, 'oneOf')), (0, _helpersJs._if)(this.schema.anyOf && !this.isCollapsed)(_templateObject17, this.xOf(this.schema, 'anyOf')), (0, _helpersJs._if)(!this.isCollapsed)(_templateObject29)) + '\n').replace(/\s*\n/g, '\n').replace(/(\<\!\-\-).+/g, '').trim(); + (0, _helpersJs._if)(!this.isCollapsed && this.schema['enum'])(_templateObject16, this['enum'](this.schema, this.isCollapsed, this.open)), (0, _helpersJs._if)(this.schema.allOf && !this.isCollapsed)(_templateObject17, this.xOf(this.schema, 'allOf')), (0, _helpersJs._if)(this.schema.oneOf && !this.isCollapsed)(_templateObject17, this.xOf(this.schema, 'oneOf')), (0, _helpersJs._if)(this.schema.anyOf && !this.isCollapsed)(_templateObject17, this.xOf(this.schema, 'anyOf'))) + '\n').replace(/\s*\n/g, '\n').replace(/(\<\!\-\-).+/g, '').trim(); } /* diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/api.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/api.mustache index cae4e8cb62b..9b78ac8f2cb 100644 --- a/modules/swagger-codegen/src/main/resources/kotlin-client/api.mustache +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/api.mustache @@ -20,7 +20,7 @@ class {{classname}}(basePath: kotlin.String = "{{{basePath}}}") : ApiClient(base fun {{operationId}}({{#allParams}}{{paramName}}: {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) : {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Unit{{/returnType}} { val localVariableBody: kotlin.Any? = {{#hasBodyParam}}{{#bodyParams}}{{paramName}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{^hasFormParams}}null{{/hasFormParams}}{{#hasFormParams}}mapOf({{#formParams}}"{{{baseName}}}" to "${{paramName}}"{{#hasMore}}, {{/hasMore}}{{/formParams}}){{/hasFormParams}}{{/hasBodyParam}} val localVariableQuery: MultiValueMap = {{^hasQueryParams}}mapOf(){{/hasQueryParams}}{{#hasQueryParams}}mapOf({{#queryParams}}"{{paramName}}" to {{#isContainer}}toMultiValue({{paramName}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf("${{paramName}}"){{/isContainer}}{{#hasMore}}, {{/hasMore}}{{/queryParams}}){{/hasQueryParams}} - val localVariableHeaders: kotlin.collections.Map = {{^headerParams}}mapOf({{#hasFormParams}}"Content-Type" to "multipart/form-data"{{/hasFormParams}}){{/headerParams}}{{#headerParams}}mapOf("{{paramName}}" to {{#isContainer}}{{paramName}}.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}{{paramName}}{{/isContainer}}){{/headerParams}} + val localVariableHeaders: kotlin.collections.Map = mapOf({{#hasFormParams}}"Content-Type" to "multipart/form-data"{{/hasFormParams}}{{^hasHeaderParams}}){{/hasHeaderParams}}{{#hasHeaderParams}}{{#hasFormParams}}, {{/hasFormParams}}{{#headerParams}}"{{paramName}}" to {{#isContainer}}{{paramName}}.joinToString(separator = collectionDelimiter("{{collectionFormat}}"){{/isContainer}}{{^isContainer}}{{paramName}}{{/isContainer}}{{#hasMore}}, {{/hasMore}}{{/headerParams}}){{/hasHeaderParams}} val localVariableConfig = RequestConfig( RequestMethod.{{httpMethod}}, "{{path}}"{{#pathParams}}.replace("{"+"{{baseName}}"+"}", "${{paramName}}"){{/pathParams}}, diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/data_class.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/data_class.mustache index 82a4b0514d0..1237ec1f431 100644 --- a/modules/swagger-codegen/src/main/resources/kotlin-client/data_class.mustache +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/data_class.mustache @@ -7,16 +7,19 @@ data class {{classname}} ( {{#requiredVars}} {{>data_class_req_var}}{{^-last}}, -{{/-last}}{{/requiredVars}}{{#hasRequired}}, -{{/hasRequired}}{{#optionalVars}}{{>data_class_opt_var}}{{^-last}}, +{{/-last}}{{/requiredVars}}{{#hasRequired}}{{#hasOptional}}, +{{/hasOptional}}{{/hasRequired}}{{#optionalVars}}{{>data_class_opt_var}}{{^-last}}, {{/-last}}{{/optionalVars}} ) { {{#hasEnums}}{{#vars}}{{#isEnum}} - enum class {{nameInCamelCase}}(val value: {{datatype}}) { - {{#_enum}} - {{{this}}}({{#isString}}"{{/isString}}{{{this}}}{{#isString}}"{{/isString}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/_enum}} + /** + * {{{description}}} + * Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}} + */ + enum class {{nameInCamelCase}}(val value: {{dataType}}){ + {{#allowableValues}}{{#enumVars}} + {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/enumVars}}{{/allowableValues}} } - {{/isEnum}}{{/vars}}{{/hasEnums}} -} \ No newline at end of file +} diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/enum_class.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/enum_class.mustache index 42ce17d35cb..791398b9789 100644 --- a/modules/swagger-codegen/src/main/resources/kotlin-client/enum_class.mustache +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/enum_class.mustache @@ -1,9 +1,9 @@ /** * {{{description}}} -* Values: {{#allowableValues}}{{#enumVars}}{{name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}} +* Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}} */ enum class {{classname}}(val value: {{dataType}}){ {{#allowableValues}}{{#enumVars}} - {{name}}({{#isString}}"{{/isString}}{{{value}}}{{#isString}}"{{/isString}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} {{/enumVars}}{{/allowableValues}} -) \ No newline at end of file +} diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiClient.kt.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiClient.kt.mustache index 49517bd1592..6c56fd17356 100644 --- a/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiClient.kt.mustache +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiClient.kt.mustache @@ -60,9 +60,9 @@ open class ApiClient(val baseUrl: String) { var urlBuilder = httpUrl.newBuilder() .addPathSegments(requestConfig.path.trimStart('/')) - requestConfig.query.forEach { k, v -> - v.forEach { queryValue -> - urlBuilder = urlBuilder.addQueryParameter(k,queryValue) + requestConfig.query.forEach { query -> + query.value.forEach { queryValue -> + urlBuilder = urlBuilder.addQueryParameter(query.key, queryValue) } } diff --git a/modules/swagger-codegen/src/main/resources/lua/.travis.yml b/modules/swagger-codegen/src/main/resources/lua/.travis.yml new file mode 100644 index 00000000000..4004e966bee --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/lua/.travis.yml @@ -0,0 +1,6 @@ +language: lua + +install: + +script: + diff --git a/modules/swagger-codegen/src/main/resources/lua/README.mustache b/modules/swagger-codegen/src/main/resources/lua/README.mustache new file mode 100644 index 00000000000..f46349d6357 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/lua/README.mustache @@ -0,0 +1,96 @@ +# Lua API client for {{packageName}} + +{{#appDescription}} +{{{appDescription}}} +{{/appDescription}} + +## Overview +This API client was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the [swagger-spec](https://github.com/swagger-api/swagger-spec) from a remote server, you can easily generate an API client. + +- API version: {{appVersion}} +- Package version: {{packageVersion}} +{{^hideGenerationTimestamp}} +- Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} +- Build package: {{generatorClass}} +{{#infoUrl}} +For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + +## Installation +Put the package under your project folder and add the following in import: +``` + "./{{packageName}}" +``` + +## Documentation for API Endpoints + +All URIs are relative to *{{basePath}}* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + +## Documentation For Models + +{{#models}}{{#model}} - [{{{classname}}}]({{modelDocPath}}{{{classname}}}.md) +{{/model}}{{/models}} + +## Documentation For Authorization +{{^authMethods}} Endpoints do not require authorization. +{{/authMethods}}{{#authMethods}}{{#last}} Authentication schemes defined for the API:{{/last}}{{/authMethods}} +{{#authMethods}} +## {{{name}}} +{{#isApiKey}}- **Type**: API key + +Example +``` + auth := context.WithValue(context.TODO(), sw.ContextAPIKey, sw.APIKey{ + Key: "APIKEY", + Prefix: "Bearer", // Omit if not necessary. + }) + r, err := client.Service.Operation(auth, args) +``` +{{/isApiKey}} +{{#isBasic}}- **Type**: HTTP basic authentication + +Example +``` + auth := context.WithValue(context.TODO(), sw.ContextBasicAuth, sw.BasicAuth{ + UserName: "username", + Password: "password", + }) + r, err := client.Service.Operation(auth, args) +``` +{{/isBasic}} +{{#isOAuth}}- **Type**: OAuth +- **Flow**: {{{flow}}} +- **Authorization URL**: {{{authorizationUrl}}} +- **Scopes**: {{^scopes}}N/A{{/scopes}} +{{#scopes}} - **{{{scope}}}**: {{{description}}} +{{/scopes}} + +Example +``` + auth := context.WithValue(context.TODO(), sw.ContextAccessToken, "ACCESSTOKENSTRING") + r, err := client.Service.Operation(auth, args) +``` + +Or via OAuth2 module to automaticly refresh tokens and perform user authentication. +``` + import "golang.org/x/oauth2" + + / .. Perform OAuth2 round trip request and obtain a token .. // + + tokenSource := oauth2cfg.TokenSource(createContext(httpClient), &token) + auth := context.WithValue(oauth2.NoContext, sw.ContextOAuth2, tokenSource) + r, err := client.Service.Operation(auth, args) +``` +{{/isOAuth}} +{{/authMethods}} + +## Author + +{{#apiInfo}}{{#apis}}{{^hasMore}}{{infoEmail}} +{{/hasMore}}{{/apis}}{{/apiInfo}} diff --git a/modules/swagger-codegen/src/main/resources/lua/api.mustache b/modules/swagger-codegen/src/main/resources/lua/api.mustache new file mode 100644 index 00000000000..3e6f6a0b48c --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/lua/api.mustache @@ -0,0 +1,152 @@ +{{>partial_header}} +--package {{packageName}} + +{{#operations}} +local http_request = require "http.request" +local http_util = require "http.util" +local dkjson = require "dkjson" +local basexx = require "basexx" + +-- model import +local {{{packageName}}}_{{classname}} = require "{{{packageName}}}.api.{{classname}}" + +local {{{packageName}}}= {} +local {{{packageName}}}_mt = { + __name = "{{{classname}}}"; + __index = {{{packageName}}}; +} + +local function new_{{classname}}(host, basePath, schemes) + local schemes_map = {} + for _,v in ipairs(schemes) do + schemes_map[v] = v + end + local default_scheme = schemes_map.https or schemes_map.http + return setmetatable({ + host = host; + basePath = basePath or "{{{basePath}}}"; + schemes = schemes_map; + default_scheme = default_scheme; + http_username = nil; + http_password = nil; + api_key = {}; + access_token = nil; + }, {{{packageName}}}_mt) +end + +{{#operation}} +function {{classname}}:{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) + local req = http_request.new_from_uri({ + scheme = self.default_scheme; + host = self.host; + path = string.format("%s{{{vendorExtensions.x-codegen-path}}}{{#queryParams}}{{#-first}}?{{/-first}}{{{baseName}}}=%s{{^-last}}&{{/-last}}{{/queryParams}}", + self.basePath{{#pathParams}}, {{paramName}}{{/pathParams}}{{#queryParams}}, http_util.encodeURIComponent({{paramName}}){{/queryParams}}); + }) + + -- set HTTP verb + req.headers:upsert(":method", "{{httpMethod}}") + {{#hasConsumes}} + -- TODO: create a function to select proper accept + -- ref: https://github.com/swagger-api/swagger-codegen/pull/6252#issuecomment-321199879 + --local var_content_type = { {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} } + req.headers:upsert("accept", {{#consumes}}{{#-first}}"{{{mediaType}}}"{{/-first}}{{/consumes}}) + + {{/hasConsumes}} + {{#hasProduces}} + -- TODO: create a function to select proper content-type + -- ref: https://github.com/swagger-api/swagger-codegen/pull/6252#issuecomment-321199879 + --local var_accept = { {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} } + req.headers:upsert("content-type", {{#produces}}{{#-first}}"{{{mediaType}}}"{{/-first}}{{/produces}}) + + {{/hasProduces}} + {{#headerParams}} + req.headers:upsert("{{baseName}}", {{paramName}}) + {{/headerParams}} + {{#formParams}} + {{#-first}} + req:set_body(http_util.dict_to_query({ + {{/-first}} + ["{{baseName}}"] = {{paramName}}; + {{#-last}} + })) + {{/-last}} + {{/formParams}} + {{#bodyParams}} + req:set_body(dkjson.encode({{paramName}})) + + {{/bodyParams}} + {{#authMethods}} + {{#isApiKey}} + {{#isKeyInHeader}} + -- api key in headers '{{keyParamName}}' + req.headers:upsert("{{{name}}}", api_key['{{{keyParamName}}}']) + {{/isKeyInHeader}} + {{#isKeyInQuery}} + -- TODO: api key in query '{{keyParamName}}' + {{/isKeyInQuery}} + {{/isApiKey}} + {{#isBasic}} + -- HTTP basic auth + req.readers:upsert("authorization", "Basic " .. basexx.to_base64(self.http_username .. " " .. self.http_password)) + {{/isBasic}} + {{#isOAuth}} + -- oAuth + req.headers:upsert("authorization", "Bearer " .. self.access_token) + {{/isOAuth}} + {{/authMethods}} + + -- make the HTTP call + local headers, stream, errno = req:go() + if not headers then + return nil, stream, errno + end + local http_status = headers:get(":status") + if http_status:sub(1,1) == "2" then + {{#returnType}} + local body, err, errno2 = stream:get_body_as_string() + -- exception when getting the HTTP body + if not body then + return nil, err, errno2 + end + stream:shutdown() + local result, _, err3 = dkjson.decode(body) + -- exception when decoding the HTTP body + if result == nil then + return nil, err3 + end + {{#returnTypeIsPrimitive}} + return result, headers + {{/returnTypeIsPrimitive}} + {{^returnTypeIsPrimitive}} + {{#isListContainer}} + for _, ob in ipairs(result) do + cast_{{returnType}}(ob) + end + return result, headers + {{/isListContainer}} + {{#isMapContainer}} + return result, headers + {{/isMapContainer}} + {{^isMapContainer}} + {{^isListContainer}} + return cast_{{returnType}}(result), headers + {{/isListContainer}} + {{/isMapContainer}} + {{/returnTypeIsPrimitive}} + {{/returnType}} + {{^returnType}} + return nil, headers + {{/returnType}} + else + local body, err, errno2 = stream:get_body_as_string() + if not body then + return nil, err, errno2 + end + stream:shutdown() + -- return the error message (http body) + return nil, http_status, body + end +end + +{{/operation}} +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/lua/api_client.mustache b/modules/swagger-codegen/src/main/resources/lua/api_client.mustache new file mode 100644 index 00000000000..9e88a17d436 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/lua/api_client.mustache @@ -0,0 +1,427 @@ +{{>partial_header}} +package {{packageName}} + +import ( + "bytes" + "encoding/json" + "encoding/xml" + "fmt" + "errors" + "io" + "mime/multipart" + "golang.org/x/oauth2" + "golang.org/x/net/context" + "net/http" + "net/url" + "time" + "os" + "path/filepath" + "reflect" + "regexp" + "strings" + "unicode/utf8" + "strconv" +) + +var ( + jsonCheck = regexp.MustCompile("(?i:[application|text]/json)") + xmlCheck = regexp.MustCompile("(?i:[application|text]/xml)") +) + +// APIClient manages communication with the {{appName}} API v{{version}} +// In most cases there should be only one, shared, APIClient. +type APIClient struct { + cfg *Configuration + common service // Reuse a single struct instead of allocating one for each service on the heap. + + // API Services +{{#apiInfo}} +{{#apis}} +{{#operations}} + {{classname}} *{{classname}}Service +{{/operations}} +{{/apis}} +{{/apiInfo}} +} + +type service struct { + client *APIClient +} + +// NewAPIClient creates a new API client. Requires a userAgent string describing your application. +// optionally a custom http.Client to allow for advanced features such as caching. +func NewAPIClient(cfg *Configuration) *APIClient { + if cfg.HTTPClient == nil { + cfg.HTTPClient = http.DefaultClient + } + + c := &APIClient{} + c.cfg = cfg + c.common.client = c + +{{#apiInfo}} + // API Services +{{#apis}} +{{#operations}} + c.{{classname}} = (*{{classname}}Service)(&c.common) +{{/operations}} +{{/apis}} +{{/apiInfo}} + + return c +} + +func atoi(in string) (int, error) { + return strconv.Atoi(in) +} + + +// selectHeaderContentType select a content type from the available list. +func selectHeaderContentType(contentTypes []string) string { + if len(contentTypes) == 0 { + return "" + } + if contains(contentTypes, "application/json") { + return "application/json" + } + return contentTypes[0] // use the first content type specified in 'consumes' +} + +// selectHeaderAccept join all accept types and return +func selectHeaderAccept(accepts []string) string { + if len(accepts) == 0 { + return "" + } + + if contains(accepts, "application/json") { + return "application/json" + } + + return strings.Join(accepts, ",") +} + +// contains is a case insenstive match, finding needle in a haystack +func contains(haystack []string, needle string) bool { + for _, a := range haystack { + if strings.ToLower(a) == strings.ToLower(needle) { + return true + } + } + return false +} + +// Verify optional parameters are of the correct type. +func typeCheckParameter(obj interface{}, expected string, name string) error { + // Make sure there is an object. + if obj == nil { + return nil + } + + // Check the type is as expected. + if reflect.TypeOf(obj).String() != expected { + return fmt.Errorf("Expected %s to be of type %s but received %s.", name, expected, reflect.TypeOf(obj).String()) + } + return nil +} + +// parameterToString convert interface{} parameters to string, using a delimiter if format is provided. +func parameterToString(obj interface{}, collectionFormat string) string { + var delimiter string + + switch collectionFormat { + case "pipes": + delimiter = "|" + case "ssv": + delimiter = " " + case "tsv": + delimiter = "\t" + case "csv": + delimiter = "," + } + + if reflect.TypeOf(obj).Kind() == reflect.Slice { + return strings.Trim(strings.Replace(fmt.Sprint(obj), " ", delimiter, -1), "[]") + } + + return fmt.Sprintf("%v", obj) +} + +// callAPI do the request. +func (c *APIClient) callAPI(request *http.Request) (*http.Response, error) { + return c.cfg.HTTPClient.Do(request) +} + +// Change base path to allow switching to mocks +func (c *APIClient) ChangeBasePath (path string) { + c.cfg.BasePath = path +} + +// prepareRequest build the request +func (c *APIClient) prepareRequest ( + ctx context.Context, + path string, method string, + postBody interface{}, + headerParams map[string]string, + queryParams url.Values, + formParams url.Values, + fileName string, + fileBytes []byte) (localVarRequest *http.Request, err error) { + + var body *bytes.Buffer + + // Detect postBody type and post. + if postBody != nil { + contentType := headerParams["Content-Type"] + if contentType == "" { + contentType = detectContentType(postBody) + headerParams["Content-Type"] = contentType + } + + body, err = setBody(postBody, contentType) + if err != nil { + return nil, err + } + } + + // add form paramters and file if available. + if len(formParams) > 0 || (len(fileBytes) > 0 && fileName != "") { + if body != nil { + return nil, errors.New("Cannot specify postBody and multipart form at the same time.") + } + body = &bytes.Buffer{} + w := multipart.NewWriter(body) + + for k, v := range formParams { + for _, iv := range v { + if strings.HasPrefix(k, "@") { // file + err = addFile(w, k[1:], iv) + if err != nil { + return nil, err + } + } else { // form value + w.WriteField(k, iv) + } + } + } + if len(fileBytes) > 0 && fileName != "" { + w.Boundary() + //_, fileNm := filepath.Split(fileName) + part, err := w.CreateFormFile("file", filepath.Base(fileName)) + if err != nil { + return nil, err + } + _, err = part.Write(fileBytes) + if err != nil { + return nil, err + } + // Set the Boundary in the Content-Type + headerParams["Content-Type"] = w.FormDataContentType() + } + + // Set Content-Length + headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) + w.Close() + } + + // Setup path and query paramters + url, err := url.Parse(path) + if err != nil { + return nil, err + } + + // Adding Query Param + query := url.Query() + for k, v := range queryParams { + for _, iv := range v { + query.Add(k, iv) + } + } + + // Encode the parameters. + url.RawQuery = query.Encode() + + // Generate a new request + if body != nil { + localVarRequest, err = http.NewRequest(method, url.String(), body) + } else { + localVarRequest, err = http.NewRequest(method, url.String(), nil) + } + if err != nil { + return nil, err + } + + // add header parameters, if any + if len(headerParams) > 0 { + headers := http.Header{} + for h, v := range headerParams { + headers.Set(h, v) + } + localVarRequest.Header = headers + } + + // Override request host, if applicable + if c.cfg.Host != "" { + localVarRequest.Host = c.cfg.Host + } + + // Add the user agent to the request. + localVarRequest.Header.Add("User-Agent", c.cfg.UserAgent) + + // Walk through any authentication. + if ctx != nil { + // OAuth2 authentication + if tok, ok := ctx.Value(ContextOAuth2).(oauth2.TokenSource); ok { + // We were able to grab an oauth2 token from the context + var latestToken *oauth2.Token + if latestToken, err = tok.Token(); err != nil { + return nil, err + } + + latestToken.SetAuthHeader(localVarRequest) + } + + // Basic HTTP Authentication + if auth, ok := ctx.Value(ContextBasicAuth).(BasicAuth); ok { + localVarRequest.SetBasicAuth(auth.UserName, auth.Password) + } + + // AccessToken Authentication + if auth, ok := ctx.Value(ContextAccessToken).(string); ok { + localVarRequest.Header.Add("Authorization", "Bearer " + auth) + } + } + + for header, value := range c.cfg.DefaultHeader { + localVarRequest.Header.Add(header, value) + } + + return localVarRequest, nil +} + + +// Add a file to the multipart request +func addFile(w *multipart.Writer, fieldName, path string) error { + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + + part, err := w.CreateFormFile(fieldName, filepath.Base(path)) + if err != nil { + return err + } + _, err = io.Copy(part, file) + + return err +} + +// Prevent trying to import "fmt" +func reportError(format string, a ...interface{}) (error) { + return fmt.Errorf(format, a...) +} + +// Set request body from an interface{} +func setBody(body interface{}, contentType string) (bodyBuf *bytes.Buffer, err error) { + if bodyBuf == nil { + bodyBuf = &bytes.Buffer{} + } + + if reader, ok := body.(io.Reader); ok { + _, err = bodyBuf.ReadFrom(reader) + } else if b, ok := body.([]byte); ok { + _, err = bodyBuf.Write(b) + } else if s, ok := body.(string); ok { + _, err = bodyBuf.WriteString(s) + } else if jsonCheck.MatchString(contentType) { + err = json.NewEncoder(bodyBuf).Encode(body) + } else if xmlCheck.MatchString(contentType) { + xml.NewEncoder(bodyBuf).Encode(body) + } + + if err != nil { + return nil, err + } + + if bodyBuf.Len() == 0 { + err = fmt.Errorf("Invalid body type %s\n", contentType) + return nil, err + } + return bodyBuf, nil +} + +// detectContentType method is used to figure out `Request.Body` content type for request header +func detectContentType(body interface{}) string { + contentType := "text/plain; charset=utf-8" + kind := reflect.TypeOf(body).Kind() + + switch kind { + case reflect.Struct, reflect.Map, reflect.Ptr: + contentType = "application/json; charset=utf-8" + case reflect.String: + contentType = "text/plain; charset=utf-8" + default: + if b, ok := body.([]byte); ok { + contentType = http.DetectContentType(b) + } else if kind == reflect.Slice { + contentType = "application/json; charset=utf-8" + } + } + + return contentType +} + + +// Ripped from https://github.com/gregjones/httpcache/blob/master/httpcache.go +type cacheControl map[string]string + +func parseCacheControl(headers http.Header) cacheControl { + cc := cacheControl{} + ccHeader := headers.Get("Cache-Control") + for _, part := range strings.Split(ccHeader, ",") { + part = strings.Trim(part, " ") + if part == "" { + continue + } + if strings.ContainsRune(part, '=') { + keyval := strings.Split(part, "=") + cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",") + } else { + cc[part] = "" + } + } + return cc +} + +// CacheExpires helper function to determine remaining time before repeating a request. +func CacheExpires(r *http.Response) (time.Time) { + // Figure out when the cache expires. + var expires time.Time + now, err := time.Parse(time.RFC1123, r.Header.Get("date")) + if err != nil { + return time.Now() + } + respCacheControl := parseCacheControl(r.Header) + + if maxAge, ok := respCacheControl["max-age"]; ok { + lifetime, err := time.ParseDuration(maxAge + "s") + if err != nil { + expires = now + } + expires = now.Add(lifetime) + } else { + expiresHeader := r.Header.Get("Expires") + if expiresHeader != "" { + expires, err = time.Parse(time.RFC1123, expiresHeader) + if err != nil { + expires = now + } + } + } + return expires +} + +func strlen(s string) (int) { + return utf8.RuneCountInString(s) +} + diff --git a/modules/swagger-codegen/src/main/resources/lua/api_doc.mustache b/modules/swagger-codegen/src/main/resources/lua/api_doc.mustache new file mode 100644 index 00000000000..70d0b96ce86 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/lua/api_doc.mustache @@ -0,0 +1,50 @@ +# {{invokerPackage}}\{{classname}}{{#description}} +{{description}}{{/description}} + +All URIs are relative to *{{basePath}}* + +Method | HTTP request | Description +------------- | ------------- | ------------- +{{#operations}}{{#operation}}[**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} +{{/operation}}{{/operations}} + +{{#operations}} +{{#operation}} +# **{{{operationId}}}** +> {{#returnType}}{{{returnType}}} {{/returnType}}{{{operationId}}}({{#authMethods}}ctx, {{/authMethods}}{{#allParams}}{{#required}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/required}}{{/allParams}}{{#hasOptionalParams}}optional{{/hasOptionalParams}}) +{{{summary}}}{{#notes}} + +{{{notes}}}{{/notes}} + +### Required Parameters +{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}} +Name | Type | Description | Notes +------------- | ------------- | ------------- | -------------{{#authMethods}} + **ctx** | **context.Context** | context containing the authentication | nil if no authentication{{/authMethods}}{{/-last}}{{/allParams}}{{#allParams}}{{#required}} + **{{paramName}}** | {{#isFile}}**{{dataType}}**{{/isFile}}{{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}[**{{dataType}}**]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} | {{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}}{{/required}}{{/allParams}}{{#hasOptionalParams}} + **optional** | **map[string]interface{}** | optional parameters | nil if no parameters + +### Optional Parameters +Optional parameters are passed through a map[string]interface{}. +{{#allParams}}{{#-last}} +Name | Type | Description | Notes +------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}}{{#allParams}} + **{{paramName}}** | {{#isFile}}**{{dataType}}**{{/isFile}}{{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}[**{{dataType}}**]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} | {{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}}{{/allParams}}{{/hasOptionalParams}} + +### Return type + +{{#returnType}}{{#returnTypeIsPrimitive}}**{{{returnType}}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{{returnType}}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}} (empty response body){{/returnType}} + +### Authorization + +{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{{name}}}](../README.md#{{{name}}}){{^-last}}, {{/-last}}{{/authMethods}} + +### HTTP request headers + + - **Content-Type**: {{#consumes}}{{{mediaType}}}{{#hasMore}}, {{/hasMore}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} + - **Accept**: {{#produces}}{{{mediaType}}}{{#hasMore}}, {{/hasMore}}{{/produces}}{{^produces}}Not defined{{/produces}} + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +{{/operation}} +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/lua/configuration.mustache b/modules/swagger-codegen/src/main/resources/lua/configuration.mustache new file mode 100644 index 00000000000..97bfa891d40 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/lua/configuration.mustache @@ -0,0 +1,43 @@ +{{>partial_header}} +package {{packageName}} + +import ( + "net/http" +) + +const ContextOAuth2 int = 1 +const ContextBasicAuth int = 2 +const ContextAccessToken int = 3 +const ContextAPIKey int = 4 + +type BasicAuth struct { + UserName string `json:"userName,omitempty"` + Password string `json:"password,omitempty"` +} + +type APIKey struct { + Key string + Prefix string +} + +type Configuration struct { + BasePath string `json:"basePath,omitempty"` + Host string `json:"host,omitempty"` + Scheme string `json:"scheme,omitempty"` + DefaultHeader map[string]string `json:"defaultHeader,omitempty"` + UserAgent string `json:"userAgent,omitempty"` + HTTPClient *http.Client +} + +func NewConfiguration() *Configuration { + cfg := &Configuration{ + BasePath: "{{{basePath}}}", + DefaultHeader: make(map[string]string), + UserAgent: "{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{{packageVersion}}}/go{{/httpUserAgent}}", + } + return cfg +} + +func (c *Configuration) AddDefaultHeader(key string, value string) { + c.DefaultHeader[key] = value +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/lua/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/lua/git_push.sh.mustache new file mode 100755 index 00000000000..a2d75234837 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/lua/git_push.sh.mustache @@ -0,0 +1,52 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 swagger-petstore-perl "minor update" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 + +if [ "$git_user_id" = "" ]; then + git_user_id="{{{gitUserId}}}" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="{{{gitRepoId}}}" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="{{{releaseNote}}}" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=`git remote` +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://github.com/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' + diff --git a/modules/swagger-codegen/src/main/resources/lua/gitignore.mustache b/modules/swagger-codegen/src/main/resources/lua/gitignore.mustache new file mode 100644 index 00000000000..180988936c5 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/lua/gitignore.mustache @@ -0,0 +1,40 @@ +# Compiled Lua sources +luac.out + +# luarocks build files +*.src.rock +*.zip +*.tar.gz + +# Object files +*.o +*.os +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo +*.def +*.exp + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex diff --git a/modules/swagger-codegen/src/main/resources/lua/model.mustache b/modules/swagger-codegen/src/main/resources/lua/model.mustache new file mode 100644 index 00000000000..65a7e3412c9 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/lua/model.mustache @@ -0,0 +1,28 @@ +{{#models}} +{{#model}} +{{>partial_header}} +-- {{classname}} class +local {{classname}} = {} +local {{classname}}_mt = { + __name = "{{classname}}"; + __index = {{classname}}; +} + +local function cast_{{classname}}(t) + return setmetatable(t, {{classname}}_mt) +end + +local function new_{{classname}}({{#vars}}{{name}}{{#hasMore}}, {{/hasMore}}{{/vars}}) + return cast_{{classname}}({ + {{#vars}} + ["{{baseName}}"] = {{name}}; + {{/vars}} + }) +end + +return { + cast = cast_{{classname}}; + new = new_{{classname}}; +} +{{/model}} +{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/lua/model_doc.mustache b/modules/swagger-codegen/src/main/resources/lua/model_doc.mustache new file mode 100644 index 00000000000..25537b2c5ed --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/lua/model_doc.mustache @@ -0,0 +1,11 @@ +{{#models}}{{#model}}# {{classname}} + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +{{#vars}}**{{name}}** | {{#isPrimitiveType}}**{{{datatype}}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{^isContainer}}{{^isDateTime}}*{{/isDateTime}}{{/isContainer}}{{{datatype}}}**]({{complexType}}.md){{/isPrimitiveType}} | {{description}} | {{^required}}[optional] {{/required}}{{#readOnly}}[readonly] {{/readOnly}}{{#defaultValue}}[default to {{{.}}}]{{/defaultValue}} +{{/vars}} + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + +{{/model}}{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/lua/partial_header.mustache b/modules/swagger-codegen/src/main/resources/lua/partial_header.mustache new file mode 100644 index 00000000000..63da5d8fc1e --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/lua/partial_header.mustache @@ -0,0 +1,13 @@ +--[[ + {{#appName}} + {{{appName}}} + + {{/appName}} + {{#appDescription}} + {{{appDescription}}} + + {{/appDescription}} + {{#version}}OpenAPI spec version: {{{version}}}{{/version}} + {{#infoEmail}}Contact: {{{infoEmail}}}{{/infoEmail}} + Generated by: https://github.com/swagger-api/swagger-codegen.git +]] diff --git a/modules/swagger-codegen/src/main/resources/nancyfx/api.mustache b/modules/swagger-codegen/src/main/resources/nancyfx/api.mustache index a2634d9ab81..21ee6fd90e8 100644 --- a/modules/swagger-codegen/src/main/resources/nancyfx/api.mustache +++ b/modules/swagger-codegen/src/main/resources/nancyfx/api.mustache @@ -5,7 +5,8 @@ using System.Collections.Generic; using Sharpility.Base; using {{packageName}}.{{packageContext}}.Models; using {{packageName}}.{{packageContext}}.Utils; -using NodaTime; +using NodaTime;{{#asyncServer}} +using System.Threading.Tasks;{{/asyncServer}} {{#imports}}using {{import}}; {{/imports}} @@ -22,15 +23,15 @@ namespace {{packageName}}.{{packageContext}}.Modules /// Sets up HTTP methods mappings. /// /// Service handling requests - public {{classname}}Module({{classname}}Service service) : base("{{{baseContext}}}") + public {{classname}}Module({{interfacePrefix}}{{classname}}Service service) : base("{{{baseContext}}}") { {{#operation}} - {{httpMethod}}["{{{path}}}"] = parameters => + {{httpMethod}}["{{{path}}}"{{#asyncServer}}, true{{/asyncServer}}] = {{#asyncServer}}async (parameters, ct){{/asyncServer}}{{^asyncServer}}parameters{{/asyncServer}} => { {{#allParams}}{{#isBodyParam}}var {{paramName}} = this.Bind<{{&dataType}}>();{{/isBodyParam}}{{^isBodyParam}}{{#isEnum}}var {{paramName}} = Parameters.ValueOf<{{>innerApiEnumName}}?>({{>innerParameterValueOfArgs}});{{/isEnum}}{{^isEnum}}var {{paramName}} = Parameters.ValueOf<{{&dataType}}>({{>innerParameterValueOfArgs}});{{/isEnum}}{{#hasMore}} {{/hasMore}}{{/isBodyParam}}{{/allParams}}{{#allParams}}{{#required}} Preconditions.IsNotNull({{paramName}}, "Required parameter: '{{paramName}}' is missing at '{{operationId}}'"); {{/required}}{{/allParams}} - {{#returnType}}return {{/returnType}}service.{{operationId}}(Context{{#allParams.0}}, {{/allParams.0}}{{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{#returnType}}{{#isListContainer}}.ToArray(){{/isListContainer}}{{/returnType}};{{^returnType}} + {{#returnType}}return {{/returnType}}{{#asyncServer}}await {{/asyncServer}}service.{{operationId}}(Context{{#allParams.0}}, {{/allParams.0}}{{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{#returnType}}{{#isListContainer}}.ToArray(){{/isListContainer}}{{/returnType}};{{^returnType}} return new Response { ContentType = "{{produces.0.mediaType}}"};{{/returnType}} }; {{/operation}} @@ -48,7 +49,7 @@ namespace {{packageName}}.{{packageContext}}.Modules /// Context of request {{#allParams}}/// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} {{/allParams}}/// {{#returnType}}{{returnType}}{{/returnType}} - {{#returnType}}{{&returnType}}{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}}(NancyContext context{{#allParams.0}}, {{/allParams.0}}{{>paramsList}});{{#hasMore}} + {{#asyncServer}}{{#returnType}}Task<{{{returnType}}}>{{/returnType}}{{^returnType}}Task{{/returnType}}{{/asyncServer}}{{^asyncServer}}{{#returnType}}{{&returnType}}{{/returnType}}{{^returnType}}void{{/returnType}}{{/asyncServer}} {{operationId}}(NancyContext context{{#allParams.0}}, {{/allParams.0}}{{>paramsList}});{{#hasMore}} {{/hasMore}}{{/operation}} } @@ -58,14 +59,14 @@ namespace {{packageName}}.{{packageContext}}.Modules /// public abstract class Abstract{{classname}}Service: {{interfacePrefix}}{{classname}}Service { - {{#operation}}public virtual {{#returnType}}{{&returnType}}{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}}(NancyContext context{{#allParams.0}}, {{/allParams.0}}{{>paramsList}}) + {{#operation}}public virtual {{#asyncServer}}{{#returnType}}Task<{{{returnType}}}>{{/returnType}}{{^returnType}}Task{{/returnType}}{{/asyncServer}}{{^asyncServer}}{{#returnType}}{{&returnType}}{{/returnType}}{{^returnType}}void{{/returnType}}{{/asyncServer}} {{operationId}}(NancyContext context{{#allParams.0}}, {{/allParams.0}}{{>paramsList}}) { - {{#returnType}}return {{/returnType}}{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + {{^asyncServer}}{{#returnType}}return {{/returnType}}{{/asyncServer}}{{#asyncServer}}return {{/asyncServer}}{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); }{{#hasMore}} {{/hasMore}}{{/operation}} - {{#operation}}protected abstract {{#returnType}}{{&returnType}}{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}}({{>paramsList}});{{#hasMore}} + {{#operation}}protected abstract {{#asyncServer}}{{#returnType}}Task<{{{returnType}}}>{{/returnType}}{{^returnType}}Task{{/returnType}}{{/asyncServer}}{{^asyncServer}}{{#returnType}}{{&returnType}}{{/returnType}}{{^returnType}}void{{/returnType}}{{/asyncServer}} {{operationId}}({{>paramsList}});{{#hasMore}} {{/hasMore}}{{/operation}} } diff --git a/modules/swagger-codegen/src/main/resources/nancyfx/innerApiEnum.mustache b/modules/swagger-codegen/src/main/resources/nancyfx/innerApiEnum.mustache index 0c6a219e7bb..586b0e423c7 100644 --- a/modules/swagger-codegen/src/main/resources/nancyfx/innerApiEnum.mustache +++ b/modules/swagger-codegen/src/main/resources/nancyfx/innerApiEnum.mustache @@ -4,7 +4,7 @@ public enum {{>innerApiEnumName}} { {{#allowableValues}} -{{#values}} {{&.}}{{^-last}}, {{/-last}} +{{#values}} {{&.}}{{^isInteger}} = {{-index}}{{/isInteger}}{{^-last}}, {{/-last}} {{/values}} {{/allowableValues}} }; diff --git a/modules/swagger-codegen/src/main/resources/nancyfx/localDateConverter.mustache b/modules/swagger-codegen/src/main/resources/nancyfx/localDateConverter.mustache new file mode 100644 index 00000000000..f8495d6fadd --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/nancyfx/localDateConverter.mustache @@ -0,0 +1,55 @@ +using Nancy.Bootstrapper; +using Nancy.Json; +using NodaTime; +using NodaTime.Text; +using System; +using System.Collections.Generic; + +namespace {{packageName}}.{{packageContext}}.Utils +{ + /// + /// (De)serializes a to a string using + /// the RFC3339 + /// full-date format. + /// + public class LocalDateConverter : JavaScriptPrimitiveConverter, IApplicationStartup + { + public override IEnumerable SupportedTypes + { + get + { + yield return typeof(LocalDate); + yield return typeof(LocalDate?); + } + } + + public void Initialize(IPipelines pipelines) + { + JsonSettings.PrimitiveConverters.Add(new LocalDateConverter()); + } + + + public override object Serialize(object obj, JavaScriptSerializer serializer) + { + if (obj is LocalDate) + { + LocalDate localDate = (LocalDate)obj; + return LocalDatePattern.IsoPattern.Format(localDate); + } + return null; + } + + public override object Deserialize(object primitiveValue, Type type, JavaScriptSerializer serializer) + { + if ((type == typeof(LocalDate) || type == typeof(LocalDate?)) && primitiveValue is string) + { + try + { + return LocalDatePattern.IsoPattern.Parse(primitiveValue as string).GetValueOrThrow(); + } + catch { } + } + return null; + } + } +} diff --git a/modules/swagger-codegen/src/main/resources/nancyfx/modelEnum.mustache b/modules/swagger-codegen/src/main/resources/nancyfx/modelEnum.mustache index 8d29373d205..303e4c71c26 100644 --- a/modules/swagger-codegen/src/main/resources/nancyfx/modelEnum.mustache +++ b/modules/swagger-codegen/src/main/resources/nancyfx/modelEnum.mustache @@ -10,6 +10,6 @@ /// /// Enum {{name}} /// - {{name}}{{#isInteger}} = {{{value}}}{{/isInteger}}{{^-last}}, + {{name}}{{#isInteger}} = {{{value}}}{{/isInteger}}{{^isInteger}} = {{-index}}{{/isInteger}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}} - } \ No newline at end of file + } diff --git a/modules/swagger-codegen/src/main/resources/nancyfx/parameters.mustache b/modules/swagger-codegen/src/main/resources/nancyfx/parameters.mustache index 75486f59f46..f760dfec75f 100644 --- a/modules/swagger-codegen/src/main/resources/nancyfx/parameters.mustache +++ b/modules/swagger-codegen/src/main/resources/nancyfx/parameters.mustache @@ -163,6 +163,8 @@ namespace {{packageName}}.{{packageContext}}.Utils parsers.Put(typeof(TimeSpan?), SafeParse(TimeSpan.Parse)); parsers.Put(typeof(ZonedDateTime), SafeParse(ParseZonedDateTime)); parsers.Put(typeof(ZonedDateTime?), SafeParse(ParseZonedDateTime)); + parsers.Put(typeof(LocalDate), SafeParse(ParseLocalDate)); + parsers.Put(typeof(LocalDate?), SafeParse(ParseLocalDate)); parsers.Put(typeof(LocalTime), SafeParse(ParseLocalTime)); parsers.Put(typeof(LocalTime?), SafeParse(ParseLocalTime)); @@ -372,6 +374,11 @@ namespace {{packageName}}.{{packageContext}}.Utils return new ZonedDateTime(Instant.FromDateTimeUtc(dateTime.ToUniversalTime()), DateTimeZone.Utc); } + private static LocalDate ParseLocalDate(string value) + { + return LocalDatePattern.IsoPattern.Parse(value).Value; + } + private static LocalTime ParseLocalTime(string value) { return LocalTimePattern.ExtendedIsoPattern.Parse(value).Value; diff --git a/modules/swagger-codegen/src/main/resources/nodejs/controller.mustache b/modules/swagger-codegen/src/main/resources/nodejs/controller.mustache index f94fe89ef7e..69ff7249f9d 100644 --- a/modules/swagger-codegen/src/main/resources/nodejs/controller.mustache +++ b/modules/swagger-codegen/src/main/resources/nodejs/controller.mustache @@ -1,13 +1,21 @@ 'use strict'; -var url = require('url'); +var utils = require('../utils/writer.js'); {{#operations}} - -var {{classname}} = require('./{{classname}}Service'); +var {{classname}} = require('../{{implFolder}}/{{classname}}Service'); {{#operation}} module.exports.{{nickname}} = function {{nickname}} (req, res, next) { - {{classname}}.{{nickname}}(req.swagger.params, res, next); + {{#allParams}} + var {{paramName}} = req.swagger.params['{{baseName}}'].value; + {{/allParams}} + {{classname}}.{{nickname}}({{#allParams}}{{paramName}}{{#hasMore}},{{/hasMore}}{{/allParams}}) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); }; {{/operation}} {{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/nodejs/service.mustache b/modules/swagger-codegen/src/main/resources/nodejs/service.mustache index e5a44836e07..90d354b1a14 100644 --- a/modules/swagger-codegen/src/main/resources/nodejs/service.mustache +++ b/modules/swagger-codegen/src/main/resources/nodejs/service.mustache @@ -2,40 +2,42 @@ {{#operations}} {{#operation}} -exports.{{{operationId}}} = function(args, res, next) { - /** - {{#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}} - **/ + +/** + {{#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}} + **/ +exports.{{{operationId}}} = function({{#allParams}}{{paramName}}{{#hasMore}},{{/hasMore}}{{/allParams}}) { + return new Promise(function(resolve, reject) { {{#returnType}} - var examples = {}; - {{#examples}} - examples['{{contentType}}'] = {{{example}}}; - {{/examples}} - if (Object.keys(examples).length > 0) { - res.setHeader('Content-Type', 'application/json'); - res.end(JSON.stringify(examples[Object.keys(examples)[0]] || {}, null, 2)); - } else { - res.end(); - } - {{/returnType}} - {{^returnType}} - res.end(); - {{/returnType}} + var examples = {}; + {{#examples}} + examples['{{contentType}}'] = {{{example}}}; + {{/examples}} + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + {{/returnType}} + {{^returnType}} + resolve(); + {{/returnType}} + }); } {{/operation}} diff --git a/modules/swagger-codegen/src/main/resources/nodejs/writer.mustache b/modules/swagger-codegen/src/main/resources/nodejs/writer.mustache new file mode 100644 index 00000000000..d79f6e1a526 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/nodejs/writer.mustache @@ -0,0 +1,43 @@ +var ResponsePayload = function(code, payload) { + this.code = code; + this.payload = payload; +} + +exports.respondWithCode = function(code, payload) { + return new ResponsePayload(code, payload); +} + +var writeJson = exports.writeJson = function(response, arg1, arg2) { + var code; + var payload; + + if(arg1 && arg1 instanceof ResponsePayload) { + writeJson(response, arg1.payload, arg1.code); + return; + } + + if(arg2 && Number.isInteger(arg2)) { + code = arg2; + } + else { + if(arg1 && Number.isInteger(arg1)) { + code = arg1; + } + } + if(code && arg1) { + payload = arg1; + } + else if(arg1) { + payload = arg1; + } + + if(!code) { + // if no response code given, we default to 200 + code = 200; + } + if(typeof payload === 'object') { + payload = JSON.stringify(payload, null, 2); + } + response.writeHead(code, {'Content-Type': 'application/json'}); + response.end(payload); +} diff --git a/modules/swagger-codegen/src/main/resources/objc/ApiClient-header.mustache b/modules/swagger-codegen/src/main/resources/objc/ApiClient-header.mustache index f50a372eb98..c18131cd699 100644 --- a/modules/swagger-codegen/src/main/resources/objc/ApiClient-header.mustache +++ b/modules/swagger-codegen/src/main/resources/objc/ApiClient-header.mustache @@ -23,6 +23,27 @@ extern NSString *const {{classPrefix}}ResponseObjectErrorKey; @property(nonatomic, strong) id<{{classPrefix}}Sanitizer> sanitizer; +/** + * Gets if the client is unreachable + * + * @return The client offline state + */ ++(BOOL) getOfflineState; + +/** + * Sets the client reachability, this may be overridden by the reachability manager if reachability changes + * + * @param status The client reachability status. + */ ++(void) setReachabilityStatus:(AFNetworkReachabilityStatus) status; + +/** + * Gets the client reachability + * + * @return The client reachability. + */ ++(AFNetworkReachabilityStatus) getReachabilityStatus; + @property (nonatomic, strong) NSDictionary< NSString *, AFHTTPRequestSerializer *>* requestSerializerForContentType; /** diff --git a/modules/swagger-codegen/src/main/resources/objc/Configuration-protocol.mustache b/modules/swagger-codegen/src/main/resources/objc/Configuration-protocol.mustache index ffb6b6971f0..946cc1bc2e5 100644 --- a/modules/swagger-codegen/src/main/resources/objc/Configuration-protocol.mustache +++ b/modules/swagger-codegen/src/main/resources/objc/Configuration-protocol.mustache @@ -75,4 +75,4 @@ static NSString * const k{{classPrefix}}APIVersion = @"{{podVersion}}"; */ @property (readonly, nonatomic, strong) NSDictionary *defaultHeaders; -@end \ No newline at end of file +@end diff --git a/modules/swagger-codegen/src/main/resources/objc/DefaultConfiguration-header.mustache b/modules/swagger-codegen/src/main/resources/objc/DefaultConfiguration-header.mustache index fc1f88f3865..438ddb5b38c 100644 --- a/modules/swagger-codegen/src/main/resources/objc/DefaultConfiguration-header.mustache +++ b/modules/swagger-codegen/src/main/resources/objc/DefaultConfiguration-header.mustache @@ -74,6 +74,11 @@ */ @property (nonatomic) NSString *sslCaCert; +/** + * The time zone to use for date serialization + */ +@property (nonatomic) NSTimeZone *serializationTimeZone; + /** * Sets API key * diff --git a/modules/swagger-codegen/src/main/resources/objc/JSONValueTransformer+ISO8601-body.mustache b/modules/swagger-codegen/src/main/resources/objc/JSONValueTransformer+ISO8601-body.mustache index b544a1dae58..a43b33a613a 100644 --- a/modules/swagger-codegen/src/main/resources/objc/JSONValueTransformer+ISO8601-body.mustache +++ b/modules/swagger-codegen/src/main/resources/objc/JSONValueTransformer+ISO8601-body.mustache @@ -1,5 +1,6 @@ #import #import "JSONValueTransformer+ISO8601.h" +#import "{{classPrefix}}Sanitizer.h" @implementation JSONValueTransformer (ISO8601) @@ -8,4 +9,9 @@ return [NSDate dateWithISO8601String:string]; } +- (NSString *)JSONObjectFromNSDate:(NSDate *)date +{ + return [{{classPrefix}}Sanitizer dateToString:date]; +} + @end diff --git a/modules/swagger-codegen/src/main/resources/objc/Model.xcdatamodel.mustache b/modules/swagger-codegen/src/main/resources/objc/Model.xcdatamodel.mustache index 90dcd63e3d9..dddac8fd743 100644 --- a/modules/swagger-codegen/src/main/resources/objc/Model.xcdatamodel.mustache +++ b/modules/swagger-codegen/src/main/resources/objc/Model.xcdatamodel.mustache @@ -2,8 +2,8 @@ {{#models}}{{#model}} -{{#isArrayModel}} -{{/isArrayModel}}{{^isArrayModel}}{{#vars}}{{#complexType}} {{/complexType}}{{^complexType}} {{/complexType}}{{#vendorExtensions.x-is-unique}} +{{#isArrayModel}} +{{/isArrayModel}}{{^isArrayModel}}{{#vars}}{{#complexType}} {{/complexType}}{{^complexType}} {{/complexType}}{{#vendorExtensions.x-is-unique}} {{/vendorExtensions.x-is-unique}} {{/vars}} {{/isArrayModel}} diff --git a/modules/swagger-codegen/src/main/resources/objc/NSManagedObject-header.mustache b/modules/swagger-codegen/src/main/resources/objc/NSManagedObject-header.mustache index 53bc9172472..fcf71612c93 100644 --- a/modules/swagger-codegen/src/main/resources/objc/NSManagedObject-header.mustache +++ b/modules/swagger-codegen/src/main/resources/objc/NSManagedObject-header.mustache @@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN @interface {{classname}}ManagedObject : NSManagedObject -@property (nullable, nonatomic, retain) NSSet<{{{arrayModelType}}}ManagedObject*>* entries; +@property (nullable, nonatomic, retain) NSOrderedSet<{{{arrayModelType}}}ManagedObject*>* entries; {{/isArrayModel}}{{^isArrayModel}} @interface {{classname}}ManagedObject : {{#parent}}{{{parent}}}ManagedObject{{/parent}}{{^parent}}NSManagedObject{{/parent}} @@ -22,7 +22,7 @@ NS_ASSUME_NONNULL_BEGIN {{#vars}} {{#description}}/* {{{description}}} {{^required}}[optional]{{/required}} */{{/description}} -@property (nullable, nonatomic, retain) {{^complexType}}{{{ datatype }}}{{/complexType}}{{#complexType}}{{#isListContainer}}NSSet<{{{complexType}}}ManagedObject*>*{{/isListContainer}}{{^isListContainer}}{{#isMapContainer}}{{{ datatype }}}{{/isMapContainer}}{{^isMapContainer}}{{{complexType}}}ManagedObject*{{/isMapContainer}}{{/isListContainer}}{{/complexType}} {{name}}; +@property (nullable, nonatomic, retain) {{^complexType}}{{{ datatype }}}{{/complexType}}{{#complexType}}{{#isListContainer}}NSOrderedSet<{{{complexType}}}ManagedObject*>*{{/isListContainer}}{{^isListContainer}}{{#isMapContainer}}{{{ datatype }}}{{/isMapContainer}}{{^isMapContainer}}{{{complexType}}}ManagedObject*{{/isMapContainer}}{{/isListContainer}}{{/complexType}} {{name}}; {{/vars}} {{/isArrayModel}} @end @@ -31,14 +31,14 @@ NS_ASSUME_NONNULL_BEGIN {{#isArrayModel}} - (void)addEntriesObject:({{arrayModelType}}ManagedObject *)value; - (void)removeEntriesObject:({{arrayModelType}}ManagedObject *)value; -- (void)addEntries:(NSSet<{{{arrayModelType}}}ManagedObject*> *)values; -- (void)removeEntries:(NSSet<{{{arrayModelType}}}ManagedObject*> *)values; +- (void)addEntries:(NSOrderedSet<{{{arrayModelType}}}ManagedObject*> *)values; +- (void)removeEntries:(NSOrderedSet<{{{arrayModelType}}}ManagedObject*> *)values; {{/isArrayModel}} {{^isArrayModel}} {{#vars}}{{#isListContainer}}{{#complexType}}- (void)add{{vendorExtensions.x-uppercaseName}}Object:({{complexType}}ManagedObject *)value; - (void)remove{{vendorExtensions.x-uppercaseName}}Object:({{complexType}}ManagedObject *)value; -- (void)add{{vendorExtensions.x-uppercaseName}}:(NSSet<{{{complexType}}}ManagedObject*> *)values; -- (void)remove{{vendorExtensions.x-uppercaseName}}:(NSSet<{{{complexType}}}ManagedObject*> *)values; +- (void)add{{vendorExtensions.x-uppercaseName}}:(NSOrderedSet<{{{complexType}}}ManagedObject*> *)values; +- (void)remove{{vendorExtensions.x-uppercaseName}}:(NSOrderedSet<{{{complexType}}}ManagedObject*> *)values; {{/complexType}}{{/isListContainer}}{{/vars}} {{/isArrayModel}} @end diff --git a/modules/swagger-codegen/src/main/resources/objc/NSManagedObjectBuilder-body.mustache b/modules/swagger-codegen/src/main/resources/objc/NSManagedObjectBuilder-body.mustache index b6b69135286..37df8840027 100644 --- a/modules/swagger-codegen/src/main/resources/objc/NSManagedObjectBuilder-body.mustache +++ b/modules/swagger-codegen/src/main/resources/objc/NSManagedObjectBuilder-body.mustache @@ -34,8 +34,14 @@ if(!managedObject || !object) { return; } -{{#vars}}{{^complexType}} managedObject.{{name}} = [object.{{name}} copy];{{/complexType}}{{#complexType}}{{#isListContainer}} if(object.{{name}}) { - NSMutableSet * convertedObjs = [NSMutableSet set]; + NSManagedObjectContext* context = managedObject.managedObjectContext; +{{#vars}}{{^complexType}} managedObject.{{name}} = [object.{{name}} copy];{{/complexType}}{{#complexType}}{{#isListContainer}} if(managedObject.{{name}}) { + for (id object in managedObject.{{name}}) { + [context deleteObject:object]; + } + } + if(object.{{name}}) { + NSMutableOrderedSet * convertedObjs = [NSMutableOrderedSet orderedSet]; for (id innerObject in object.{{name}}) { id convertedObj = [self.{{name}}Builder {{complexType}}ManagedObjectFrom{{complexType}}:innerObject context:managedObject.managedObjectContext]; [convertedObjs addObject:convertedObj]; @@ -47,7 +53,7 @@ } else { [self.{{name}}Builder update{{complexType}}ManagedObject:managedObject.{{name}} with{{complexType}}:object.{{name}}]; }{{/isMapContainer}}{{#isMapContainer}}managedObject.{{name}} = [object.{{name}} copy];{{/isMapContainer}}{{/isListContainer}}{{/complexType}} -{{/vars}}{{#isArrayModel}} NSMutableSet * convertedObjs = [NSMutableSet set]; +{{/vars}}{{#isArrayModel}} NSMutableOrderedSet * convertedObjs = [NSMutableOrderedSet orderedSet]; for (id innerObject in object) { id convertedObj = [self.entriesBuilder {{arrayModelType}}ManagedObjectFrom{{arrayModelType}}:innerObject context:managedObject.managedObjectContext]; [convertedObjs addObject:convertedObj]; diff --git a/modules/swagger-codegen/src/main/resources/objc/Sanitizer-body.mustache b/modules/swagger-codegen/src/main/resources/objc/Sanitizer-body.mustache index 465633a902c..e1f758ce6b2 100644 --- a/modules/swagger-codegen/src/main/resources/objc/Sanitizer-body.mustache +++ b/modules/swagger-codegen/src/main/resources/objc/Sanitizer-body.mustache @@ -1,6 +1,7 @@ #import "{{classPrefix}}Sanitizer.h" #import "{{classPrefix}}Object.h" #import "{{classPrefix}}QueryParamCollection.h" +#import "{{classPrefix}}DefaultConfiguration.h" #import NSString * const k{{classPrefix}}ApplicationJSONType = @"application/json"; @@ -63,7 +64,7 @@ NSString * {{classPrefix}}PercentEscapedStringFromString(NSString *string) { return object; } else if ([object isKindOfClass:[NSDate class]]) { - return [self dateParameterToString:object]; + return [{{classPrefix}}Sanitizer dateToString:object]; } else if ([object isKindOfClass:[NSArray class]]) { NSArray *objectArray = object; @@ -107,7 +108,7 @@ NSString * {{classPrefix}}PercentEscapedStringFromString(NSString *string) { return [param stringValue]; } else if ([param isKindOfClass:[NSDate class]]) { - return [self dateParameterToString:param]; + return [{{classPrefix}}Sanitizer dateToString:param]; } else if ([param isKindOfClass:[NSArray class]]) { NSMutableArray *mutableParam = [NSMutableArray array]; @@ -125,8 +126,9 @@ NSString * {{classPrefix}}PercentEscapedStringFromString(NSString *string) { } } -- (NSString *)dateParameterToString:(id)param { - return [param ISO8601String]; ++ (NSString *)dateToString:(id)date { + NSTimeZone* timeZone = [{{classPrefix}}DefaultConfiguration sharedConfig].serializationTimeZone; + return [date ISO8601StringWithTimeZone:timeZone usingCalendar:nil]; } #pragma mark - Utility Methods diff --git a/modules/swagger-codegen/src/main/resources/objc/Sanitizer-header.mustache b/modules/swagger-codegen/src/main/resources/objc/Sanitizer-header.mustache index b6e77edb3f3..4ad7f10a234 100644 --- a/modules/swagger-codegen/src/main/resources/objc/Sanitizer-header.mustache +++ b/modules/swagger-codegen/src/main/resources/objc/Sanitizer-header.mustache @@ -20,6 +20,11 @@ extern NSString * const k{{classPrefix}}ApplicationJSONType; */ - (NSString *) parameterToString: (id) param; +/** + * Convert date to NSString + */ ++ (NSString *)dateToString:(id)date; + /** * Detects Accept header from accepts NSArray * diff --git a/modules/swagger-codegen/src/main/resources/objc/api-body.mustache b/modules/swagger-codegen/src/main/resources/objc/api-body.mustache index c9afce2f27a..f083aed1cd7 100644 --- a/modules/swagger-codegen/src/main/resources/objc/api-body.mustache +++ b/modules/swagger-codegen/src/main/resources/objc/api-body.mustache @@ -92,7 +92,14 @@ NSInteger k{{classname}}MissingParamErrorCode = 234513; {{#collectionFormat}} queryParams[@"{{baseName}}"] = [[{{classPrefix}}QueryParamCollection alloc] initWithValuesAndFormat: {{paramName}} format: @"{{collectionFormat}}"]; {{/collectionFormat}} - {{^collectionFormat}}queryParams[@"{{baseName}}"] = {{paramName}};{{/collectionFormat}} + {{^collectionFormat}} + {{#isBoolean}} + queryParams[@"{{baseName}}"] = [{{paramName}} isEqual:@(YES)] ? @"true" : @"false"; + {{/isBoolean}} + {{^isBoolean}} + queryParams[@"{{baseName}}"] = {{paramName}}; + {{/isBoolean}} + {{/collectionFormat}} } {{/queryParams}} NSMutableDictionary* headerParams = [NSMutableDictionary dictionaryWithDictionary:self.apiClient.configuration.defaultHeaders]; diff --git a/modules/swagger-codegen/src/main/resources/objc/api-header.mustache b/modules/swagger-codegen/src/main/resources/objc/api-header.mustache index 266e7355fd6..96f1a05ed9d 100644 --- a/modules/swagger-codegen/src/main/resources/objc/api-header.mustache +++ b/modules/swagger-codegen/src/main/resources/objc/api-header.mustache @@ -22,7 +22,7 @@ extern NSInteger k{{classname}}MissingParamErrorCode; /// {{/allParams}}{{#responses}} /// code:{{{code}}} message:"{{{message}}}"{{#hasMore}},{{/hasMore}}{{/responses}} /// -/// @return {{{returnType}}} +/// @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} -(NSURLSessionTask*) {{#vendorExtensions.x-objc-operationId}}{{vendorExtensions.x-objc-operationId}}{{/vendorExtensions.x-objc-operationId}}{{^vendorExtensions.x-objc-operationId}}{{nickname}}{{#hasParams}}With{{vendorExtensions.firstParamAltName}}{{/hasParams}}{{^hasParams}}WithCompletionHandler: {{/hasParams}}{{/vendorExtensions.x-objc-operationId}}{{#allParams}}{{#secondaryParam}} {{paramName}}{{/secondaryParam}}: ({{{dataType}}}) {{paramName}}{{/allParams}} {{#hasParams}}completionHandler: {{/hasParams}}(void (^)({{#returnBaseType}}{{{returnType}}} output, {{/returnBaseType}}NSError* error)) handler; diff --git a/modules/swagger-codegen/src/main/resources/objc/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/objc/git_push.sh.mustache index e153ce23ecf..a2d75234837 100644 --- a/modules/swagger-codegen/src/main/resources/objc/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/objc/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/objc/model-header.mustache b/modules/swagger-codegen/src/main/resources/objc/model-header.mustache index 04570e71a2f..07f671c99ff 100644 --- a/modules/swagger-codegen/src/main/resources/objc/model-header.mustache +++ b/modules/swagger-codegen/src/main/resources/objc/model-header.mustache @@ -5,6 +5,11 @@ {{#imports}}#import "{{import}}.h" {{/imports}} +{{#imports}} +@protocol {{import}}; +@class {{import}}; +{{/imports}} + {{newline}} {{#models}} {{#model}} diff --git a/modules/swagger-codegen/src/main/resources/perl/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/perl/ApiClient.mustache index e5f12ca0bec..e4e43b725c1 100644 --- a/modules/swagger-codegen/src/main/resources/perl/ApiClient.mustache +++ b/modules/swagger-codegen/src/main/resources/perl/ApiClient.mustache @@ -26,22 +26,25 @@ use Module::Runtime qw(use_module); use {{moduleName}}::Configuration; -use base 'Class::Singleton'; -sub _new_instance -{ +sub new { my $class = shift; + + my $config; + if ( $_[0] && ref $_[0] && ref $_[0] eq '{{moduleName}}::Configuration' ) { + $config = $_[0]; + } else { + $config = {{moduleName}}::Configuration->new(@_); + } + my (%args) = ( 'ua' => LWP::UserAgent->new, - 'base_url' => '{{{basePath}}}', - @_ + 'config' => $config, ); return bless \%args, $class; } -sub _cfg {'{{moduleName}}::Configuration'} - # Set the user agent of the API client # # @param string $user_agent The user agent of the API client @@ -78,7 +81,7 @@ sub call_api { $self->update_params_for_auth($header_params, $query_params, $auth_settings); - my $_url = $self->{base_url} . $resource_path; + my $_url = $self->{config}{base_url} . $resource_path; # build query if (%$query_params) { @@ -125,8 +128,8 @@ sub call_api { else { } - $self->{ua}->timeout($self->{http_timeout} || ${{moduleName}}::Configuration::http_timeout); - $self->{ua}->agent($self->{http_user_agent} || ${{moduleName}}::Configuration::http_user_agent); + $self->{ua}->timeout($self->{http_timeout} || $self->{config}{http_timeout}); + $self->{ua}->agent($self->{http_user_agent} || $self->{config}{http_user_agent}); $log->debugf("REQUEST: %s", $_request->as_string); my $_response = $self->{ua}->request($_request); @@ -300,11 +303,11 @@ sub get_api_key_with_prefix { my ($self, $key_name) = @_; - my $api_key = ${{moduleName}}::Configuration::api_key->{$key_name}; + my $api_key = $self->{config}{api_key}{$key_name}; return unless $api_key; - my $prefix = ${{moduleName}}::Configuration::api_key_prefix->{$key_name}; + my $prefix = $self->{config}{api_key_prefix}{$key_name}; return $prefix ? "$prefix $api_key" : $api_key; } @@ -335,11 +338,11 @@ sub update_params_for_auth { if ($api_key) { $query_params->{'{{keyParamName}}'} = $api_key; }{{/isKeyInQuery}}{{/isApiKey}}{{#isBasic}} - if (${{moduleName}}::Configuration::username || ${{moduleName}}::Configuration::password) { - $header_params->{'Authorization'} = 'Basic ' . encode_base64(${{moduleName}}::Configuration::username . ":" . ${{moduleName}}::Configuration::password); + if ($self->{config}{username} || $self->{config}{password}) { + $header_params->{'Authorization'} = 'Basic ' . encode_base64($self->{config}{username} . ":" . $self->{config}{password}); }{{/isBasic}}{{#isOAuth}} - if (${{moduleName}}::Configuration::access_token) { - $header_params->{'Authorization'} = 'Bearer ' . ${{moduleName}}::Configuration::access_token; + if ($self->{config}{access_token}) { + $header_params->{'Authorization'} = 'Bearer ' . $self->{config}{access_token}; }{{/isOAuth}} } {{/authMethods}} @@ -356,7 +359,7 @@ sub update_params_for_auth { sub _global_auth_setup { my ($self, $header_params, $query_params) = @_; - my $tokens = $self->_cfg->get_tokens; + my $tokens = $self->{config}->get_tokens; return unless keys %$tokens; # basic diff --git a/modules/swagger-codegen/src/main/resources/perl/ApiFactory.mustache b/modules/swagger-codegen/src/main/resources/perl/ApiFactory.mustache index cbda8660c20..3b064e4291d 100644 --- a/modules/swagger-codegen/src/main/resources/perl/ApiFactory.mustache +++ b/modules/swagger-codegen/src/main/resources/perl/ApiFactory.mustache @@ -49,19 +49,28 @@ my %_apis = map { $_ =~ /^{{moduleName}}::(.*)$/; $1 => $_ } grep {$_ =~ /Api$/} usesub '{{moduleName}}'; -=head1 new() +=head1 new($api_client) - Any parameters are optional, and are passed to and stored on the api_client object. - - base_url: (optional) - supply this to change the default base URL taken from the Swagger definition. + create a new {{moduleName}}::ApiFactory instance with the given {{moduleName}}::ApiClient instance. + +=head1 new(%paramters) + + Any parameters are optional, and are passed to and stored on the api_client object. + See L<{{moduleName}}::ApiClient> and L<{{moduleName}}::Configuration> for valid paramters + =cut sub new { - my ($class, %p) = (shift, @_); - $p{api_client} = {{moduleName}}::ApiClient->instance(%p); - return bless \%p, $class; + my ($class) = shift; + + my $api_client; + if ($_[0] && ref $_[0] && ref $_[0] eq '{{moduleName}}::ApiClient' ) { + $api_client = $_[0]; + } else { + $api_client = {{moduleName}}::ApiClient->new(@_); + } + bless { api_client => $api_client }, $class; } =head1 get_api($which) @@ -78,7 +87,7 @@ sub get_api { my ($self, $which) = @_; croak "API not specified" unless $which; my $api_class = $_apis{"${which}Api"} || croak "No known API for '$which'"; - return $api_class->new(api_client => $self->api_client); + return $api_class->new($self->api_client); } =head1 api_client() diff --git a/modules/swagger-codegen/src/main/resources/perl/Configuration.mustache b/modules/swagger-codegen/src/main/resources/perl/Configuration.mustache index 135349fdd82..b237cf7961e 100644 --- a/modules/swagger-codegen/src/main/resources/perl/Configuration.mustache +++ b/modules/swagger-codegen/src/main/resources/perl/Configuration.mustache @@ -15,71 +15,142 @@ use Carp; use constant VERSION => '{{moduleVersion}}'; -# class/static variables -our $http_timeout = 180; -our $http_user_agent = '{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{{moduleVersion}}}/perl{{/httpUserAgent}}'; +=head1 Name -# authentication setting -our $api_key = {}; -our $api_key_prefix = {}; -our $api_key_in = {}; + {{moduleName}}::Configuration - holds the configuration for all {{moduleName}} Modules -# username and password for HTTP basic authentication -our $username = ''; -our $password = ''; +=head1 new(%paramters) + +=over 4 + +=item http_timeout: (optional) + +Integer. timeout for HTTP requests in seconds + +default: 180 + +=item http_user_agent: (optional) + +String. custom UserAgent header + +default: {{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{{moduleVersion}}}/perl{{/httpUserAgent}} + +=item api_key: (optional) + +Hashref. Keyed on the name of each key (there can be multiple tokens). + + api_key => { + secretKey => 'aaaabbbbccccdddd', + anotherKey => '1111222233334444', + }; + +=item api_key_prefix: (optional) + +Hashref. Keyed on the name of each key (there can be multiple tokens). Note not all api keys require a prefix. + + api_key_prefix => { + secretKey => 'string', + anotherKey => 'same or some other string', + }; + +=item api_key_in: (optional) + +=item username: (optional) + +String. The username for basic auth. + +=item password: (optional) + +String. The password for basic auth. + +=item access_token: (optional) + +String. The OAuth access token. + +=item base_url: (optional) + +String. The base URL of the API + +default: {{{basePath}}} + +=back + +=cut + +sub new { + my ($self, %p) = (shift,@_); + + # class/static variables + $p{http_timeout} //= 180; + $p{http_user_agent} //= '{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{{moduleVersion}}}/perl{{/httpUserAgent}}'; + + # authentication setting + $p{api_key} //= {}; + $p{api_key_prefix} //= {}; + $p{api_key_in} //= {}; + + # username and password for HTTP basic authentication + $p{username} //= ''; + $p{password} //= ''; + + # access token for OAuth + $p{access_token} //= ''; + + # base_url + $p{base_url} //= '{{{basePath}}}'; + + return bless \%p => $self; +} -# access token for OAuth -our $access_token = ''; sub get_tokens { - my $class = shift; + my $self = shift; my $tokens = {}; - $tokens->{username} = $username if $username; - $tokens->{password} = $password if $password; - $tokens->{access_token} = $access_token if $access_token; + $tokens->{username} = $self->{username} if $self->{username}; + $tokens->{password} = $self->{password} if $self->{password}; + $tokens->{access_token} = $self->{access_token} if $self->{access_token}; - foreach my $token_name (keys %{ $api_key }) { - $tokens->{$token_name}->{token} = $api_key->{$token_name}; - $tokens->{$token_name}->{prefix} = $api_key_prefix->{$token_name}; - $tokens->{$token_name}->{in} = $api_key_in->{$token_name}; + foreach my $token_name (keys %{ $self->{api_key} }) { + $tokens->{$token_name}->{token} = $self->{api_key}{$token_name}; + $tokens->{$token_name}->{prefix} = $self->{api_key_prefix}{$token_name}; + $tokens->{$token_name}->{in} = $self->{api_key_in}{$token_name}; } return $tokens; } sub clear_tokens { - my $class = shift; - my %tokens = %{$class->get_tokens}; # copy + my $self = shift; + my %tokens = %{$self->get_tokens}; # copy - $username = undef; - $password = undef; - $access_token = undef; + $self->{username} = ''; + $self->{password} = ''; + $self->{access_token} = ''; - $api_key = {}; - $api_key_prefix = {}; - $api_key_in = {}; + $self->{api_key} = {}; + $self->{api_key_prefix} = {}; + $self->{api_key_in} = {}; return \%tokens; } sub accept_tokens { - my ($class, $tokens) = @_; + my ($self, $tokens) = @_; foreach my $known_name (qw(username password access_token)) { next unless $tokens->{$known_name}; - eval "\$$known_name = delete \$tokens->{\$known_name}"; - die $@ if $@; + $self->{$known_name} = delete $tokens->{$known_name}; } foreach my $token_name (keys %$tokens) { - $api_key->{$token_name} = $tokens->{$token_name}->{token}; - if ($tokens->{$token_name}->{prefix}) { - $api_key_prefix->{$token_name} = $tokens->{$token_name}->{prefix}; + $self->{api_key}{$token_name} = $tokens->{$token_name}{token}; + if ($tokens->{$token_name}{prefix}) { + $self->{api_key_prefix}{$token_name} = $tokens->{$token_name}{prefix}; } my $in = $tokens->{$token_name}->{in} || 'head'; croak "Tokens can only go in 'head' or 'query' (not in '$in')" unless $in =~ /^(?:head|query)$/; - $api_key_in->{$token_name} = $in; + $self->{api_key_in}{$token_name} = $in; } } diff --git a/modules/swagger-codegen/src/main/resources/perl/README.mustache b/modules/swagger-codegen/src/main/resources/perl/README.mustache index 512ddfbd065..beff33444b9 100644 --- a/modules/swagger-codegen/src/main/resources/perl/README.mustache +++ b/modules/swagger-codegen/src/main/resources/perl/README.mustache @@ -94,37 +94,37 @@ you are accessing. Usually `prefix` and `in` will be determined by the code gene the spec and you will not need to set them at run time. If not, `in` will default to 'head' and `prefix` to the empty string. -The tokens will be placed in the `{{moduleName}}::Configuration` namespace +The tokens will be placed in a L<{{moduleName}}::Configuration> instance as follows, but you don't need to know about this. -- `${{moduleName}}::Configuration::username` +- `$cfg->{username}` String. The username for basic auth. -- `${{moduleName}}::Configuration::password` +- `$cfg->{password}` String. The password for basic auth. -- `${{moduleName}}::Configuration::api_key` +- `$cfg->{api_key}` Hashref. Keyed on the name of each key (there can be multiple tokens). - ${{moduleName}}::Configuration::api_key = { + $cfg->{api_key} = { secretKey => 'aaaabbbbccccdddd', anotherKey => '1111222233334444', }; -- `${{moduleName}}::Configuration::api_key_prefix` +- `$cfg->{api_key_prefix}` Hashref. Keyed on the name of each key (there can be multiple tokens). Note not all api keys require a prefix. - ${{moduleName}}::Configuration::api_key_prefix = { + $cfg->{api_key_prefix} = { secretKey => 'string', anotherKey => 'same or some other string', }; -- `${{moduleName}}::Configuration::access_token` +- `$cfg->{access_token}` String. The OAuth access token. @@ -133,8 +133,7 @@ as follows, but you don't need to know about this. ## `base_url` The generated code has the `base_url` already set as a default value. This method -returns (and optionally sets, but only if the API client has not been -created yet) the current value of `base_url`. +returns the current value of `base_url`. ## `api_factory` @@ -253,21 +252,22 @@ use warnings; {{/model}}{{/models}} # for displaying the API response data use Data::Dumper; -use {{{moduleName}}}::Configuration; use {{moduleName}}::{{classname}}; + +my $api_instance = {{moduleName}}::{{classname}}->new( {{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#operation}}{{#-first}}{{#hasAuthMethods}}{{#authMethods}}{{#isBasic}} -# Configure HTTP basic authorization: {{{name}}} -${{{moduleName}}}::Configuration::username = 'YOUR_USERNAME'; -${{{moduleName}}}::Configuration::password = 'YOUR_PASSWORD';{{/isBasic}}{{#isApiKey}} -# Configure API key authorization: {{{name}}} -${{{moduleName}}}::Configuration::api_key->{'{{{keyParamName}}}'} = 'YOUR_API_KEY'; -# uncomment below to setup prefix (e.g. Bearer) for API key, if needed -#${{{moduleName}}}::Configuration::api_key_prefix->{'{{{keyParamName}}}'} = 'Bearer';{{/isApiKey}}{{#isOAuth}} -# Configure OAuth2 access token for authorization: {{{name}}} -${{{moduleName}}}::Configuration::access_token = 'YOUR_ACCESS_TOKEN';{{/isOAuth}}{{/authMethods}} + # Configure HTTP basic authorization: {{{name}}} + username => 'YOUR_USERNAME', + password => 'YOUR_PASSWORD',{{/isBasic}}{{#isApiKey}} + # Configure API key authorization: {{{name}}} + api_key => {'{{{keyParamName}}}' => 'YOUR_API_KEY'}, + # uncomment below to setup prefix (e.g. Bearer) for API key, if needed + #api_key_prefix => {'{{{keyParamName}}}' => 'Bearer'},{{/isApiKey}}{{#isOAuth}} + # Configure OAuth2 access token for authorization: {{{name}}} + access_token => 'YOUR_ACCESS_TOKEN',{{/isOAuth}}{{/authMethods}} {{/hasAuthMethods}} +); -my $api_instance = {{moduleName}}::{{classname}}->new(); {{#allParams}}my ${{paramName}} = {{#isListContainer}}[{{/isListContainer}}{{#isBodyParam}}{{{moduleName}}}::Object::{{dataType}}->new(){{/isBodyParam}}{{^isBodyParam}}{{{example}}}{{/isBodyParam}}{{#isListContainer}}]{{/isListContainer}}; # {{{dataType}}} | {{{description}}} {{/allParams}} diff --git a/modules/swagger-codegen/src/main/resources/perl/Role.mustache b/modules/swagger-codegen/src/main/resources/perl/Role.mustache index f853faeb726..c8d86f57dff 100644 --- a/modules/swagger-codegen/src/main/resources/perl/Role.mustache +++ b/modules/swagger-codegen/src/main/resources/perl/Role.mustache @@ -34,8 +34,8 @@ has tokens => ( is => 'ro', ); has _cfg => ( is => 'ro', - isa => 'Str', - default => '{{moduleName}}::Configuration', + isa => '{{moduleName}}::Configuration', + default => sub { {{moduleName}}::Configuration->new() }, ); has version_info => ( is => 'ro', @@ -196,39 +196,39 @@ you are accessing. Usually C and C will be determined by the code ge the spec and you will not need to set them at run time. If not, C will default to 'head' and C to the empty string. -The tokens will be placed in the C<{{moduleName}}::Configuration> namespace +The tokens will be placed in a L<{{moduleName}}::Configuration> instance as follows, but you don't need to know about this. =over 4 -=item C<${{moduleName}}::Configuration::username> +=item C<$cfg-\>{username}> String. The username for basic auth. -=item C<${{moduleName}}::Configuration::password> +=item C<$cfg-\>{password}> String. The password for basic auth. -=item C<${{moduleName}}::Configuration::api_key> +=item C<$cfg-\>{api_key}> Hashref. Keyed on the name of each key (there can be multiple tokens). - ${{moduleName}}::Configuration::api_key = { + $cfg->{api_key} = { secretKey => 'aaaabbbbccccdddd', anotherKey => '1111222233334444', }; -=item C<${{moduleName}}::Configuration::api_key_prefix> +=item C<$cfg->{api_key_prefix}> Hashref. Keyed on the name of each key (there can be multiple tokens). Note not all api keys require a prefix. - ${{moduleName}}::Configuration::api_key_prefix = { + $cfg->{api_key_prefix} = { secretKey => 'string', anotherKey => 'same or some other string', }; -=item C<${{moduleName}}::Configuration::access_token> +=item C<$config-\>{access_token}> String. The OAuth access token. @@ -239,8 +239,7 @@ String. The OAuth access token. =head2 C The generated code has the C already set as a default value. This method -returns (and optionally sets, but only if the API client has not been -created yet) the current value of C. +returns the current value of C. =head2 C diff --git a/modules/swagger-codegen/src/main/resources/perl/api.mustache b/modules/swagger-codegen/src/main/resources/perl/api.mustache index 70e532ee82e..7aeb164dbf3 100644 --- a/modules/swagger-codegen/src/main/resources/perl/api.mustache +++ b/modules/swagger-codegen/src/main/resources/perl/api.mustache @@ -15,25 +15,22 @@ use Carp qw( croak ); use Log::Any qw($log); use {{moduleName}}::ApiClient; -use {{moduleName}}::Configuration; use base "Class::Data::Inheritable"; __PACKAGE__->mk_classdata('method_documentation' => {}); sub new { - my $class = shift; - my (%self) = ( - 'api_client' => {{moduleName}}::ApiClient->instance, - @_ - ); - - #my $self = { - # #api_client => $options->{api_client} - # api_client => $default_api_client - #}; - - bless \%self, $class; + my $class = shift; + my $api_client; + + if ($_[0] && ref $_[0] && ref $_[0] eq '{{moduleName}}::ApiClient' ) { + $api_client = $_[0]; + } else { + $api_client = {{moduleName}}::ApiClient->new(@_); + } + + bless { api_client => $api_client }, $class; } {{#operations}} diff --git a/modules/swagger-codegen/src/main/resources/perl/api_doc.mustache b/modules/swagger-codegen/src/main/resources/perl/api_doc.mustache index acc6b4a003f..99d5ad5198c 100644 --- a/modules/swagger-codegen/src/main/resources/perl/api_doc.mustache +++ b/modules/swagger-codegen/src/main/resources/perl/api_doc.mustache @@ -25,21 +25,21 @@ Method | HTTP request | Description ### Example ```perl use Data::Dumper; -use {{{moduleName}}}::Configuration; use {{moduleName}}::{{classname}}; +my $api_instance = {{moduleName}}::{{classname}}->new( {{#hasAuthMethods}}{{#authMethods}}{{#isBasic}} -# Configure HTTP basic authorization: {{{name}}} -${{{moduleName}}}::Configuration::username = 'YOUR_USERNAME'; -${{{moduleName}}}::Configuration::password = 'YOUR_PASSWORD';{{/isBasic}}{{#isApiKey}} -# Configure API key authorization: {{{name}}} -${{{moduleName}}}::Configuration::api_key->{'{{{keyParamName}}}'} = 'YOUR_API_KEY'; -# uncomment below to setup prefix (e.g. Bearer) for API key, if needed -#${{{moduleName}}}::Configuration::api_key_prefix->{'{{{keyParamName}}}'} = "Bearer";{{/isApiKey}}{{#isOAuth}} -# Configure OAuth2 access token for authorization: {{{name}}} -${{{moduleName}}}::Configuration::access_token = 'YOUR_ACCESS_TOKEN';{{/isOAuth}}{{/authMethods}} + # Configure HTTP basic authorization: {{{name}}} + username => 'YOUR_USERNAME', + password => 'YOUR_PASSWORD',{{/isBasic}}{{#isApiKey}} + # Configure API key authorization: {{{name}}} + api_key => {'{{{keyParamName}}}' => 'YOUR_API_KEY'}, + # uncomment below to setup prefix (e.g. Bearer) for API key, if needed + #api_key_prefix => {'{{{keyParamName}}}' => 'Bearer'},{{/isApiKey}}{{#isOAuth}} + # Configure OAuth2 access token for authorization: {{{name}}} + access_token => 'YOUR_ACCESS_TOKEN',{{/isOAuth}}{{/authMethods}} {{/hasAuthMethods}} +); -my $api_instance = {{moduleName}}::{{classname}}->new(); {{#allParams}}my ${{paramName}} = {{#isListContainer}}[{{/isListContainer}}{{#isBodyParam}}{{{moduleName}}}::Object::{{dataType}}->new(){{/isBodyParam}}{{^isBodyParam}}{{{example}}}{{/isBodyParam}}{{#isListContainer}}]{{/isListContainer}}; # {{{dataType}}} | {{{description}}} {{/allParams}} diff --git a/modules/swagger-codegen/src/main/resources/perl/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/perl/git_push.sh.mustache index e153ce23ecf..a2d75234837 100755 --- a/modules/swagger-codegen/src/main/resources/perl/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/perl/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/perl/object.mustache b/modules/swagger-codegen/src/main/resources/perl/object.mustache index bb19479c61d..d5d8fdd8f40 100644 --- a/modules/swagger-codegen/src/main/resources/perl/object.mustache +++ b/modules/swagger-codegen/src/main/resources/perl/object.mustache @@ -19,6 +19,10 @@ use Log::Any qw($log); use Date::Parse; use DateTime; +{{#imports}} +use {{moduleName}}::Object::{{.}}; +{{/imports}} + use base ("Class::Accessor", "Class::Data::Inheritable"); diff --git a/modules/swagger-codegen/src/main/resources/silex/.htaccess b/modules/swagger-codegen/src/main/resources/php-silex/.htaccess similarity index 100% rename from modules/swagger-codegen/src/main/resources/silex/.htaccess rename to modules/swagger-codegen/src/main/resources/php-silex/.htaccess diff --git a/modules/swagger-codegen/src/main/resources/silex/README.mustache b/modules/swagger-codegen/src/main/resources/php-silex/README.mustache similarity index 100% rename from modules/swagger-codegen/src/main/resources/silex/README.mustache rename to modules/swagger-codegen/src/main/resources/php-silex/README.mustache diff --git a/modules/swagger-codegen/src/main/resources/silex/composer.json b/modules/swagger-codegen/src/main/resources/php-silex/composer.json similarity index 100% rename from modules/swagger-codegen/src/main/resources/silex/composer.json rename to modules/swagger-codegen/src/main/resources/php-silex/composer.json diff --git a/modules/swagger-codegen/src/main/resources/silex/index.mustache b/modules/swagger-codegen/src/main/resources/php-silex/index.mustache similarity index 100% rename from modules/swagger-codegen/src/main/resources/silex/index.mustache rename to modules/swagger-codegen/src/main/resources/php-silex/index.mustache diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/Controller.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/Controller.mustache index af937c7d99f..dec27347f82 100644 --- a/modules/swagger-codegen/src/main/resources/php-symfony/Controller.mustache +++ b/modules/swagger-codegen/src/main/resources/php-symfony/Controller.mustache @@ -19,9 +19,10 @@ namespace {{controllerPackage}}; -use Symfony\Bundle\FrameworkBundle\Controller\Controller as BaseController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\HttpException; +use {{servicePackage}}\SerializerInterface; +use {{servicePackage}}\ValidatorInterface; /** * Controller Class Doc Comment @@ -31,212 +32,147 @@ use Symfony\Component\HttpKernel\Exception\HttpException; * @author Swagger Codegen team * @link https://github.com/swagger-api/swagger-codegen */ -class Controller extends BaseController +class Controller { - - /** - * This will return a response with code 400. Usage example: - * return $this->createBadRequestResponse('Unable to access this page!'); - * - * @param string $message A message - * - * @return Response - */ - public function createBadRequestResponse($message = 'Bad Request.') - { - return new Response($message, 400); - } - - /** - * This will return an error response. Usage example: - * return $this->createErrorResponse(new UnauthorizedHttpException()); - * - * @param HttpException $exception An HTTP exception - * - * @return Response - */ - public function createErrorResponse(HttpException $exception) - { - $statusCode = $exception->getStatusCode(); - $headers = array_merge($exception->getHeaders(), ['Content-Type' => 'application/json']); - - $json = $this->exceptionToArray($exception); - $json['statusCode'] = $statusCode; - - return new Response(json_encode($json, 15, 512), $statusCode, $headers); - } - - /** - * Serializes data to a given type format. - * - * @param mixed $data The data to serialize. - * @param string $class The source data class. - * @param string $format The target serialization format. - * - * @return string A serialized data string. - */ - public function serialize($data, $format) - { - return $this->get('{{bundleAlias}}.model.model_serializer')->serialize($data, $format); - } - - /** - * Deserializes data from a given type format. - * - * @param string $data The data to deserialize. - * @param string $class The target data class. - * @param string $format The source serialization format. - * - * @return mixed A deserialized data. - */ - public function deserialize($data, $class, $format) - { - return $this->get('{{bundleAlias}}.model.model_serializer')->deserialize($data, $class, $format); - } - - /** - * Decodes a string value. - * - * @param string $string The string value to decode. - * @param string $dataType The data type of the parameter. - * - * @return mixed The decoded value. - */ - public function fromString($string, $dataType) - { - if ($dataType === 'integer' || $dataType === 'number') { - return $this->toNumber($string); - } - if ($dataType === 'bool') { - return $this->toBoolean($string); - } - if ($dataType === '\DateTime') { - return $this->toDateTime($string); - } - - return $string; - } - - /** - * Decodes a header value. - * - * @param string $header The header value to decode. - * @param string $dataType The data type of the parameter. - * - * @return mixed The decoded value. - */ - public function fromHeader($header, $dataType) - { - return $this->fromString($header, $dataType); - } - - /** - * Decodes a query value. - * - * @param string $query The query value to decode. - * @param string $dataType The data type of the parameter. - * - * @return mixed The decoded value. - */ - public function fromQuery($query, $dataType) - { - return $this->fromString($query, $dataType); - } - - /** - * Decodes a path value. - * - * @param string $path The path value to decode. - * @param string $dataType The data type of the parameter. - * - * @return mixed The decoded value. - */ - public function fromPath($path, $dataType) - { - return $this->fromString($path, $dataType); - } - - /** - * Decodes a form value. - * - * @param string $form The form value to decode. - * @param string $dataType The data type of the parameter. - * - * @return mixed The decoded value. - */ - public function fromForm($form, $dataType) - { - return $this->fromString($form, $dataType); - } - - /** - * Decoded a string to a number. - * - * @param string $string The string to decode. - * - * @return number|null A decoded number, or null, if not a valid string. - */ - private function toNumber($string) - { - if (is_numeric($string)) { - return $string + 0; - } - - return null; - } - - /** - * Decoded a string to a boolean. - * - * @param string $string The string to decode. - * - * @return boolean|null A decoded boolean, or null, if not a valid string. - */ - private function toBoolean($string) - { - if ($string === 'true') { - return true; - } - if ($string === 'false') { - return false; - } - - return null; - } - - /** - * Decoded a string to a date time. - * - * @param string $string The string to decode. - * - * @return \DateTime|null A decoded date time, or null, if not a valid string. - */ - private function toDateTime($string) - { - if ($dateTime = date_create($string)) { - return $dateTime; - } - - return null; - } - - /** - * Converts an exception to a serializable array. - * - * @param \Exception|null $exception - * - * @return array - */ - private function exceptionToArray(\Exception $exception = null) - { - if (null === $exception) { - return null; - } - - return [ - 'message' => $exception->getMessage(), - 'type' => get_class($exception), - 'previous' => $this->exceptionToArray($exception->getPrevious()), - ]; - } + protected $validator; + protected $serializer; + protected $apiServer; + + public function setValidator(ValidatorInterface $validator) + { + $this->validator = $validator; + } + + public function setSerializer(SerializerInterface $serializer) + { + $this->serializer = $serializer; + } + + public function setApiServer($server) + { + $this->apiServer = $server; + } + + /** + * This will return a response with code 400. Usage example: + * return $this->createBadRequestResponse('Unable to access this page!'); + * + * @param string $message A message + * + * @return Response + */ + public function createBadRequestResponse($message = 'Bad Request.') + { + return new Response($message, 400); + } + + /** + * This will return an error response. Usage example: + * return $this->createErrorResponse(new UnauthorizedHttpException()); + * + * @param HttpException $exception An HTTP exception + * + * @return Response + */ + public function createErrorResponse(HttpException $exception) + { + $statusCode = $exception->getStatusCode(); + $headers = array_merge($exception->getHeaders(), ['Content-Type' => 'application/json']); + + $json = $this->exceptionToArray($exception); + $json['statusCode'] = $statusCode; + + return new Response(json_encode($json, 15, 512), $statusCode, $headers); + } + + /** + * Serializes data to a given type format. + * + * @param mixed $data The data to serialize. + * @param string $class The source data class. + * @param string $format The target serialization format. + * + * @return string A serialized data string. + */ + protected function serialize($data, $format) + { + return $this->serializer->serialize($data, $format); + } + + /** + * Deserializes data from a given type format. + * + * @param string $data The data to deserialize. + * @param string $class The target data class. + * @param string $format The source serialization format. + * + * @return mixed A deserialized data. + */ + protected function deserialize($data, $class, $format) + { + return $this->serializer->deserialize($data, $class, $format); + } + + protected function validate($data, $asserts = null) + { + $errors = $this->validator->validate($data, $asserts); + + if (count($errors) > 0) { + $errorsString = (string)$errors; + return $this->createBadRequestResponse($errorsString); + } + } + + /** + * Converts an exception to a serializable array. + * + * @param \Exception|null $exception + * + * @return array + */ + private function exceptionToArray(\Exception $exception = null) + { + if (null === $exception) { + return null; + } + + if (!$this->container->get('kernel')->isDebug()) { + return [ + 'message' => $exception->getMessage(), + ]; + } + + return [ + 'message' => $exception->getMessage(), + 'type' => get_class($exception), + 'previous' => $this->exceptionToArray($exception->getPrevious()), + ]; + } + + protected function getOutputFormat($accept, array $produced) + { + // Figure out what the client accepts + $accept = preg_split("/[\s,]+/", $accept); + + if (in_array('*/*', $accept) || in_array('application/*', $accept)) { + // Prefer JSON if the client has no preference + if (in_array('application/json', $produced)) { + return 'application/json'; + } + if (in_array('application/xml', $produced)) { + return 'application/xml'; + } + } + + if (in_array('application/json', $accept) && in_array('application/json', $produced)) { + return 'application/json'; + } + + if (in_array('application/xml', $accept) && in_array('application/xml', $produced)) { + return 'application/xml'; + } + + // If we reach this point, we don't have a common ground between server and client + return null; + } } diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/ModelInterface.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/ModelInterface.mustache deleted file mode 100644 index a568374d6d3..00000000000 --- a/modules/swagger-codegen/src/main/resources/php-symfony/ModelInterface.mustache +++ /dev/null @@ -1,55 +0,0 @@ -partial_header}} -/** - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen - * Do not edit the class manually. - */ - -namespace {{modelPackage}}; - -/** - * Interface abstracting model access. - * - * @package {{modelPackage}} - * @author Swagger Codegen team - */ -interface ModelInterface -{ - - /** - * The original name of the model. - * - * @return string - */ - public function modelName(); - - /** - * Array of property to mappings. - * - * @return array[] - */ - public function modelAttributes(); - - /** - * Validate all the properties in the model - * - * Return true if all passed. - * - * @return bool True if all properties are valid - */ - public function isValid(); -} - - diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/ModelSerializer.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/ModelSerializer.mustache deleted file mode 100644 index c2064b60a8d..00000000000 --- a/modules/swagger-codegen/src/main/resources/php-symfony/ModelSerializer.mustache +++ /dev/null @@ -1,174 +0,0 @@ -partial_header}} -/** - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen - * Do not edit the class manually. - */ - -namespace {{modelPackage}}; - -/** - * ModelSerializer Class Doc Comment - * - * @category Class - * @package {{modelPackage}} - * @author Swagger Codegen team - * @link https://github.com/swagger-api/swagger-codegen - */ -class ModelSerializer -{ - - /** - * Serializes data to a given type format. - * - * @param mixed $data The data to serialize. - * @param string $format The target serialization format. - * - * @return string A serialized data string. - * @throws \InvalidArgumentException When invalid serialization format was used. - */ - public function serialize($data, $format) - { - $normalized = $this->normalize($data); - if ($format === 'json') { - return json_encode($normalized, 15, 512); - } - - throw new \InvalidArgumentException('Unsupported serialization format: '.$format); - } - - /** - * Deserializes data from a given type format. - * - * @param string $data The data to deserialize. - * @param string $class The target class to deserialize to. - * @param string $format The source serialization format. - * - * @return mixed A deserialized value. - * @throws \InvalidArgumentException When invalid serialization format was used. - */ - public function deserialize($data, $class, $format) - { - switch ($format) { - case 'json': - $normalized = json_decode($data, true, 512, 15); - break; - default: - throw new \InvalidArgumentException('Unsupported serialization format: '.$format); - } - - return $this->denormalize($normalized, $class); - } - - public function normalize($data, $format = null) - { - if (is_scalar($data) || null === $data) { - return $data; - } - - if (is_array($data)) { - return array_map(function ($value) use ($format) { - return $this->normalize($value, $format); - }, $data); - } - - if ($data instanceof \DateTime) { - return ($format === 'date') ? $data->format('Y-m-d') : $data->format(\DateTime::ATOM); - } - - if ($data instanceof ModelInterface) { - $values = []; - foreach ($data->modelAttributes() as $name => $attribute) { - list($baseName, , $format, , $getter) = $attribute; - $value = $this->normalize($data->$getter(), $format); - if ($value !== null && method_exists($data, 'getAllowableEnumValues') - && !in_array($value, $data::getAllowableEnumValues())) { - $imploded = implode("', '", $data::getAllowableEnumValues()); - throw new \InvalidArgumentException("Invalid value for enum '$data', must be one of: '$imploded'"); - } - - if ($value !== null) { - $values[$baseName] = $value; - } - } - - return $values; - } - - return (string) $data; - } - - public function denormalize($data, $class, $format = null) - { - if ($data === null) { - return null; - } - - if (in_array($class, ['DateTime', 'bool', 'boolean', 'byte', 'double', 'float', 'int', 'integer', 'mixed', 'number', 'object', 'string', 'void'], true)) { - settype($data, $class); - return $data; - } - - // Denormalize array - if (substr($class, -2) === '[]') { - $innerClass = substr($class, 0, -2); - return array_map(function ($value) use ($format, $innerClass) { - return $this->denormalize($value, $innerClass, $format); - }, $data); - } - - if (!class_exists($class)) { - return $data; - } - - // Denormalize enum - if (method_exists($class, 'getAllowableEnumValues')) { - if (!in_array($data, $class::getAllowableEnumValues())) { - $imploded = implode("', '", $class::getAllowableEnumValues()); - throw new \InvalidArgumentException("Invalid value for enum '$class', must be one of: '$imploded'"); - } - - return $data; - } - - // If a discriminator is defined and points to a valid subclass, use it. - $discriminator = $class::DISCRIMINATOR; - if (!empty($discriminator) && isset($data[$discriminator]) && is_string($data[$discriminator])) { - $subclass = '{{modelPackage}}\\'.$data[$discriminator]; - if (is_subclass_of($subclass, $class)) { - $class = $subclass; - } - } - - // Denormalize another model - $values = new $class(); - if ($values instanceof ModelInterface) { - foreach ($values->modelAttributes() as $name => $attribute) { - list($baseName, $innerClass, $format, $setter) = $attribute; - - if (!isset($data[$baseName])) { - continue; - } - - $value = $this->denormalize($data[$baseName], $innerClass, $format); - $values->$setter($value); - } - - return $values; - } - - return $data; - } -} diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/README.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/README.mustache index d61acfa1c84..0b72cf635fa 100644 --- a/modules/swagger-codegen/src/main/resources/php-symfony/README.mustache +++ b/modules/swagger-codegen/src/main/resources/php-symfony/README.mustache @@ -23,28 +23,28 @@ PHP 5.4.0 and later ## Installation & Usage -To install the bindings via [Composer](http://getcomposer.org/), add the following to `composer.json`: +To install the dependencies via [Composer](http://getcomposer.org/), add the following repository to `composer.json` of your Symfony project: -``` +```json { - "repositories": [ - { - "type": "git", - "url": "https://github.com/{{#composerVendorName}}{{.}}{{/composerVendorName}}{{^composerVendorName}}{{gitUserId}}{{/composerVendorName}}/{{#composerProjectName}}{{.}}{{/composerProjectName}}{{^composerProjectName}}{{gitRepoId}}{{/composerProjectName}}.git" - } - ], - "require": { - "{{#composerVendorName}}{{.}}{{/composerVendorName}}{{^composerVendorName}}{{gitUserId}}{{/composerVendorName}}/{{#composerProjectName}}{{.}}{{/composerProjectName}}{{^composerProjectName}}{{gitRepoId}}{{/composerProjectName}}": "*@dev" - } + "repositories": [{ + "type": "path", + "url": "//Path to your generated swagger bundle" + }], } ``` -Then run `composer install` +Then run: + +``` +composer require {{#composerVendorName}}{{.}}{{/composerVendorName}}{{^composerVendorName}}{{gitUserId}}{{/composerVendorName}}/{{#composerProjectName}}{{.}}{{/composerProjectName}}{{^composerProjectName}}{{gitRepoId}}{{/composerProjectName}}:dev-master +``` +to add the generated swagger bundle as a dependency. ## Tests -To run the unit tests: +To run the unit tests for the generated bundle, first navigate to the directory containing the code, then run the following commands: ``` composer install diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/api.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/api.mustache index 0caef2cfc6d..49d24a41cbd 100644 --- a/modules/swagger-codegen/src/main/resources/php-symfony/api.mustache +++ b/modules/swagger-codegen/src/main/resources/php-symfony/api.mustache @@ -18,7 +18,8 @@ namespace {{apiPackage}}; -{{#operations}}{{#imports}}use {{this}}; +use Symfony\Component\HttpFoundation\File\UploadedFile; +{{#operations}}{{#imports}}use {{import}}; {{/imports}} /** @@ -56,20 +57,15 @@ interface {{classname}} * {{/description}} {{#allParams}} - * @param {{vendorExtensions.x-simpleName}} ${{paramName}} {{description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} + * @param {{vendorExtensions.x-commentType}} ${{paramName}} {{description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} {{/allParams}} + * @param integer $responseCode The HTTP response code to return + * @param array $responseHeaders Additional HTTP headers to return with the response () * - {{#responses}} - {{#vendorExtensions.x-symfonyExceptionSimple}} - * @throws {{vendorExtensions.x-symfonyExceptionSimple}} {{message}} - {{/vendorExtensions.x-symfonyExceptionSimple}} - {{^vendorExtensions.x-symfonyExceptionSimple}} - * @return {{^dataType}}void{{/dataType}}{{#dataType}}{{vendorExtensions.x-simpleName}}{{/dataType}} {{message}} + * @return {{{vendorExtensions.x-commentType}}} * - {{/vendorExtensions.x-symfonyExceptionSimple}} - {{/responses}} */ - public function {{operationId}}({{#allParams}}{{#vendorExtensions.x-parameterType}}{{vendorExtensions.x-parameterType}} {{/vendorExtensions.x-parameterType}}${{paramName}}{{^required}} = {{#defaultValue}}'{{{.}}}'{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + public function {{operationId}}({{#allParams}}{{#vendorExtensions.x-parameterType}}{{vendorExtensions.x-parameterType}} {{/vendorExtensions.x-parameterType}}${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}, {{/allParams}}&$responseCode, array &$responseHeaders); {{/operation}} } {{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/api_controller.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/api_controller.mustache index c78a507203e..cdf7fe7a6fa 100644 --- a/modules/swagger-codegen/src/main/resources/php-symfony/api_controller.mustache +++ b/modules/swagger-codegen/src/main/resources/php-symfony/api_controller.mustache @@ -23,8 +23,9 @@ use \Exception; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\Validator\Constraints as Assert; use {{apiPackage}}\{{classname}}; -{{#imports}}use {{this}}; +{{#imports}}use {{import}}; {{/imports}} /** @@ -53,8 +54,28 @@ class {{controllerName}} extends Controller * @param Request $request The Symfony request to handle. * @return Response The Symfony response. */ - public function {{operationId}}Action(Request $request) + public function {{operationId}}Action(Request $request{{#hasPathParams}}{{#pathParams}}, ${{paramName}}{{/pathParams}}{{/hasPathParams}}) { + {{#bodyParams}} + // Make sure that the client is providing something that we can consume + $consumes = [{{#consumes}}'{{{mediaType}}}'{{#hasMore}}, {{/hasMore}}{{/consumes}}]; + $inputFormat = $request->headers->has('Content-Type')?$request->headers->get('Content-Type'):$consumes[0]; + if (!in_array($inputFormat, $consumes)) { + // We can't consume the content that the client is sending us + return new Response('', 415); + } + + {{/bodyParams}} + // Figure out what data format to return to the client + $produces = [{{#produces}}'{{{mediaType}}}'{{#hasMore}}, {{/hasMore}}{{/produces}}]; + // Figure out what the client accepts + $clientAccepts = $request->headers->has('Accept')?$request->headers->get('Accept'):'*/*'; + $responseFormat = $this->getOutputFormat($clientAccepts, $produces); + if ($responseFormat === null) { + return new Response('', 406); + } + + // Handle authentication {{#authMethods}} // Authentication '{{name}}' required {{#isApiKey}} @@ -76,81 +97,57 @@ class {{controllerName}} extends Controller $security{{name}} = $request->headers->get('authorization'); {{/isOAuth}} {{/authMethods}} + + // Read out all input parameter values into variables {{#queryParams}} - // Handle query params - ${{paramName}} = $this->fromQuery($request->query->get('{{paramName}}'), '{{dataType}}'); + ${{paramName}} = $request->query->get('{{paramName}}'); {{/queryParams}} {{#headerParams}} - // Handle header params - ${{paramName}} = $this->fromHeader($request->headers->get('{{paramName}}'), '{{dataType}}'); + ${{paramName}} = $request->headers->get('{{baseName}}'); {{/headerParams}} - {{#pathParams}} - // Handle path params - ${{paramName}} = $this->fromPath($request->attributes->get('{{paramName}}'), '{{dataType}}'); - {{/pathParams}} {{#formParams}} {{#isFile}} - // Handle file params ${{paramName}} = $request->files->get('{{paramName}}'); {{/isFile}} {{^isFile}} - // Handle form params - ${{paramName}} = $this->fromForm($request->request->get('{{paramName}}'), '{{dataType}}'); + ${{paramName}} = $request->request->get('{{paramName}}'); {{/isFile}} {{/formParams}} {{#bodyParams}} - // Handle body params - ${{paramName}} = $this->deserialize($request->getContent(), '{{{dataType}}}', 'json'); + ${{paramName}} = $request->getContent(); {{/bodyParams}} - // Parse incoming parameters - {{#allParams}} - {{#required}} - // Verify the required parameter '{{paramName}}' is set - if (${{paramName}} === null) { - return $this->createBadRequestResponse('Missing the required parameter ${{paramName}} when calling {{operationId}}'); - } + // Use the default value if no value was provided + {{^required}} + {{#isContainer}} + {{#items}} + {{#defaultValue}} + ${{paramName}} = ${{paramName}}?:[{{{defaultValue}}}]; + {{/defaultValue}} + {{/items}} + {{/isContainer}} + {{^isContainer}} + {{#defaultValue}} + ${{paramName}} = ${{paramName}}?:{{{defaultValue}}}; + {{/defaultValue}} + {{/isContainer}} {{/required}} - {{#hasValidation}} - {{#maxLength}} - if ({{^required}}!is_null(${{paramName}}) && {{/required}}(strlen(${{paramName}}) > {{maxLength}})) { - return $this->createBadRequestResponse('Invalid length for "${{paramName}}" when calling {{classname}}.{{operationId}}, must be smaller than or equal to {{maxLength}}.'); - } - {{/maxLength}} - {{#minLength}} - if ({{^required}}!is_null(${{paramName}}) && {{/required}}(strlen(${{paramName}}) < {{minLength}})) { - return $this->createBadRequestResponse('Invalid length for "${{paramName}}" when calling {{classname}}.{{operationId}}, must be bigger than or equal to {{minLength}}.'); - } - {{/minLength}} - {{#maximum}} - if ({{^required}}!is_null(${{paramName}}) && {{/required}}(${{paramName}} >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}})) { - return $this->createBadRequestResponse('Invalid value for "${{paramName}}" when calling {{classname}}.{{operationId}}, must be smaller than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}{{maximum}}.'); - } - {{/maximum}} - {{#minimum}} - if ({{^required}}!is_null(${{paramName}}) && {{/required}}(${{paramName}} <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}})) { - return $this->createBadRequestResponse('Invalid value for "${{paramName}}" when calling {{classname}}.{{operationId}}, must be bigger than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}{{minimum}}.'); - } - {{/minimum}} - {{#pattern}} - if ({{^required}}!is_null(${{paramName}}) && {{/required}}!preg_match("{{{pattern}}}", ${{paramName}})) { - return $this->createBadRequestResponse("Invalid value for \"{{paramName}}\" when calling {{classname}}.{{operationId}}, must conform to the pattern {{{pattern}}}."); - } - {{/pattern}} - {{#maxItems}} - if ({{^required}}!is_null(${{paramName}}) && {{/required}}(count(${{paramName}}) > {{maxItems}})) { - return $this->createBadRequestResponse('Invalid value for "${{paramName}}" when calling {{classname}}.{{operationId}}, number of items must be less than or equal to {{maxItems}}.'); - } - {{/maxItems}} - {{#minItems}} - if ({{^required}}!is_null(${{paramName}}) && {{/required}}(count(${{paramName}}) < {{minItems}})) { - return $this->createBadRequestResponse('Invalid value for "${{paramName}}" when calling {{classname}}.{{operationId}}, number of items must be greater than or equal to {{minItems}}.'); - } - {{/minItems}} - {{/hasValidation}} + + // Deserialize the input values that needs it + {{#allParams}} + {{^isFile}} + {{#isBodyParam}} + ${{paramName}} = $this->deserialize(${{paramName}}, '{{#isContainer}}{{#items}}array<{{datatype}}>{{/items}}{{/isContainer}}{{^isContainer}}{{dataType}}{{/isContainer}}', $inputFormat); + {{/isBodyParam}} + {{^isBodyParam}} + ${{paramName}} = $this->deserialize(${{paramName}}, '{{#isContainer}}array<{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}{{^collectionFormat}}csv{{/collectionFormat}},{{dataType}}>{{/isContainer}}{{^isContainer}}{{dataType}}{{/isContainer}}', 'string'); + {{/isBodyParam}} + {{/isFile}} {{/allParams}} - // Call the API interface + // Validate the input values +{{>api_input_validation}} + try { $handler = $this->getApiHandler(); @@ -158,33 +155,35 @@ class {{controllerName}} extends Controller // Set authentication method '{{name}}' $handler->set{{name}}($security{{name}}); {{/authMethods}} - {{#returnType}} - // Expecting a return value (exception otherwise) - $result = $handler->{{operationId}}({{#allParams}}${{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + + // Make the call to the business logic + $responseCode = {{#returnType}}200{{/returnType}}{{^returnType}}204{{/returnType}}; + $responseHeaders = []; + $result = $handler->{{operationId}}({{#allParams}}${{paramName}}, {{/allParams}}$responseCode, $responseHeaders); + // Find default response message + $message = '{{#responses}}{{#isDefault}}{{message}}{{/isDefault}}{{/responses}}'; + + // Find a more specific message, if available + switch ($responseCode) { {{#responses}} - {{^vendorExtensions.x-symfonyExceptionSimple}} - // Handle {{code}} response: {{message}} - $content = $this->serialize($result, 'json'); - return new Response($content, {{code}}, [ - 'Content-Type' => 'application/json', - 'X-Swagger-Message' => '{{message}}', - ]); - {{/vendorExtensions.x-symfonyExceptionSimple}} + case {{code}}: + $message = '{{message}}'; + break; {{/responses}} - {{/returnType}} - {{^returnType}} - // No return type expected; return empty response - $handler->{{operationId}}({{#allParams}}${{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); - return new Response('', 204); - {{/returnType}} - {{#responses}} - {{#vendorExtensions.x-symfonyExceptionSimple}} - } catch ({{vendorExtensions.x-symfonyExceptionSimple}} $exception) { - // {{message}} - return $this->createErrorResponse($exception); - {{/vendorExtensions.x-symfonyExceptionSimple}} - {{/responses}} + } + + return new Response( + $result?$this->serialize($result, $responseFormat):'', + $responseCode, + array_merge( + $responseHeaders, + [ + 'Content-Type' => $responseFormat, + 'X-Swagger-Message' => $message + ] + ) + ); } catch (Exception $fallthrough) { return $this->createErrorResponse(new HttpException(500, 'An unsuspected error occurred.', $fallthrough)); } @@ -197,7 +196,7 @@ class {{controllerName}} extends Controller */ public function getApiHandler() { - return $this->get('{{bundleAlias}}.api.api_server')->getApiHandler('{{pathPrefix}}'); + return $this->apiServer->getApiHandler('{{pathPrefix}}'); } } {{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/api_input_validation.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/api_input_validation.mustache new file mode 100644 index 00000000000..99792587651 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/php-symfony/api_input_validation.mustache @@ -0,0 +1,88 @@ +{{#allParams}} + $asserts = []; +{{#required}} + $asserts[] = new Assert\NotNull(); +{{/required}} +{{#isEnum}} + {{#isContainer}} + $asserts[] = new Assert\All([ + {{#items}} + new Assert\Choice([ {{#allowableValues}}{{#enumVars}}{{{value}}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}} ]) + {{/items}} + ]); + {{/isContainer}} + {{^isContainer}} + $asserts[] = new Assert\Choice([ {{#allowableValues}}{{#enumVars}}{{{value}}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}} ]); + {{/isContainer}} +{{/isEnum}} +{{#isContainer}} + {{#items}} + $asserts[] = new Assert\All([ + new Assert\Type("{{datatype}}") + ]); + {{/items}} +{{/isContainer}} +{{^isContainer}} + {{#isDate}} + $asserts[] = new Assert\Date(); + {{/isDate}} + {{#isDateTime}} + $asserts[] = new Assert\DateTime(); + {{/isDateTime}} + {{^isDate}} + {{^isDateTime}} + {{#isFile}} + $asserts[] = new Assert\File(); + {{/isFile}} + {{^isFile}} + $asserts[] = new Assert\Type("{{dataType}}"); + {{/isFile}} + {{/isDateTime}} + {{/isDate}} +{{/isContainer}} +{{#hasValidation}} + {{#maxLength}} + $asserts[] = new Assert\Length([ + 'max' => {{maxLength}} + ]); + {{/maxLength}} + {{#minLength}} + $asserts[] = new Assert\Length([ + 'min' => {{minLength}} + ]); + {{/minLength}} + {{#minimum}} + {{#exclusiveMinimum}} + $asserts[] = new Assert\GreaterThan({{minimum}}); + {{/exclusiveMinimum}} + {{^exclusiveMinimum}} + $asserts[] = new Assert\GreaterThanOrEqual({{minimum}}); + {{/exclusiveMinimum}} + {{/minimum}} + {{#maximum}} + {{#exclusiveMaximum}} + $asserts[] = new Assert\LessThan({{minimum}}); + {{/exclusiveMaximum}} + {{^exclusiveMaximum}} + $asserts[] = new Assert\LessThanOrEqual({{minimum}}); + {{/exclusiveMaximum}} + {{/maximum}} + {{#pattern}} + $asserts[] = new Assert\Regex("/{{pattern}}/"); + {{/pattern}} + {{#maxItems}} + $asserts[] = new Assert\Count([ + 'max' => {{maxItems}} + ]); + {{/maxItems}} + {{#minItems}} + $asserts[] = new Assert\Count([ + 'min' => {{minItems}} + ]); + {{/minItems}} +{{/hasValidation}} + $response = $this->validate(${{paramName}}, $asserts); + if ($response instanceof Response) { + return $response; + } +{{/allParams}} diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/api_test.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/api_test.mustache deleted file mode 100644 index c1f16c70cea..00000000000 --- a/modules/swagger-codegen/src/main/resources/php-symfony/api_test.mustache +++ /dev/null @@ -1,77 +0,0 @@ -partial_header}} -/** - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen - * Please update the test case below to test the endpoint. - */ - -namespace {{apiTestsPackage}}; - -use \{{invokerPackage}}\Configuration; -use \{{invokerPackage}}\ApiClient; -use \{{invokerPackage}}\ApiException; -use \{{invokerPackage}}\ObjectSerializer; - -/** - * {{classname}}Test Class Doc Comment - * - * @category Class - * @package {{apiTestsPackage}} - * @author Swagger Codegen team - * @link https://github.com/swagger-api/swagger-codegen - */ -{{#operations}}class {{classname}}Test extends \PHPUnit_Framework_TestCase -{ - - /** - * Setup before running any test cases - */ - public static function setUpBeforeClass() - { - } - - /** - * Setup before running each test case - */ - public function setUp() - { - } - - /** - * Clean up after running each test case - */ - public function tearDown() - { - } - - /** - * Clean up after running all test cases - */ - public static function tearDownAfterClass() - { - } - {{#operation}} - - /** - * Test case for {{{operationId}}} - * - * {{{summary}}}. - * - */ - public function test{{operationIdCamelCase}}() - { - } - {{/operation}} -} -{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/composer.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/composer.mustache index eda7618bf65..5d6156df550 100644 --- a/modules/swagger-codegen/src/main/resources/php-symfony/composer.mustache +++ b/modules/swagger-codegen/src/main/resources/php-symfony/composer.mustache @@ -23,13 +23,17 @@ "ext-curl": "*", "ext-json": "*", "ext-mbstring": "*", + "symfony/validator": "*", + "jms/serializer-bundle": "*", "symfony/framework-bundle": "^2.3|^3.0" }, "require-dev": { "phpunit/phpunit": "~4.8", "satooshi/php-coveralls": "~1.0", "squizlabs/php_codesniffer": "~2.6", - "friendsofphp/php-cs-fixer": "~1.12" + "friendsofphp/php-cs-fixer": "~1.12", + "symfony/browser-kit": "*", + "hoa/regex": "~1.0" }, "autoload": { "psr-4": { "{{escapedInvokerPackage}}\\" : "{{srcBasePath}}/" } diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/git_push.sh.mustache index a9b0f28edfb..f65b794638f 100755 --- a/modules/swagger-codegen/src/main/resources/php-symfony/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/php-symfony/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/model.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/model.mustache index 52b0934530d..02d96acb45f 100644 --- a/modules/swagger-codegen/src/main/resources/php-symfony/model.mustache +++ b/modules/swagger-codegen/src/main/resources/php-symfony/model.mustache @@ -20,12 +20,10 @@ */ namespace {{modelPackage}}; -{{^isEnum}} -use \ArrayAccess; -{{#useStatements}}use {{this}}; -{{/useStatements}} -{{/isEnum}} +use Symfony\Component\Validator\Constraints as Assert; +use JMS\Serializer\Annotation\Type; +use JMS\Serializer\Annotation\SerializedName; /** * Class representing the {{classname}} model. @@ -37,5 +35,5 @@ use \ArrayAccess; * @package {{modelPackage}} * @author Swagger Codegen team */ -{{#isEnum}}{{>model_enum}}{{/isEnum}}{{^isEnum}}{{>model_generic}}{{/isEnum}} +{{>model_generic}} {{/model}}{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/model_generic.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/model_generic.mustache index 165a96adc06..15dd3594fa7 100644 --- a/modules/swagger-codegen/src/main/resources/php-symfony/model_generic.mustache +++ b/modules/swagger-codegen/src/main/resources/php-symfony/model_generic.mustache @@ -1,49 +1,6 @@ -class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}implements ModelInterface, ArrayAccess +class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}} { - const DISCRIMINATOR = {{#discriminator}}'{{discriminator}}'{{/discriminator}}{{^discriminator}}null{{/discriminator}}; - - /** - * The original name of the model. - * @var string - */ - protected static $_name = '{{name}}'; - - /** - * Array of property to type mappings. Used for (de)serialization - * @var array[] - */ - protected static $_attributes = [{{#vars}} - '{{name}}' => ['{{baseName}}', '{{{vendorExtensions.x-fullType}}}', {{#dataFormat}}'{{{dataFormat}}}'{{/dataFormat}}{{^dataFormat}}null{{/dataFormat}}, '{{setter}}', '{{getter}}'],{{/vars}} - ]; - {{#vars}}{{#isEnum}} - - /** - * Allowed values of {{name}} - */{{#allowableValues}}{{#enumVars}} - const {{enumName}}_{{{name}}} = {{{value}}};{{/enumVars}}{{/allowableValues}}{{/isEnum}}{{/vars}} - - {{#vars}}{{#isEnum}} - /** - * Gets allowable values of the enum - * @return string[] - */ - public function {{getter}}AllowableValues() - { - return [ - {{#allowableValues}}{{#enumVars}}self::{{enumName}}_{{{name}}},{{^-last}} - {{/-last}}{{/enumVars}}{{/allowableValues}} - ]; - } - {{/isEnum}}{{/vars}} - {{#vars}} - /**{{#description}} - * {{description}} - * - {{/description}} - * @var {{{datatype}}}{{^required}}|null{{/required}} - */ - protected ${{name}}; - + {{#vars}}{{>model_variables}} {{/vars}} /** * Constructor @@ -58,193 +15,13 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple {{#vars}} $this->{{name}} = isset($data['{{name}}']) ? $data['{{name}}'] : {{#defaultValue}}{{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}; {{/vars}} - {{#discriminator}} - - // Initialize discriminator property with the model name. - foreach (self::$_attributes as $_name => $attribute) { - list($baseName) = $attribute; - if ('{{discriminator}}' === $baseName) { - $this->$_name = static::$_name; - } - } - {{/discriminator}} } - - /** - * show all the invalid properties with reasons. - * - * @return array invalid properties with reasons - */ - public function listInvalidProperties() - { - {{#parent}} - $invalid_properties = parent::listInvalidProperties(); - {{/parent}} - {{^parent}} - $invalid_properties = []; - {{/parent}} - - {{#vars}} - {{#required}} - if ($this->{{name}} === null) { - $invalid_properties[] = "'{{name}}' can't be null"; - } - {{/required}} - {{#isEnum}} - {{^isContainer}} - $allowedValues = $this->{{getter}}AllowableValues(); - if (!in_array($this->{{name}}, $allowedValues, true)) { - $invalid_properties[] = sprintf( - "invalid value for '{{name}}', must be one of '%s'", - implode("', '", $allowedValues) - ); - } - - {{/isContainer}} - {{/isEnum}} - {{#hasValidation}} - {{#maxLength}} - if ({{^required}}!is_null($this->{{name}}) && {{/required}}(strlen($this->{{name}}) > {{maxLength}})) { - $invalid_properties[] = "invalid value for '{{name}}', the character length must be smaller than or equal to {{{maxLength}}}."; - } - - {{/maxLength}} - {{#minLength}} - if ({{^required}}!is_null($this->{{name}}) && {{/required}}(strlen($this->{{name}}) < {{minLength}})) { - $invalid_properties[] = "invalid value for '{{name}}', the character length must be bigger than or equal to {{{minLength}}}."; - } - - {{/minLength}} - {{#maximum}} - if ({{^required}}!is_null($this->{{name}}) && {{/required}}($this->{{name}} >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}})) { - $invalid_properties[] = "invalid value for '{{name}}', must be smaller than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}{{maximum}}."; - } - - {{/maximum}} - {{#minimum}} - if ({{^required}}!is_null($this->{{name}}) && {{/required}}($this->{{name}} <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}})) { - $invalid_properties[] = "invalid value for '{{name}}', must be bigger than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}{{minimum}}."; - } - - {{/minimum}} - {{#pattern}} - if ({{^required}}!is_null($this->{{name}}) && {{/required}}!preg_match("{{{pattern}}}", $this->{{name}})) { - $invalid_properties[] = "invalid value for '{{name}}', must be conform to the pattern {{{pattern}}}."; - } - - {{/pattern}} - {{#maxItems}} - if ({{^required}}!is_null($this->{{name}}) && {{/required}}(count($this->{{name}}) > {{maxItems}})) { - $invalid_properties[] = "invalid value for '{{name}}', number of items must be less than or equal to {{{maxItems}}}."; - } - - {{/maxItems}} - {{#minItems}} - if ({{^required}}!is_null($this->{{name}}) && {{/required}}(count($this->{{name}}) < {{minItems}})) { - $invalid_properties[] = "invalid value for '{{name}}', number of items must be greater than or equal to {{{minItems}}}."; - } - - {{/minItems}} - {{/hasValidation}} - {{/vars}} - return $invalid_properties; - } - - /** - * The original name of the model. - * - * @return string - */ - public function modelName() { - return self::$_name; - } - - /** - * Array of property to mappings. - * - * @return array[] - */ - public function modelAttributes() { - {{#parentSchema}}return array_merge(parent::$_attributes, self::$_attributes);{{/parentSchema}} - {{^parentSchema}}return self::$_attributes;{{/parentSchema}} - } - - /** - * Validate all the properties in the model - * - * Return true if all passed. - * - * @return bool True if all properties are valid - */ - public function isValid() - { - {{#parent}} - if (!parent::isValid()) { - return false; - } - - {{/parent}} - {{#vars}} - {{#required}} - if ($this->{{name}} === null) { - return false; - } - {{/required}} - {{#isEnum}} - {{^isContainer}} - $allowedValues = $this->{{getter}}AllowableValues(); - if (!in_array($this->{{name}}, $allowedValues)) { - return false; - } - {{/isContainer}} - {{/isEnum}} - {{#hasValidation}} - {{#maxLength}} - if (strlen($this->{{name}}) > {{maxLength}}) { - return false; - } - {{/maxLength}} - {{#minLength}} - if (strlen($this->{{name}}) < {{minLength}}) { - return false; - } - {{/minLength}} - {{#maximum}} - if ($this->{{name}} >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}}) { - return false; - } - {{/maximum}} - {{#minimum}} - if ($this->{{name}} <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}}) { - return false; - } - {{/minimum}} - {{#pattern}} - if (!preg_match("{{{pattern}}}", $this->{{name}})) { - return false; - } - {{/pattern}} - {{#maxItems}} - if (count($this->{{name}}) > {{maxItems}}) { - return false; - } - {{/maxItems}} - {{#minItems}} - if (count($this->{{name}}) < {{minItems}}) { - return false; - } - {{/minItems}} - {{/hasValidation}} - {{/vars}} - return true; - } - {{#vars}} /** * Gets {{name}}. * - * @return {{{datatype}}}{{^required}}|null{{/required}} + * @return {{{vendorExtensions.x-commentType}}}{{^required}}|null{{/required}} */ public function {{getter}}() { @@ -254,113 +31,15 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple /** * Sets {{name}}. * - * @param {{{datatype}}}{{^required}}|null{{/required}} ${{name}}{{#description}} {{{description}}}{{/description}} + * @param {{{vendorExtensions.x-commentType}}}{{^required}}|null{{/required}} ${{name}}{{#description}} {{{description}}}{{/description}} * * @return $this */ - public function {{setter}}({{#vendorExtensions.x-typeAnnotation}}{{vendorExtensions.x-typeAnnotation}} {{/vendorExtensions.x-typeAnnotation}}${{name}}{{^required}} = null{{/required}}) + public function {{setter}}({{#vendorExtensions.x-parameterType}}{{vendorExtensions.x-parameterType}} {{/vendorExtensions.x-parameterType}}${{name}}{{^required}} = null{{/required}}) { - {{#isEnum}} - $allowedValues = $this->{{getter}}AllowableValues(); - {{^isContainer}} - if ({{^required}}${{name}} !== null && {{/required}}!in_array(${{{name}}}, $allowedValues, true)) { - throw new \InvalidArgumentException( - sprintf( - "Invalid value for '{{name}}', must be one of '%s'", - implode("', '", $allowedValues) - ) - ); - } - {{/isContainer}} - {{#isContainer}} - if ({{^required}}!is_null(${{name}}) && {{/required}}array_diff(${{{name}}}, $allowedValues)) { - throw new \InvalidArgumentException( - sprintf( - "Invalid value for '{{name}}', must be one of '%s'", - implode("', '", $allowedValues) - ) - ); - } - {{/isContainer}} - {{/isEnum}} - {{#hasValidation}} - {{#maxLength}} - if ({{^required}}!is_null(${{name}}) && {{/required}}(strlen(${{name}}) > {{maxLength}})) { - throw new \InvalidArgumentException('invalid length for ${{name}} when calling {{classname}}.{{operationId}}, must be smaller than or equal to {{maxLength}}.'); - }{{/maxLength}} - {{#minLength}} - if ({{^required}}!is_null(${{name}}) && {{/required}}(strlen(${{name}}) < {{minLength}})) { - throw new \InvalidArgumentException('invalid length for ${{name}} when calling {{classname}}.{{operationId}}, must be bigger than or equal to {{minLength}}.'); - } - {{/minLength}} - {{#maximum}} - if ({{^required}}!is_null(${{name}}) && {{/required}}(${{name}} >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}})) { - throw new \InvalidArgumentException('invalid value for ${{name}} when calling {{classname}}.{{operationId}}, must be smaller than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}{{maximum}}.'); - } - {{/maximum}} - {{#minimum}} - if ({{^required}}!is_null(${{name}}) && {{/required}}(${{name}} <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}})) { - throw new \InvalidArgumentException('invalid value for ${{name}} when calling {{classname}}.{{operationId}}, must be bigger than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}{{minimum}}.'); - } - {{/minimum}} - {{#pattern}} - if ({{^required}}!is_null(${{name}}) && {{/required}}(!preg_match("{{{pattern}}}", ${{name}}))) { - throw new \InvalidArgumentException("invalid value for ${{name}} when calling {{classname}}.{{operationId}}, must conform to the pattern {{{pattern}}}."); - } - {{/pattern}} - {{#maxItems}} - if ({{^required}}!is_null(${{name}}) && {{/required}}(count(${{name}}) > {{maxItems}})) { - throw new \InvalidArgumentException('invalid value for ${{name}} when calling {{classname}}.{{operationId}}, number of items must be less than or equal to {{maxItems}}.'); - }{{/maxItems}} - {{#minItems}} - if ({{^required}}!is_null(${{name}}) && {{/required}}(count(${{name}}) < {{minItems}})) { - throw new \InvalidArgumentException('invalid length for ${{name}} when calling {{classname}}.{{operationId}}, number of items must be greater than or equal to {{minItems}}.'); - } - {{/minItems}} - {{/hasValidation}} $this->{{name}} = ${{name}}; return $this; } {{/vars}} - /** - * Returns true if offset exists. False otherwise. - * @param integer $offset Offset - * @return boolean - */ - public function offsetExists($offset) - { - return isset($this->$offset); - } - - /** - * Gets offset. - * @param integer $offset Offset - * @return mixed - */ - public function offsetGet($offset) - { - return isset($this->$offset) ? $this->$offset : null; - } - - /** - * Sets value based on offset. - * @param string $offset Offset - * @param mixed $value Value to be set - * @return void - */ - public function offsetSet($offset, $value) - { - $this->$offset = $value; - } - - /** - * Unsets offset. - * @param integer $offset Offset - * @return void - */ - public function offsetUnset($offset) - { - $this->$offset = null; - } } diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/model_variables.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/model_variables.mustache new file mode 100644 index 00000000000..d05831defa3 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/php-symfony/model_variables.mustache @@ -0,0 +1,91 @@ + /** + {{#description}} + * {{description}} + * + {{/description}} + * @var {{{vendorExtensions.x-commentType}}}{{^required}}|null{{/required}} + * @SerializedName("{{baseName}}") +{{#required}} + * @Assert\NotNull() +{{/required}} +{{#isEnum}} + {{#isContainer}} + * @Assert\All({ + {{#items}} + * @Assert\Choice({ {{#allowableValues}}{{#enumVars}}{{{value}}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}} }) + {{/items}} + * }) + {{/isContainer}} + {{^isContainer}} + * @Assert\Choice({ {{#allowableValues}}{{#enumVars}}{{{value}}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}} }) + {{/isContainer}} +{{/isEnum}} +{{#isContainer}} + * @Assert\All({ + {{#items}} + * @Assert\Type("{{datatype}}") + {{/items}} + * }) + {{#items}} + * @Type("array<{{datatype}}>") + {{/items}} +{{/isContainer}} +{{^isContainer}} + {{#isDate}} + * @Assert\Date() + * @Type("DateTime") + {{/isDate}} + {{#isDateTime}} + * @Assert\DateTime() + * @Type("DateTime") + {{/isDateTime}} + {{^isDate}} + {{^isDateTime}} + * @Assert\Type("{{datatype}}") + * @Type("{{datatype}}") + {{/isDateTime}} + {{/isDate}} +{{/isContainer}} +{{#hasValidation}} + {{#maxLength}} + * @Assert\Length( + * max = {{maxLength}} + * ) + {{/maxLength}} + {{#minLength}} + * @Assert\Length( + * min = {{minLength}} + * ) + {{/minLength}} + {{#minimum}} + {{#exclusiveMinimum}} + * @Assert\GreaterThan({{minimum}}) + {{/exclusiveMinimum}} + {{^exclusiveMinimum}} + * @Assert\GreaterThanOrEqual({{minimum}}) + {{/exclusiveMinimum}} + {{/minimum}} + {{#maximum}} + {{#exclusiveMaximum}} + * @Assert\LessThan({{minimum}}) + {{/exclusiveMaximum}} + {{^exclusiveMaximum}} + * @Assert\LessThanOrEqual({{minimum}}) + {{/exclusiveMaximum}} + {{/maximum}} + {{#pattern}} + * @Assert\Regex("/{{pattern}}/") + {{/pattern}} + {{#maxItems}} + * @Assert\Count( + * max = {{maxItems}} + * ) + {{/maxItems}} + {{#minItems}} + * @Assert\Count( + * min = {{minItems}} + * ) + {{/minItems}} +{{/hasValidation}} + */ + protected ${{name}}; diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/routing.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/routing.mustache index e91e9349c06..e0294a956d1 100644 --- a/modules/swagger-codegen/src/main/resources/php-symfony/routing.mustache +++ b/modules/swagger-codegen/src/main/resources/php-symfony/routing.mustache @@ -11,7 +11,29 @@ path: {{path}} methods: [{{httpMethod}}] defaults: - _controller: {{bundleClassName}}:{{baseName}}:{{operationId}} + _controller: {{bundleAlias}}.controller.{{pathPrefix}}:{{operationId}}Action + {{#hasPathParams}} + requirements: + {{/hasPathParams}} + {{#pathParams}} + {{#pattern}} + {{paramName}}: '{{pattern}}' + {{/pattern}} + {{^pattern}} + {{#isLong}} + {{paramName}}: '\d+' + {{/isLong}} + {{#isInteger}} + {{paramName}}: '\d+' + {{/isInteger}} + {{#isString}} + {{paramName}}: '[a-z0-9]+' + {{/isString}} + {{#isBoolean}} + {{paramName}}: 'true|false' + {{/isBoolean}} + {{/pattern}} + {{/pathParams}} {{/operation}} {{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/serialization/JmsSerializer.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/serialization/JmsSerializer.mustache new file mode 100644 index 00000000000..e4e7c96499b --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/php-symfony/serialization/JmsSerializer.mustache @@ -0,0 +1,115 @@ +serializer = SerializerBuilder::create() + ->setDeserializationVisitor('json', new StrictJsonDeserializationVisitor($naming_strategy)) + ->setDeserializationVisitor('xml', new XmlDeserializationVisitor($naming_strategy)) + ->build(); + } + + public function serialize($data, $format) + { + return SerializerBuilder::create()->build()->serialize($data, $this->convertFormat($format)); + } + + public function deserialize($data, $type, $format) + { + if ($format == 'string') { + return $this->deserializeString($data, $type); + } + + // If we end up here, let JMS serializer handle the deserialization + return $this->serializer->deserialize($data, $type, $this->convertFormat($format)); + } + + private function convertFormat($format) + { + switch ($format) { + case 'application/json': + return 'json'; + case 'application/xml': + return 'xml'; + } + + return null; + } + + private function deserializeString($data, $type) + { + // Figure out if we have an array format + if (1 === preg_match('/array<(csv|ssv|tsv|pipes),(int|string)>/i', $type, $matches)) { + return $this->deserializeArrayString($matches[1], $matches[2], $data); + } + + switch ($type) { + case 'int': + case 'integer': + if (is_int($data)) { + return $data; + } + + if (is_numeric($data)) { + return $data + 0; + } + + break; + case 'string': + break; + case 'boolean': + case 'bool': + if (strtolower($data) === 'true') { + return true; + } + + if (strtolower($data) === 'false') { + return false; + } + + break; + } + + // If we end up here, just return data + return $data; + } + + private function deserializeArrayString($format, $type, $data) + { + // Parse the string using the correct separator + switch ($format) { + case 'csv': + $data = explode(',', $data); + break; + case 'ssv': + $data = explode(' ', $data); + break; + case 'tsv': + $data = explode("\t", $data); + break; + case 'pipes': + $data = explode('|', $data); + break; + default; + $data = []; + } + + // Deserialize each of the array elements + foreach ($data as $key => $item) { + $data[$key] = $this->deserializeString($item, $type); + } + + return $data; + } +} diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/serialization/SerializerInterface.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/serialization/SerializerInterface.mustache new file mode 100644 index 00000000000..60ad8724648 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/php-symfony/serialization/SerializerInterface.mustache @@ -0,0 +1,27 @@ +getCurrentPath()) > 0) { + $property = sprintf('property "%s" to be ', implode('.', $context->getCurrentPath())); + } else { + $property = ''; + } + + return new static(sprintf( + 'Expected %s%s, but got %s: %s', + $property, + $expected_type, + gettype($actual_value), + json_encode($actual_value) + )); + } +} diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/services.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/services.mustache index 89e2bb8a96b..f6aec0cc2e0 100644 --- a/modules/swagger-codegen/src/main/resources/php-symfony/services.mustache +++ b/modules/swagger-codegen/src/main/resources/php-symfony/services.mustache @@ -2,9 +2,33 @@ # https://github.com/swagger-api/swagger-codegen # Do not edit the class manually. +parameters: + {{bundleAlias}}.serializer: '{{servicePackage}}\JmsSerializer' + {{bundleAlias}}.validator: '{{servicePackage}}\SymfonyValidator' + services: {{bundleAlias}}.api.api_server: class: {{apiPackage}}\ApiServer {{bundleAlias}}.model.model_serializer: class: {{modelPackage}}\ModelSerializer + + {{bundleAlias}}.service.serializer: + class: %{{bundleAlias}}.serializer% + + {{bundleAlias}}.service.validator: + class: %{{bundleAlias}}.validator% + +{{#apiInfo}} +{{#apis}} +{{#operations}} + {{bundleAlias}}.controller.{{pathPrefix}}: + class: {{controllerPackage}}\{{baseName}}Controller + calls: + - [setSerializer, ['@{{bundleAlias}}.service.serializer']] + - [setValidator, ['@{{bundleAlias}}.service.validator']] + - [setApiServer, ['@{{bundleAlias}}.api.api_server']] + +{{/operations}} +{{/apis}} +{{/apiInfo}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/testing/AppKernel.php b/modules/swagger-codegen/src/main/resources/php-symfony/testing/AppKernel.php new file mode 100644 index 00000000000..631690bc978 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/php-symfony/testing/AppKernel.php @@ -0,0 +1,21 @@ +load(__DIR__.'/test_config.yml'); + } +} diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/testing/api_test.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/testing/api_test.mustache new file mode 100644 index 00000000000..d337c1e3df6 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/php-symfony/testing/api_test.mustache @@ -0,0 +1,116 @@ +partial_header}} +/** + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen + * Please update the test case below to test the endpoint. + */ + +namespace {{apiTestsPackage}}; + +use {{invokerPackage}}\Configuration; +use {{invokerPackage}}\ApiClient; +use {{invokerPackage}}\ApiException; +use {{invokerPackage}}\ObjectSerializer; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + +/** + * {{classname}}Test Class Doc Comment + * + * @category Class + * @package {{apiTestsPackage}} + * @author Swagger Codegen team + * @link https://github.com/swagger-api/swagger-codegen + */ +{{#operations}}class {{classname}}Test extends WebTestCase +{ + + /** + * Setup before running any test cases + */ + public static function setUpBeforeClass() + { + } + + /** + * Setup before running each test case + */ + public function setUp() + { + } + + /** + * Clean up after running each test case + */ + public function tearDown() + { + } + + /** + * Clean up after running all test cases + */ + public static function tearDownAfterClass() + { + } + {{#operation}} + + /** + * Test case for {{{operationId}}} + * + * {{{summary}}}. + * + */ + public function test{{operationIdCamelCase}}() + { + $client = static::createClient(); + + $path = '{{path}}'; + {{#pathParams}} + {{=<% %>=}} + $pattern = '{<%paramName%>}'; + <%={{ }}=%> + {{#pattern}} + $data = $this->genTestData('{{pattern}}'); + {{/pattern}} + {{^pattern}} + {{#isLong}} + $data = $this->genTestData('\d+'); + {{/isLong}} + {{#isInteger}} + $data = $this->genTestData('\d+'); + {{/isInteger}} + {{#isString}} + $data = $this->genTestData('[a-z0-9]+'); + {{/isString}} + {{#isBoolean}} + $data = $this->genTestData('true|false'); + {{/isBoolean}} + {{/pattern}} + $path = str_replace($pattern, $data, $path); + {{/pathParams}} + + $crawler = $client->request('{{httpMethod}}', $path); + } + {{/operation}} + + protected function genTestData($regexp) + { + $grammar = new \Hoa\File\Read('hoa://Library/Regex/Grammar.pp'); + $compiler = \Hoa\Compiler\Llk\Llk::load($grammar); + $ast = $compiler->parse($regexp); + $generator = new \Hoa\Regex\Visitor\Isotropic(new \Hoa\Math\Sampler\Random()); + + return $generator->visit($ast); + } +} +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/model_test.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/testing/model_test.mustache similarity index 95% rename from modules/swagger-codegen/src/main/resources/php-symfony/model_test.mustache rename to modules/swagger-codegen/src/main/resources/php-symfony/testing/model_test.mustache index 797f9a2af2a..b4b9cf012a3 100644 --- a/modules/swagger-codegen/src/main/resources/php-symfony/model_test.mustache +++ b/modules/swagger-codegen/src/main/resources/php-symfony/testing/model_test.mustache @@ -19,7 +19,7 @@ * Please update the test case below to test the model. */ -namespace {{modelTestsPackage}}; +namespace {{modelPackage}}; /** * {{classname}}Test Class Doc Comment @@ -67,6 +67,7 @@ class {{classname}}Test extends \PHPUnit_Framework_TestCase */ public function test{{classname}}() { + $test{{classname}} = new {{classname}}(); } {{#vars}} diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/phpunit.xml.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/testing/phpunit.xml.mustache similarity index 90% rename from modules/swagger-codegen/src/main/resources/php-symfony/phpunit.xml.mustache rename to modules/swagger-codegen/src/main/resources/php-symfony/testing/phpunit.xml.mustache index 5de6fea575c..bd5b2978ee0 100644 --- a/modules/swagger-codegen/src/main/resources/php-symfony/phpunit.xml.mustache +++ b/modules/swagger-codegen/src/main/resources/php-symfony/testing/phpunit.xml.mustache @@ -12,6 +12,10 @@ + + + + {{apiSrcPath}} diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/testing/pom.xml b/modules/swagger-codegen/src/main/resources/php-symfony/testing/pom.xml new file mode 100644 index 00000000000..614b3d33f98 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/php-symfony/testing/pom.xml @@ -0,0 +1,57 @@ + + 4.0.0 + com.penneo + PhpSymfonyPetstoreServerTests + pom + 1.0-SNAPSHOT + PHP Symfony Swagger Petstore Server + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory} + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + bundle-install + pre-integration-test + + exec + + + composer + + install + + + + + bundle-test + integration-test + + exec + + + vendor/bin/phpunit + + + + + + + + diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/testing/test_config.yml b/modules/swagger-codegen/src/main/resources/php-symfony/testing/test_config.yml new file mode 100644 index 00000000000..10c88a274c9 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/php-symfony/testing/test_config.yml @@ -0,0 +1,8 @@ +imports: + - { resource: "../Resources/config/services.yml" } + +framework: + secret: "testsecret" + test: ~ + router: + resource: "%kernel.root_dir%/../Resources/config/routing.yml" diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/validation/SymfonyValidator.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/validation/SymfonyValidator.mustache new file mode 100644 index 00000000000..3b6c5dd0ef6 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/php-symfony/validation/SymfonyValidator.mustache @@ -0,0 +1,20 @@ +validator = Validation::createValidator(); + } + + public function validate($value, $constraints = null, $groups = null) + { + return $this->validator->validate($value, $constraints, $groups); + } +} diff --git a/modules/swagger-codegen/src/main/resources/php-symfony/validation/ValidatorInterface.mustache b/modules/swagger-codegen/src/main/resources/php-symfony/validation/ValidatorInterface.mustache new file mode 100644 index 00000000000..e2a0d9badc5 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/php-symfony/validation/ValidatorInterface.mustache @@ -0,0 +1,25 @@ +partial_header}} +/** + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen + * Do not edit the class manually. + */ + +namespace {{invokerPackage}}; + +/** + * Configuration Class Doc Comment + * PHP version 5 + * + * @category Class + * @package {{invokerPackage}} + * @author Swagger Codegen team + * @link https://github.com/swagger-api/swagger-codegen + */ +class Configuration +{ + private static $defaultConfiguration; + + /** + * Associate array to store API key(s) + * + * @var string[] + */ + protected $apiKeys = []; + + /** + * Associate array to store API prefix (e.g. Bearer) + * + * @var string[] + */ + protected $apiKeyPrefixes = []; + + /** + * Access token for OAuth + * + * @var string + */ + protected $accessToken = ''; + + /** + * Username for HTTP basic authentication + * + * @var string + */ + protected $username = ''; + + /** + * Password for HTTP basic authentication + * + * @var string + */ + protected $password = ''; + + /** + * The host + * + * @var string + */ + protected $host = '{{basePath}}'; + + /** + * User agent of the HTTP request, set to "PHP-Swagger" by default + * + * @var string + */ + protected $userAgent = '{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{#artifactVersion}}{{{.}}}{{/artifactVersion}}{{^artifactVersion}}1.0.0{{/artifactVersion}}/php{{/httpUserAgent}}'; + + /** + * Debug switch (default set to false) + * + * @var bool + */ + protected $debug = false; + + /** + * Debug file location (log to STDOUT by default) + * + * @var string + */ + protected $debugFile = 'php://output'; + + /** + * Debug file location (log to STDOUT by default) + * + * @var string + */ + protected $tempFolderPath; + + /** + * Constructor + */ + public function __construct() + { + $this->tempFolderPath = sys_get_temp_dir(); + } + + /** + * Sets API key + * + * @param string $apiKeyIdentifier API key identifier (authentication scheme) + * @param string $key API key or token + * + * @return $this + */ + public function setApiKey($apiKeyIdentifier, $key) + { + $this->apiKeys[$apiKeyIdentifier] = $key; + return $this; + } + + /** + * Gets API key + * + * @param string $apiKeyIdentifier API key identifier (authentication scheme) + * + * @return string API key or token + */ + public function getApiKey($apiKeyIdentifier) + { + return isset($this->apiKeys[$apiKeyIdentifier]) ? $this->apiKeys[$apiKeyIdentifier] : null; + } + + /** + * Sets the prefix for API key (e.g. Bearer) + * + * @param string $apiKeyIdentifier API key identifier (authentication scheme) + * @param string $prefix API key prefix, e.g. Bearer + * + * @return $this + */ + public function setApiKeyPrefix($apiKeyIdentifier, $prefix) + { + $this->apiKeyPrefixes[$apiKeyIdentifier] = $prefix; + return $this; + } + + /** + * Gets API key prefix + * + * @param string $apiKeyIdentifier API key identifier (authentication scheme) + * + * @return string + */ + public function getApiKeyPrefix($apiKeyIdentifier) + { + return isset($this->apiKeyPrefixes[$apiKeyIdentifier]) ? $this->apiKeyPrefixes[$apiKeyIdentifier] : null; + } + + /** + * Sets the access token for OAuth + * + * @param string $accessToken Token for OAuth + * + * @return $this + */ + public function setAccessToken($accessToken) + { + $this->accessToken = $accessToken; + return $this; + } + + /** + * Gets the access token for OAuth + * + * @return string Access token for OAuth + */ + public function getAccessToken() + { + return $this->accessToken; + } + + /** + * Sets the username for HTTP basic authentication + * + * @param string $username Username for HTTP basic authentication + * + * @return $this + */ + public function setUsername($username) + { + $this->username = $username; + return $this; + } + + /** + * Gets the username for HTTP basic authentication + * + * @return string Username for HTTP basic authentication + */ + public function getUsername() + { + return $this->username; + } + + /** + * Sets the password for HTTP basic authentication + * + * @param string $password Password for HTTP basic authentication + * + * @return $this + */ + public function setPassword($password) + { + $this->password = $password; + return $this; + } + + /** + * Gets the password for HTTP basic authentication + * + * @return string Password for HTTP basic authentication + */ + public function getPassword() + { + return $this->password; + } + + /** + * Sets the host + * + * @param string $host Host + * + * @return $this + */ + public function setHost($host) + { + $this->host = $host; + return $this; + } + + /** + * Gets the host + * + * @return string Host + */ + public function getHost() + { + return $this->host; + } + + /** + * Sets the user agent of the api client + * + * @param string $userAgent the user agent of the api client + * + * @throws \InvalidArgumentException + * @return $this + */ + public function setUserAgent($userAgent) + { + if (!is_string($userAgent)) { + throw new \InvalidArgumentException('User-agent must be a string.'); + } + + $this->userAgent = $userAgent; + return $this; + } + + /** + * Gets the user agent of the api client + * + * @return string user agent + */ + public function getUserAgent() + { + return $this->userAgent; + } + + /** + * Sets debug flag + * + * @param bool $debug Debug flag + * + * @return $this + */ + public function setDebug($debug) + { + $this->debug = $debug; + return $this; + } + + /** + * Gets the debug flag + * + * @return bool + */ + public function getDebug() + { + return $this->debug; + } + + /** + * Sets the debug file + * + * @param string $debugFile Debug file + * + * @return $this + */ + public function setDebugFile($debugFile) + { + $this->debugFile = $debugFile; + return $this; + } + + /** + * Gets the debug file + * + * @return string + */ + public function getDebugFile() + { + return $this->debugFile; + } + + /** + * Sets the temp folder path + * + * @param string $tempFolderPath Temp folder path + * + * @return $this + */ + public function setTempFolderPath($tempFolderPath) + { + $this->tempFolderPath = $tempFolderPath; + return $this; + } + + /** + * Gets the temp folder path + * + * @return string Temp folder path + */ + public function getTempFolderPath() + { + return $this->tempFolderPath; + } + + /** + * Gets the default configuration instance + * + * @return Configuration + */ + public static function getDefaultConfiguration() + { + if (self::$defaultConfiguration === null) { + self::$defaultConfiguration = new Configuration(); + } + + return self::$defaultConfiguration; + } + + /** + * Sets the detault configuration instance + * + * @param Configuration $config An instance of the Configuration Object + * + * @return void + */ + public static function setDefaultConfiguration(Configuration $config) + { + self::$defaultConfiguration = $config; + } + + /** + * Gets the essential information for debugging + * + * @return string The report for debugging + */ + public static function toDebugReport() + { + $report = 'PHP SDK ({{invokerPackage}}) Debug Report:' . PHP_EOL; + $report .= ' OS: ' . php_uname() . PHP_EOL; + $report .= ' PHP Version: ' . PHP_VERSION . PHP_EOL; + $report .= ' OpenAPI Spec Version: {{version}}' . PHP_EOL; + {{#artifactVersion}} + $report .= ' SDK Package Version: {{artifactVersion}}' . PHP_EOL; + {{/artifactVersion}} + $report .= ' Temp Folder Path: ' . self::getDefaultConfiguration()->getTempFolderPath() . PHP_EOL; + + return $report; + } + + /** + * Get API key (with prefix if set) + * + * @param string $apiKeyIdentifier name of apikey + * + * @return string API key with the prefix + */ + public function getApiKeyWithPrefix($apiKeyIdentifier) + { + $prefix = $this->getApiKeyPrefix($apiKeyIdentifier); + $apiKey = $this->getApiKey($apiKeyIdentifier); + + if ($apiKey === null) { + return null; + } + + if ($prefix === null) { + $keyWithPrefix = $apiKey; + } else { + $keyWithPrefix = $prefix . ' ' . $apiKey; + } + + return $keyWithPrefix; + } +} diff --git a/modules/swagger-codegen/src/main/resources/php/HeaderSelector.mustache b/modules/swagger-codegen/src/main/resources/php/HeaderSelector.mustache new file mode 100644 index 00000000000..909beb134d2 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/php/HeaderSelector.mustache @@ -0,0 +1,100 @@ +partial_header}} +/** + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen + * Do not edit the class manually. + */ + +namespace {{invokerPackage}}; + +use \Exception; + +/** + * ApiException Class Doc Comment + * + * @category Class + * @package {{invokerPackage}} + * @author Swagger Codegen team + * @link https://github.com/swagger-api/swagger-codegen + */ +class HeaderSelector +{ + + /** + * @param string[] $accept + * @param string[] $contentTypes + * @return array + */ + public function selectHeaders($accept, $contentTypes) + { + $headers = []; + + $accept = $this->selectAcceptHeader($accept); + if ($accept !== null) { + $headers['Accept'] = $accept; + } + + $headers['Content-Type'] = $this->selectContentTypeHeader($contentTypes); + return $headers; + } + + /** + * @param string[] $accept + * @return array + */ + public function selectHeadersForMultipart($accept) + { + $headers = $this->selectHeaders($accept, []); + + unset($headers['Content-Type']); + return $headers; + } + + /** + * Return the header 'Accept' based on an array of Accept provided + * + * @param string[] $accept Array of header + * + * @return string Accept (e.g. application/json) + */ + private function selectAcceptHeader($accept) + { + if (count($accept) === 0 || (count($accept) === 1 && $accept[0] === '')) { + return null; + } elseif (preg_grep("/application\/json/i", $accept)) { + return 'application/json'; + } else { + return implode(',', $accept); + } + } + + /** + * Return the content type based on an array of content-type provided + * + * @param string[] $contentType Array fo content-type + * + * @return string Content-Type (e.g. application/json) + */ + private function selectContentTypeHeader($contentType) + { + if (count($contentType) === 0 || (count($contentType) === 1 && $contentType[0] === '')) { + return 'application/json'; + } elseif (preg_grep("/application\/json/i", $contentType)) { + return 'application/json'; + } else { + return implode(',', $contentType); + } + } +} + diff --git a/modules/swagger-codegen/src/main/resources/php/ModelInterface.mustache b/modules/swagger-codegen/src/main/resources/php/ModelInterface.mustache new file mode 100644 index 00000000000..538bf661693 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/php/ModelInterface.mustache @@ -0,0 +1,86 @@ +partial_header}} +/** + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen + * Do not edit the class manually. + */ + +namespace {{modelPackage}}; + +/** + * Interface abstracting model access. + * + * @package {{modelPackage}} + * @author Swagger Codegen team + */ +interface ModelInterface +{ + /** + * The original name of the model. + * + * @return string + */ + public function getModelName(); + + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ + public static function swaggerTypes(); + + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ + public static function swaggerFormats(); + + /** + * Array of attributes where the key is the local name, and the value is the original name + * + * @return array + */ + public static function attributeMap(); + + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ + public static function setters(); + + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ + public static function getters(); + + /** + * Show all the invalid properties with reasons. + * + * @return array + */ + public function listInvalidProperties(); + + /** + * Validate all the properties in the model + * return true if all passed + * + * @return bool + */ + public function valid(); +} diff --git a/modules/swagger-codegen/src/main/resources/php/ObjectSerializer.mustache b/modules/swagger-codegen/src/main/resources/php/ObjectSerializer.mustache index 86ca232a6cc..f07a4d18cc0 100644 --- a/modules/swagger-codegen/src/main/resources/php/ObjectSerializer.mustache +++ b/modules/swagger-codegen/src/main/resources/php/ObjectSerializer.mustache @@ -55,7 +55,9 @@ class ObjectSerializer foreach ($data::swaggerTypes() as $property => $swaggerType) { $getter = $data::getters()[$property]; $value = $data->$getter(); - if ($value !== null && method_exists($swaggerType, 'getAllowableEnumValues') + if ($value !== null + && !in_array($swaggerType, [{{&primitives}}], true) + && method_exists($swaggerType, 'getAllowableEnumValues') && !in_array($value, $swaggerType::getAllowableEnumValues())) { $imploded = implode("', '", $swaggerType::getAllowableEnumValues()); throw new \InvalidArgumentException("Invalid value for enum '$swaggerType', must be one of: '$imploded'"); @@ -95,9 +97,9 @@ class ObjectSerializer * * @return string the serialized object */ - public function toPathValue($value) + public static function toPathValue($value) { - return rawurlencode($this->toString($value)); + return rawurlencode(self::toString($value)); } /** @@ -110,12 +112,12 @@ class ObjectSerializer * * @return string the serialized object */ - public function toQueryValue($object) + public static function toQueryValue($object) { if (is_array($object)) { return implode(',', $object); } else { - return $this->toString($object); + return self::toString($object); } } @@ -128,9 +130,9 @@ class ObjectSerializer * * @return string the header string */ - public function toHeaderValue($value) + public static function toHeaderValue($value) { - return $this->toString($value); + return self::toString($value); } /** @@ -142,12 +144,12 @@ class ObjectSerializer * * @return string the form string */ - public function toFormValue($value) + public static function toFormValue($value) { if ($value instanceof \SplFileObject) { return $value->getRealPath(); } else { - return $this->toString($value); + return self::toString($value); } } @@ -160,7 +162,7 @@ class ObjectSerializer * * @return string the header string */ - public function toString($value) + public static function toString($value) { if ($value instanceof \DateTime) { // datetime in ISO8601 format return $value->format(\DateTime::ATOM); @@ -179,7 +181,7 @@ class ObjectSerializer * * @return string */ - public function serializeCollection(array $collection, $collectionFormat, $allowCollectionFormatMulti = false) + public static function serializeCollection(array $collection, $collectionFormat, $allowCollectionFormatMulti = false) { if ($allowCollectionFormatMulti && ('multi' === $collectionFormat)) { // http_build_query() almost does the job for us. We just @@ -254,6 +256,8 @@ class ObjectSerializer settype($data, $class); return $data; } elseif ($class === '\SplFileObject') { + /** @var \Psr\Http\Message\StreamInterface $data */ + // determine file name if (array_key_exists('Content-Disposition', $httpHeaders) && preg_match('/inline; filename=[\'"]?([^\'"\s]+)[\'"]?$/i', $httpHeaders['Content-Disposition'], $match)) { @@ -261,13 +265,14 @@ class ObjectSerializer } else { $filename = tempnam(Configuration::getDefaultConfiguration()->getTempFolderPath(), ''); } - $deserialized = new \SplFileObject($filename, "w"); - $byte_written = $deserialized->fwrite($data); - if (Configuration::getDefaultConfiguration()->getDebug()) { - error_log("[DEBUG] Written $byte_written byte to $filename. Please move the file to a proper folder or delete the temp file after processing.".PHP_EOL, 3, Configuration::getDefaultConfiguration()->getDebugFile()); + + $file = fopen($filename, 'w'); + while ($chunk = $data->read(200)) { + fwrite($file, $chunk); } + fclose($file); - return $deserialized; + return new \SplFileObject($filename, 'r'); } elseif (method_exists($class, 'getAllowableEnumValues')) { if (!in_array($data, $class::getAllowableEnumValues())) { $imploded = implode("', '", $class::getAllowableEnumValues()); diff --git a/modules/swagger-codegen/src/main/resources/php/README.mustache b/modules/swagger-codegen/src/main/resources/php/README.mustache index ab0da1711f2..f130f944d67 100644 --- a/modules/swagger-codegen/src/main/resources/php/README.mustache +++ b/modules/swagger-codegen/src/main/resources/php/README.mustache @@ -19,7 +19,7 @@ For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) ## Requirements -PHP 5.4.0 and later +PHP 5.5 and later ## Installation & Usage ### Composer @@ -47,7 +47,7 @@ Then run `composer install` Download the files and include `autoload.php`: ```php - require_once('/path/to/{{packagePath}}/autoload.php'); + require_once('/path/to/{{packagePath}}/vendor/autoload.php'); ``` ## Tests @@ -68,22 +68,27 @@ Please follow the [installation procedure](#installation--usage) and then run th require_once(__DIR__ . '/vendor/autoload.php'); {{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#operation}}{{#-first}}{{#hasAuthMethods}}{{#authMethods}}{{#isBasic}} // Configure HTTP basic authorization: {{{name}}} -{{{invokerPackage}}}\Configuration::getDefaultConfiguration()->setUsername('YOUR_USERNAME'); -{{{invokerPackage}}}\Configuration::getDefaultConfiguration()->setPassword('YOUR_PASSWORD');{{/isBasic}}{{#isApiKey}} + ->setUsername('YOUR_USERNAME') + ->setPassword('YOUR_PASSWORD');{{/isBasic}}{{#isApiKey}} // Configure API key authorization: {{{name}}} -{{{invokerPackage}}}\Configuration::getDefaultConfiguration()->setApiKey('{{{keyParamName}}}', 'YOUR_API_KEY'); +$config = {{{invokerPackage}}}\Configuration::getDefaultConfiguration()->setApiKey('{{{keyParamName}}}', 'YOUR_API_KEY'); // Uncomment below to setup prefix (e.g. Bearer) for API key, if needed -// {{{invokerPackage}}}\Configuration::getDefaultConfiguration()->setApiKeyPrefix('{{{keyParamName}}}', 'Bearer');{{/isApiKey}}{{#isOAuth}} +// $config = {{{invokerPackage}}}\Configuration::getDefaultConfiguration()->setApiKeyPrefix('{{{keyParamName}}}', 'Bearer');{{/isApiKey}}{{#isOAuth}} // Configure OAuth2 access token for authorization: {{{name}}} -{{{invokerPackage}}}\Configuration::getDefaultConfiguration()->setAccessToken('YOUR_ACCESS_TOKEN');{{/isOAuth}}{{/authMethods}} +$config = {{{invokerPackage}}}\Configuration::getDefaultConfiguration()->setAccessToken('YOUR_ACCESS_TOKEN');{{/isOAuth}}{{/authMethods}} {{/hasAuthMethods}} -$api_instance = new {{invokerPackage}}\Api\{{classname}}(); +$apiInstance = new {{invokerPackage}}\Api\{{classname}}( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(){{#hasAuthMethods}}, + $config{{/hasAuthMethods}} +); {{#allParams}}${{paramName}} = {{{example}}}; // {{{dataType}}} | {{{description}}} {{/allParams}} try { - {{#returnType}}$result = {{/returnType}}$api_instance->{{{operationId}}}({{#allParams}}${{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{#returnType}} + {{#returnType}}$result = {{/returnType}}$apiInstance->{{{operationId}}}({{#allParams}}${{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{#returnType}} print_r($result);{{/returnType}} } catch (Exception $e) { echo 'Exception when calling {{classname}}->{{operationId}}: ', $e->getMessage(), PHP_EOL; diff --git a/modules/swagger-codegen/src/main/resources/php/api.mustache b/modules/swagger-codegen/src/main/resources/php/api.mustache index 1efbe254d8f..5fffd0de57f 100644 --- a/modules/swagger-codegen/src/main/resources/php/api.mustache +++ b/modules/swagger-codegen/src/main/resources/php/api.mustache @@ -18,10 +18,16 @@ namespace {{apiPackage}}; -use \{{invokerPackage}}\ApiClient; -use \{{invokerPackage}}\ApiException; -use \{{invokerPackage}}\Configuration; -use \{{invokerPackage}}\ObjectSerializer; +use GuzzleHttp\Client; +use GuzzleHttp\ClientInterface; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Psr7\MultipartStream; +use GuzzleHttp\Psr7\Request; +use GuzzleHttp\RequestOptions; +use {{invokerPackage}}\ApiException; +use {{invokerPackage}}\Configuration; +use {{invokerPackage}}\HeaderSelector; +use {{invokerPackage}}\ObjectSerializer; /** * {{classname}} Class Doc Comment @@ -34,50 +40,39 @@ use \{{invokerPackage}}\ObjectSerializer; {{#operations}}class {{classname}} { /** - * API Client - * - * @var \{{invokerPackage}}\ApiClient instance of the ApiClient + * @var ClientInterface */ - protected $apiClient; + protected $client; /** - * Constructor - * - * @param \{{invokerPackage}}\ApiClient|null $apiClient The api client to use + * @var Configuration */ - public function __construct(\{{invokerPackage}}\ApiClient $apiClient = null) - { - if ($apiClient === null) { - $apiClient = new ApiClient(); - } - - $this->apiClient = $apiClient; - } + protected $config; /** - * Get API client - * - * @return \{{invokerPackage}}\ApiClient get the API client + * @param ClientInterface $client + * @param Configuration $config + * @param HeaderSelector $selector */ - public function getApiClient() - { - return $this->apiClient; + public function __construct( + ClientInterface $client = null, + Configuration $config = null, + HeaderSelector $selector = null + ) { + $this->client = $client ?: new Client(); + $this->config = $config ?: new Configuration(); + $this->headerSelector = $selector ?: new HeaderSelector(); } /** - * Set the API client - * - * @param \{{invokerPackage}}\ApiClient $apiClient set the API client - * - * @return {{classname}} + * @return Configuration */ - public function setApiClient(\{{invokerPackage}}\ApiClient $apiClient) + public function getConfig() { - $this->apiClient = $apiClient; - return $this; + return $this->config; } - {{#operation}} +{{#operation}} /** * Operation {{{operationId}}} {{#summary}} @@ -90,15 +85,17 @@ use \{{invokerPackage}}\ObjectSerializer; * {{/description}} {{#allParams}} - * @param {{dataType}} ${{paramName}} {{description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} + * @param {{dataType}} ${{paramName}}{{#description}} {{description}}{{/description}}{{^description}} {{paramName}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} {{/allParams}} + * * @throws \{{invokerPackage}}\ApiException on non-2xx response + * @throws \InvalidArgumentException * @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} */ public function {{operationId}}({{#allParams}}${{paramName}}{{^required}} = {{#defaultValue}}'{{{.}}}'{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) { - list($response) = $this->{{operationId}}WithHttpInfo({{#allParams}}${{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); - return $response; + {{#returnType}}list($response) = {{/returnType}}$this->{{operationId}}WithHttpInfo({{#allParams}}${{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{#returnType}} + return $response;{{/returnType}} } /** @@ -113,198 +110,402 @@ use \{{invokerPackage}}\ObjectSerializer; * {{/description}} {{#allParams}} - * @param {{dataType}} ${{paramName}} {{description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} + * @param {{dataType}} ${{paramName}}{{#description}} {{description}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} {{/allParams}} + * * @throws \{{invokerPackage}}\ApiException on non-2xx response + * @throws \InvalidArgumentException * @return array of {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}null{{/returnType}}, HTTP status code, HTTP response headers (array of strings) */ public function {{operationId}}WithHttpInfo({{#allParams}}${{paramName}}{{^required}} = {{#defaultValue}}'{{{.}}}'{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) + { + $returnType = '{{returnType}}'; + $request = $this->{{operationId}}Request({{#allParams}}${{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + + try { + $options = $this->createHttpClientOption(); + try { + $response = $this->client->send($request, $options); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null, + $e->getResponse()->getBody()->getContents() + ); + } + + $statusCode = $response->getStatusCode(); + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $request->getUri() + ), + $statusCode, + $response->getHeaders(), + $response->getBody() + ); + } + + {{#returnType}} + $responseBody = $response->getBody(); + if ($returnType === '\SplFileObject') { + $content = $responseBody; //stream goes to serializer + } else { + $content = $responseBody->getContents(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + {{/returnType}} + {{^returnType}} + return [null, $statusCode, $response->getHeaders()]; + {{/returnType}} + + } catch (ApiException $e) { + switch ($e->getCode()) { + {{#responses}} + {{#dataType}} + {{^isWildcard}}case {{code}}:{{/isWildcard}}{{#isWildcard}}default:{{/isWildcard}} + $data = ObjectSerializer::deserialize( + $e->getResponseBody(), + '{{dataType}}', + $e->getResponseHeaders() + ); + $e->setResponseObject($data); + break; + {{/dataType}} + {{/responses}} + } + throw $e; + } + } + + /** + * Operation {{{operationId}}}Async + * + * {{{summary}}} + * +{{#description}} + * {{.}} + * +{{/description}} +{{#allParams}} + * @param {{dataType}} ${{paramName}}{{#description}} {{description}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} +{{/allParams}} + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function {{operationId}}Async({{#allParams}}${{paramName}}{{^required}} = {{#defaultValue}}'{{{.}}}'{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) + { + return $this->{{operationId}}AsyncWithHttpInfo({{#allParams}}${{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) + ->then( + function ($response) { + return $response[0]; + } + ); + } + + /** + * Operation {{{operationId}}}AsyncWithHttpInfo + * + * {{{summary}}} + * +{{#description}} + * {{.}} + * +{{/description}} +{{#allParams}} + * @param {{dataType}} ${{paramName}}{{#description}} {{description}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} +{{/allParams}} + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public function {{operationId}}AsyncWithHttpInfo({{#allParams}}${{paramName}}{{^required}} = {{#defaultValue}}'{{{.}}}'{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) + { + $returnType = '{{returnType}}'; + $request = $this->{{operationId}}Request({{#allParams}}${{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + + return $this->client + ->sendAsync($request, $this->createHttpClientOption()) + ->then( + function ($response) use ($returnType) { + {{#returnType}} + $responseBody = $response->getBody(); + if ($returnType === '\SplFileObject') { + $content = $responseBody; //stream goes to serializer + } else { + $content = $responseBody->getContents(); + if ($returnType !== 'string') { + $content = json_decode($content); + } + } + + return [ + ObjectSerializer::deserialize($content, $returnType, []), + $response->getStatusCode(), + $response->getHeaders() + ]; + {{/returnType}} + {{^returnType}} + return [null, $response->getStatusCode(), $response->getHeaders()]; + {{/returnType}} + }, + function ($exception) { + $response = $exception->getResponse(); + $statusCode = $response->getStatusCode(); + throw new ApiException( + sprintf( + '[%d] Error connecting to the API (%s)', + $statusCode, + $exception->getRequest()->getUri() + ), + $statusCode, + $response->getHeaders(), + $response->getBody() + ); + } + ); + } + + /** + * Create request for operation '{{{operationId}}}' + * +{{#allParams}} + * @param {{dataType}} ${{paramName}}{{#description}} {{description}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} +{{/allParams}} + * + * @throws \InvalidArgumentException + * @return \GuzzleHttp\Psr7\Request + */ + protected function {{operationId}}Request({{#allParams}}${{paramName}}{{^required}} = {{#defaultValue}}'{{{.}}}'{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) { {{#allParams}} {{#required}} // verify the required parameter '{{paramName}}' is set if (${{paramName}} === null) { - throw new \InvalidArgumentException('Missing the required parameter ${{paramName}} when calling {{operationId}}'); + throw new \InvalidArgumentException( + 'Missing the required parameter ${{paramName}} when calling {{operationId}}' + ); } {{/required}} {{#hasValidation}} {{#maxLength}} - if ({{^required}}!is_null(${{paramName}}) && {{/required}}(strlen(${{paramName}}) > {{maxLength}})) { + if ({{^required}}${{paramName}} !== null && {{/required}}strlen(${{paramName}}) > {{maxLength}}) { throw new \InvalidArgumentException('invalid length for "${{paramName}}" when calling {{classname}}.{{operationId}}, must be smaller than or equal to {{maxLength}}.'); } {{/maxLength}} {{#minLength}} - if ({{^required}}!is_null(${{paramName}}) && {{/required}}(strlen(${{paramName}}) < {{minLength}})) { + if ({{^required}}${{paramName}} !== null && {{/required}}strlen(${{paramName}}) < {{minLength}}) { throw new \InvalidArgumentException('invalid length for "${{paramName}}" when calling {{classname}}.{{operationId}}, must be bigger than or equal to {{minLength}}.'); } {{/minLength}} {{#maximum}} - if ({{^required}}!is_null(${{paramName}}) && {{/required}}(${{paramName}} >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}})) { + if ({{^required}}${{paramName}} !== null && {{/required}}${{paramName}} >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}}) { throw new \InvalidArgumentException('invalid value for "${{paramName}}" when calling {{classname}}.{{operationId}}, must be smaller than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}{{maximum}}.'); } {{/maximum}} {{#minimum}} - if ({{^required}}!is_null(${{paramName}}) && {{/required}}(${{paramName}} <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}})) { + if ({{^required}}${{paramName}} !== null && {{/required}}${{paramName}} <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}}) { throw new \InvalidArgumentException('invalid value for "${{paramName}}" when calling {{classname}}.{{operationId}}, must be bigger than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}{{minimum}}.'); } {{/minimum}} {{#pattern}} - if ({{^required}}!is_null(${{paramName}}) && {{/required}}!preg_match("{{{pattern}}}", ${{paramName}})) { + if ({{^required}}${{paramName}} !== null && {{/required}}!preg_match("{{{pattern}}}", ${{paramName}})) { throw new \InvalidArgumentException("invalid value for \"{{paramName}}\" when calling {{classname}}.{{operationId}}, must conform to the pattern {{{pattern}}}."); } {{/pattern}} {{#maxItems}} - if ({{^required}}!is_null(${{paramName}}) && {{/required}}(count(${{paramName}}) > {{maxItems}})) { + if ({{^required}}${{paramName}} !== null && {{/required}}count(${{paramName}}) > {{maxItems}}) { throw new \InvalidArgumentException('invalid value for "${{paramName}}" when calling {{classname}}.{{operationId}}, number of items must be less than or equal to {{maxItems}}.'); } {{/maxItems}} {{#minItems}} - if ({{^required}}!is_null(${{paramName}}) && {{/required}}(count(${{paramName}}) < {{minItems}})) { + if ({{^required}}${{paramName}} !== null && {{/required}}count(${{paramName}}) < {{minItems}}) { throw new \InvalidArgumentException('invalid value for "${{paramName}}" when calling {{classname}}.{{operationId}}, number of items must be greater than or equal to {{minItems}}.'); } {{/minItems}} {{/hasValidation}} {{/allParams}} - // parse inputs - $resourcePath = "{{{path}}}"; - $httpBody = ''; + + $resourcePath = '{{{path}}}'; + $formParams = []; $queryParams = []; $headerParams = []; - $formParams = []; - $_header_accept = $this->apiClient->selectHeaderAccept([{{#produces}}'{{{mediaType}}}'{{#hasMore}}, {{/hasMore}}{{/produces}}]); - if (!is_null($_header_accept)) { - $headerParams['Accept'] = $_header_accept; - } - $headerParams['Content-Type'] = $this->apiClient->selectHeaderContentType([{{#consumes}}'{{{mediaType}}}'{{#hasMore}}, {{/hasMore}}{{/consumes}}]); + $httpBody = ''; + $multipart = false; {{#queryParams}} // query params {{#collectionFormat}} if (is_array(${{paramName}})) { - ${{paramName}} = $this->apiClient->getSerializer()->serializeCollection(${{paramName}}, '{{collectionFormat}}', true); + ${{paramName}} = ObjectSerializer::serializeCollection(${{paramName}}, '{{collectionFormat}}', true); } {{/collectionFormat}} if (${{paramName}} !== null) { - $queryParams['{{baseName}}'] = $this->apiClient->getSerializer()->toQueryValue(${{paramName}}); + $queryParams['{{baseName}}'] = ObjectSerializer::toQueryValue(${{paramName}}); } {{/queryParams}} {{#headerParams}} // header params {{#collectionFormat}} if (is_array(${{paramName}})) { - ${{paramName}} = $this->apiClient->getSerializer()->serializeCollection(${{paramName}}, '{{collectionFormat}}'); + ${{paramName}} = ObjectSerializer::serializeCollection(${{paramName}}, '{{collectionFormat}}'); } {{/collectionFormat}} if (${{paramName}} !== null) { - $headerParams['{{baseName}}'] = $this->apiClient->getSerializer()->toHeaderValue(${{paramName}}); + $headerParams['{{baseName}}'] = ObjectSerializer::toHeaderValue(${{paramName}}); } {{/headerParams}} + {{#pathParams}} // path params {{#collectionFormat}} if (is_array(${{paramName}})) { - ${{paramName}} = $this->apiClient->getSerializer()->serializeCollection(${{paramName}}, '{{collectionFormat}}'); + ${{paramName}} = ObjectSerializer::serializeCollection(${{paramName}}, '{{collectionFormat}}'); } {{/collectionFormat}} if (${{paramName}} !== null) { $resourcePath = str_replace( - "{" . "{{baseName}}" . "}", - $this->apiClient->getSerializer()->toPathValue(${{paramName}}), + '{' . '{{baseName}}' . '}', + ObjectSerializer::toPathValue(${{paramName}}), $resourcePath ); } {{/pathParams}} + {{#formParams}} // form params if (${{paramName}} !== null) { {{#isFile}} - // PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax - // See: https://wiki.php.net/rfc/curl-file-upload - if (function_exists('curl_file_create')) { - $formParams['{{baseName}}'] = curl_file_create($this->apiClient->getSerializer()->toFormValue(${{paramName}})); - } else { - $formParams['{{baseName}}'] = '@' . $this->apiClient->getSerializer()->toFormValue(${{paramName}}); - } + $multipart = true; + $formParams['{{baseName}}'] = \GuzzleHttp\Psr7\try_fopen(ObjectSerializer::toFormValue(${{paramName}}), 'rb'); {{/isFile}} {{^isFile}} - $formParams['{{baseName}}'] = $this->apiClient->getSerializer()->toFormValue(${{paramName}}); + $formParams['{{baseName}}'] = ObjectSerializer::toFormValue(${{paramName}}); {{/isFile}} } {{/formParams}} - {{#bodyParams}} // body params $_tempBody = null; + {{#bodyParams}} if (isset(${{paramName}})) { $_tempBody = ${{paramName}}; } {{/bodyParams}} + if ($multipart) { + $headers= $this->headerSelector->selectHeadersForMultipart( + [{{#produces}}'{{{mediaType}}}'{{#hasMore}}, {{/hasMore}}{{/produces}}] + ); + } else { + $headers = $this->headerSelector->selectHeaders( + [{{#produces}}'{{{mediaType}}}'{{#hasMore}}, {{/hasMore}}{{/produces}}], + [{{#consumes}}'{{{mediaType}}}'{{#hasMore}}, {{/hasMore}}{{/consumes}}] + ); + } + // for model (json/xml) if (isset($_tempBody)) { - $httpBody = $_tempBody; // $_tempBody is the method argument, if present + // $_tempBody is the method argument, if present + $httpBody = $_tempBody; + // \stdClass has no __toString(), so we should encode it manually + if ($httpBody instanceof \stdClass && $headers['Content-Type'] === 'application/json') { + $httpBody = \GuzzleHttp\json_encode($httpBody); + } } elseif (count($formParams) > 0) { - $httpBody = $formParams; // for HTTP post (form) + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValue + ]; + } + // for HTTP post (form) + $httpBody = new MultipartStream($multipartContents); + + } elseif ($headers['Content-Type'] === 'application/json') { + $httpBody = \GuzzleHttp\json_encode($formParams); + + } else { + // for HTTP post (form) + $httpBody = \GuzzleHttp\Psr7\build_query($formParams); + } } + {{#authMethods}} {{#isApiKey}} // this endpoint requires API key authentication - $apiKey = $this->apiClient->getApiKeyWithPrefix('{{keyParamName}}'); - if (strlen($apiKey) !== 0) { - {{#isKeyInHeader}}$headerParams['{{keyParamName}}'] = $apiKey;{{/isKeyInHeader}}{{#isKeyInQuery}}$queryParams['{{keyParamName}}'] = $apiKey;{{/isKeyInQuery}} + $apiKey = $this->config->getApiKeyWithPrefix('{{keyParamName}}'); + if ($apiKey !== null) { + {{#isKeyInHeader}}$headers['{{keyParamName}}'] = $apiKey;{{/isKeyInHeader}}{{#isKeyInQuery}}$queryParams['{{keyParamName}}'] = $apiKey;{{/isKeyInQuery}} } {{/isApiKey}} {{#isBasic}} // this endpoint requires HTTP basic authentication - if (strlen($this->apiClient->getConfig()->getUsername()) !== 0 or strlen($this->apiClient->getConfig()->getPassword()) !== 0) { - $headerParams['Authorization'] = 'Basic ' . base64_encode($this->apiClient->getConfig()->getUsername() . ":" . $this->apiClient->getConfig()->getPassword()); + if ($this->config->getUsername() !== null || $this->config->getPassword() !== null) { + $headers['Authorization'] = 'Basic ' . base64_encode($this->config->getUsername() . ":" . $this->config->getPassword()); } {{/isBasic}} {{#isOAuth}} // this endpoint requires OAuth (access token) - if (strlen($this->apiClient->getConfig()->getAccessToken()) !== 0) { - $headerParams['Authorization'] = 'Bearer ' . $this->apiClient->getConfig()->getAccessToken(); + if ($this->config->getAccessToken() !== null) { + $headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken(); } {{/isOAuth}} {{/authMethods}} - // make the API Call - try { - list($response, $statusCode, $httpHeader) = $this->apiClient->callApi( - $resourcePath, - '{{httpMethod}}', - $queryParams, - $httpBody, - $headerParams, - {{#returnType}} - '{{returnType}}', - {{/returnType}} - {{^returnType}} - null, - {{/returnType}} - '{{{path}}}' - ); - - {{#returnType}} - return [$this->apiClient->getSerializer()->deserialize($response, '{{returnType}}', $httpHeader), $statusCode, $httpHeader]; - {{/returnType}} - {{^returnType}} - return [null, $statusCode, $httpHeader]; - {{/returnType}} - } catch (ApiException $e) { - switch ($e->getCode()) { - {{#responses}} - {{#dataType}} - {{^isWildcard}}case {{code}}:{{/isWildcard}}{{#isWildcard}}default:{{/isWildcard}} - $data = $this->apiClient->getSerializer()->deserialize($e->getResponseBody(), '{{dataType}}', $e->getResponseHeaders()); - $e->setResponseObject($data); - break; - {{/dataType}} - {{/responses}} - } - throw $e; + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + $query = \GuzzleHttp\Psr7\build_query($queryParams); + return new Request( + '{{httpMethod}}', + $this->config->getHost() . $resourcePath . ($query ? "?{$query}" : ''), + $headers, + $httpBody + ); } + {{/operation}} + /** + * Create http client option + * + * @throws \RuntimeException on file opening failure + * @return array of http client options + */ + protected function createHttpClientOption() + { + $options = []; + if ($this->config->getDebug()) { + $options[RequestOptions::DEBUG] = fopen($this->config->getDebugFile(), 'a'); + if (!$options[RequestOptions::DEBUG]) { + throw new \RuntimeException('Failed to open the debug file: ' . $this->config->getDebugFile()); + } + } + + return $options; + } } {{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/php/api_doc.mustache b/modules/swagger-codegen/src/main/resources/php/api_doc.mustache index 75ec5f8acd8..4118b83c30d 100644 --- a/modules/swagger-codegen/src/main/resources/php/api_doc.mustache +++ b/modules/swagger-codegen/src/main/resources/php/api_doc.mustache @@ -23,22 +23,29 @@ Method | HTTP request | Description require_once(__DIR__ . '/vendor/autoload.php'); {{#hasAuthMethods}}{{#authMethods}}{{#isBasic}} // Configure HTTP basic authorization: {{{name}}} -{{{invokerPackage}}}\Configuration::getDefaultConfiguration()->setUsername('YOUR_USERNAME'); -{{{invokerPackage}}}\Configuration::getDefaultConfiguration()->setPassword('YOUR_PASSWORD');{{/isBasic}}{{#isApiKey}} +$config = {{{invokerPackage}}}\Configuration::getDefaultConfiguration() + ->setUsername('YOUR_USERNAME') + ->setPassword('YOUR_PASSWORD'); +{{/isBasic}}{{#isApiKey}} // Configure API key authorization: {{{name}}} -{{{invokerPackage}}}\Configuration::getDefaultConfiguration()->setApiKey('{{{keyParamName}}}', 'YOUR_API_KEY'); +$config = {{{invokerPackage}}}\Configuration::getDefaultConfiguration()->setApiKey('{{{keyParamName}}}', 'YOUR_API_KEY'); // Uncomment below to setup prefix (e.g. Bearer) for API key, if needed -// {{{invokerPackage}}}\Configuration::getDefaultConfiguration()->setApiKeyPrefix('{{{keyParamName}}}', 'Bearer');{{/isApiKey}}{{#isOAuth}} +// $config = {{{invokerPackage}}}\Configuration::getDefaultConfiguration()->setApiKeyPrefix('{{{keyParamName}}}', 'Bearer');{{/isApiKey}}{{#isOAuth}} // Configure OAuth2 access token for authorization: {{{name}}} -{{{invokerPackage}}}\Configuration::getDefaultConfiguration()->setAccessToken('YOUR_ACCESS_TOKEN');{{/isOAuth}}{{/authMethods}} +$config = {{{invokerPackage}}}\Configuration::getDefaultConfiguration()->setAccessToken('YOUR_ACCESS_TOKEN');{{/isOAuth}}{{/authMethods}} {{/hasAuthMethods}} -$api_instance = new {{invokerPackage}}\Api\{{classname}}(); +$apiInstance = new {{invokerPackage}}\Api\{{classname}}( + // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`. + // This is optional, `GuzzleHttp\Client` will be used as default. + new GuzzleHttp\Client(){{#hasAuthMethods}}, + $config{{/hasAuthMethods}} +); {{#allParams}}${{paramName}} = {{{example}}}; // {{{dataType}}} | {{{description}}} {{/allParams}} try { - {{#returnType}}$result = {{/returnType}}$api_instance->{{{operationId}}}({{#allParams}}${{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{#returnType}} + {{#returnType}}$result = {{/returnType}}$apiInstance->{{{operationId}}}({{#allParams}}${{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{#returnType}} print_r($result);{{/returnType}} } catch (Exception $e) { echo 'Exception when calling {{classname}}->{{operationId}}: ', $e->getMessage(), PHP_EOL; diff --git a/modules/swagger-codegen/src/main/resources/php/api_test.mustache b/modules/swagger-codegen/src/main/resources/php/api_test.mustache index adf6455ef9b..af93752482d 100644 --- a/modules/swagger-codegen/src/main/resources/php/api_test.mustache +++ b/modules/swagger-codegen/src/main/resources/php/api_test.mustache @@ -19,7 +19,6 @@ namespace {{invokerPackage}}; use \{{invokerPackage}}\Configuration; -use \{{invokerPackage}}\ApiClient; use \{{invokerPackage}}\ApiException; use \{{invokerPackage}}\ObjectSerializer; diff --git a/modules/swagger-codegen/src/main/resources/php/autoload.mustache b/modules/swagger-codegen/src/main/resources/php/autoload.mustache deleted file mode 100644 index 28ce32ae50a..00000000000 --- a/modules/swagger-codegen/src/main/resources/php/autoload.mustache +++ /dev/null @@ -1,44 +0,0 @@ -partial_header}} -/** - * An example of a project-specific implementation. - * - * After registering this autoload function with SPL, the following line - * would cause the function to attempt to load the \{{invokerPackage}}\Baz\Qux class - * from /path/to/project/{{srcBasePath}}/Baz/Qux.php: - * - * new \{{invokerPackage}}\Baz\Qux; - * - * @param string $class The fully-qualified class name. - * - * @return void - */ -spl_autoload_register(function ($class) { - - // project-specific namespace prefix - $prefix = '{{escapedInvokerPackage}}\\'; - - // base directory for the namespace prefix - $base_dir = __DIR__ . '/{{srcBasePath}}/'; - - // does the class use the namespace prefix? - $len = strlen($prefix); - if (strncmp($prefix, $class, $len) !== 0) { - // no, move to the next registered autoloader - return; - } - - // get the relative class name - $relative_class = substr($class, $len); - - // replace the namespace prefix with the base directory, replace namespace - // separators with directory separators in the relative class name, append - // with .php - $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php'; - - // if the file exists, require it - if (file_exists($file)) { - require $file; - } -}); diff --git a/modules/swagger-codegen/src/main/resources/php/composer.mustache b/modules/swagger-codegen/src/main/resources/php/composer.mustache index 51bf2d5e286..0a7732fafea 100644 --- a/modules/swagger-codegen/src/main/resources/php/composer.mustache +++ b/modules/swagger-codegen/src/main/resources/php/composer.mustache @@ -19,14 +19,14 @@ } ], "require": { - "php": ">=5.4", + "php": ">=5.5", "ext-curl": "*", "ext-json": "*", - "ext-mbstring": "*" + "ext-mbstring": "*", + "guzzlehttp/guzzle": "^6.2" }, "require-dev": { - "phpunit/phpunit": "~4.8", - "satooshi/php-coveralls": "~1.0", + "phpunit/phpunit": "^4.8", "squizlabs/php_codesniffer": "~2.6", "friendsofphp/php-cs-fixer": "~1.12" }, diff --git a/modules/swagger-codegen/src/main/resources/php/configuration.mustache b/modules/swagger-codegen/src/main/resources/php/configuration.mustache deleted file mode 100644 index eee903c0da7..00000000000 --- a/modules/swagger-codegen/src/main/resources/php/configuration.mustache +++ /dev/null @@ -1,728 +0,0 @@ -partial_header}} -/** - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen - * Do not edit the class manually. - */ - -namespace {{invokerPackage}}; - -/** - * Configuration Class Doc Comment - * PHP version 5 - * - * @category Class - * @package {{invokerPackage}} - * @author Swagger Codegen team - * @link https://github.com/swagger-api/swagger-codegen - */ -class Configuration -{ - private static $defaultConfiguration; - - /** - * Associate array to store API key(s) - * - * @var string[] - */ - protected $apiKeys = []; - - /** - * Associate array to store API prefix (e.g. Bearer) - * - * @var string[] - */ - protected $apiKeyPrefixes = []; - - /** - * Access token for OAuth - * - * @var string - */ - protected $accessToken = ''; - - /** - * Username for HTTP basic authentication - * - * @var string - */ - protected $username = ''; - - /** - * Password for HTTP basic authentication - * - * @var string - */ - protected $password = ''; - - /** - * The default header(s) - * - * @var array - */ - protected $defaultHeaders = []; - - /** - * The host - * - * @var string - */ - protected $host = '{{{basePath}}}'; - - /** - * Timeout (second) of the HTTP request, by default set to 0, no timeout - * - * @var string - */ - protected $curlTimeout = 0; - - /** - * Timeout (second) of the HTTP connection, by default set to 0, no timeout - * - * @var string - */ - protected $curlConnectTimeout = 0; - - /** - * User agent of the HTTP request, set to "PHP-Swagger" by default - * - * @var string - */ - protected $userAgent = '{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{#artifactVersion}}{{{.}}}{{/artifactVersion}}{{^artifactVersion}}1.0.0{{/artifactVersion}}/php{{/httpUserAgent}}'; - - /** - * Debug switch (default set to false) - * - * @var bool - */ - protected $debug = false; - - /** - * Debug file location (log to STDOUT by default) - * - * @var string - */ - protected $debugFile = 'php://output'; - - /** - * Debug file location (log to STDOUT by default) - * - * @var string - */ - protected $tempFolderPath; - - /** - * Indicates if SSL verification should be enabled or disabled. - * - * This is useful if the host uses a self-signed SSL certificate. - * - * @var boolean True if the certificate should be validated, false otherwise. - */ - protected $sslVerification = true; - - /** - * Curl proxy host - * - * @var string - */ - protected $proxyHost; - - /** - * Curl proxy port - * - * @var integer - */ - protected $proxyPort; - - /** - * Curl proxy type, e.g. CURLPROXY_HTTP or CURLPROXY_SOCKS5 - * - * @see https://secure.php.net/manual/en/function.curl-setopt.php - * @var integer - */ - protected $proxyType; - - /** - * Curl proxy username - * - * @var string - */ - protected $proxyUser; - - /** - * Curl proxy password - * - * @var string - */ - protected $proxyPassword; - - /** - * Allow Curl encoding header - * - * @var bool - */ - protected $allowEncoding = false; - - /** - * Constructor - */ - public function __construct() - { - $this->tempFolderPath = sys_get_temp_dir(); - } - - /** - * Sets API key - * - * @param string $apiKeyIdentifier API key identifier (authentication scheme) - * @param string $key API key or token - * - * @return $this - */ - public function setApiKey($apiKeyIdentifier, $key) - { - $this->apiKeys[$apiKeyIdentifier] = $key; - return $this; - } - - /** - * Gets API key - * - * @param string $apiKeyIdentifier API key identifier (authentication scheme) - * - * @return string API key or token - */ - public function getApiKey($apiKeyIdentifier) - { - return isset($this->apiKeys[$apiKeyIdentifier]) ? $this->apiKeys[$apiKeyIdentifier] : null; - } - - /** - * Sets the prefix for API key (e.g. Bearer) - * - * @param string $apiKeyIdentifier API key identifier (authentication scheme) - * @param string $prefix API key prefix, e.g. Bearer - * - * @return $this - */ - public function setApiKeyPrefix($apiKeyIdentifier, $prefix) - { - $this->apiKeyPrefixes[$apiKeyIdentifier] = $prefix; - return $this; - } - - /** - * Gets API key prefix - * - * @param string $apiKeyIdentifier API key identifier (authentication scheme) - * - * @return string - */ - public function getApiKeyPrefix($apiKeyIdentifier) - { - return isset($this->apiKeyPrefixes[$apiKeyIdentifier]) ? $this->apiKeyPrefixes[$apiKeyIdentifier] : null; - } - - /** - * Sets the access token for OAuth - * - * @param string $accessToken Token for OAuth - * - * @return $this - */ - public function setAccessToken($accessToken) - { - $this->accessToken = $accessToken; - return $this; - } - - /** - * Gets the access token for OAuth - * - * @return string Access token for OAuth - */ - public function getAccessToken() - { - return $this->accessToken; - } - - /** - * Sets the username for HTTP basic authentication - * - * @param string $username Username for HTTP basic authentication - * - * @return $this - */ - public function setUsername($username) - { - $this->username = $username; - return $this; - } - - /** - * Gets the username for HTTP basic authentication - * - * @return string Username for HTTP basic authentication - */ - public function getUsername() - { - return $this->username; - } - - /** - * Sets the password for HTTP basic authentication - * - * @param string $password Password for HTTP basic authentication - * - * @return $this - */ - public function setPassword($password) - { - $this->password = $password; - return $this; - } - - /** - * Gets the password for HTTP basic authentication - * - * @return string Password for HTTP basic authentication - */ - public function getPassword() - { - return $this->password; - } - - /** - * Adds a default header - * - * @param string $headerName header name (e.g. Token) - * @param string $headerValue header value (e.g. 1z8wp3) - * - * @throws \InvalidArgumentException - * @return $this - */ - public function addDefaultHeader($headerName, $headerValue) - { - if (!is_string($headerName)) { - throw new \InvalidArgumentException('Header name must be a string.'); - } - - $this->defaultHeaders[$headerName] = $headerValue; - return $this; - } - - /** - * Gets the default header - * - * @return array An array of default header(s) - */ - public function getDefaultHeaders() - { - return $this->defaultHeaders; - } - - /** - * Deletes a default header - * - * @param string $headerName the header to delete - * - * @return $this - */ - public function deleteDefaultHeader($headerName) - { - unset($this->defaultHeaders[$headerName]); - return $this; - } - - /** - * Sets the host - * - * @param string $host Host - * - * @return $this - */ - public function setHost($host) - { - $this->host = $host; - return $this; - } - - /** - * Gets the host - * - * @return string Host - */ - public function getHost() - { - return $this->host; - } - - /** - * Sets the user agent of the api client - * - * @param string $userAgent the user agent of the api client - * - * @throws \InvalidArgumentException - * @return $this - */ - public function setUserAgent($userAgent) - { - if (!is_string($userAgent)) { - throw new \InvalidArgumentException('User-agent must be a string.'); - } - - $this->userAgent = $userAgent; - return $this; - } - - /** - * Gets the user agent of the api client - * - * @return string user agent - */ - public function getUserAgent() - { - return $this->userAgent; - } - - /** - * Sets the HTTP timeout value - * - * @param integer $seconds Number of seconds before timing out [set to 0 for no timeout] - * - * @throws \InvalidArgumentException - * @return $this - */ - public function setCurlTimeout($seconds) - { - if (!is_numeric($seconds) || $seconds < 0) { - throw new \InvalidArgumentException('Timeout value must be numeric and a non-negative number.'); - } - - $this->curlTimeout = $seconds; - return $this; - } - - /** - * Gets the HTTP timeout value - * - * @return string HTTP timeout value - */ - public function getCurlTimeout() - { - return $this->curlTimeout; - } - - /** - * Sets the HTTP connect timeout value - * - * @param integer $seconds Number of seconds before connection times out [set to 0 for no timeout] - * - * @throws \InvalidArgumentException - * @return $this - */ - public function setCurlConnectTimeout($seconds) - { - if (!is_numeric($seconds) || $seconds < 0) { - throw new \InvalidArgumentException('Connect timeout value must be numeric and a non-negative number.'); - } - - $this->curlConnectTimeout = $seconds; - return $this; - } - - /** - * Set whether to accept encoding - * @param bool $allowEncoding - * - * @return $this - */ - public function setAllowEncoding($allowEncoding) - { - $this->allowEncoding = $allowEncoding; - return $this; - } - - /** - * Gets the HTTP connect timeout value - * - * @return string HTTP connect timeout value - */ - public function getCurlConnectTimeout() - { - return $this->curlConnectTimeout; - } - - /** - * Get whether to allow encoding - * - * @return bool - */ - public function getAllowEncoding() - { - return $this->allowEncoding; - } - - /** - * Sets the HTTP Proxy Host - * - * @param string $proxyHost HTTP Proxy URL - * - * @return $this - */ - public function setCurlProxyHost($proxyHost) - { - $this->proxyHost = $proxyHost; - return $this; - } - - /** - * Gets the HTTP Proxy Host - * - * @return string - */ - public function getCurlProxyHost() - { - return $this->proxyHost; - } - - /** - * Sets the HTTP Proxy Port - * - * @param integer $proxyPort HTTP Proxy Port - * - * @return $this - */ - public function setCurlProxyPort($proxyPort) - { - $this->proxyPort = $proxyPort; - return $this; - } - - /** - * Gets the HTTP Proxy Port - * - * @return integer - */ - public function getCurlProxyPort() - { - return $this->proxyPort; - } - - /** - * Sets the HTTP Proxy Type - * - * @param integer $proxyType HTTP Proxy Type - * - * @return $this - */ - public function setCurlProxyType($proxyType) - { - $this->proxyType = $proxyType; - return $this; - } - - /** - * Gets the HTTP Proxy Type - * - * @return integer - */ - public function getCurlProxyType() - { - return $this->proxyType; - } - - /** - * Sets the HTTP Proxy User - * - * @param string $proxyUser HTTP Proxy User - * - * @return $this - */ - public function setCurlProxyUser($proxyUser) - { - $this->proxyUser = $proxyUser; - return $this; - } - - /** - * Gets the HTTP Proxy User - * - * @return string - */ - public function getCurlProxyUser() - { - return $this->proxyUser; - } - - /** - * Sets the HTTP Proxy Password - * - * @param string $proxyPassword HTTP Proxy Password - * - * @return $this - */ - public function setCurlProxyPassword($proxyPassword) - { - $this->proxyPassword = $proxyPassword; - return $this; - } - - /** - * Gets the HTTP Proxy Password - * - * @return string - */ - public function getCurlProxyPassword() - { - return $this->proxyPassword; - } - - /** - * Sets debug flag - * - * @param bool $debug Debug flag - * - * @return $this - */ - public function setDebug($debug) - { - $this->debug = $debug; - return $this; - } - - /** - * Gets the debug flag - * - * @return bool - */ - public function getDebug() - { - return $this->debug; - } - - /** - * Sets the debug file - * - * @param string $debugFile Debug file - * - * @return $this - */ - public function setDebugFile($debugFile) - { - $this->debugFile = $debugFile; - return $this; - } - - /** - * Gets the debug file - * - * @return string - */ - public function getDebugFile() - { - return $this->debugFile; - } - - /** - * Sets the temp folder path - * - * @param string $tempFolderPath Temp folder path - * - * @return $this - */ - public function setTempFolderPath($tempFolderPath) - { - $this->tempFolderPath = $tempFolderPath; - return $this; - } - - /** - * Gets the temp folder path - * - * @return string Temp folder path - */ - public function getTempFolderPath() - { - return $this->tempFolderPath; - } - - /** - * Sets if SSL verification should be enabled or disabled - * - * @param boolean $sslVerification True if the certificate should be validated, false otherwise - * - * @return $this - */ - public function setSSLVerification($sslVerification) - { - $this->sslVerification = $sslVerification; - return $this; - } - - /** - * Gets if SSL verification should be enabled or disabled - * - * @return boolean True if the certificate should be validated, false otherwise - */ - public function getSSLVerification() - { - return $this->sslVerification; - } - - /** - * Gets the default configuration instance - * - * @return Configuration - */ - public static function getDefaultConfiguration() - { - if (self::$defaultConfiguration === null) { - self::$defaultConfiguration = new Configuration(); - } - - return self::$defaultConfiguration; - } - - /** - * Sets the detault configuration instance - * - * @param Configuration $config An instance of the Configuration Object - * - * @return void - */ - public static function setDefaultConfiguration(Configuration $config) - { - self::$defaultConfiguration = $config; - } - - /** - * Gets the essential information for debugging - * - * @return string The report for debugging - */ - public static function toDebugReport() - { - $report = 'PHP SDK ({{invokerPackage}}) Debug Report:' . PHP_EOL; - $report .= ' OS: ' . php_uname() . PHP_EOL; - $report .= ' PHP Version: ' . PHP_VERSION . PHP_EOL; - $report .= ' OpenAPI Spec Version: {{version}}' . PHP_EOL; - {{#artifactVersion}} - $report .= ' SDK Package Version: {{artifactVersion}}' . PHP_EOL; - {{/artifactVersion}} - $report .= ' Temp Folder Path: ' . self::getDefaultConfiguration()->getTempFolderPath() . PHP_EOL; - - return $report; - } -} diff --git a/modules/swagger-codegen/src/main/resources/php/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/php/git_push.sh.mustache index a9b0f28edfb..f65b794638f 100755 --- a/modules/swagger-codegen/src/main/resources/php/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/php/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/php/model.mustache b/modules/swagger-codegen/src/main/resources/php/model.mustache index 2c1fc33e82e..9382cfa5ff5 100644 --- a/modules/swagger-codegen/src/main/resources/php/model.mustache +++ b/modules/swagger-codegen/src/main/resources/php/model.mustache @@ -8,7 +8,7 @@ * * @category Class * @package {{invokerPackage}} - * @author Swaagger Codegen team + * @author Swagger Codegen team * @link https://github.com/swagger-api/swagger-codegen */ @@ -21,20 +21,23 @@ namespace {{modelPackage}}; {{^isEnum}} +{{^parentSchema}} use \ArrayAccess; +{{/parentSchema}} {{/isEnum}} +use \{{invokerPackage}}\ObjectSerializer; /** * {{classname}} Class Doc Comment * - * @category Class + * @category Class {{#description}} * @description {{description}} {{/description}} - * @package {{invokerPackage}} - * @author Swagger Codegen team - * @link https://github.com/swagger-api/swagger-codegen + * @package {{invokerPackage}} + * @author Swagger Codegen team + * @link https://github.com/swagger-api/swagger-codegen */ {{#isEnum}}{{>model_enum}}{{/isEnum}}{{^isEnum}}{{>model_generic}}{{/isEnum}} {{/model}}{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/php/model_generic.mustache b/modules/swagger-codegen/src/main/resources/php/model_generic.mustache index 611cdcb013f..eab63f63a9c 100644 --- a/modules/swagger-codegen/src/main/resources/php/model_generic.mustache +++ b/modules/swagger-codegen/src/main/resources/php/model_generic.mustache @@ -1,15 +1,17 @@ -class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}implements ArrayAccess +class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}{{^parentSchema}}implements ModelInterface, ArrayAccess{{/parentSchema}} { const DISCRIMINATOR = {{#discriminator}}'{{discriminator}}'{{/discriminator}}{{^discriminator}}null{{/discriminator}}; /** * The original name of the model. + * * @var string */ protected static $swaggerModelName = '{{name}}'; /** * Array of property to type mappings. Used for (de)serialization + * * @var string[] */ protected static $swaggerTypes = [ @@ -19,6 +21,7 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple /** * Array of property to format mappings. Used for (de)serialization + * * @var string[] */ protected static $swaggerFormats = [ @@ -26,18 +29,30 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple {{/hasMore}}{{/vars}} ]; + /** + * Array of property to type mappings. Used for (de)serialization + * + * @return array + */ public static function swaggerTypes() { return self::$swaggerTypes{{#parentSchema}} + parent::swaggerTypes(){{/parentSchema}}; } + /** + * Array of property to format mappings. Used for (de)serialization + * + * @return array + */ public static function swaggerFormats() { return self::$swaggerFormats{{#parentSchema}} + parent::swaggerFormats(){{/parentSchema}}; } /** - * Array of attributes where the key is the local name, and the value is the original name + * Array of attributes where the key is the local name, + * and the value is the original name + * * @var string[] */ protected static $attributeMap = [ @@ -45,9 +60,9 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple {{/hasMore}}{{/vars}} ]; - /** * Array of attributes to setter functions (for deserialization of responses) + * * @var string[] */ protected static $setters = [ @@ -55,9 +70,9 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple {{/hasMore}}{{/vars}} ]; - /** * Array of attributes to getter functions (for serialization of requests) + * * @var string[] */ protected static $getters = [ @@ -65,27 +80,54 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple {{/hasMore}}{{/vars}} ]; + /** + * Array of attributes where the key is the local name, + * and the value is the original name + * + * @return array + */ public static function attributeMap() { return {{#parentSchema}}parent::attributeMap() + {{/parentSchema}}self::$attributeMap; } + /** + * Array of attributes to setter functions (for deserialization of responses) + * + * @return array + */ public static function setters() { return {{#parentSchema}}parent::setters() + {{/parentSchema}}self::$setters; } + /** + * Array of attributes to getter functions (for serialization of requests) + * + * @return array + */ public static function getters() { return {{#parentSchema}}parent::getters() + {{/parentSchema}}self::$getters; } + /** + * The original name of the model. + * + * @return string + */ + public function getModelName() + { + return self::$swaggerModelName; + } + {{#vars}}{{#isEnum}}{{#allowableValues}}{{#enumVars}}const {{enumName}}_{{{name}}} = {{{value}}}; {{/enumVars}}{{/allowableValues}}{{/isEnum}}{{/vars}} {{#vars}}{{#isEnum}} /** * Gets allowable values of the enum + * * @return string[] */ public function {{getter}}AllowableValues() @@ -97,15 +139,20 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple } {{/isEnum}}{{/vars}} + {{^parentSchema}} /** * Associative array for storing property values + * * @var mixed[] */ protected $container = []; + {{/parentSchema}} /** * Constructor - * @param mixed[] $data Associated array of property values initializing the model + * + * @param mixed[] $data Associated array of property values + * initializing the model */ public function __construct(array $data = null) { @@ -125,32 +172,32 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple } /** - * show all the invalid properties with reasons. + * Show all the invalid properties with reasons. * * @return array invalid properties with reasons */ public function listInvalidProperties() { {{#parent}} - $invalid_properties = parent::listInvalidProperties(); + $invalidProperties = parent::listInvalidProperties(); {{/parent}} {{^parent}} - $invalid_properties = []; + $invalidProperties = []; {{/parent}} {{#vars}} {{#required}} if ($this->container['{{name}}'] === null) { - $invalid_properties[] = "'{{name}}' can't be null"; + $invalidProperties[] = "'{{name}}' can't be null"; } {{/required}} {{#isEnum}} {{^isContainer}} - $allowed_values = $this->{{getter}}AllowableValues(); - if (!in_array($this->container['{{name}}'], $allowed_values)) { - $invalid_properties[] = sprintf( + $allowedValues = $this->{{getter}}AllowableValues(); + if (!in_array($this->container['{{name}}'], $allowedValues)) { + $invalidProperties[] = sprintf( "invalid value for '{{name}}', must be one of '%s'", - implode("', '", $allowed_values) + implode("', '", $allowedValues) ); } @@ -159,53 +206,53 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple {{#hasValidation}} {{#maxLength}} if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}(strlen($this->container['{{name}}']) > {{maxLength}})) { - $invalid_properties[] = "invalid value for '{{name}}', the character length must be smaller than or equal to {{{maxLength}}}."; + $invalidProperties[] = "invalid value for '{{name}}', the character length must be smaller than or equal to {{{maxLength}}}."; } {{/maxLength}} {{#minLength}} if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}(strlen($this->container['{{name}}']) < {{minLength}})) { - $invalid_properties[] = "invalid value for '{{name}}', the character length must be bigger than or equal to {{{minLength}}}."; + $invalidProperties[] = "invalid value for '{{name}}', the character length must be bigger than or equal to {{{minLength}}}."; } {{/minLength}} {{#maximum}} if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}($this->container['{{name}}'] >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}})) { - $invalid_properties[] = "invalid value for '{{name}}', must be smaller than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}{{maximum}}."; + $invalidProperties[] = "invalid value for '{{name}}', must be smaller than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}{{maximum}}."; } {{/maximum}} {{#minimum}} if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}($this->container['{{name}}'] <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}})) { - $invalid_properties[] = "invalid value for '{{name}}', must be bigger than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}{{minimum}}."; + $invalidProperties[] = "invalid value for '{{name}}', must be bigger than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}{{minimum}}."; } {{/minimum}} {{#pattern}} if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}!preg_match("{{{pattern}}}", $this->container['{{name}}'])) { - $invalid_properties[] = "invalid value for '{{name}}', must be conform to the pattern {{{pattern}}}."; + $invalidProperties[] = "invalid value for '{{name}}', must be conform to the pattern {{{pattern}}}."; } {{/pattern}} {{#maxItems}} if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}(count($this->container['{{name}}']) > {{maxItems}})) { - $invalid_properties[] = "invalid value for '{{name}}', number of items must be less than or equal to {{{maxItems}}}."; + $invalidProperties[] = "invalid value for '{{name}}', number of items must be less than or equal to {{{maxItems}}}."; } {{/maxItems}} {{#minItems}} if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}(count($this->container['{{name}}']) < {{minItems}})) { - $invalid_properties[] = "invalid value for '{{name}}', number of items must be greater than or equal to {{{minItems}}}."; + $invalidProperties[] = "invalid value for '{{name}}', number of items must be greater than or equal to {{{minItems}}}."; } {{/minItems}} {{/hasValidation}} {{/vars}} - return $invalid_properties; + return $invalidProperties; } /** - * validate all the properties in the model + * Validate all the properties in the model * return true if all passed * * @return bool True if all properties are valid @@ -226,8 +273,8 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple {{/required}} {{#isEnum}} {{^isContainer}} - $allowed_values = $this->{{getter}}AllowableValues(); - if (!in_array($this->container['{{name}}'], $allowed_values)) { + $allowedValues = $this->{{getter}}AllowableValues(); + if (!in_array($this->container['{{name}}'], $allowedValues)) { return false; } {{/isContainer}} @@ -277,6 +324,7 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple /** * Gets {{name}} + * * @return {{datatype}} */ public function {{getter}}() @@ -286,29 +334,31 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple /** * Sets {{name}} - * @param {{datatype}} ${{name}}{{#description}} {{{description}}}{{/description}} + * + * @param {{datatype}} ${{name}}{{#description}} {{{description}}}{{/description}}{{^description}} {{{name}}}{{/description}} + * * @return $this */ public function {{setter}}(${{name}}) { {{#isEnum}} - $allowed_values = $this->{{getter}}AllowableValues(); + $allowedValues = $this->{{getter}}AllowableValues(); {{^isContainer}} - if ({{^required}}!is_null(${{name}}) && {{/required}}!in_array(${{{name}}}, $allowed_values)) { + if ({{^required}}!is_null(${{name}}) && {{/required}}!in_array(${{{name}}}, $allowedValues)) { throw new \InvalidArgumentException( sprintf( "Invalid value for '{{name}}', must be one of '%s'", - implode("', '", $allowed_values) + implode("', '", $allowedValues) ) ); } {{/isContainer}} {{#isContainer}} - if ({{^required}}!is_null(${{name}}) && {{/required}}array_diff(${{{name}}}, $allowed_values)) { + if ({{^required}}!is_null(${{name}}) && {{/required}}array_diff(${{{name}}}, $allowedValues)) { throw new \InvalidArgumentException( sprintf( "Invalid value for '{{name}}', must be one of '%s'", - implode("', '", $allowed_values) + implode("', '", $allowedValues) ) ); } @@ -356,7 +406,9 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple {{/vars}} /** * Returns true if offset exists. False otherwise. - * @param integer $offset Offset + * + * @param integer $offset Offset + * * @return boolean */ public function offsetExists($offset) @@ -366,7 +418,9 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple /** * Gets offset. - * @param integer $offset Offset + * + * @param integer $offset Offset + * * @return mixed */ public function offsetGet($offset) @@ -376,8 +430,10 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple /** * Sets value based on offset. - * @param integer $offset Offset - * @param mixed $value Value to be set + * + * @param integer $offset Offset + * @param mixed $value Value to be set + * * @return void */ public function offsetSet($offset, $value) @@ -391,7 +447,9 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple /** * Unsets offset. - * @param integer $offset Offset + * + * @param integer $offset Offset + * * @return void */ public function offsetUnset($offset) @@ -401,14 +459,18 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}imple /** * Gets the string presentation of the object + * * @return string */ public function __toString() { if (defined('JSON_PRETTY_PRINT')) { // use JSON pretty print - return json_encode(\{{invokerPackage}}\ObjectSerializer::sanitizeForSerialization($this), JSON_PRETTY_PRINT); + return json_encode( + ObjectSerializer::sanitizeForSerialization($this), + JSON_PRETTY_PRINT + ); } - return json_encode(\{{invokerPackage}}\ObjectSerializer::sanitizeForSerialization($this)); + return json_encode(ObjectSerializer::sanitizeForSerialization($this)); } } diff --git a/modules/swagger-codegen/src/main/resources/php/partial_header.mustache b/modules/swagger-codegen/src/main/resources/php/partial_header.mustache index 810c48d3122..dafd3b3f35f 100644 --- a/modules/swagger-codegen/src/main/resources/php/partial_header.mustache +++ b/modules/swagger-codegen/src/main/resources/php/partial_header.mustache @@ -10,5 +10,4 @@ * {{#version}}OpenAPI spec version: {{{version}}}{{/version}} * {{#infoEmail}}Contact: {{{infoEmail}}}{{/infoEmail}} * Generated by: https://github.com/swagger-api/swagger-codegen.git - * */ diff --git a/modules/swagger-codegen/src/main/resources/pistache-server/api-header.mustache b/modules/swagger-codegen/src/main/resources/pistache-server/api-header.mustache index 71a25b54225..0ca66264e66 100644 --- a/modules/swagger-codegen/src/main/resources/pistache-server/api-header.mustache +++ b/modules/swagger-codegen/src/main/resources/pistache-server/api-header.mustache @@ -25,7 +25,7 @@ using namespace {{modelNamespace}}; class {{declspec}} {{classname}} { public: - {{classname}}(Net::Address addr); + {{classname}}(Pistache::Address addr); virtual ~{{classname}}() {}; void init(size_t thr); void start(); @@ -37,12 +37,12 @@ private: void setupRoutes(); {{#operation}} - void {{operationIdSnakeCase}}_handler(const Net::Rest::Request &request, Net::Http::ResponseWriter response); + void {{operationIdSnakeCase}}_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); {{/operation}} - void {{classnameSnakeLowerCase}}_default_handler(const Net::Rest::Request &request, Net::Http::ResponseWriter response); + void {{classnameSnakeLowerCase}}_default_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); - std::shared_ptr httpEndpoint; - Net::Rest::Router router; + std::shared_ptr httpEndpoint; + Pistache::Rest::Router router; {{#operation}} @@ -56,10 +56,10 @@ private: {{#allParams}} /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} {{/allParams}} - virtual void {{operationIdSnakeCase}}({{#allParams}}const {{#isPrimitiveType}}{{{dataType}}}{{/isPrimitiveType}}{{^isPrimitiveType}}{{{baseType}}}{{/isPrimitiveType}} &{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}{{#hasParams}}, {{/hasParams}}Net::Http::ResponseWriter &response) = 0; + virtual void {{operationIdSnakeCase}}({{#allParams}}const {{#isPrimitiveType}}{{{dataType}}}{{/isPrimitiveType}}{{^isPrimitiveType}}{{{baseType}}}{{/isPrimitiveType}} &{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}{{#hasParams}}, {{/hasParams}}Pistache::Http::ResponseWriter &response) = 0; {{/vendorExtensions.x-codegen-pistache-isParsingSupported}} {{^vendorExtensions.x-codegen-pistache-isParsingSupported}} - virtual void {{operationIdSnakeCase}}(const Net::Rest::Request &request, Net::Http::ResponseWriter &response) = 0; + virtual void {{operationIdSnakeCase}}(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter &response) = 0; {{/vendorExtensions.x-codegen-pistache-isParsingSupported}} {{/operation}} diff --git a/modules/swagger-codegen/src/main/resources/pistache-server/api-impl-header.mustache b/modules/swagger-codegen/src/main/resources/pistache-server/api-impl-header.mustache index aed4920efdb..28d77ec101f 100644 --- a/modules/swagger-codegen/src/main/resources/pistache-server/api-impl-header.mustache +++ b/modules/swagger-codegen/src/main/resources/pistache-server/api-impl-header.mustache @@ -28,15 +28,15 @@ using namespace {{modelNamespace}}; class {{classname}}Impl : public {{apiNamespace}}::{{classname}} { public: - {{classname}}Impl(Net::Address addr); + {{classname}}Impl(Pistache::Address addr); ~{{classname}}Impl() { }; {{#operation}} {{#vendorExtensions.x-codegen-pistache-isParsingSupported}} - void {{operationIdSnakeCase}}({{#allParams}}const {{#isPrimitiveType}}{{{dataType}}}{{/isPrimitiveType}}{{^isPrimitiveType}}{{{baseType}}}{{/isPrimitiveType}} &{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}{{#hasParams}}, {{/hasParams}}Net::Http::ResponseWriter &response); + void {{operationIdSnakeCase}}({{#allParams}}const {{#isPrimitiveType}}{{{dataType}}}{{/isPrimitiveType}}{{^isPrimitiveType}}{{{baseType}}}{{/isPrimitiveType}} &{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}{{#hasParams}}, {{/hasParams}}Pistache::Http::ResponseWriter &response); {{/vendorExtensions.x-codegen-pistache-isParsingSupported}} {{^vendorExtensions.x-codegen-pistache-isParsingSupported}} - void {{operationIdSnakeCase}}(const Net::Rest::Request &request, Net::Http::ResponseWriter &response); + void {{operationIdSnakeCase}}(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter &response); {{/vendorExtensions.x-codegen-pistache-isParsingSupported}} {{/operation}} diff --git a/modules/swagger-codegen/src/main/resources/pistache-server/api-impl-source.mustache b/modules/swagger-codegen/src/main/resources/pistache-server/api-impl-source.mustache index 8e4771bdd13..1109a8d10df 100644 --- a/modules/swagger-codegen/src/main/resources/pistache-server/api-impl-source.mustache +++ b/modules/swagger-codegen/src/main/resources/pistache-server/api-impl-source.mustache @@ -9,19 +9,19 @@ namespace {{this}} { using namespace {{modelNamespace}}; -{{classname}}Impl::{{classname}}Impl(Net::Address addr) +{{classname}}Impl::{{classname}}Impl(Pistache::Address addr) : {{classname}}(addr) { } {{#operation}} {{#vendorExtensions.x-codegen-pistache-isParsingSupported}} -void {{classname}}Impl::{{operationIdSnakeCase}}({{#allParams}}const {{#isPrimitiveType}}{{{dataType}}}{{/isPrimitiveType}}{{^isPrimitiveType}}{{{baseType}}}{{/isPrimitiveType}} &{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}{{#hasParams}}, {{/hasParams}}Net::Http::ResponseWriter &response) { - response.send(Net::Http::Code::Ok, "Do some magic\n"); +void {{classname}}Impl::{{operationIdSnakeCase}}({{#allParams}}const {{#isPrimitiveType}}{{{dataType}}}{{/isPrimitiveType}}{{^isPrimitiveType}}{{{baseType}}}{{/isPrimitiveType}} &{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}{{#hasParams}}, {{/hasParams}}Pistache::Http::ResponseWriter &response) { + response.send(Pistache::Http::Code::Ok, "Do some magic\n"); } {{/vendorExtensions.x-codegen-pistache-isParsingSupported}} {{^vendorExtensions.x-codegen-pistache-isParsingSupported}} -void {{classname}}Impl::{{operationIdSnakeCase}}(const Net::Rest::Request &request, Net::Http::ResponseWriter &response){ - response.send(Net::Http::Code::Ok, "Do some magic\n"); +void {{classname}}Impl::{{operationIdSnakeCase}}(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter &response){ + response.send(Pistache::Http::Code::Ok, "Do some magic\n"); } {{/vendorExtensions.x-codegen-pistache-isParsingSupported}} {{/operation}} diff --git a/modules/swagger-codegen/src/main/resources/pistache-server/api-source.mustache b/modules/swagger-codegen/src/main/resources/pistache-server/api-source.mustache index 1413cb69cdb..cfae1635b0a 100644 --- a/modules/swagger-codegen/src/main/resources/pistache-server/api-source.mustache +++ b/modules/swagger-codegen/src/main/resources/pistache-server/api-source.mustache @@ -9,14 +9,14 @@ namespace {{this}} { using namespace {{modelNamespace}}; -{{classname}}::{{classname}}(Net::Address addr) - : httpEndpoint(std::make_shared(addr)) +{{classname}}::{{classname}}(Pistache::Address addr) + : httpEndpoint(std::make_shared(addr)) { }; void {{classname}}::init(size_t thr = 2) { - auto opts = Net::Http::Endpoint::options() + auto opts = Pistache::Http::Endpoint::options() .threads(thr) - .flags(Net::Tcp::Options::InstallSignalHandler); + .flags(Pistache::Tcp::Options::InstallSignalHandler); httpEndpoint->init(opts); setupRoutes(); } @@ -31,7 +31,7 @@ void {{classname}}::shutdown() { } void {{classname}}::setupRoutes() { - using namespace Net::Rest; + using namespace Pistache::Rest; {{#operation}} Routes::{{httpMethod}}(router, base + "{{{vendorExtensions.x-codegen-pistache-path}}}", Routes::bind(&{{classname}}::{{operationIdSnakeCase}}_handler, this)); @@ -42,7 +42,7 @@ void {{classname}}::setupRoutes() { } {{#operation}} -void {{classname}}::{{operationIdSnakeCase}}_handler(const Net::Rest::Request &request, Net::Http::ResponseWriter response) { +void {{classname}}::{{operationIdSnakeCase}}_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { {{#vendorExtensions.x-codegen-pistache-isParsingSupported}} {{#hasPathParams}} // Getting the path params @@ -90,15 +90,15 @@ void {{classname}}::{{operationIdSnakeCase}}_handler(const Net::Rest::Request &r {{/vendorExtensions.x-codegen-pistache-isParsingSupported}} } catch (std::runtime_error & e) { //send a 400 error - response.send(Net::Http::Code::Bad_Request, e.what()); + response.send(Pistache::Http::Code::Bad_Request, e.what()); return; } } {{/operation}} -void {{classname}}::{{classnameSnakeLowerCase}}_default_handler(const Net::Rest::Request &request, Net::Http::ResponseWriter response) { - response.send(Net::Http::Code::Not_Found, "The requested method does not exist"); +void {{classname}}::{{classnameSnakeLowerCase}}_default_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { + response.send(Pistache::Http::Code::Not_Found, "The requested method does not exist"); } {{#apiNamespaceDeclarations}} diff --git a/modules/swagger-codegen/src/main/resources/pistache-server/cmake.mustache b/modules/swagger-codegen/src/main/resources/pistache-server/cmake.mustache index 0996f6f9640..12b066f22b7 100644 --- a/modules/swagger-codegen/src/main/resources/pistache-server/cmake.mustache +++ b/modules/swagger-codegen/src/main/resources/pistache-server/cmake.mustache @@ -41,7 +41,7 @@ ${<%classnameSnakeUpperCase%>_SERVER_SOURCES}) <%#apiInfo.apis%> <%#operations%> -target_link_libraries(<%classnameSnakeLowerCase%>_server net) +target_link_libraries(<%classnameSnakeLowerCase%>_server pistache pthread) <%/operations%> <%/apiInfo.apis%> diff --git a/modules/swagger-codegen/src/main/resources/pistache-server/main-api-server.mustache b/modules/swagger-codegen/src/main/resources/pistache-server/main-api-server.mustache index d535b74a610..302e1c8cd66 100644 --- a/modules/swagger-codegen/src/main/resources/pistache-server/main-api-server.mustache +++ b/modules/swagger-codegen/src/main/resources/pistache-server/main-api-server.mustache @@ -9,7 +9,7 @@ using namespace {{apiNamespace}}; int main() { - Net::Address addr(Net::Ipv4::any(), Net::Port(8080)); + Pistache::Address addr(Pistache::Ipv4::any(), Pistache::Port(8080)); {{classname}}Impl server(addr); server.init(2); diff --git a/modules/swagger-codegen/src/main/resources/python/__init__api.mustache b/modules/swagger-codegen/src/main/resources/python/__init__api.mustache index 989a5ea8382..db658a10fa8 100644 --- a/modules/swagger-codegen/src/main/resources/python/__init__api.mustache +++ b/modules/swagger-codegen/src/main/resources/python/__init__api.mustache @@ -1,8 +1,7 @@ from __future__ import absolute_import +# flake8: noqa + # import apis into api package -{{#apiInfo}} -{{#apis}} -from .{{classVarName}} import {{classname}} -{{/apis}} -{{/apiInfo}} +{{#apiInfo}}{{#apis}}from {{apiPackage}}.{{classVarName}} import {{classname}} +{{/apis}}{{/apiInfo}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/python/__init__model.mustache b/modules/swagger-codegen/src/main/resources/python/__init__model.mustache index 1a9eaf62794..2266b3d17f4 100644 --- a/modules/swagger-codegen/src/main/resources/python/__init__model.mustache +++ b/modules/swagger-codegen/src/main/resources/python/__init__model.mustache @@ -1,9 +1,10 @@ # coding: utf-8 +# flake8: noqa {{>partial_header}} from __future__ import absolute_import # import models into model package -{{#models}}{{#model}}from .{{classFilename}} import {{classname}}{{/model}} +{{#models}}{{#model}}from {{modelPackage}}.{{classFilename}} import {{classname}}{{/model}} {{/models}} diff --git a/modules/swagger-codegen/src/main/resources/python/__init__package.mustache b/modules/swagger-codegen/src/main/resources/python/__init__package.mustache index 6528c480eed..cd42506f540 100644 --- a/modules/swagger-codegen/src/main/resources/python/__init__package.mustache +++ b/modules/swagger-codegen/src/main/resources/python/__init__package.mustache @@ -1,18 +1,17 @@ # coding: utf-8 +# flake8: noqa + {{>partial_header}} from __future__ import absolute_import -# import models into sdk package -{{#models}}{{#model}}from .models.{{classFilename}} import {{classname}} -{{/model}}{{/models}} # import apis into sdk package -{{#apiInfo}}{{#apis}}from .apis.{{classVarName}} import {{classname}} +{{#apiInfo}}{{#apis}}from {{apiPackage}}.{{classVarName}} import {{classname}} {{/apis}}{{/apiInfo}} # import ApiClient -from .api_client import ApiClient - -from .configuration import Configuration - -configuration = Configuration() +from {{packageName}}.api_client import ApiClient +from {{packageName}}.configuration import Configuration +# import models into sdk package +{{#models}}{{#model}}from {{modelPackage}}.{{classFilename}} import {{classname}} +{{/model}}{{/models}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/python/api.mustache b/modules/swagger-codegen/src/main/resources/python/api.mustache index eb6d1fc68cb..a65946f578d 100644 --- a/modules/swagger-codegen/src/main/resources/python/api.mustache +++ b/modules/swagger-codegen/src/main/resources/python/api.mustache @@ -4,58 +4,45 @@ from __future__ import absolute_import -import sys -import os -import re +import re # noqa: F401 # python 2 and python 3 compatibility library -from six import iteritems +import six -from ..configuration import Configuration -from ..api_client import ApiClient +from {{packageName}}.api_client import ApiClient {{#operations}} class {{classname}}(object): - """ - NOTE: This class is auto generated by the swagger code generator program. + """NOTE: This class is auto generated by the swagger code generator program. + Do not edit the class manually. Ref: https://github.com/swagger-api/swagger-codegen """ def __init__(self, api_client=None): - config = Configuration() - if api_client: - self.api_client = api_client - else: - if not config.api_client: - config.api_client = ApiClient() - self.api_client = config.api_client + if api_client is None: + api_client = ApiClient() + self.api_client = api_client {{#operation}} - def {{operationId}}(self, {{#sortParamsByRequiredFlag}}{{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}{{/sortParamsByRequiredFlag}}**kwargs): - """ -{{#summary}} - {{{summary}}} -{{/summary}} + def {{operationId}}(self, {{#sortParamsByRequiredFlag}}{{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}{{/sortParamsByRequiredFlag}}**kwargs): # noqa: E501 + """{{#summary}}{{.}}{{/summary}}{{^summary}}{{operationId}}{{/summary}} # noqa: E501 + {{#notes}} - {{{notes}}} + {{{notes}}} # noqa: E501 {{/notes}} This method makes a synchronous HTTP request by default. To make an - asynchronous HTTP request, please define a `callback` function - to be invoked when receiving the response. - >>> def callback_function(response): - >>> pprint(response) - >>> + asynchronous HTTP request, please pass async=True {{#sortParamsByRequiredFlag}} - >>> thread = api.{{operationId}}({{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}callback=callback_function) + >>> thread = api.{{operationId}}({{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}async=True) {{/sortParamsByRequiredFlag}} {{^sortParamsByRequiredFlag}} - >>> thread = api.{{operationId}}({{#allParams}}{{#required}}{{paramName}}={{paramName}}_value, {{/required}}{{/allParams}}callback=callback_function) + >>> thread = api.{{operationId}}({{#allParams}}{{#required}}{{paramName}}={{paramName}}_value, {{/required}}{{/allParams}}async=True) {{/sortParamsByRequiredFlag}} + >>> result = thread.get() - :param callback function: The callback function - for asynchronous request. (optional) + :param async bool {{#allParams}} :param {{dataType}} {{paramName}}:{{#description}} {{{description}}}{{/description}}{{#required}} (required){{/required}}{{#optional}}(optional){{/optional}} {{/allParams}} @@ -64,35 +51,29 @@ class {{classname}}(object): returns the request thread. """ kwargs['_return_http_data_only'] = True - if kwargs.get('callback'): - return self.{{operationId}}_with_http_info({{#sortParamsByRequiredFlag}}{{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}{{/sortParamsByRequiredFlag}}**kwargs) + if kwargs.get('async'): + return self.{{operationId}}_with_http_info({{#sortParamsByRequiredFlag}}{{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}{{/sortParamsByRequiredFlag}}**kwargs) # noqa: E501 else: - (data) = self.{{operationId}}_with_http_info({{#sortParamsByRequiredFlag}}{{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}{{/sortParamsByRequiredFlag}}**kwargs) + (data) = self.{{operationId}}_with_http_info({{#sortParamsByRequiredFlag}}{{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}{{/sortParamsByRequiredFlag}}**kwargs) # noqa: E501 return data - def {{operationId}}_with_http_info(self, {{#sortParamsByRequiredFlag}}{{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}{{/sortParamsByRequiredFlag}}**kwargs): - """ -{{#summary}} - {{{summary}}} -{{/summary}} + def {{operationId}}_with_http_info(self, {{#sortParamsByRequiredFlag}}{{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}{{/sortParamsByRequiredFlag}}**kwargs): # noqa: E501 + """{{#summary}}{{.}}{{/summary}}{{^summary}}{{operationId}}{{/summary}} # noqa: E501 + {{#notes}} - {{{notes}}} + {{{notes}}} # noqa: E501 {{/notes}} This method makes a synchronous HTTP request by default. To make an - asynchronous HTTP request, please define a `callback` function - to be invoked when receiving the response. - >>> def callback_function(response): - >>> pprint(response) - >>> + asynchronous HTTP request, please pass async=True {{#sortParamsByRequiredFlag}} - >>> thread = api.{{operationId}}_with_http_info({{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}callback=callback_function) + >>> thread = api.{{operationId}}_with_http_info({{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}async=True) {{/sortParamsByRequiredFlag}} {{^sortParamsByRequiredFlag}} - >>> thread = api.{{operationId}}_with_http_info({{#allParams}}{{#required}}{{paramName}}={{paramName}}_value, {{/required}}{{/allParams}}callback=callback_function) + >>> thread = api.{{operationId}}_with_http_info({{#allParams}}{{#required}}{{paramName}}={{paramName}}_value, {{/required}}{{/allParams}}async=True) {{/sortParamsByRequiredFlag}} + >>> result = thread.get() - :param callback function: The callback function - for asynchronous request. (optional) + :param async bool {{#allParams}} :param {{dataType}} {{paramName}}:{{#description}} {{{description}}}{{/description}}{{#required}} (required){{/required}}{{#optional}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/optional}} {{/allParams}} @@ -101,14 +82,14 @@ class {{classname}}(object): returns the request thread. """ - all_params = [{{#allParams}}'{{paramName}}'{{#hasMore}}, {{/hasMore}}{{/allParams}}] - all_params.append('callback') + all_params = [{{#allParams}}'{{paramName}}'{{#hasMore}}, {{/hasMore}}{{/allParams}}] # noqa: E501 + all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') all_params.append('_request_timeout') params = locals() - for key, val in iteritems(params['kwargs']): + for key, val in six.iteritems(params['kwargs']): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" @@ -119,44 +100,48 @@ class {{classname}}(object): {{#allParams}} {{#required}} # verify the required parameter '{{paramName}}' is set - if ('{{paramName}}' not in params) or (params['{{paramName}}'] is None): - raise ValueError("Missing the required parameter `{{paramName}}` when calling `{{operationId}}`") + if ('{{paramName}}' not in params or + params['{{paramName}}'] is None): + raise ValueError("Missing the required parameter `{{paramName}}` when calling `{{operationId}}`") # noqa: E501 {{/required}} {{/allParams}} {{#allParams}} {{#hasValidation}} {{#maxLength}} - if '{{paramName}}' in params and len(params['{{paramName}}']) > {{maxLength}}: - raise ValueError("Invalid value for parameter `{{paramName}}` when calling `{{operationId}}`, length must be less than or equal to `{{maxLength}}`") + if ('{{paramName}}' in params and + len(params['{{paramName}}']) > {{maxLength}}): + raise ValueError("Invalid value for parameter `{{paramName}}` when calling `{{operationId}}`, length must be less than or equal to `{{maxLength}}`") # noqa: E501 {{/maxLength}} {{#minLength}} - if '{{paramName}}' in params and len(params['{{paramName}}']) < {{minLength}}: - raise ValueError("Invalid value for parameter `{{paramName}}` when calling `{{operationId}}`, length must be greater than or equal to `{{minLength}}`") + if ('{{paramName}}' in params and + len(params['{{paramName}}']) < {{minLength}}): + raise ValueError("Invalid value for parameter `{{paramName}}` when calling `{{operationId}}`, length must be greater than or equal to `{{minLength}}`") # noqa: E501 {{/minLength}} {{#maximum}} - if '{{paramName}}' in params and params['{{paramName}}'] >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}}: - raise ValueError("Invalid value for parameter `{{paramName}}` when calling `{{operationId}}`, must be a value less than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}`{{maximum}}`") + if '{{paramName}}' in params and params['{{paramName}}'] >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}}: # noqa: E501 + raise ValueError("Invalid value for parameter `{{paramName}}` when calling `{{operationId}}`, must be a value less than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}`{{maximum}}`") # noqa: E501 {{/maximum}} {{#minimum}} - if '{{paramName}}' in params and params['{{paramName}}'] <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}}: - raise ValueError("Invalid value for parameter `{{paramName}}` when calling `{{operationId}}`, must be a value greater than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}`{{minimum}}`") + if '{{paramName}}' in params and params['{{paramName}}'] <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}}: # noqa: E501 + raise ValueError("Invalid value for parameter `{{paramName}}` when calling `{{operationId}}`, must be a value greater than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}`{{minimum}}`") # noqa: E501 {{/minimum}} {{#pattern}} - if '{{paramName}}' in params and not re.search('{{{vendorExtensions.x-regex}}}', params['{{paramName}}']{{#vendorExtensions.x-modifiers}}{{#-first}}, flags={{/-first}}re.{{.}}{{^-last}} | {{/-last}}{{/vendorExtensions.x-modifiers}}): - raise ValueError("Invalid value for parameter `{{paramName}}` when calling `{{operationId}}`, must conform to the pattern `{{{pattern}}}`") + if '{{paramName}}' in params and not re.search('{{{vendorExtensions.x-regex}}}', params['{{paramName}}']{{#vendorExtensions.x-modifiers}}{{#-first}}, flags={{/-first}}re.{{.}}{{^-last}} | {{/-last}}{{/vendorExtensions.x-modifiers}}): # noqa: E501 + raise ValueError("Invalid value for parameter `{{paramName}}` when calling `{{operationId}}`, must conform to the pattern `{{{pattern}}}`") # noqa: E501 {{/pattern}} {{#maxItems}} - if '{{paramName}}' in params and len(params['{{paramName}}']) > {{maxItems}}: - raise ValueError("Invalid value for parameter `{{paramName}}` when calling `{{operationId}}`, number of items must be less than or equal to `{{maxItems}}`") + if ('{{paramName}}' in params and + len(params['{{paramName}}']) > {{maxItems}}): + raise ValueError("Invalid value for parameter `{{paramName}}` when calling `{{operationId}}`, number of items must be less than or equal to `{{maxItems}}`") # noqa: E501 {{/maxItems}} {{#minItems}} - if '{{paramName}}' in params and len(params['{{paramName}}']) < {{minItems}}: - raise ValueError("Invalid value for parameter `{{paramName}}` when calling `{{operationId}}`, number of items must be greater than or equal to `{{minItems}}`") + if ('{{paramName}}' in params and + len(params['{{paramName}}']) < {{minItems}}): + raise ValueError("Invalid value for parameter `{{paramName}}` when calling `{{operationId}}`, number of items must be greater than or equal to `{{minItems}}`") # noqa: E501 {{/minItems}} {{/hasValidation}} {{#-last}} - {{/-last}} {{/allParams}} collection_formats = {} @@ -164,30 +149,30 @@ class {{classname}}(object): path_params = {} {{#pathParams}} if '{{paramName}}' in params: - path_params['{{baseName}}'] = params['{{paramName}}']{{#isListContainer}} - collection_formats['{{baseName}}'] = '{{collectionFormat}}'{{/isListContainer}} + path_params['{{baseName}}'] = params['{{paramName}}']{{#isListContainer}} # noqa: E501 + collection_formats['{{baseName}}'] = '{{collectionFormat}}'{{/isListContainer}} # noqa: E501 {{/pathParams}} query_params = [] {{#queryParams}} if '{{paramName}}' in params: - query_params.append(('{{baseName}}', params['{{paramName}}'])){{#isListContainer}} - collection_formats['{{baseName}}'] = '{{collectionFormat}}'{{/isListContainer}} + query_params.append(('{{baseName}}', params['{{paramName}}'])){{#isListContainer}} # noqa: E501 + collection_formats['{{baseName}}'] = '{{collectionFormat}}'{{/isListContainer}} # noqa: E501 {{/queryParams}} header_params = {} {{#headerParams}} if '{{paramName}}' in params: - header_params['{{baseName}}'] = params['{{paramName}}']{{#isListContainer}} - collection_formats['{{baseName}}'] = '{{collectionFormat}}'{{/isListContainer}} + header_params['{{baseName}}'] = params['{{paramName}}']{{#isListContainer}} # noqa: E501 + collection_formats['{{baseName}}'] = '{{collectionFormat}}'{{/isListContainer}} # noqa: E501 {{/headerParams}} form_params = [] local_var_files = {} {{#formParams}} if '{{paramName}}' in params: - {{#notFile}}form_params.append(('{{baseName}}', params['{{paramName}}'])){{/notFile}}{{#isFile}}local_var_files['{{baseName}}'] = params['{{paramName}}']{{/isFile}}{{#isListContainer}} - collection_formats['{{baseName}}'] = '{{collectionFormat}}'{{/isListContainer}} + {{#notFile}}form_params.append(('{{baseName}}', params['{{paramName}}'])){{/notFile}}{{#isFile}}local_var_files['{{baseName}}'] = params['{{paramName}}']{{/isFile}}{{#isListContainer}} # noqa: E501 + collection_formats['{{baseName}}'] = '{{collectionFormat}}'{{/isListContainer}} # noqa: E501 {{/formParams}} body_params = None @@ -197,32 +182,33 @@ class {{classname}}(object): {{/bodyParam}} {{#hasProduces}} # HTTP header `Accept` - header_params['Accept'] = self.api_client.\ - select_header_accept([{{#produces}}'{{{mediaType}}}'{{#hasMore}}, {{/hasMore}}{{/produces}}]) + header_params['Accept'] = self.api_client.select_header_accept( + [{{#produces}}'{{{mediaType}}}'{{#hasMore}}, {{/hasMore}}{{/produces}}]) # noqa: E501 {{/hasProduces}} {{#hasConsumes}} # HTTP header `Content-Type` - header_params['Content-Type'] = self.api_client.\ - select_header_content_type([{{#consumes}}'{{{mediaType}}}'{{#hasMore}}, {{/hasMore}}{{/consumes}}]) + header_params['Content-Type'] = self.api_client.select_header_content_type( # noqa: E501 + [{{#consumes}}'{{{mediaType}}}'{{#hasMore}}, {{/hasMore}}{{/consumes}}]) # noqa: E501 {{/hasConsumes}} # Authentication setting - auth_settings = [{{#authMethods}}'{{name}}'{{#hasMore}}, {{/hasMore}}{{/authMethods}}] - - return self.api_client.call_api('{{{path}}}', '{{httpMethod}}', - path_params, - query_params, - header_params, - body=body_params, - post_params=form_params, - files=local_var_files, - response_type={{#returnType}}'{{returnType}}'{{/returnType}}{{^returnType}}None{{/returnType}}, - auth_settings=auth_settings, - callback=params.get('callback'), - _return_http_data_only=params.get('_return_http_data_only'), - _preload_content=params.get('_preload_content', True), - _request_timeout=params.get('_request_timeout'), - collection_formats=collection_formats) + auth_settings = [{{#authMethods}}'{{name}}'{{#hasMore}}, {{/hasMore}}{{/authMethods}}] # noqa: E501 + + return self.api_client.call_api( + '{{{path}}}', '{{httpMethod}}', + path_params, + query_params, + header_params, + body=body_params, + post_params=form_params, + files=local_var_files, + response_type={{#returnType}}'{{returnType}}'{{/returnType}}{{^returnType}}None{{/returnType}}, # noqa: E501 + auth_settings=auth_settings, + async=params.get('async'), + _return_http_data_only=params.get('_return_http_data_only'), + _preload_content=params.get('_preload_content', True), + _request_timeout=params.get('_request_timeout'), + collection_formats=collection_formats) {{/operation}} {{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/python/api_client.mustache b/modules/swagger-codegen/src/main/resources/python/api_client.mustache index 382080e9ad2..6a9a1c2744c 100644 --- a/modules/swagger-codegen/src/main/resources/python/api_client.mustache +++ b/modules/swagger-codegen/src/main/resources/python/api_client.mustache @@ -2,27 +2,28 @@ {{>partial_header}} from __future__ import absolute_import -import os -import re +import datetime import json import mimetypes +from multiprocessing.pool import ThreadPool +import os +import re import tempfile -import threading - -from datetime import date, datetime # python 2 and python 3 compatibility library -from six import PY3, integer_types, iteritems, text_type +import six from six.moves.urllib.parse import quote +{{#tornado}} +import tornado.gen +{{/tornado}} -from . import models -from .configuration import Configuration -from .rest import ApiException, RESTClientObject +from {{packageName}}.configuration import Configuration +import {{modelPackage}} +from {{packageName}} import rest class ApiClient(object): - """ - Generic API client for Swagger client library builds. + """Generic API client for Swagger client library builds. Swagger generic API client. This client handles the client- server communication, and is invariant across implementations. Specifics of @@ -33,64 +34,68 @@ class ApiClient(object): Ref: https://github.com/swagger-api/swagger-codegen Do not edit the class manually. - :param host: The base path for the server to call. + :param configuration: .Configuration object for this client :param header_name: a header to pass when making calls to the API. - :param header_value: a header value to pass when making calls to the API. + :param header_value: a header value to pass when making calls to + the API. + :param cookie: a cookie to include in the header when making calls + to the API """ - PRIMITIVE_TYPES = (float, bool, bytes, text_type) + integer_types + PRIMITIVE_TYPES = (float, bool, bytes, six.text_type) + six.integer_types NATIVE_TYPES_MAPPING = { 'int': int, - 'long': int if PY3 else long, + 'long': int if six.PY3 else long, # noqa: F821 'float': float, 'str': str, 'bool': bool, - 'date': date, - 'datetime': datetime, + 'date': datetime.date, + 'datetime': datetime.datetime, 'object': object, } - def __init__(self, host=None, header_name=None, header_value=None, cookie=None): - """ - Constructor of the class. - """ - self.rest_client = RESTClientObject() + def __init__(self, configuration=None, header_name=None, header_value=None, + cookie=None): + if configuration is None: + configuration = Configuration() + self.configuration = configuration + + self.pool = ThreadPool() + self.rest_client = rest.RESTClientObject(configuration) self.default_headers = {} if header_name is not None: self.default_headers[header_name] = header_value - if host is None: - self.host = Configuration().host - else: - self.host = host self.cookie = cookie # Set default User-Agent. self.user_agent = '{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{{packageVersion}}}/python{{/httpUserAgent}}' + def __del__(self): + self.pool.close() + self.pool.join() + @property def user_agent(self): - """ - Gets user agent. - """ + """User agent for this API client""" return self.default_headers['User-Agent'] @user_agent.setter def user_agent(self, value): - """ - Sets user agent. - """ self.default_headers['User-Agent'] = value def set_default_header(self, header_name, header_value): self.default_headers[header_name] = header_value - def __call_api(self, resource_path, method, - path_params=None, query_params=None, header_params=None, - body=None, post_params=None, files=None, - response_type=None, auth_settings=None, callback=None, - _return_http_data_only=None, collection_formats=None, _preload_content=True, - _request_timeout=None): + {{#tornado}} + @tornado.gen.coroutine + {{/tornado}} + {{#asyncio}}async {{/asyncio}}def __call_api( + self, resource_path, method, path_params=None, + query_params=None, header_params=None, body=None, post_params=None, + files=None, response_type=None, auth_settings=None, + _return_http_data_only=None, collection_formats=None, + _preload_content=True, _request_timeout=None): - config = Configuration() + config = self.configuration # header parameters header_params = header_params or {} @@ -110,7 +115,9 @@ class ApiClient(object): for k, v in path_params: # specified safe chars, encode everything resource_path = resource_path.replace( - '{%s}' % k, quote(str(v), safe=config.safe_chars_for_path_param)) + '{%s}' % k, + quote(str(v), safe=config.safe_chars_for_path_param) + ) # query parameters if query_params: @@ -133,15 +140,14 @@ class ApiClient(object): body = self.sanitize_for_serialization(body) # request url - url = self.host + resource_path + url = self.configuration.host + resource_path # perform request and return response - response_data = self.request(method, url, - query_params=query_params, - headers=header_params, - post_params=post_params, body=body, - _preload_content=_preload_content, - _request_timeout=_request_timeout) + response_data = {{#asyncio}}await {{/asyncio}}{{#tornado}}yield {{/tornado}}self.request( + method, url, query_params=query_params, headers=header_params, + post_params=post_params, body=body, + _preload_content=_preload_content, + _request_timeout=_request_timeout) self.last_response = response_data @@ -153,19 +159,23 @@ class ApiClient(object): else: return_data = None - if callback: - if _return_http_data_only: - callback(return_data) - else: - callback((return_data, response_data.status, response_data.getheaders())) - elif _return_http_data_only: +{{^tornado}} + if _return_http_data_only: return (return_data) else: - return (return_data, response_data.status, response_data.getheaders()) + return (return_data, response_data.status, + response_data.getheaders()) +{{/tornado}} +{{#tornado}} + if _return_http_data_only: + raise tornado.gen.Return(return_data) + else: + raise tornado.gen.Return((return_data, response_data.status, + response_data.getheaders())) +{{/tornado}} def sanitize_for_serialization(self, obj): - """ - Builds a JSON POST object. + """Builds a JSON POST object. If obj is None, return None. If obj is str, int, long, float, bool, return directly. @@ -188,7 +198,7 @@ class ApiClient(object): elif isinstance(obj, tuple): return tuple(self.sanitize_for_serialization(sub_obj) for sub_obj in obj) - elif isinstance(obj, (datetime, date)): + elif isinstance(obj, (datetime.datetime, datetime.date)): return obj.isoformat() if isinstance(obj, dict): @@ -200,15 +210,14 @@ class ApiClient(object): # Convert attribute name to json key in # model definition for request. obj_dict = {obj.attribute_map[attr]: getattr(obj, attr) - for attr, _ in iteritems(obj.swagger_types) + for attr, _ in six.iteritems(obj.swagger_types) if getattr(obj, attr) is not None} return {key: self.sanitize_for_serialization(val) - for key, val in iteritems(obj_dict)} + for key, val in six.iteritems(obj_dict)} def deserialize(self, response, response_type): - """ - Deserializes response into an object. + """Deserializes response into an object. :param response: RESTResponse object to be deserialized. :param response_type: class literal for @@ -230,8 +239,7 @@ class ApiClient(object): return self.__deserialize(data, response_type) def __deserialize(self, data, klass): - """ - Deserializes dict, list, str into an object. + """Deserializes dict, list, str into an object. :param data: dict, list or str. :param klass: class literal, or string of class name. @@ -250,21 +258,21 @@ class ApiClient(object): if klass.startswith('dict('): sub_kls = re.match('dict\(([^,]*), (.*)\)', klass).group(2) return {k: self.__deserialize(v, sub_kls) - for k, v in iteritems(data)} + for k, v in six.iteritems(data)} # convert str to class if klass in self.NATIVE_TYPES_MAPPING: klass = self.NATIVE_TYPES_MAPPING[klass] else: - klass = getattr(models, klass) + klass = getattr({{modelPackage}}, klass) if klass in self.PRIMITIVE_TYPES: return self.__deserialize_primitive(data, klass) elif klass == object: return self.__deserialize_object(data) - elif klass == date: + elif klass == datetime.date: return self.__deserialize_date(data) - elif klass == datetime: + elif klass == datetime.datetime: return self.__deserialize_datatime(data) else: return self.__deserialize_model(data, klass) @@ -272,12 +280,12 @@ class ApiClient(object): def call_api(self, resource_path, method, path_params=None, query_params=None, header_params=None, body=None, post_params=None, files=None, - response_type=None, auth_settings=None, callback=None, - _return_http_data_only=None, collection_formats=None, _preload_content=True, - _request_timeout=None): - """ - Makes the HTTP request (synchronous) and return the deserialized data. - To make an async request, define a function for callback. + response_type=None, auth_settings=None, async=None, + _return_http_data_only=None, collection_formats=None, + _preload_content=True, _request_timeout=None): + """Makes the HTTP request (synchronous) and returns deserialized data. + + To make an async request, set the async parameter. :param resource_path: Path to method endpoint. :param method: Method to call. @@ -292,46 +300,47 @@ class ApiClient(object): :param response: Response data type. :param files dict: key -> filename, value -> filepath, for `multipart/form-data`. - :param callback function: Callback function for asynchronous request. - If provide this parameter, - the request will be called asynchronously. - :param _return_http_data_only: response data without head status code and headers + :param async bool: execute request asynchronously + :param _return_http_data_only: response data without head status code + and headers :param collection_formats: dict of collection formats for path, query, header, and post parameters. - :param _preload_content: if False, the urllib3.HTTPResponse object will be returned without - reading/decoding response data. Default is True. - :param _request_timeout: timeout setting for this request. If one number provided, it will be total request - timeout. It can also be a pair (tuple) of (connection, read) timeouts. + :param _preload_content: if False, the urllib3.HTTPResponse object will + be returned without reading/decoding response + data. Default is True. + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. :return: - If provide parameter callback, + If async parameter is True, the request will be called asynchronously. The method will return the request thread. - If parameter callback is None, + If parameter async is False or missing, then the method will return the response directly. """ - if callback is None: + if not async: return self.__call_api(resource_path, method, path_params, query_params, header_params, body, post_params, files, - response_type, auth_settings, callback, - _return_http_data_only, collection_formats, _preload_content, _request_timeout) + response_type, auth_settings, + _return_http_data_only, collection_formats, + _preload_content, _request_timeout) else: - thread = threading.Thread(target=self.__call_api, - args=(resource_path, method, - path_params, query_params, - header_params, body, - post_params, files, - response_type, auth_settings, - callback, _return_http_data_only, - collection_formats, _preload_content, _request_timeout)) - thread.start() + thread = self.pool.apply_async(self.__call_api, (resource_path, + method, path_params, query_params, + header_params, body, + post_params, files, + response_type, auth_settings, + _return_http_data_only, + collection_formats, + _preload_content, _request_timeout)) return thread def request(self, method, url, query_params=None, headers=None, - post_params=None, body=None, _preload_content=True, _request_timeout=None): - """ - Makes the HTTP request using RESTClient. - """ + post_params=None, body=None, _preload_content=True, + _request_timeout=None): + """Makes the HTTP request using RESTClient.""" if method == "GET": return self.rest_client.GET(url, query_params=query_params, @@ -390,8 +399,7 @@ class ApiClient(object): ) def parameters_to_tuples(self, params, collection_formats): - """ - Get parameters as list of tuples, formatting collections. + """Get parameters as list of tuples, formatting collections. :param params: Parameters as dict or list of two-tuples :param dict collection_formats: Parameter collection formats @@ -400,7 +408,7 @@ class ApiClient(object): new_params = [] if collection_formats is None: collection_formats = {} - for k, v in iteritems(params) if isinstance(params, dict) else params: + for k, v in six.iteritems(params) if isinstance(params, dict) else params: # noqa: E501 if k in collection_formats: collection_format = collection_formats[k] if collection_format == 'multi': @@ -421,8 +429,7 @@ class ApiClient(object): return new_params def prepare_post_parameters(self, post_params=None, files=None): - """ - Builds form parameters. + """Builds form parameters. :param post_params: Normal form parameters. :param files: File parameters. @@ -434,7 +441,7 @@ class ApiClient(object): params = post_params if files: - for k, v in iteritems(files): + for k, v in six.iteritems(files): if not v: continue file_names = v if type(v) is list else [v] @@ -442,15 +449,15 @@ class ApiClient(object): with open(n, 'rb') as f: filename = os.path.basename(f.name) filedata = f.read() - mimetype = mimetypes.\ - guess_type(filename)[0] or 'application/octet-stream' - params.append(tuple([k, tuple([filename, filedata, mimetype])])) + mimetype = (mimetypes.guess_type(filename)[0] or + 'application/octet-stream') + params.append( + tuple([k, tuple([filename, filedata, mimetype])])) return params def select_header_accept(self, accepts): - """ - Returns `Accept` based on an array of accepts provided. + """Returns `Accept` based on an array of accepts provided. :param accepts: List of headers. :return: Accept (e.g. application/json). @@ -466,8 +473,7 @@ class ApiClient(object): return ', '.join(accepts) def select_header_content_type(self, content_types): - """ - Returns `Content-Type` based on an array of content_types provided. + """Returns `Content-Type` based on an array of content_types provided. :param content_types: List of content-types. :return: Content-Type (e.g. application/json). @@ -483,20 +489,17 @@ class ApiClient(object): return content_types[0] def update_params_for_auth(self, headers, querys, auth_settings): - """ - Updates header and query params based on authentication setting. + """Updates header and query params based on authentication setting. :param headers: Header parameters dict to be updated. :param querys: Query parameters tuple list to be updated. :param auth_settings: Authentication setting identifiers list. """ - config = Configuration() - if not auth_settings: return for auth in auth_settings: - auth_setting = config.auth_settings().get(auth) + auth_setting = self.configuration.auth_settings().get(auth) if auth_setting: if not auth_setting['value']: continue @@ -510,34 +513,31 @@ class ApiClient(object): ) def __deserialize_file(self, response): - """ + """Deserializes body to file + Saves response body into a file in a temporary folder, using the filename from the `Content-Disposition` header if provided. :param response: RESTResponse. :return: file path. """ - config = Configuration() - - fd, path = tempfile.mkstemp(dir=config.temp_folder_path) + fd, path = tempfile.mkstemp(dir=self.configuration.temp_folder_path) os.close(fd) os.remove(path) content_disposition = response.getheader("Content-Disposition") if content_disposition: - filename = re.\ - search(r'filename=[\'"]?([^\'"\s]+)[\'"]?', content_disposition).\ - group(1) + filename = re.search(r'filename=[\'"]?([^\'"\s]+)[\'"]?', + content_disposition).group(1) path = os.path.join(os.path.dirname(path), filename) - with open(path, "w") as f: + with open(path, "wb") as f: f.write(response.data) return path def __deserialize_primitive(self, data, klass): - """ - Deserializes string to primitive type. + """Deserializes string to primitive type. :param data: str. :param klass: class literal. @@ -547,21 +547,19 @@ class ApiClient(object): try: return klass(data) except UnicodeEncodeError: - return unicode(data) + return six.u(data) except TypeError: return data def __deserialize_object(self, value): - """ - Return a original value. + """Return a original value. :return: object. """ return value def __deserialize_date(self, string): - """ - Deserializes string to date. + """Deserializes string to date. :param string: str. :return: date. @@ -572,14 +570,13 @@ class ApiClient(object): except ImportError: return string except ValueError: - raise ApiException( + raise rest.ApiException( status=0, - reason="Failed to parse `{0}` into a date object".format(string) + reason="Failed to parse `{0}` as date object".format(string) ) def __deserialize_datatime(self, string): - """ - Deserializes string to datetime. + """Deserializes string to datetime. The string should be in iso8601 datetime format. @@ -592,33 +589,39 @@ class ApiClient(object): except ImportError: return string except ValueError: - raise ApiException( + raise rest.ApiException( status=0, reason=( - "Failed to parse `{0}` into a datetime object" + "Failed to parse `{0}` as datetime object" .format(string) ) ) def __deserialize_model(self, data, klass): - """ - Deserializes list or dict to model. + """Deserializes list or dict to model. :param data: dict, list. :param klass: class literal. :return: model object. """ - if not klass.swagger_types: + + if not klass.swagger_types and not hasattr(klass, + 'get_real_child_model'): return data kwargs = {} - for attr, attr_type in iteritems(klass.swagger_types): - if data is not None \ - and klass.attribute_map[attr] in data \ - and isinstance(data, (list, dict)): - value = data[klass.attribute_map[attr]] - kwargs[attr] = self.__deserialize(value, attr_type) - - instance = klass(**kwargs) - + if klass.swagger_types is not None: + for attr, attr_type in six.iteritems(klass.swagger_types): + if (data is not None and + klass.attribute_map[attr] in data and + isinstance(data, (list, dict))): + value = data[klass.attribute_map[attr]] + kwargs[attr] = self.__deserialize(value, attr_type) + + instance = klass(**kwargs) + + if hasattr(instance, 'get_real_child_model'): + klass_name = instance.get_real_child_model(data) + if klass_name: + instance = self.__deserialize(data, klass_name) return instance diff --git a/modules/swagger-codegen/src/main/resources/python/api_doc.mustache b/modules/swagger-codegen/src/main/resources/python/api_doc.mustache index 278b160e524..32c26d929d1 100644 --- a/modules/swagger-codegen/src/main/resources/python/api_doc.mustache +++ b/modules/swagger-codegen/src/main/resources/python/api_doc.mustache @@ -17,7 +17,7 @@ Method | HTTP request | Description {{{notes}}}{{/notes}} -### Example +### Example ```python from __future__ import print_function import time @@ -26,22 +26,32 @@ from {{{packageName}}}.rest import ApiException from pprint import pprint {{#hasAuthMethods}}{{#authMethods}}{{#isBasic}} # Configure HTTP basic authorization: {{{name}}} -{{{packageName}}}.configuration.username = 'YOUR_USERNAME' -{{{packageName}}}.configuration.password = 'YOUR_PASSWORD'{{/isBasic}}{{#isApiKey}} +configuration = {{{packageName}}}.Configuration() +configuration.username = 'YOUR_USERNAME' +configuration.password = 'YOUR_PASSWORD'{{/isBasic}}{{#isApiKey}} # Configure API key authorization: {{{name}}} -{{{packageName}}}.configuration.api_key['{{{keyParamName}}}'] = 'YOUR_API_KEY' +configuration = {{{packageName}}}.Configuration() +configuration.api_key['{{{keyParamName}}}'] = 'YOUR_API_KEY' # Uncomment below to setup prefix (e.g. Bearer) for API key, if needed -# {{{packageName}}}.configuration.api_key_prefix['{{{keyParamName}}}'] = 'Bearer'{{/isApiKey}}{{#isOAuth}} +# configuration.api_key_prefix['{{{keyParamName}}}'] = 'Bearer'{{/isApiKey}}{{#isOAuth}} # Configure OAuth2 access token for authorization: {{{name}}} -{{{packageName}}}.configuration.access_token = 'YOUR_ACCESS_TOKEN'{{/isOAuth}}{{/authMethods}} +configuration = {{{packageName}}}.Configuration() +configuration.access_token = 'YOUR_ACCESS_TOKEN'{{/isOAuth}}{{/authMethods}} + +# create an instance of the API class +api_instance = {{{packageName}}}.{{{classname}}}({{{packageName}}}.ApiClient(configuration)) +{{#allParams}}{{paramName}} = {{{example}}} # {{{dataType}}} | {{{description}}}{{^required}} (optional){{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} +{{/allParams}} {{/hasAuthMethods}} +{{^hasAuthMethods}} # create an instance of the API class api_instance = {{{packageName}}}.{{{classname}}}() {{#allParams}}{{paramName}} = {{{example}}} # {{{dataType}}} | {{{description}}}{{^required}} (optional){{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} {{/allParams}} +{{/hasAuthMethods}} -try: +try: {{#summary}} # {{{.}}} {{/summary}} {{#returnType}}api_response = {{/returnType}}api_instance.{{{operationId}}}({{#allParams}}{{#required}}{{paramName}}{{/required}}{{^required}}{{paramName}}={{paramName}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{#returnType}} pprint(api_response){{/returnType}} diff --git a/modules/swagger-codegen/src/main/resources/python/api_test.mustache b/modules/swagger-codegen/src/main/resources/python/api_test.mustache index f0f07f7189e..90fafc15f33 100644 --- a/modules/swagger-codegen/src/main/resources/python/api_test.mustache +++ b/modules/swagger-codegen/src/main/resources/python/api_test.mustache @@ -4,30 +4,29 @@ from __future__ import absolute_import -import os -import sys import unittest import {{packageName}} +from {{apiPackage}}.{{classVarName}} import {{classname}} # noqa: E501 from {{packageName}}.rest import ApiException -from {{packageName}}.apis.{{classVarName}} import {{classname}} class {{#operations}}Test{{classname}}(unittest.TestCase): - """ {{classname}} unit test stubs """ + """{{classname}} unit test stubs""" def setUp(self): - self.api = {{packageName}}.apis.{{classVarName}}.{{classname}}() + self.api = {{apiPackage}}.{{classVarName}}.{{classname}}() # noqa: E501 def tearDown(self): pass {{#operation}} def test_{{operationId}}(self): - """ - Test case for {{{operationId}}} + """Test case for {{{operationId}}} - {{{summary}}} +{{#summary}} + {{{summary}}} # noqa: E501 +{{/summary}} """ pass diff --git a/modules/swagger-codegen/src/main/resources/python/asyncio/rest.mustache b/modules/swagger-codegen/src/main/resources/python/asyncio/rest.mustache new file mode 100644 index 00000000000..986902f56d0 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/python/asyncio/rest.mustache @@ -0,0 +1,250 @@ +# coding: utf-8 + +{{>partial_header}} + +import io +import json +import logging +import re +import ssl + +import aiohttp +import certifi +# python 2 and python 3 compatibility library +from six.moves.urllib.parse import urlencode + +logger = logging.getLogger(__name__) + + +class RESTResponse(io.IOBase): + + def __init__(self, resp, data): + self.aiohttp_response = resp + self.status = resp.status + self.reason = resp.reason + self.data = data + + def getheaders(self): + """Returns a CIMultiDictProxy of the response headers.""" + return self.aiohttp_response.headers + + def getheader(self, name, default=None): + """Returns a given response header.""" + return self.aiohttp_response.headers.get(name, default) + + +class RESTClientObject(object): + + def __init__(self, configuration, pools_size=4, maxsize=4): + # maxsize is number of requests to host that are allowed in parallel + # ca_certs vs cert_file vs key_file + # http://stackoverflow.com/a/23957365/2985775 + + # ca_certs + if configuration.ssl_ca_cert: + ca_certs = configuration.ssl_ca_cert + else: + # if not set certificate file, use Mozilla's root certificates. + ca_certs = certifi.where() + + ssl_context = ssl.SSLContext() + ssl_context.load_verify_locations(cafile=ca_certs) + if configuration.cert_file: + ssl_context.load_cert_chain( + configuration.cert_file, keyfile=configuration.key_file + ) + + connector = aiohttp.TCPConnector( + limit=maxsize, + ssl_context=ssl_context, + verify_ssl=configuration.verify_ssl + ) + + # https pool manager + if configuration.proxy: + self.pool_manager = aiohttp.ClientSession( + connector=connector, + proxy=configuration.proxy + ) + else: + self.pool_manager = aiohttp.ClientSession( + connector=connector + ) + + async def request(self, method, url, query_params=None, headers=None, + body=None, post_params=None, _preload_content=True, + _request_timeout=None): + """Execute request + + :param method: http request method + :param url: http request url + :param query_params: query parameters in the url + :param headers: http request headers + :param body: request json body, for `application/json` + :param post_params: request post parameters, + `application/x-www-form-urlencoded` + and `multipart/form-data` + :param _preload_content: this is a non-applicable field for + the AiohttpClient. + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + """ + method = method.upper() + assert method in ['GET', 'HEAD', 'DELETE', 'POST', 'PUT', + 'PATCH', 'OPTIONS'] + + if post_params and body: + raise ValueError( + "body parameter cannot be used with post_params parameter." + ) + + post_params = post_params or {} + headers = headers or {} + timeout = _request_timeout or 5 * 60 + + if 'Content-Type' not in headers: + headers['Content-Type'] = 'application/json' + + args = { + "method": method, + "url": url, + "timeout": timeout, + "headers": headers + } + # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE` + if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']: + if query_params: + url += '?' + urlencode(query_params) + if re.search('json', headers['Content-Type'], re.IGNORECASE): + if body is not None: + body = json.dumps(body) + args["data"] = body + elif headers['Content-Type'] == 'application/x-www-form-urlencoded': # noqa: E501 + data = aiohttp.FormData() + for k, v in post_params.items(): + data.add_field(k, v) + args["data"] = data + elif headers['Content-Type'] == 'multipart/form-data': + args["data"] = post_params + # Pass a `bytes` parameter directly in the body to support + # other content types than Json when `body` argument is provided + # in serialized form + elif isinstance(body, bytes): + args["data"] = body + else: + # Cannot generate the request from given parameters + msg = """Cannot prepare a request message for provided + arguments. Please check that your arguments match + declared content type.""" + raise ApiException(status=0, reason=msg) + else: + args["data"] = query_params + + async with self.pool_manager.request(**args) as r: + data = await r.text() + r = RESTResponse(r, data) + + # log response body + logger.debug("response body: %s", r.data) + + if not 200 <= r.status <= 299: + raise ApiException(http_resp=r) + + return r + + async def GET(self, url, headers=None, query_params=None, + _preload_content=True, _request_timeout=None): + return (await self.request("GET", url, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + query_params=query_params)) + + async def HEAD(self, url, headers=None, query_params=None, + _preload_content=True, _request_timeout=None): + return (await self.request("HEAD", url, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + query_params=query_params)) + + async def OPTIONS(self, url, headers=None, query_params=None, + post_params=None, body=None, _preload_content=True, + _request_timeout=None): + return (await self.request("OPTIONS", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body)) + + async def DELETE(self, url, headers=None, query_params=None, body=None, + _preload_content=True, _request_timeout=None): + return (await self.request("DELETE", url, + headers=headers, + query_params=query_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body)) + + async def POST(self, url, headers=None, query_params=None, + post_params=None, body=None, _preload_content=True, + _request_timeout=None): + return (await self.request("POST", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body)) + + async def PUT(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + return (await self.request("PUT", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body)) + + async def PATCH(self, url, headers=None, query_params=None, + post_params=None, body=None, _preload_content=True, + _request_timeout=None): + return (await self.request("PATCH", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body)) + + +class ApiException(Exception): + + def __init__(self, status=None, reason=None, http_resp=None): + if http_resp: + self.status = http_resp.status + self.reason = http_resp.reason + self.body = http_resp.data + self.headers = http_resp.getheaders() + else: + self.status = status + self.reason = reason + self.body = None + self.headers = None + + def __str__(self): + """Custom error messages for exception""" + error_message = "({0})\nReason: {1}\n".format(self.status, self.reason) + if self.headers: + error_message += "HTTP response headers: {0}\n".format( + self.headers) + + if self.body: + error_message += "HTTP response body: {0}\n".format(self.body) + + return error_message diff --git a/modules/swagger-codegen/src/main/resources/python/configuration.mustache b/modules/swagger-codegen/src/main/resources/python/configuration.mustache index 6c055604665..84e6ad4d208 100644 --- a/modules/swagger-codegen/src/main/resources/python/configuration.mustache +++ b/modules/swagger-codegen/src/main/resources/python/configuration.mustache @@ -4,41 +4,41 @@ from __future__ import absolute_import -import urllib3 - -import sys +import copy import logging +import multiprocessing +import sys +import urllib3 -from six import iteritems +import six from six.moves import http_client as httplib -def singleton(cls, *args, **kw): - instances = {} +class TypeWithDefault(type): + def __init__(cls, name, bases, dct): + super(TypeWithDefault, cls).__init__(name, bases, dct) + cls._default = None - def _singleton(): - if cls not in instances: - instances[cls] = cls(*args, **kw) - return instances[cls] - return _singleton + def __call__(cls): + if cls._default is None: + cls._default = type.__call__(cls) + return copy.copy(cls._default) + def set_default(cls, default): + cls._default = copy.copy(default) + + +class Configuration(six.with_metaclass(TypeWithDefault, object)): + """NOTE: This class is auto generated by the swagger code generator program. -@singleton -class Configuration(object): - """ - NOTE: This class is auto generated by the swagger code generator program. Ref: https://github.com/swagger-api/swagger-codegen Do not edit the class manually. """ def __init__(self): - """ - Constructor - """ + """Constructor""" # Default Base url self.host = "{{{basePath}}}" - # Default api client - self.api_client = None # Temp file folder for downloading files self.temp_folder_path = None @@ -71,7 +71,8 @@ class Configuration(object): self.debug = False # SSL/TLS verification - # Set this to false to skip verifying SSL certificate when calling API from https server. + # Set this to false to skip verifying SSL certificate when calling API + # from https server. self.verify_ssl = True # Set this to customize the certificate file to verify the peer. self.ssl_ca_cert = None @@ -79,6 +80,15 @@ class Configuration(object): self.cert_file = None # client key file self.key_file = None + # Set this to True/False to enable/disable SSL hostname verification. + self.assert_hostname = None + + # urllib3 connection pool's maximum number of connections saved + # per pool. urllib3 uses 1 connection as default value, but this is + # not the best value when you are making a lot of possibly parallel + # requests to the same host, which is often the case here. + # cpu_count * 5 is used as default value to increase performance. + self.connection_pool_maxsize = multiprocessing.cpu_count() * 5 # Proxy URL self.proxy = None @@ -87,18 +97,22 @@ class Configuration(object): @property def logger_file(self): - """ - Gets the logger_file. + """The logger file. + + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. + + :param value: The logger_file path. + :type: str """ return self.__logger_file @logger_file.setter def logger_file(self, value): - """ - Sets the logger_file. + """The logger file. - If the logger_file is None, then add stream handler and remove file handler. - Otherwise, add file handler and remove stream handler. + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. :param value: The logger_file path. :type: str @@ -109,7 +123,7 @@ class Configuration(object): # then add file handler and remove stream handler. self.logger_file_handler = logging.FileHandler(self.__logger_file) self.logger_file_handler.setFormatter(self.logger_formatter) - for _, logger in iteritems(self.logger): + for _, logger in six.iteritems(self.logger): logger.addHandler(self.logger_file_handler) if self.logger_stream_handler: logger.removeHandler(self.logger_stream_handler) @@ -118,22 +132,23 @@ class Configuration(object): # then add stream handler and remove file handler. self.logger_stream_handler = logging.StreamHandler() self.logger_stream_handler.setFormatter(self.logger_formatter) - for _, logger in iteritems(self.logger): + for _, logger in six.iteritems(self.logger): logger.addHandler(self.logger_stream_handler) if self.logger_file_handler: logger.removeHandler(self.logger_file_handler) @property def debug(self): - """ - Gets the debug status. + """Debug status + + :param value: The debug status, True or False. + :type: bool """ return self.__debug @debug.setter def debug(self, value): - """ - Sets the debug status. + """Debug status :param value: The debug status, True or False. :type: bool @@ -141,29 +156,32 @@ class Configuration(object): self.__debug = value if self.__debug: # if debug status is True, turn on debug logging - for _, logger in iteritems(self.logger): + for _, logger in six.iteritems(self.logger): logger.setLevel(logging.DEBUG) # turn on httplib debug httplib.HTTPConnection.debuglevel = 1 else: # if debug status is False, turn off debug logging, # setting log level to default `logging.WARNING` - for _, logger in iteritems(self.logger): + for _, logger in six.iteritems(self.logger): logger.setLevel(logging.WARNING) # turn off httplib debug httplib.HTTPConnection.debuglevel = 0 @property def logger_format(self): - """ - Gets the logger_format. + """The logger format. + + The logger_formatter will be updated when sets logger_format. + + :param value: The format string. + :type: str """ return self.__logger_format @logger_format.setter def logger_format(self, value): - """ - Sets the logger_format. + """The logger format. The logger_formatter will be updated when sets logger_format. @@ -174,29 +192,28 @@ class Configuration(object): self.logger_formatter = logging.Formatter(self.__logger_format) def get_api_key_with_prefix(self, identifier): - """ - Gets API key (with prefix if set). + """Gets API key (with prefix if set). :param identifier: The identifier of apiKey. :return: The token for api key authentication. """ - if self.api_key.get(identifier) and self.api_key_prefix.get(identifier): - return self.api_key_prefix[identifier] + ' ' + self.api_key[identifier] + if (self.api_key.get(identifier) and + self.api_key_prefix.get(identifier)): + return self.api_key_prefix[identifier] + ' ' + self.api_key[identifier] # noqa: E501 elif self.api_key.get(identifier): return self.api_key[identifier] def get_basic_auth_token(self): - """ - Gets HTTP basic authentication header (string). + """Gets HTTP basic authentication header (string). :return: The token for basic HTTP authentication. """ - return urllib3.util.make_headers(basic_auth=self.username + ':' + self.password)\ - .get('authorization') + return urllib3.util.make_headers( + basic_auth=self.username + ':' + self.password + ).get('authorization') def auth_settings(self): - """ - Gets Auth Settings dict for api client. + """Gets Auth Settings dict for api client. :return: The Auth Settings information dict. """ @@ -231,8 +248,7 @@ class Configuration(object): } def to_debug_report(self): - """ - Gets the essential information for debugging. + """Gets the essential information for debugging. :return: The report for debugging. """ diff --git a/modules/swagger-codegen/src/main/resources/python/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/python/git_push.sh.mustache index e153ce23ecf..a2d75234837 100755 --- a/modules/swagger-codegen/src/main/resources/python/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/python/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/python/model.mustache b/modules/swagger-codegen/src/main/resources/python/model.mustache index 308eeb8071e..5668793b3f6 100644 --- a/modules/swagger-codegen/src/main/resources/python/model.mustache +++ b/modules/swagger-codegen/src/main/resources/python/model.mustache @@ -2,27 +2,31 @@ {{>partial_header}} -{{#models}} -{{#model}} -from pprint import pformat -from six import iteritems -import re +import pprint +import re # noqa: F401 + +import six +{{#imports}}{{#-first}} +{{/-first}} +{{import}} # noqa: F401,E501 +{{/imports}} +{{#models}} +{{#model}} class {{classname}}(object): - """ - NOTE: This class is auto generated by the swagger code generator program. + """NOTE: This class is auto generated by the swagger code generator program. + Do not edit the class manually. - """ + """{{#allowableValues}} -{{#allowableValues}} """ allowed enum values """ {{#enumVars}} - {{name}} = {{{value}}} -{{/enumVars}} -{{/allowableValues}} + {{name}} = {{{value}}}{{^-last}} +{{/-last}} +{{/enumVars}}{{/allowableValues}} """ Attributes: @@ -32,88 +36,96 @@ class {{classname}}(object): and the value is json key in definition. """ swagger_types = { - {{#vars}}'{{name}}': '{{{datatype}}}'{{#hasMore}}, - {{/hasMore}}{{/vars}} +{{#vars}} + '{{name}}': '{{{datatype}}}'{{#hasMore}},{{/hasMore}} +{{/vars}} } attribute_map = { - {{#vars}}'{{name}}': '{{baseName}}'{{#hasMore}}, - {{/hasMore}}{{/vars}} +{{#vars}} + '{{name}}': '{{baseName}}'{{#hasMore}},{{/hasMore}} +{{/vars}} } +{{#discriminator}} - def __init__(self{{#vars}}, {{name}}={{#defaultValue}}{{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}None{{/defaultValue}}{{/vars}}): - """ - {{classname}} - a model defined in Swagger - """ + discriminator_value_class_map = { + {{#children}}'{{^vendorExtensions.x-discriminator-value}}{{name}}{{/vendorExtensions.x-discriminator-value}}{{#vendorExtensions.x-discriminator-value}}{{{vendorExtensions.x-discriminator-value}}}{{/vendorExtensions.x-discriminator-value}}': '{{{classname}}}'{{^-last}}, + {{/-last}}{{/children}} + } +{{/discriminator}} -{{#vars}} + def __init__(self{{#vars}}, {{name}}={{#defaultValue}}{{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}None{{/defaultValue}}{{/vars}}): # noqa: E501 + """{{classname}} - a model defined in Swagger""" # noqa: E501 +{{#vars}}{{#-first}} +{{/-first}} self._{{name}} = None {{/vars}} - -{{#vars}} + self.discriminator = {{#discriminator}}'{{discriminator}}'{{/discriminator}}{{^discriminator}}None{{/discriminator}} +{{#vars}}{{#-first}} +{{/-first}} {{#required}} self.{{name}} = {{name}} {{/required}} {{^required}} if {{name}} is not None: - self.{{name}} = {{name}} + self.{{name}} = {{name}} {{/required}} {{/vars}} {{#vars}} @property def {{name}}(self): - """ - Gets the {{name}} of this {{classname}}. -{{#description}} - {{{description}}} + """Gets the {{name}} of this {{classname}}. # noqa: E501 + +{{#description}} + {{{description}}} # noqa: E501 {{/description}} - :return: The {{name}} of this {{classname}}. + :return: The {{name}} of this {{classname}}. # noqa: E501 :rtype: {{datatype}} """ return self._{{name}} @{{name}}.setter def {{name}}(self, {{name}}): - """ - Sets the {{name}} of this {{classname}}. + """Sets the {{name}} of this {{classname}}. + {{#description}} - {{{description}}} + {{{description}}} # noqa: E501 {{/description}} - :param {{name}}: The {{name}} of this {{classname}}. + :param {{name}}: The {{name}} of this {{classname}}. # noqa: E501 :type: {{datatype}} """ {{#required}} if {{name}} is None: - raise ValueError("Invalid value for `{{name}}`, must not be `None`") + raise ValueError("Invalid value for `{{name}}`, must not be `None`") # noqa: E501 {{/required}} {{#isEnum}} {{#isContainer}} - allowed_values = [{{#allowableValues}}{{#values}}{{#items.isString}}"{{/items.isString}}{{{this}}}{{#items.isString}}"{{/items.isString}}{{^-last}}, {{/-last}}{{/values}}{{/allowableValues}}] + allowed_values = [{{#allowableValues}}{{#values}}{{#items.isString}}"{{/items.isString}}{{{this}}}{{#items.isString}}"{{/items.isString}}{{^-last}}, {{/-last}}{{/values}}{{/allowableValues}}] # noqa: E501 {{#isListContainer}} if not set({{{name}}}).issubset(set(allowed_values)): raise ValueError( - "Invalid values for `{{{name}}}` [{0}], must be a subset of [{1}]" - .format(", ".join(map(str, set({{{name}}})-set(allowed_values))), + "Invalid values for `{{{name}}}` [{0}], must be a subset of [{1}]" # noqa: E501 + .format(", ".join(map(str, set({{{name}}}) - set(allowed_values))), # noqa: E501 ", ".join(map(str, allowed_values))) ) {{/isListContainer}} {{#isMapContainer}} if not set({{{name}}}.keys()).issubset(set(allowed_values)): raise ValueError( - "Invalid keys in `{{{name}}}` [{0}], must be a subset of [{1}]" - .format(", ".join(map(str, set({{{name}}}.keys())-set(allowed_values))), + "Invalid keys in `{{{name}}}` [{0}], must be a subset of [{1}]" # noqa: E501 + .format(", ".join(map(str, set({{{name}}}.keys()) - set(allowed_values))), # noqa: E501 ", ".join(map(str, allowed_values))) ) {{/isMapContainer}} {{/isContainer}} {{^isContainer}} - allowed_values = [{{#allowableValues}}{{#values}}{{#isString}}"{{/isString}}{{{this}}}{{#isString}}"{{/isString}}{{^-last}}, {{/-last}}{{/values}}{{/allowableValues}}] + allowed_values = [{{#allowableValues}}{{#values}}{{#isString}}"{{/isString}}{{{this}}}{{#isString}}"{{/isString}}{{^-last}}, {{/-last}}{{/values}}{{/allowableValues}}] # noqa: E501 if {{{name}}} not in allowed_values: raise ValueError( - "Invalid value for `{{{name}}}` ({0}), must be one of {1}" + "Invalid value for `{{{name}}}` ({0}), must be one of {1}" # noqa: E501 .format({{{name}}}, allowed_values) ) {{/isContainer}} @@ -122,31 +134,31 @@ class {{classname}}(object): {{#hasValidation}} {{#maxLength}} if {{name}} is not None and len({{name}}) > {{maxLength}}: - raise ValueError("Invalid value for `{{name}}`, length must be less than or equal to `{{maxLength}}`") + raise ValueError("Invalid value for `{{name}}`, length must be less than or equal to `{{maxLength}}`") # noqa: E501 {{/maxLength}} {{#minLength}} if {{name}} is not None and len({{name}}) < {{minLength}}: - raise ValueError("Invalid value for `{{name}}`, length must be greater than or equal to `{{minLength}}`") + raise ValueError("Invalid value for `{{name}}`, length must be greater than or equal to `{{minLength}}`") # noqa: E501 {{/minLength}} {{#maximum}} - if {{name}} is not None and {{name}} >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}}: - raise ValueError("Invalid value for `{{name}}`, must be a value less than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}`{{maximum}}`") + if {{name}} is not None and {{name}} >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}}: # noqa: E501 + raise ValueError("Invalid value for `{{name}}`, must be a value less than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}`{{maximum}}`") # noqa: E501 {{/maximum}} {{#minimum}} - if {{name}} is not None and {{name}} <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}}: - raise ValueError("Invalid value for `{{name}}`, must be a value greater than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}`{{minimum}}`") + if {{name}} is not None and {{name}} <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}}: # noqa: E501 + raise ValueError("Invalid value for `{{name}}`, must be a value greater than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}`{{minimum}}`") # noqa: E501 {{/minimum}} {{#pattern}} - if {{name}} is not None and not re.search('{{{vendorExtensions.x-regex}}}', {{name}}{{#vendorExtensions.x-modifiers}}{{#-first}}, flags={{/-first}}re.{{.}}{{^-last}} | {{/-last}}{{/vendorExtensions.x-modifiers}}): - raise ValueError("Invalid value for `{{name}}`, must be a follow pattern or equal to `{{{pattern}}}`") + if {{name}} is not None and not re.search('{{{vendorExtensions.x-regex}}}', {{name}}{{#vendorExtensions.x-modifiers}}{{#-first}}, flags={{/-first}}re.{{.}}{{^-last}} | {{/-last}}{{/vendorExtensions.x-modifiers}}): # noqa: E501 + raise ValueError("Invalid value for `{{name}}`, must be a follow pattern or equal to `{{{pattern}}}`") # noqa: E501 {{/pattern}} {{#maxItems}} if {{name}} is not None and len({{name}}) > {{maxItems}}: - raise ValueError("Invalid value for `{{name}}`, number of items must be less than or equal to `{{maxItems}}`") + raise ValueError("Invalid value for `{{name}}`, number of items must be less than or equal to `{{maxItems}}`") # noqa: E501 {{/maxItems}} {{#minItems}} if {{name}} is not None and len({{name}}) < {{minItems}}: - raise ValueError("Invalid value for `{{name}}`, number of items must be greater than or equal to `{{minItems}}`") + raise ValueError("Invalid value for `{{name}}`, number of items must be greater than or equal to `{{minItems}}`") # noqa: E501 {{/minItems}} {{/hasValidation}} {{/isEnum}} @@ -154,13 +166,18 @@ class {{classname}}(object): self._{{name}} = {{name}} {{/vars}} +{{#discriminator}} + def get_real_child_model(self, data): + """Returns the real base class specified by the discriminator""" + discriminator_value = data[self.discriminator].lower() + return self.discriminator_value_class_map.get(discriminator_value) + +{{/discriminator}} def to_dict(self): - """ - Returns the model properties as a dict - """ + """Returns the model properties as a dict""" result = {} - for attr, _ in iteritems(self.swagger_types): + for attr, _ in six.iteritems(self.swagger_types): value = getattr(self, attr) if isinstance(value, list): result[attr] = list(map( @@ -181,30 +198,22 @@ class {{classname}}(object): return result def to_str(self): - """ - Returns the string representation of the model - """ - return pformat(self.to_dict()) + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) def __repr__(self): - """ - For `print` and `pprint` - """ + """For `print` and `pprint`""" return self.to_str() def __eq__(self, other): - """ - Returns true if both objects are equal - """ + """Returns true if both objects are equal""" if not isinstance(other, {{classname}}): return False return self.__dict__ == other.__dict__ def __ne__(self, other): - """ - Returns true if both objects are not equal - """ + """Returns true if both objects are not equal""" return not self == other {{/model}} {{/models}} diff --git a/modules/swagger-codegen/src/main/resources/python/model_test.mustache b/modules/swagger-codegen/src/main/resources/python/model_test.mustache index 5f1b116b776..765e4f9c2c0 100644 --- a/modules/swagger-codegen/src/main/resources/python/model_test.mustache +++ b/modules/swagger-codegen/src/main/resources/python/model_test.mustache @@ -4,19 +4,17 @@ from __future__ import absolute_import -import os -import sys import unittest {{#models}} {{#model}} import {{packageName}} +from {{modelPackage}}.{{classFilename}} import {{classname}} # noqa: E501 from {{packageName}}.rest import ApiException -from {{packageName}}.models.{{classFilename}} import {{classname}} class Test{{classname}}(unittest.TestCase): - """ {{classname}} unit test stubs """ + """{{classname}} unit test stubs""" def setUp(self): pass @@ -25,11 +23,9 @@ class Test{{classname}}(unittest.TestCase): pass def test{{classname}}(self): - """ - Test {{classname}} - """ + """Test {{classname}}""" # FIXME: construct object with mandatory attributes with example values - #model = {{packageName}}.models.{{classFilename}}.{{classname}}() + # model = {{packageName}}.models.{{classFilename}}.{{classname}}() # noqa: E501 pass {{/model}} diff --git a/modules/swagger-codegen/src/main/resources/python/partial_header.mustache b/modules/swagger-codegen/src/main/resources/python/partial_header.mustache index b0c8ebed3ca..513ad04cdbd 100644 --- a/modules/swagger-codegen/src/main/resources/python/partial_header.mustache +++ b/modules/swagger-codegen/src/main/resources/python/partial_header.mustache @@ -4,7 +4,7 @@ {{/appName}} {{#appDescription}} - {{{appDescription}}} + {{{appDescription}}} # noqa: E501 {{/appDescription}} {{#version}}OpenAPI spec version: {{{version}}}{{/version}} diff --git a/modules/swagger-codegen/src/main/resources/python/rest.mustache b/modules/swagger-codegen/src/main/resources/python/rest.mustache index b801938dc52..ff9b019dc58 100644 --- a/modules/swagger-codegen/src/main/resources/python/rest.mustache +++ b/modules/swagger-codegen/src/main/resources/python/rest.mustache @@ -6,17 +6,15 @@ from __future__ import absolute_import import io import json -import ssl -import certifi import logging import re +import ssl +import certifi # python 2 and python 3 compatibility library -from six import PY3 +import six from six.moves.urllib.parse import urlencode -from .configuration import Configuration - try: import urllib3 except ImportError: @@ -35,60 +33,57 @@ class RESTResponse(io.IOBase): self.data = resp.data def getheaders(self): - """ - Returns a dictionary of the response headers. - """ + """Returns a dictionary of the response headers.""" return self.urllib3_response.getheaders() def getheader(self, name, default=None): - """ - Returns a given response header. - """ + """Returns a given response header.""" return self.urllib3_response.getheader(name, default) class RESTClientObject(object): - def __init__(self, pools_size=4, maxsize=4): + def __init__(self, configuration, pools_size=4, maxsize=None): # urllib3.PoolManager will pass all kw parameters to connectionpool - # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75 - # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680 - # maxsize is the number of requests to host that are allowed in parallel - # ca_certs vs cert_file vs key_file - # http://stackoverflow.com/a/23957365/2985775 + # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75 # noqa: E501 + # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680 # noqa: E501 + # maxsize is the number of requests to host that are allowed in parallel # noqa: E501 + # Custom SSL certificates and client certificates: http://urllib3.readthedocs.io/en/latest/advanced-usage.html # noqa: E501 # cert_reqs - if Configuration().verify_ssl: + if configuration.verify_ssl: cert_reqs = ssl.CERT_REQUIRED else: cert_reqs = ssl.CERT_NONE # ca_certs - if Configuration().ssl_ca_cert: - ca_certs = Configuration().ssl_ca_cert + if configuration.ssl_ca_cert: + ca_certs = configuration.ssl_ca_cert else: # if not set certificate file, use Mozilla's root certificates. ca_certs = certifi.where() - # cert_file - cert_file = Configuration().cert_file + addition_pool_args = {} + if configuration.assert_hostname is not None: + addition_pool_args['assert_hostname'] = configuration.assert_hostname # noqa: E501 - # key file - key_file = Configuration().key_file - - # proxy - proxy = Configuration().proxy + if maxsize is None: + if configuration.connection_pool_maxsize is not None: + maxsize = configuration.connection_pool_maxsize + else: + maxsize = 4 # https pool manager - if proxy: + if configuration.proxy: self.pool_manager = urllib3.ProxyManager( num_pools=pools_size, maxsize=maxsize, cert_reqs=cert_reqs, ca_certs=ca_certs, - cert_file=cert_file, - key_file=key_file, - proxy_url=proxy + cert_file=configuration.cert_file, + key_file=configuration.key_file, + proxy_url=configuration.proxy, + **addition_pool_args ) else: self.pool_manager = urllib3.PoolManager( @@ -96,14 +91,16 @@ class RESTClientObject(object): maxsize=maxsize, cert_reqs=cert_reqs, ca_certs=ca_certs, - cert_file=cert_file, - key_file=key_file + cert_file=configuration.cert_file, + key_file=configuration.key_file, + **addition_pool_args ) - def request(self, method, url, query_params=None, headers=None, - body=None, post_params=None, _preload_content=True, _request_timeout=None): - """ + body=None, post_params=None, _preload_content=True, + _request_timeout=None): + """Perform requests. + :param method: http request method :param url: http request url :param query_params: query parameters in the url @@ -112,13 +109,17 @@ class RESTClientObject(object): :param post_params: request post parameters, `application/x-www-form-urlencoded` and `multipart/form-data` - :param _preload_content: if False, the urllib3.HTTPResponse object will be returned without - reading/decoding response data. Default is True. - :param _request_timeout: timeout setting for this request. If one number provided, it will be total request - timeout. It can also be a pair (tuple) of (connection, read) timeouts. + :param _preload_content: if False, the urllib3.HTTPResponse object will + be returned without reading/decoding response + data. Default is True. + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. """ method = method.upper() - assert method in ['GET', 'HEAD', 'DELETE', 'POST', 'PUT', 'PATCH', 'OPTIONS'] + assert method in ['GET', 'HEAD', 'DELETE', 'POST', 'PUT', + 'PATCH', 'OPTIONS'] if post_params and body: raise ValueError( @@ -130,10 +131,12 @@ class RESTClientObject(object): timeout = None if _request_timeout: - if isinstance(_request_timeout, (int, ) if PY3 else (int, long)): + if isinstance(_request_timeout, (int, ) if six.PY3 else (int, long)): # noqa: E501,F821 timeout = urllib3.Timeout(total=_request_timeout) - elif isinstance(_request_timeout, tuple) and len(_request_timeout) == 2: - timeout = urllib3.Timeout(connect=_request_timeout[0], read=_request_timeout[1]) + elif (isinstance(_request_timeout, tuple) and + len(_request_timeout) == 2): + timeout = urllib3.Timeout( + connect=_request_timeout[0], read=_request_timeout[1]) if 'Content-Type' not in headers: headers['Content-Type'] = 'application/json' @@ -145,44 +148,50 @@ class RESTClientObject(object): url += '?' + urlencode(query_params) if re.search('json', headers['Content-Type'], re.IGNORECASE): request_body = None - if body: + if body is not None: request_body = json.dumps(body) - r = self.pool_manager.request(method, url, - body=request_body, - preload_content=_preload_content, - timeout=timeout, - headers=headers) - elif headers['Content-Type'] == 'application/x-www-form-urlencoded': - r = self.pool_manager.request(method, url, - fields=post_params, - encode_multipart=False, - preload_content=_preload_content, - timeout=timeout, - headers=headers) + r = self.pool_manager.request( + method, url, + body=request_body, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + elif headers['Content-Type'] == 'application/x-www-form-urlencoded': # noqa: E501 + r = self.pool_manager.request( + method, url, + fields=post_params, + encode_multipart=False, + preload_content=_preload_content, + timeout=timeout, + headers=headers) elif headers['Content-Type'] == 'multipart/form-data': - # must del headers['Content-Type'], or the correct Content-Type - # which generated by urllib3 will be overwritten. + # must del headers['Content-Type'], or the correct + # Content-Type which generated by urllib3 will be + # overwritten. del headers['Content-Type'] - r = self.pool_manager.request(method, url, - fields=post_params, - encode_multipart=True, - preload_content=_preload_content, - timeout=timeout, - headers=headers) + r = self.pool_manager.request( + method, url, + fields=post_params, + encode_multipart=True, + preload_content=_preload_content, + timeout=timeout, + headers=headers) # Pass a `string` parameter directly in the body to support - # other content types than Json when `body` argument is provided - # in serialized form + # other content types than Json when `body` argument is + # provided in serialized form elif isinstance(body, str): request_body = body - r = self.pool_manager.request(method, url, - body=request_body, - preload_content=_preload_content, - timeout=timeout, - headers=headers) + r = self.pool_manager.request( + method, url, + body=request_body, + preload_content=_preload_content, + timeout=timeout, + headers=headers) else: # Cannot generate the request from given parameters - msg = """Cannot prepare a request message for provided arguments. - Please check that your arguments match declared content type.""" + msg = """Cannot prepare a request message for provided + arguments. Please check that your arguments match + declared content type.""" raise ApiException(status=0, reason=msg) # For `GET`, `HEAD` else: @@ -200,7 +209,7 @@ class RESTClientObject(object): # In the python 3, the response.data is bytes. # we need to decode it to string. - if PY3: + if six.PY3: r.data = r.data.decode('utf8') # log response body @@ -211,22 +220,24 @@ class RESTClientObject(object): return r - def GET(self, url, headers=None, query_params=None, _preload_content=True, _request_timeout=None): + def GET(self, url, headers=None, query_params=None, _preload_content=True, + _request_timeout=None): return self.request("GET", url, headers=headers, _preload_content=_preload_content, _request_timeout=_request_timeout, query_params=query_params) - def HEAD(self, url, headers=None, query_params=None, _preload_content=True, _request_timeout=None): + def HEAD(self, url, headers=None, query_params=None, _preload_content=True, + _request_timeout=None): return self.request("HEAD", url, headers=headers, _preload_content=_preload_content, _request_timeout=_request_timeout, query_params=query_params) - def OPTIONS(self, url, headers=None, query_params=None, post_params=None, body=None, _preload_content=True, - _request_timeout=None): + def OPTIONS(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): return self.request("OPTIONS", url, headers=headers, query_params=query_params, @@ -235,7 +246,8 @@ class RESTClientObject(object): _request_timeout=_request_timeout, body=body) - def DELETE(self, url, headers=None, query_params=None, body=None, _preload_content=True, _request_timeout=None): + def DELETE(self, url, headers=None, query_params=None, body=None, + _preload_content=True, _request_timeout=None): return self.request("DELETE", url, headers=headers, query_params=query_params, @@ -243,8 +255,8 @@ class RESTClientObject(object): _request_timeout=_request_timeout, body=body) - def POST(self, url, headers=None, query_params=None, post_params=None, body=None, _preload_content=True, - _request_timeout=None): + def POST(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): return self.request("POST", url, headers=headers, query_params=query_params, @@ -253,8 +265,8 @@ class RESTClientObject(object): _request_timeout=_request_timeout, body=body) - def PUT(self, url, headers=None, query_params=None, post_params=None, body=None, _preload_content=True, - _request_timeout=None): + def PUT(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): return self.request("PUT", url, headers=headers, query_params=query_params, @@ -263,8 +275,8 @@ class RESTClientObject(object): _request_timeout=_request_timeout, body=body) - def PATCH(self, url, headers=None, query_params=None, post_params=None, body=None, _preload_content=True, - _request_timeout=None): + def PATCH(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): return self.request("PATCH", url, headers=headers, query_params=query_params, @@ -289,13 +301,12 @@ class ApiException(Exception): self.headers = None def __str__(self): - """ - Custom error messages for exception - """ + """Custom error messages for exception""" error_message = "({0})\n"\ "Reason: {1}\n".format(self.status, self.reason) if self.headers: - error_message += "HTTP response headers: {0}\n".format(self.headers) + error_message += "HTTP response headers: {0}\n".format( + self.headers) if self.body: error_message += "HTTP response body: {0}\n".format(self.body) diff --git a/modules/swagger-codegen/src/main/resources/python/setup.mustache b/modules/swagger-codegen/src/main/resources/python/setup.mustache index 6fc7f198d15..e7107e9be94 100644 --- a/modules/swagger-codegen/src/main/resources/python/setup.mustache +++ b/modules/swagger-codegen/src/main/resources/python/setup.mustache @@ -2,8 +2,7 @@ {{>partial_header}} -import sys -from setuptools import setup, find_packages +from setuptools import setup, find_packages # noqa: H301 NAME = "{{{projectName}}}" VERSION = "{{packageVersion}}" @@ -18,6 +17,12 @@ VERSION = "{{packageVersion}}" # http://pypi.python.org/pypi/setuptools REQUIRES = ["urllib3 >= 1.15", "six >= 1.10", "certifi", "python-dateutil"] +{{#asyncio}} +REQUIRES.append("aiohttp") +{{/asyncio}} +{{#tornado}} +REQUIRES.append("tornado") +{{/tornado}} setup( name=NAME, @@ -30,7 +35,7 @@ setup( packages=find_packages(), include_package_data=True, long_description="""\ - {{appDescription}} + {{appDescription}} # noqa: E501 """ ) {{/hasMore}} diff --git a/modules/swagger-codegen/src/main/resources/python/tornado/rest.mustache b/modules/swagger-codegen/src/main/resources/python/tornado/rest.mustache new file mode 100644 index 00000000000..90593fc98a9 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/python/tornado/rest.mustache @@ -0,0 +1,250 @@ +# coding: utf-8 + +{{>partial_header}} + +import io +import json +import logging +import re + +# python 2 and python 3 compatibility library +import six +from six.moves.urllib.parse import urlencode +import tornado +import tornado.gen +from tornado import httpclient +from urllib3.filepost import encode_multipart_formdata + +logger = logging.getLogger(__name__) + + +class RESTResponse(io.IOBase): + + def __init__(self, resp, data): + self.tornado_response = resp + self.status = resp.code + self.reason = resp.reason + self.data = data + + def getheaders(self): + """Returns a CIMultiDictProxy of the response headers.""" + return self.tornado_response.headers + + def getheader(self, name, default=None): + """Returns a given response header.""" + return self.tornado_response.headers.get(name, default) + + +class RESTClientObject(object): + + def __init__(self, configuration, pools_size=4, maxsize=4): + # maxsize is number of requests to host that are allowed in parallel + + self.ca_certs = configuration.ssl_ca_cert + self.client_key = configuration.key_file + self.client_cert = configuration.cert_file + + self.proxy_port = self.proxy_host = None + + # https pool manager + if configuration.proxy: + self.proxy_port = 80 + self.proxy_host = configuration.proxy + + self.pool_manager = httpclient.AsyncHTTPClient() + + @tornado.gen.coroutine + def request(self, method, url, query_params=None, headers=None, body=None, + post_params=None, _preload_content=True, + _request_timeout=None): + """Execute Request + + :param method: http request method + :param url: http request url + :param query_params: query parameters in the url + :param headers: http request headers + :param body: request json body, for `application/json` + :param post_params: request post parameters, + `application/x-www-form-urlencoded` + and `multipart/form-data` + :param _preload_content: this is a non-applicable field for + the AiohttpClient. + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + """ + method = method.upper() + assert method in ['GET', 'HEAD', 'DELETE', 'POST', 'PUT', + 'PATCH', 'OPTIONS'] + + if post_params and body: + raise ValueError( + "body parameter cannot be used with post_params parameter." + ) + + request = httpclient.HTTPRequest(url) + request.ca_certs = self.ca_certs + request.client_key = self.client_key + request.client_cert = self.client_cert + request.proxy_host = self.proxy_host + request.proxy_port = self.proxy_port + request.method = method + if headers: + request.headers = headers + if 'Content-Type' not in headers: + request.headers['Content-Type'] = 'application/json' + request.request_timeout = _request_timeout or 5 * 60 + + post_params = post_params or {} + + if query_params: + request.url += '?' + urlencode(query_params) + + # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE` + if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']: + if re.search('json', headers['Content-Type'], re.IGNORECASE): + if body: + body = json.dumps(body) + request.body = body + elif headers['Content-Type'] == 'application/x-www-form-urlencoded': # noqa: E501 + request.body = urlencode(post_params) + elif headers['Content-Type'] == 'multipart/form-data': + multipart = encode_multipart_formdata(post_params) + request.body, headers['Content-Type'] = multipart + # Pass a `bytes` parameter directly in the body to support + # other content types than Json when `body` argument is provided + # in serialized form + elif isinstance(body, bytes): + request.body = body + else: + # Cannot generate the request from given parameters + msg = """Cannot prepare a request message for provided + arguments. Please check that your arguments match + declared content type.""" + raise ApiException(status=0, reason=msg) + + r = yield self.pool_manager.fetch(request, raise_error=False) + + if _preload_content: + + r = RESTResponse(r, r.body) + # In the python 3, the response.data is bytes. + # we need to decode it to string. + if six.PY3: + r.data = r.data.decode('utf8') + + # log response body + logger.debug("response body: %s", r.data) + + if not 200 <= r.status <= 299: + raise ApiException(http_resp=r) + + raise tornado.gen.Return(r) + + @tornado.gen.coroutine + def GET(self, url, headers=None, query_params=None, _preload_content=True, + _request_timeout=None): + result = yield self.request("GET", url, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + query_params=query_params) + raise tornado.gen.Return(result) + + @tornado.gen.coroutine + def HEAD(self, url, headers=None, query_params=None, _preload_content=True, + _request_timeout=None): + result = yield self.request("HEAD", url, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + query_params=query_params) + raise tornado.gen.Return(result) + + @tornado.gen.coroutine + def OPTIONS(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + result = yield self.request("OPTIONS", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + raise tornado.gen.Return(result) + + @tornado.gen.coroutine + def DELETE(self, url, headers=None, query_params=None, body=None, + _preload_content=True, _request_timeout=None): + result = yield self.request("DELETE", url, + headers=headers, + query_params=query_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + raise tornado.gen.Return(result) + + @tornado.gen.coroutine + def POST(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + result = yield self.request("POST", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + raise tornado.gen.Return(result) + + @tornado.gen.coroutine + def PUT(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + result = yield self.request("PUT", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + raise tornado.gen.Return(result) + + @tornado.gen.coroutine + def PATCH(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + result = yield self.request("PATCH", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + raise tornado.gen.Return(result) + + +class ApiException(Exception): + + def __init__(self, status=None, reason=None, http_resp=None): + if http_resp: + self.status = http_resp.status + self.reason = http_resp.reason + self.body = http_resp.data + self.headers = http_resp.getheaders() + else: + self.status = status + self.reason = reason + self.body = None + self.headers = None + + def __str__(self): + """Custom error messages for exception""" + error_message = "({0})\nReason: {1}\n".format( + self.status, self.reason) + if self.headers: + error_message += "HTTP response headers: {0}\n".format( + self.headers) + + if self.body: + error_message += "HTTP response body: {0}\n".format(self.body) + + return error_message diff --git a/modules/swagger-codegen/src/main/resources/qt5cpp/HttpRequest.cpp.mustache b/modules/swagger-codegen/src/main/resources/qt5cpp/HttpRequest.cpp.mustache index 998defc4828..8f042bcf0c8 100644 --- a/modules/swagger-codegen/src/main/resources/qt5cpp/HttpRequest.cpp.mustache +++ b/modules/swagger-codegen/src/main/resources/qt5cpp/HttpRequest.cpp.mustache @@ -253,6 +253,9 @@ void HttpRequestWorker::execute(HttpRequestInput *input) { // prepare connection QNetworkRequest request = QNetworkRequest(QUrl(input->url_str)); + if (HttpRequestWorker::sslDefaultConfiguration != nullptr) { + request.setSslConfiguration(*HttpRequestWorker::sslDefaultConfiguration); + } request.setRawHeader("User-Agent", "Swagger-Client"); foreach(QString key, input->headers.keys()) { request.setRawHeader(key.toStdString().c_str(), input->headers.value(key).toStdString().c_str()); @@ -300,17 +303,15 @@ void HttpRequestWorker::execute(HttpRequestInput *input) { void HttpRequestWorker::on_manager_finished(QNetworkReply *reply) { error_type = reply->error(); - if (error_type == QNetworkReply::NoError) { - response = reply->readAll(); - } - else { - error_str = reply->errorString(); - } + response = reply->readAll(); + error_str = reply->errorString(); reply->deleteLater(); emit on_execution_finished(this); } +QSslConfiguration* HttpRequestWorker::sslDefaultConfiguration; + {{#cppNamespaceDeclarations}} } diff --git a/modules/swagger-codegen/src/main/resources/qt5cpp/HttpRequest.h.mustache b/modules/swagger-codegen/src/main/resources/qt5cpp/HttpRequest.h.mustache index 71beb69d41f..4351b48a43c 100644 --- a/modules/swagger-codegen/src/main/resources/qt5cpp/HttpRequest.h.mustache +++ b/modules/swagger-codegen/src/main/resources/qt5cpp/HttpRequest.h.mustache @@ -64,6 +64,7 @@ public: QString http_attribute_encode(QString attribute_name, QString input); void execute(HttpRequestInput *input); + static QSslConfiguration* sslDefaultConfiguration; signals: void on_execution_finished(HttpRequestWorker *worker); diff --git a/modules/swagger-codegen/src/main/resources/qt5cpp/api-body.mustache b/modules/swagger-codegen/src/main/resources/qt5cpp/api-body.mustache index f454a58fb29..a451eaa8275 100644 --- a/modules/swagger-codegen/src/main/resources/qt5cpp/api-body.mustache +++ b/modules/swagger-codegen/src/main/resources/qt5cpp/api-body.mustache @@ -186,8 +186,12 @@ void {{/returnType}} worker->deleteLater(); - emit {{nickname}}Signal({{#returnType}}output{{/returnType}}); - emit {{nickname}}SignalE({{#returnType}}output, {{/returnType}}error_type, error_str); + if (worker->error_type == QNetworkReply::NoError) { + emit {{nickname}}Signal({{#returnType}}output{{/returnType}}); + } else { + emit {{nickname}}SignalE({{#returnType}}output, {{/returnType}}error_type, error_str); + emit {{nickname}}SignalEFull(worker, error_type, error_str); + } } {{/operation}} diff --git a/modules/swagger-codegen/src/main/resources/qt5cpp/api-header.mustache b/modules/swagger-codegen/src/main/resources/qt5cpp/api-header.mustache index 66b7a0be0c2..6c1aec1ed2a 100644 --- a/modules/swagger-codegen/src/main/resources/qt5cpp/api-header.mustache +++ b/modules/swagger-codegen/src/main/resources/qt5cpp/api-header.mustache @@ -35,6 +35,8 @@ signals: {{/operation}}{{/operations}} {{#operations}}{{#operation}}void {{nickname}}SignalE({{#returnType}}{{{returnType}}} summary, {{/returnType}}QNetworkReply::NetworkError error_type, QString& error_str); {{/operation}}{{/operations}} + {{#operations}}{{#operation}}void {{nickname}}SignalEFull(HttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); + {{/operation}}{{/operations}} }; {{#cppNamespaceDeclarations}} diff --git a/modules/swagger-codegen/src/main/resources/qt5cpp/model-body.mustache b/modules/swagger-codegen/src/main/resources/qt5cpp/model-body.mustache index ba23d2e61f3..896c53a75ed 100644 --- a/modules/swagger-codegen/src/main/resources/qt5cpp/model-body.mustache +++ b/modules/swagger-codegen/src/main/resources/qt5cpp/model-body.mustache @@ -35,9 +35,10 @@ void void {{classname}}::cleanup() { - {{#vars}}{{#complexType}} + {{#vars}} + {{#complexType}} if({{name}} != nullptr) { - {{#isContainer}}QList<{{complexType}}*>* arr = {{name}}; + {{#isContainer}}{{#isListContainer}}QList<{{complexType}}*>* arr = {{name}};{{/isListContainer}}{{#isMapContainer}}QMap* arr = {{name}};{{/isMapContainer}} foreach({{complexType}}* o, *arr) { delete o; } @@ -58,11 +59,33 @@ void void {{classname}}::fromJsonObject(QJsonObject &pJson) { {{#vars}} - {{^isContainer}}::{{cppNamespace}}::setValue(&{{name}}, pJson["{{baseName}}"], "{{baseType}}", "{{complexType}}");{{/isContainer}} - {{#isContainer}} - {{#complexType}}::{{cppNamespace}}::setValue(&{{name}}, pJson["{{baseName}}"], "{{baseType}}", "{{complexType}}");{{/complexType}} - {{^complexType}}::{{cppNamespace}}::setValue(&{{name}}, pJson["{{baseName}}"], "{{baseType}}", "{{items.baseType}}");{{/complexType}} + {{^isContainer}} + ::{{cppNamespace}}::setValue(&{{name}}, pJson["{{baseName}}"], "{{baseType}}", "{{complexType}}"); {{/isContainer}} + {{#isListContainer}} + {{#complexType}} + ::{{cppNamespace}}::setValue(&{{name}}, pJson["{{baseName}}"], "{{baseType}}", "{{complexType}}"); + {{/complexType}} + {{^complexType}} + ::{{cppNamespace}}::setValue(&{{name}}, pJson["{{baseName}}"], "{{baseType}}", "{{items.baseType}}"); + {{/complexType}} + {{/isListContainer}} + {{#isMapContainer}} + if( pJson["{{baseName}}"].isObject()){ + auto varmap = pJson["{{baseName}}"].toObject().toVariantMap(); + if(varmap.count() > 0){ + for(auto val : varmap.keys() ){ + { + {{items.baseType}} *{{name}}_item = new {{items.baseType}}(); + auto jsonval = QJsonValue::fromVariant(varmap[val]); + ::{{cppNamespace}}::setValue(&{{name}}_item, jsonval, "{{items.baseType}}", "{{items.baseType}}"); + {{name}}->insert({{name}}->end(), val, {{name}}_item); + } + } + } + } + + {{/isMapContainer}} {{/vars}} } @@ -79,18 +102,52 @@ QString QJsonObject* {{classname}}::asJsonObject() { QJsonObject* obj = new QJsonObject(); - {{#vars}}{{#complexType}}{{^isContainer}}{{#complexType}} - toJsonValue(QString("{{baseName}}"), {{name}}, obj, QString("{{complexType}}"));{{/complexType}}{{^complexType}} + {{#vars}} + {{#complexType}} + {{^isContainer}} + {{#complexType}} + toJsonValue(QString("{{baseName}}"), {{name}}, obj, QString("{{complexType}}")); + {{/complexType}} + {{^complexType}} if({{name}} != nullptr && *{{name}} != nullptr) { obj->insert("{{name}}", QJsonValue(*{{name}})); - }{{/complexType}}{{/isContainer}}{{#isContainer}} + } + {{/complexType}} + {{/isContainer}} + {{#isListContainer}} QJsonArray {{name}}JsonArray; toJsonArray((QList*){{name}}, &{{name}}JsonArray, "{{name}}", "{{complexType}}"); - obj->insert("{{baseName}}", {{name}}JsonArray);{{/isContainer}}{{/complexType}}{{^complexType}}{{^isContainer}} - obj->insert("{{baseName}}", QJsonValue({{name}}));{{/isContainer}}{{#isContainer}} + obj->insert("{{baseName}}", {{name}}JsonArray); + {{/isListContainer}} + {{#isMapContainer}} + QJsonArray {{name}}JsonArray; + for(auto keyval : {{name}}->keys()){ + QJsonObject {{name}}_jobj; + toJsonValue(keyval, ((*{{name}})[keyval]), &{{name}}_jobj, "{{complexType}}"); + {{name}}JsonArray.append({{name}}_jobj); + } + obj->insert("{{baseName}}", {{name}}JsonArray); + {{/isMapContainer}} + {{/complexType}} + {{^complexType}} + {{^isContainer}} + obj->insert("{{baseName}}", QJsonValue({{name}})); + {{/isContainer}} + {{#isListContainer}} QJsonArray {{name}}JsonArray; toJsonArray((QList*){{name}}, &{{name}}JsonArray, "{{name}}", "{{items.baseType}}"); - obj->insert("{{baseName}}", {{name}}JsonArray);{{/isContainer}}{{/complexType}} + obj->insert("{{baseName}}", {{name}}JsonArray); + {{/isListContainer}} + {{#isMapContainer}} + QJsonArray {{name}}JsonArray; + for(auto keyval : {{name}}->keys()){ + QJsonObject {{name}}_jobj; + toJsonValue(keyval, ((*{{name}})[keyval]), &{{name}}_jobj, "{{items.baseType}}"); + {{name}}JsonArray.append(portsobj); + } + obj->insert("{{baseName}}", {{name}}JsonArray); + {{/isMapContainer}} + {{/complexType}} {{/vars}} return obj; diff --git a/modules/swagger-codegen/src/main/resources/qt5cpp/model-header.mustache b/modules/swagger-codegen/src/main/resources/qt5cpp/model-header.mustache index 0a55f3aab24..d2866f60e57 100644 --- a/modules/swagger-codegen/src/main/resources/qt5cpp/model-header.mustache +++ b/modules/swagger-codegen/src/main/resources/qt5cpp/model-header.mustache @@ -16,7 +16,8 @@ #include "SWGObject.h" -{{#models}}{{#model}} +{{#models}} +{{#model}} {{#cppNamespaceDeclarations}} namespace {{this}} { {{/cppNamespaceDeclarations}} diff --git a/modules/swagger-codegen/src/main/resources/qt5cpp/model.mustache b/modules/swagger-codegen/src/main/resources/qt5cpp/model.mustache index 4d3590b8afe..80bf2842537 100644 --- a/modules/swagger-codegen/src/main/resources/qt5cpp/model.mustache +++ b/modules/swagger-codegen/src/main/resources/qt5cpp/model.mustache @@ -21,7 +21,8 @@ using namespace Tizen::Web::Json; {{#imports}}{{{import}}} {{/imports}} -{{#models}}{{#model}} +{{#models}} +{{#model}} {{#cppNamespaceDeclarations}} namespace {{this}} { @@ -47,11 +48,12 @@ public: {{#vars}} {{datatype}} {{getter}}(); - void {{setter}}({{datatype}} {{name}}); + void {{setter}}({{{datatype}}} {{name}}); {{/vars}} private: - {{#vars}}{{datatype}} {{name}}; + {{#vars}} + {{{datatype}}} {{name}}; {{/vars}} }; diff --git a/modules/swagger-codegen/src/main/resources/r/.travis.yml b/modules/swagger-codegen/src/main/resources/r/.travis.yml new file mode 100644 index 00000000000..3f05544a724 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/r/.travis.yml @@ -0,0 +1,3 @@ +# ref: https://docs.travis-ci.com/user/languages/r/ +language: r +cache: packages diff --git a/modules/swagger-codegen/src/main/resources/r/README.mustache b/modules/swagger-codegen/src/main/resources/r/README.mustache new file mode 100644 index 00000000000..a79bef44805 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/r/README.mustache @@ -0,0 +1,41 @@ +# R API client for {{packageName}} + +{{#appDescription}} +{{{appDescription}}} +{{/appDescription}} + +## Overview +This API client was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the [OpenAPI/Swagger spec](https://github.com/swagger-api/swagger-spec) from a remote server, you can easily generate an API client. + +- API version: {{appVersion}} +- Package version: {{packageVersion}} +{{^hideGenerationTimestamp}} +- Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} +- Build package: {{generatorClass}} +{{#infoUrl}} +For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + +## Installation +You'll need the `devtools` package in order to build the API. +Make sure you have a proper CRAN repository from which you can download packages. + +### Prerequisites +Install the `devtools` package with the following command. +```R +if(!require(devtools)) { install.packages("devtools") } +``` + +### Installation of the API package +Make sure you set the working directory to where the API code is located. +Then execute +```R +library(devtools) +install(".") +``` + +## Author + +{{#apiInfo}}{{#apis}}{{^hasMore}}{{infoEmail}} +{{/hasMore}}{{/apis}}{{/apiInfo}} diff --git a/modules/swagger-codegen/src/main/resources/r/Rbuildignore.mustache b/modules/swagger-codegen/src/main/resources/r/Rbuildignore.mustache new file mode 100644 index 00000000000..91114bf2f2b --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/r/Rbuildignore.mustache @@ -0,0 +1,2 @@ +^.*\.Rproj$ +^\.Rproj\.user$ diff --git a/modules/swagger-codegen/src/main/resources/r/api.mustache b/modules/swagger-codegen/src/main/resources/r/api.mustache new file mode 100644 index 00000000000..c47ef969681 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/r/api.mustache @@ -0,0 +1,115 @@ +{{>partial_header}} +{{#operations}} +#' @title {{baseName}} operations +#' @description {{importPath}} +#' +#' @field path Stores url path of the request. +#' @field apiClient Handles the client-server communication. +#' @field userAgent Set the user agent of the request. +#' +#' @importFrom R6 R6Class +#' +#' @section Methods: +#' \describe{ +{{#operation}} +#' +#' {{operationId}} {{summary}} +#' +{{/operation}} +#' } +#' +#' @export +{{classname}} <- R6::R6Class( + '{{classname}}', + public = list( + userAgent = "{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{{packageVersion}}}/r{{/httpUserAgent}}", + apiClient = NULL, + initialize = function(apiClient){ + if (!missing(apiClient)) { + self$apiClient <- apiClient + } + else { + self$apiClient <- ApiClient$new() + } + }, + {{#operation}} + {{operationId}} = function({{#allParams}}{{paramName}}, {{/allParams}}...){ + args <- list(...) + queryParams <- list() + headerParams <- character() + + {{#hasHeaderParams}} + {{#headerParams}} + if (!missing(`{{paramName}}`)) { + headerParams['{{baseName}}'] <- `{{paramName}}` + } + + {{/headerParams}} + {{/hasHeaderParams}} + {{#hasQueryParams}} + {{#queryParams}} + if (!missing(`{{paramName}}`)) { + queryParams['{{baseName}}'] <- {{paramName}} + } + + {{/queryParams}} + {{/hasQueryParams}} + {{#hasFormParams}} + body <- list( + {{#formParams}} + {{^isFile}} + "{{baseName}}" = {{paramName}}{{#hasMore}},{{/hasMore}} + {{/isFile}} + {{#isFile}} + "{{baseName}}" = httr::upload_file({{paramName}}){{#hasMore}},{{/hasMore}} + {{/isFile}} + {{/formParams}} + ) + + {{/hasFormParams}} + {{#hasBodyParam}} + {{#bodyParams}} + if (!missing(`{{paramName}}`)) { + body <- `{{paramName}}`$toJSONString() + } else { + body <- NULL + } + + {{/bodyParams}} + {{/hasBodyParam}} + urlPath <- "{{path}}" + {{#hasPathParams}} + {{#pathParams}} + if (!missing(`{{paramName}}`)) { + urlPath <- gsub(paste0("\\{", "{{baseName}}", "\\}"), `{{paramName}}`, urlPath) + } + + {{/pathParams}} + {{/hasPathParams}} + resp <- self$apiClient$callApi(url = paste0(self$apiClient$basePath, urlPath), + method = "{{httpMethod}}", + queryParams = queryParams, + headerParams = headerParams, + body = body, + ...) + + if (httr::status_code(resp) >= 200 && httr::status_code(resp) <= 299) { + {{#returnType}} + returnObject <- {{returnType}}$new() + result <- returnObject$fromJSON(httr::content(resp, "text", encoding = "UTF-8")) + Response$new(returnObject, resp) + {{/returnType}} + {{^returnType}} + # void response, no need to return anything + {{/returnType}} + } else if (httr::status_code(resp) >= 400 && httr::status_code(resp) <= 499) { + Response$new("API client error", resp) + } else if (httr::status_code(resp) >= 500 && httr::status_code(resp) <= 599) { + Response$new("API server error", resp) + } + + }{{#hasMore}},{{/hasMore}} + {{/operation}} + ) +) +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/r/api_client.mustache b/modules/swagger-codegen/src/main/resources/r/api_client.mustache new file mode 100644 index 00000000000..49e22e1c5c0 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/r/api_client.mustache @@ -0,0 +1,64 @@ +{{>partial_header}} + +#' ApiClient Class +#' +#' Generic API client for Swagger client library builds. +#' Swagger generic API client. This client handles the client- +#' server communication, and is invariant across implementations. Specifics of +#' the methods and models for each application are generated from the Swagger +#' templates. +#' +#' NOTE: This class is auto generated by the swagger code generator program. +#' Ref: https://github.com/swagger-api/swagger-codegen +#' Do not edit the class manually. +#' +#' @export +ApiClient <- R6::R6Class( + 'ApiClient', + public = list( + basePath = "{{{basePath}}}", + configuration = NULL, + userAgent = NULL, + defaultHeaders = NULL, + initialize = function(basePath, configuration, defaultHeaders){ + if (!missing(basePath)) { + self$basePath <- basePath + } + + if (!missing(configuration)) { + self$configuration <- configuration + } + + if (!missing(defaultHeaders)) { + self$defaultHeaders <- defaultHeaders + } + + self$`userAgent` <- '{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{{packageVersion}}}/r{{/httpUserAgent}}' + }, + callApi = function(url, method, queryParams, headerParams, body, ...){ + headers <- httr::add_headers(headerParams) + + if (method == "GET") { + httr::GET(url, queryParams, headers, ...) + } + else if (method == "POST") { + httr::POST(url, queryParams, headers, body = body, ...) + } + else if (method == "PUT") { + httr::PUT(url, queryParams, headers, body = body, ...) + } + else if (method == "PATCH") { + httr::PATCH(url, queryParams, headers, body = body, ...) + } + else if (method == "HEAD") { + httr::HEAD(url, queryParams, headers, ...) + } + else if (method == "DELETE") { + httr::DELETE(url, queryParams, headers, ...) + } + else { + stop("http method must be `GET`, `HEAD`, `OPTIONS`, `POST`, `PATCH`, `PUT` or `DELETE`.") + } + } + ) +) \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/r/api_doc.mustache b/modules/swagger-codegen/src/main/resources/r/api_doc.mustache new file mode 100644 index 00000000000..70d0b96ce86 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/r/api_doc.mustache @@ -0,0 +1,50 @@ +# {{invokerPackage}}\{{classname}}{{#description}} +{{description}}{{/description}} + +All URIs are relative to *{{basePath}}* + +Method | HTTP request | Description +------------- | ------------- | ------------- +{{#operations}}{{#operation}}[**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} +{{/operation}}{{/operations}} + +{{#operations}} +{{#operation}} +# **{{{operationId}}}** +> {{#returnType}}{{{returnType}}} {{/returnType}}{{{operationId}}}({{#authMethods}}ctx, {{/authMethods}}{{#allParams}}{{#required}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/required}}{{/allParams}}{{#hasOptionalParams}}optional{{/hasOptionalParams}}) +{{{summary}}}{{#notes}} + +{{{notes}}}{{/notes}} + +### Required Parameters +{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}} +Name | Type | Description | Notes +------------- | ------------- | ------------- | -------------{{#authMethods}} + **ctx** | **context.Context** | context containing the authentication | nil if no authentication{{/authMethods}}{{/-last}}{{/allParams}}{{#allParams}}{{#required}} + **{{paramName}}** | {{#isFile}}**{{dataType}}**{{/isFile}}{{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}[**{{dataType}}**]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} | {{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}}{{/required}}{{/allParams}}{{#hasOptionalParams}} + **optional** | **map[string]interface{}** | optional parameters | nil if no parameters + +### Optional Parameters +Optional parameters are passed through a map[string]interface{}. +{{#allParams}}{{#-last}} +Name | Type | Description | Notes +------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}}{{#allParams}} + **{{paramName}}** | {{#isFile}}**{{dataType}}**{{/isFile}}{{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}[**{{dataType}}**]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} | {{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}}{{/allParams}}{{/hasOptionalParams}} + +### Return type + +{{#returnType}}{{#returnTypeIsPrimitive}}**{{{returnType}}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{{returnType}}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}} (empty response body){{/returnType}} + +### Authorization + +{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{{name}}}](../README.md#{{{name}}}){{^-last}}, {{/-last}}{{/authMethods}} + +### HTTP request headers + + - **Content-Type**: {{#consumes}}{{{mediaType}}}{{#hasMore}}, {{/hasMore}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} + - **Accept**: {{#produces}}{{{mediaType}}}{{#hasMore}}, {{/hasMore}}{{/produces}}{{^produces}}Not defined{{/produces}} + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +{{/operation}} +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/r/description.mustache b/modules/swagger-codegen/src/main/resources/r/description.mustache new file mode 100644 index 00000000000..d79645cdafe --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/r/description.mustache @@ -0,0 +1,12 @@ +Package: {{{packageName}}} +Title: R Package Client for {{{appName}}} +Version: {{packageVersion}} +Authors@R: person("Swagger Codegen community", email = "apiteam@swagger.io", role = c("aut", "cre")) +Description: {{{appDescription}}}{{^appDescription}}R Package Client for {{{appName}}}{{/appDescription}} +Depends: R (>= 3.3.3) +Encoding: UTF-8 +License: Unlicense +LazyData: true +Suggests: testthat +Imports: jsonlite, httr, R6 +RoxygenNote: 6.0.1.9000 diff --git a/modules/swagger-codegen/src/main/resources/r/element.mustache b/modules/swagger-codegen/src/main/resources/r/element.mustache new file mode 100644 index 00000000000..c8b6632f72c --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/r/element.mustache @@ -0,0 +1,24 @@ +#' Element Class +#' +#' Element Class +#' @export +Element <- R6::R6Class( + 'Element', + public = list( + id = NULL, + name = NULL, + initialize = function(id,name){ + if (!missing(id)) { + stopifnot(is.numeric(id), length(id) == 1) + self$id <- id + } + if (!missing(name)) { + stopifnot(is.character(name), length(name) == 1) + self$name <- name + } + }, + toJSON = function() { + sprintf('{"id":%d,"name":"%s"}', self$id, self$name) + } + ) +) \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/r/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/r/git_push.sh.mustache new file mode 100755 index 00000000000..a2d75234837 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/r/git_push.sh.mustache @@ -0,0 +1,52 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 swagger-petstore-perl "minor update" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 + +if [ "$git_user_id" = "" ]; then + git_user_id="{{{gitUserId}}}" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="{{{gitRepoId}}}" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="{{{releaseNote}}}" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=`git remote` +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://github.com/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' + diff --git a/modules/swagger-codegen/src/main/resources/r/gitignore.mustache b/modules/swagger-codegen/src/main/resources/r/gitignore.mustache new file mode 100644 index 00000000000..5d21150e0ca --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/r/gitignore.mustache @@ -0,0 +1,35 @@ +# ref: https://github.com/github/gitignore/blob/master/R.gitignore + +# History files +.Rhistory +.Rapp.history + +# Session Data files +.RData + +# Example code in package build process +*-Ex.R + +# Output files from R CMD build +/*.tar.gz + +# Output files from R CMD check +/*.Rcheck/ + +# RStudio files +.Rproj.user/ + +# produced vignettes +vignettes/*.html +vignettes/*.pdf + +# OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3 +.httr-oauth + +# knitr and R markdown default cache directories +/*_cache/ +/cache/ + +# Temporary files created by R markdown +*.utf8.md +*.knit.md diff --git a/modules/swagger-codegen/src/main/resources/r/model.mustache b/modules/swagger-codegen/src/main/resources/r/model.mustache new file mode 100644 index 00000000000..94ae74085e2 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/r/model.mustache @@ -0,0 +1,139 @@ +{{#models}} +{{#model}} +{{>partial_header}} + +#' {{classname}} Class +#' +{{#vars}} +#' @field {{baseName}} {{title}} +{{/vars}} +#' +#' @importFrom R6 R6Class +#' @importFrom jsonlite fromJSON toJSON +#' @export +{{classname}} <- R6::R6Class( + '{{classname}}', + public = list( + {{#vars}} + `{{{baseName}}}` = NULL, + {{/vars}} + initialize = function({{#vars}}`{{baseName}}`{{#hasMore}}, {{/hasMore}}{{/vars}}){ + {{#vars}} + if (!missing(`{{baseName}}`)) { + {{^isListContainer}} + {{#isInteger}} + stopifnot(is.numeric(`{{baseName}}`), length(`{{baseName}}`) == 1) + {{/isInteger}} + {{#isLong}} + stopifnot(is.numeric(`{{baseName}}`), length(`{{baseName}}`) == 1) + {{/isLong}} + {{#isFloat}} + stopifnot(is.numeric(`{{baseName}}`), length(`{{baseName}}`) == 1) + {{/isFloat}} + {{#isDouble}} + stopifnot(is.numeric(`{{baseName}}`), length(`{{baseName}}`) == 1) + {{/isDouble}} + {{#isString}} + stopifnot(is.character(`{{baseName}}`), length(`{{baseName}}`) == 1) + {{/isString}} + {{#isDate}} + stopifnot(is.character(`{{baseName}}`), length(`{{baseName}}`) == 1) + {{/isDate}} + {{#isDateTime}} + stopifnot(is.character(`{{baseName}}`), length(`{{baseName}}`) == 1) + {{/isDateTime}} + {{^isPrimitiveType}} + stopifnot(R6::is.R6(`{{baseName}}`)) + {{/isPrimitiveType}} + {{/isListContainer}} + {{#isListContainer}} + {{#isPrimitiveType}} + stopifnot(is.list(`{{baseName}}`), length(`{{baseName}}`) != 0) + lapply(`{{baseName}}`, function(x) stopifnot(is.character(x))) + {{/isPrimitiveType}} + {{^isPrimitiveType}} + stopifnot(is.list(`{{baseName}}`), length(`{{baseName}}`) != 0) + lapply(`{{baseName}}`, function(x) stopifnot(R6::is.R6(x))) + {{/isPrimitiveType}} + {{/isListContainer}} + self$`{{baseName}}` <- `{{baseName}}` + } + {{/vars}} + }, + toJSON = function() { + {{classname}}Object <- list() + {{#vars}} + if (!is.null(self$`{{baseName}}`)) { + {{classname}}Object[['{{baseName}}']] <- {{#isListContainer}}{{#isPrimitiveType}}self$`{{baseName}}`{{/isPrimitiveType}}{{^isPrimitiveType}}lapply(self$`{{baseName}}`, function(x) x$toJSON()){{/isPrimitiveType}}{{/isListContainer}}{{^isListContainer}}self$`{{baseName}}`{{^isPrimitiveType}}$toJSON(){{/isPrimitiveType}}{{/isListContainer}} + } + {{/vars}} + + {{classname}}Object + }, + fromJSON = function({{classname}}Json) { + {{classname}}Object <- jsonlite::fromJSON({{classname}}Json) + {{#vars}} + if (!is.null({{classname}}Object$`{{baseName}}`)) { + {{#isPrimitiveType}} + self$`{{baseName}}` <- {{classname}}Object$`{{baseName}}` + {{/isPrimitiveType}} + {{^isPrimitiveType}} + {{#isListContainer}} + self$`{{baseName}}` <- lapply({{classname}}Object$`{{baseName}}`, function(x) { + {{baseName}}Object <- {{datatype}}$new() + {{baseName}}Object$fromJSON(jsonlite::toJSON(x, auto_unbox = TRUE)) + {{baseName}}Object + }) + {{/isListContainer}} + {{^isListContainer}} + {{baseName}}Object <- {{datatype}}$new() + {{baseName}}Object$fromJSON(jsonlite::toJSON({{classname}}Object${{baseName}}, auto_unbox = TRUE)) + self$`{{baseName}}` <- {{baseName}}Object + {{/isListContainer}} + {{/isPrimitiveType}} + } + {{/vars}} + }, + toJSONString = function() { + sprintf( + '{ + {{#vars}} + "{{baseName}}": {{#isListContainer}}[{{/isListContainer}}{{#isPrimitiveType}}{{#isNumeric}}%d{{/isNumeric}}{{^isNumeric}}%s{{/isNumeric}}{{/isPrimitiveType}}{{^isPrimitiveType}}%s{{/isPrimitiveType}}{{#isListContainer}}]{{/isListContainer}}{{#hasMore}},{{/hasMore}} + {{/vars}} + }', + {{#vars}} + {{#isListContainer}} + {{#isPrimitiveType}} + lapply(self$`{{baseName}}`, function(x) paste(paste0('"', x, '"'), sep=",")){{#hasMore}},{{/hasMore}} + {{/isPrimitiveType}} + {{^isPrimitiveType}} + lapply(self$`{{baseName}}`, function(x) paste(x$toJSON(), sep=",")){{#hasMore}},{{/hasMore}} + {{/isPrimitiveType}} + {{/isListContainer}} + {{^isListContainer}} + self$`{{baseName}}`{{^isPrimitiveType}}$toJSON(){{/isPrimitiveType}}{{#hasMore}},{{/hasMore}} + {{/isListContainer}} + {{/vars}} + ) + }, + fromJSONString = function({{classname}}Json) { + {{classname}}Object <- jsonlite::fromJSON({{classname}}Json) + {{#vars}} + {{#isPrimitiveType}} + self$`{{baseName}}` <- {{classname}}Object$`{{baseName}}` + {{/isPrimitiveType}} + {{^isPrimitiveType}} + {{#isListContainer}} + self$`{{baseName}}` <- lapply({{classname}}Object$`{{baseName}}`, function(x) {{datatype}}$new()$fromJSON(jsonlite::toJSON(x, auto_unbox = TRUE))) + {{/isListContainer}} + {{^isListContainer}} + {{datatype}}Object -> {{datatype}}$new() + self$`{{baseName}}` <- {{datatype}}Object$fromJSON(jsonlite::toJSON({{classname}}Object${{baseName}}, auto_unbox = TRUE)) + {{/isListContainer}} + {{/isPrimitiveType}} + {{/vars}} + } + ) +) +{{/model}} +{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/r/model_doc.mustache b/modules/swagger-codegen/src/main/resources/r/model_doc.mustache new file mode 100644 index 00000000000..25537b2c5ed --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/r/model_doc.mustache @@ -0,0 +1,11 @@ +{{#models}}{{#model}}# {{classname}} + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +{{#vars}}**{{name}}** | {{#isPrimitiveType}}**{{{datatype}}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{^isContainer}}{{^isDateTime}}*{{/isDateTime}}{{/isContainer}}{{{datatype}}}**]({{complexType}}.md){{/isPrimitiveType}} | {{description}} | {{^required}}[optional] {{/required}}{{#readOnly}}[readonly] {{/readOnly}}{{#defaultValue}}[default to {{{.}}}]{{/defaultValue}} +{{/vars}} + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + +{{/model}}{{/models}} diff --git a/samples/client/petstore/elixir/lib/swagger_petstore/model/$special[model.name].ex b/modules/swagger-codegen/src/main/resources/r/namespace.mustache similarity index 100% rename from samples/client/petstore/elixir/lib/swagger_petstore/model/$special[model.name].ex rename to modules/swagger-codegen/src/main/resources/r/namespace.mustache diff --git a/modules/swagger-codegen/src/main/resources/r/partial_header.mustache b/modules/swagger-codegen/src/main/resources/r/partial_header.mustache new file mode 100644 index 00000000000..0e69c393243 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/r/partial_header.mustache @@ -0,0 +1,11 @@ +{{#appName}} +# {{{appName}}} +# +{{/appName}} +{{#appDescription}} +# {{{appDescription}}} +# +{{/appDescription}} +# {{#version}}OpenAPI spec version: {{{version}}}{{/version}} +# {{#infoEmail}}Contact: {{{infoEmail}}}{{/infoEmail}} +# Generated by: https://github.com/swagger-api/swagger-codegen.git diff --git a/modules/swagger-codegen/src/main/resources/r/response.mustache b/modules/swagger-codegen/src/main/resources/r/response.mustache new file mode 100644 index 00000000000..42873beb4e6 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/r/response.mustache @@ -0,0 +1,15 @@ +#' Response Class +#' +#' Response Class +#' @export +Response <- R6::R6Class( + 'Response', + public = list( + content = NULL, + response = NULL, + initialize = function(content, response){ + self$content <- content + self$response <- response + } + ) +) \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/restbed/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/restbed/git_push.sh.mustache index c7d7c390acf..c7a337655c3 100644 --- a/modules/swagger-codegen/src/main/resources/restbed/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/restbed/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/ruby/api_client.mustache b/modules/swagger-codegen/src/main/resources/ruby/api_client.mustache index f2a71385cb4..bf5cd927502 100644 --- a/modules/swagger-codegen/src/main/resources/ruby/api_client.mustache +++ b/modules/swagger-codegen/src/main/resources/ruby/api_client.mustache @@ -274,7 +274,7 @@ module {{moduleName}} data = {} form_params.each do |key, value| case value - when File, Array, nil + when ::File, ::Array, nil # let typhoeus handle File, Array and nil parameters data[key] = value else diff --git a/modules/swagger-codegen/src/main/resources/ruby/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/ruby/git_push.sh.mustache index 052b8991126..62855652e65 100755 --- a/modules/swagger-codegen/src/main/resources/ruby/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/ruby/git_push.sh.mustache @@ -39,7 +39,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/ruby/partial_model_enum_class.mustache b/modules/swagger-codegen/src/main/resources/ruby/partial_model_enum_class.mustache index e0e1ca9403d..f2f366900ef 100644 --- a/modules/swagger-codegen/src/main/resources/ruby/partial_model_enum_class.mustache +++ b/modules/swagger-codegen/src/main/resources/ruby/partial_model_enum_class.mustache @@ -1,4 +1,13 @@ class {{classname}} {{#allowableValues}}{{#enumVars}} {{{name}}} = {{{value}}}.freeze{{/enumVars}}{{/allowableValues}} + + # Builds the enum from string + # @param [String] The enum value in the form of the string + # @return [String] The enum value + def build_from_hash(value) + constantValues = {{classname}}.constants.select{|c| {{classname}}::const_get(c) == value} + raise "Invalid ENUM value #{value} for class #{{{classname}}}" if constantValues.empty? + value + end end diff --git a/modules/swagger-codegen/src/main/resources/ruby/partial_model_generic.mustache b/modules/swagger-codegen/src/main/resources/ruby/partial_model_generic.mustache index 00a75b25396..d1882aa2641 100644 --- a/modules/swagger-codegen/src/main/resources/ruby/partial_model_generic.mustache +++ b/modules/swagger-codegen/src/main/resources/ruby/partial_model_generic.mustache @@ -56,11 +56,16 @@ {{#vars}} if attributes.has_key?(:'{{{baseName}}}') - {{#isContainer}} + {{#isListContainer}} if (value = attributes[:'{{{baseName}}}']).is_a?(Array) self.{{{name}}} = value end - {{/isContainer}} + {{/isListContainer}} + {{#isMapContainer}} + if (value = attributes[:'{{{baseName}}}']).is_a?(Hash) + self.{{{name}}} = value + end + {{/isMapContainer}} {{^isContainer}} self.{{{name}}} = attributes[:'{{{baseName}}}'] {{/isContainer}} diff --git a/modules/swagger-codegen/src/main/resources/rust-server/Cargo.mustache b/modules/swagger-codegen/src/main/resources/rust-server/Cargo.mustache new file mode 100644 index 00000000000..8cab22ff513 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust-server/Cargo.mustache @@ -0,0 +1,34 @@ +[package] +name = "{{packageName}}" +version = "{{appVersion}}" +authors = [{{#infoEmail}}"{{infoEmail}}"{{/infoEmail}}] + +[features] +default = ["client", "server"] +client = ["serde_json", {{#usesXml}}"serde-xml-rs", {{/usesXml}}"serde_ignored", "hyper", "hyper-openssl", "uuid"{{#apiHasFile}}, "multipart"{{/apiHasFile}}] +server = ["serde_json", {{#usesXml}}"serde-xml-rs", {{/usesXml}}"serde_ignored", "hyper", "iron", "router", "bodyparser", "urlencoded", "uuid"{{#apiHasFile}}, "multipart"{{/apiHasFile}}] + +[dependencies] +bodyparser = {version = "0.7", optional = true} +chrono = { version = "0.4", features = ["serde"] } +futures = "0.1" +hyper = {version = "0.10", optional = true} +hyper-openssl = {version = "0.2", optional = true } +iron = {version = "0.5", optional = true} +lazy_static = "0.2" +log = "0.3.0" +multipart = {version = "0.13", optional = true} +router = {version = "0.5", optional = true} +serde = "1.0" +serde_derive = "1.0" +serde_ignored = {version = "0.0.4", optional = true} +serde_json = {version = "1.0", optional = true} +swagger = "0.7" +urlencoded = {version = "0.5", optional = true} +uuid = {version = "0.5", optional = true, features = ["serde", "v4"]} +# ToDo: this should be updated to point at the official crate once +# https://github.com/RReverser/serde-xml-rs/pull/45 is accepted upstream +{{#usesXml}}serde-xml-rs = {git = "git://github.com/Metaswitch/serde-xml-rs.git" , branch = "master", optional = true}{{/usesXml}} + +[dev-dependencies] +clap = "2.25" diff --git a/modules/swagger-codegen/src/main/resources/rust-server/README.mustache b/modules/swagger-codegen/src/main/resources/rust-server/README.mustache new file mode 100644 index 00000000000..c790d7d68b0 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust-server/README.mustache @@ -0,0 +1,59 @@ +# Rust API for {{packageName}} + +{{#appDescription}} +{{{appDescription}}} +{{/appDescription}} + +## Overview +This client/server was generated by the [swagger-codegen] +(https://github.com/swagger-api/swagger-codegen) project. +By using the [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate a server stub. +- + +To see how to make this your own, look here: + +[README](https://github.com/swagger-api/swagger-codegen/blob/master/README.md) + +- API version: {{appVersion}} +- Build date: {{generatedDate}} +{{#infoUrl}} +For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + +## Examples + +Run examples with: + +``` +cargo run --example +``` + +To pass in arguments to the examples, put them after `--`, for example: + +``` +cargo run --example client -- --help +``` + +### Running the server +To run the server, follow these simple steps: + +``` +cargo run --example server +``` + +### Running a client +To run a client, follow one of the following simple steps: + +```{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} +cargo run --example client {{operationId}}{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +``` + +### HTTPS +The examples can be run in HTTPS mode by passing in the flag `--https`, for example: + +``` +cargo run --example server -- --https +``` + +This will use the keys/certificates from the examples directory. Note that the server chain is signed with +`CN=localhost`. diff --git a/modules/swagger-codegen/src/main/resources/rust-server/cargo-config b/modules/swagger-codegen/src/main/resources/rust-server/cargo-config new file mode 100644 index 00000000000..b8acc9c00c8 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust-server/cargo-config @@ -0,0 +1,18 @@ +[build] +rustflags = [ + "-W", "missing_docs", # detects missing documentation for public members + + "-W", "trivial_casts", # detects trivial casts which could be removed + + "-W", "trivial_numeric_casts", # detects trivial casts of numeric types which could be removed + + "-W", "unsafe_code", # usage of `unsafe` code + + "-W", "unused_qualifications", # detects unnecessarily qualified names + + "-W", "unused_extern_crates", # extern crates that are never used + + "-W", "unused_import_braces", # unnecessary braces around an imported item + + "-D", "warnings", # all warnings should be denied +] diff --git a/modules/swagger-codegen/src/main/resources/rust-server/client.mustache b/modules/swagger-codegen/src/main/resources/rust-server/client.mustache new file mode 100644 index 00000000000..c168f502698 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust-server/client.mustache @@ -0,0 +1,336 @@ +#![allow(unused_extern_crates)] +extern crate hyper_openssl; +extern crate chrono; +{{#apiHasFile}}extern crate multipart;{{/apiHasFile}} + +{{#apiHasFile}}use multipart::client::lazy::Multipart;{{/apiHasFile}} +use hyper; +use hyper::client::IntoUrl; +use hyper::mime; +use hyper::header::{Headers, ContentType}; +use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value}; +use hyper::Url; +use self::hyper_openssl::openssl; +use futures; +use futures::{Future, Stream}; +use futures::{future, stream}; +use std::borrow::Cow; +use std::io::{Read, Error}; +use std::error; +use std::fmt; +use std::path::Path; +use std::sync::Arc; +use std::str; + +use mimetypes; + +use serde_json; +{{#usesXml}}use serde_xml_rs;{{/usesXml}} + +#[allow(unused_imports)] +use std::collections::{HashMap, BTreeMap}; +#[allow(unused_imports)] +use swagger; + +use swagger::{Context, ApiError, XSpanId}; + +use {Api{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}, + {{operationId}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + }; +use models; + +/// Convert input into a base path, e.g. "http://example:123". Also checks the scheme as it goes. +fn into_base_path(input: T, correct_scheme: Option<&'static str>) -> Result { + // First convert to Url, since a base path is a subset of Url. + let url = input.into_url()?; + + let scheme = url.scheme(); + + // Check the scheme if necessary + if let Some(correct_scheme) = correct_scheme { + if scheme != correct_scheme { + return Err(ClientInitError::InvalidScheme); + } + } + + let host = url.host().ok_or_else(|| ClientInitError::MissingHost)?; + let port = url.port().map(|x| format!(":{}", x)).unwrap_or_default(); + Ok(format!("{}://{}{}", scheme, host, port)) +} + +/// A client that implements the API by making HTTP calls out to a server. +#[derive(Clone)] +pub struct Client { + base_path: String, + hyper_client: Arc hyper::client::Client + Sync + Send>, +} + +impl fmt::Debug for Client { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Client {{ base_path: {} }}", self.base_path) + } +} + +impl Client { + pub fn try_new_http(base_path: T) -> Result + where T: IntoUrl + { + Ok(Client { + base_path: into_base_path(base_path, Some("http"))?, + hyper_client: Arc::new(hyper::client::Client::new), + }) + } + + pub fn try_new_https(base_path: T, + ca_certificate: CA) + -> Result + where T: IntoUrl, + CA: AsRef + { + let ca_certificate = ca_certificate.as_ref().to_owned(); + + let https_hyper_client = move || { + // SSL implementation + let mut ssl = openssl::ssl::SslConnectorBuilder::new(openssl::ssl::SslMethod::tls()).unwrap(); + + // Server authentication + ssl.builder_mut().set_ca_file(ca_certificate.clone()).unwrap(); + + let ssl = hyper_openssl::OpensslClient::from(ssl.build()); + let connector = hyper::net::HttpsConnector::new(ssl); + hyper::client::Client::with_connector(connector) + }; + + Ok(Client { + base_path: into_base_path(base_path, Some("https"))?, + hyper_client: Arc::new(https_hyper_client), + }) + } + + pub fn try_new_https_mutual(base_path: T, + ca_certificate: CA, + client_key: K, + client_certificate: C) + -> Result + where T: IntoUrl, + CA: AsRef, + K: AsRef, + C: AsRef + { + let ca_certificate = ca_certificate.as_ref().to_owned(); + let client_key = client_key.as_ref().to_owned(); + let client_certificate = client_certificate.as_ref().to_owned(); + + let https_mutual_hyper_client = move || { + // SSL implementation + let mut ssl = openssl::ssl::SslConnectorBuilder::new(openssl::ssl::SslMethod::tls()).unwrap(); + + // Server authentication + ssl.builder_mut().set_ca_file(ca_certificate.clone()).unwrap(); + + // Client authentication + ssl.builder_mut().set_private_key_file(client_key.clone(), openssl::x509::X509_FILETYPE_PEM).unwrap(); + ssl.builder_mut().set_certificate_chain_file(client_certificate.clone()).unwrap(); + ssl.builder_mut().check_private_key().unwrap(); + + let ssl = hyper_openssl::OpensslClient::from(ssl.build()); + let connector = hyper::net::HttpsConnector::new(ssl); + hyper::client::Client::with_connector(connector) + }; + + Ok(Client { + base_path: into_base_path(base_path, Some("https"))?, + hyper_client: Arc::new(https_mutual_hyper_client) + }) + } + + /// Constructor for creating a `Client` by passing in a pre-made `hyper` client. + /// + /// One should avoid relying on this function if possible, since it adds a dependency on the underlying transport + /// implementation, which it would be better to abstract away. Therefore, using this function may lead to a loss of + /// code generality, which may make it harder to move the application to a serverless environment, for example. + /// + /// The reason for this function's existence is to support legacy test code, which did mocking at the hyper layer. + /// This is not a recommended way to write new tests. If other reasons are found for using this function, they + /// should be mentioned here. + pub fn try_new_with_hyper_client(base_path: T, + hyper_client: Arc hyper::client::Client + Sync + Send>) + -> Result + where T: IntoUrl + { + Ok(Client { + base_path: into_base_path(base_path, None)?, + hyper_client: hyper_client + }) + } +} + +impl Api for Client { +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} + fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, param_{{paramName}}: {{^required}}{{#isFile}}Box{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &Context) -> Box + Send> { +{{#queryParams}}{{#-first}} + // Query parameters +{{/-first}}{{#required}} let query_{{paramName}} = format!("{{baseName}}={{=<% %>=}}{<% paramName %>}<%={{ }}=%>&", {{paramName}}=param_{{paramName}}{{#isListContainer}}.join(","){{/isListContainer}}{{^isListContainer}}.to_string(){{/isListContainer}}); +{{/required}}{{^required}} let query_{{paramName}} = param_{{paramName}}.map_or_else(String::new, |query| format!("{{baseName}}={{=<% %>=}}{<% paramName %>}<%={{ }}=%>&", {{paramName}}=query{{#isListContainer}}.join(","){{/isListContainer}}{{^isListContainer}}.to_string(){{/isListContainer}})); +{{/required}}{{/queryParams}} + + let url = format!("{}{{basePathWithoutHost}}{{path}}?{{#queryParams}}{{=<% %>=}}{<% paramName %>}<%={{ }}=%>{{/queryParams}}", self.base_path{{#pathParams}}, {{baseName}}=param_{{paramName}}.to_string(){{/pathParams}}{{#queryParams}}, {{paramName}}=query_{{paramName}}{{/queryParams}}); + +{{#vendorExtensions}}{{#hasFile}} // Form data body + let mut multipart = Multipart::new();{{/hasFile}}{{/vendorExtensions}}{{#formParams}}{{#isFile}} + +{{^required}} if let Ok(Some(param_{{paramName}})) = param_{{paramName}}.wait() { {{/required}} +{{^required}} {{/required}} match convert_stream_to_string(param_{{paramName}}) { +{{^required}} {{/required}} Ok(param_{{paramName}}) => { + // Add file to multipart form. + multipart.add_text("{{paramName}}", param_{{paramName}}); + }, +{{^required}} {{/required}} Err(err) => return Box::new(futures::done(Err(err))), +{{^required}} {{/required}} } + {{^required}}}{{/required}}{{/isFile}}{{/formParams}}{{#vendorExtensions}}{{#hasFile}} + + let mut fields = match multipart.prepare() { + Ok(fields) => fields, + Err(err) => return Box::new(futures::done(Err(ApiError(format!("Unable to build request: {}", err))))), + }; + + let mut body_string = String::new(); + let body = fields.to_body().read_to_string(&mut body_string); + let boundary = fields.boundary(); + let multipart_header = Mime(TopLevel::Multipart, SubLevel::FormData, vec![(Attr::Boundary, Value::Ext(boundary.to_string()))]);{{/hasFile}}{{/vendorExtensions}}{{#bodyParam}}{{#-first}} + // Body parameter +{{/-first}}{{#required}}{{#vendorExtensions}}{{#consumesXml}} +{{^has_namespace}} let body = serde_xml_rs::to_string(¶m_{{paramName}}).expect("impossible to fail to serialize");{{/has_namespace}}{{#has_namespace}} + let mut namespaces = BTreeMap::new(); + // An empty string is used to indicate a global namespace in xmltree. + namespaces.insert("".to_string(), models::namespaces::{{uppercase_data_type}}.clone()); + let body = serde_xml_rs::to_string_with_namespaces(¶m_{{paramName}}, namespaces).expect("impossible to fail to serialize");{{/has_namespace}}{{/consumesXml}}{{^consumesXml}} + let body = serde_json::to_string(¶m_{{paramName}}).expect("impossible to fail to serialize");{{/consumesXml}}{{/vendorExtensions}} +{{/required}}{{^required}} let body = param_{{paramName}}.map(|ref body| { +{{#vendorExtensions}}{{#consumesXml}} +{{^has_namespace}} serde_xml_rs::to_string(body).expect("impossible to fail to serialize"){{/has_namespace}}{{#has_namespace}} + let mut namespaces = BTreeMap::new(); + // An empty string is used to indicate a global namespace in xmltree. + namespaces.insert("".to_string(), models::namespaces::{{uppercase_data_type}}.clone()); + serde_xml_rs::to_string_with_namespaces(body, namespaces).expect("impossible to fail to serialize"){{/has_namespace}}{{/consumesXml}}{{^consumesXml}} + serde_json::to_string(body).expect("impossible to fail to serialize"){{/consumesXml}}{{/vendorExtensions}} + });{{/required}}{{/bodyParam}} + let hyper_client = (self.hyper_client)(); + let request = hyper_client.request(hyper::method::Method::{{#vendorExtensions}}{{HttpMethod}}{{/vendorExtensions}}, &url); + let mut custom_headers = hyper::header::Headers::new(); + +{{#bodyParam}}{{#required}} let request = request.body(&body); +{{/required}}{{^required}} let request = match body { + Some(ref body) => request.body(body), + None => request, + }; +{{/required}} + + custom_headers.set(ContentType(mimetypes::requests::{{#vendorExtensions}}{{uppercase_operation_id}}{{/vendorExtensions}}.clone())); +{{/bodyParam}} + context.x_span_id.as_ref().map(|header| custom_headers.set(XSpanId(header.clone()))); +{{#headerParams}}{{#-first}} + // Header parameters +{{/-first}}{{^isMapContainer}} header! { (Request{{vendorExtensions.typeName}}, "{{baseName}}") => {{#isListContainer}}({{{baseType}}})*{{/isListContainer}}{{^isListContainer}}[{{{dataType}}}]{{/isListContainer}} } +{{#required}} custom_headers.set(Request{{vendorExtensions.typeName}}(param_{{paramName}}{{#isListContainer}}.clone(){{/isListContainer}})); +{{/required}}{{^required}} param_{{paramName}}.map(|header| custom_headers.set(Request{{vendorExtensions.typeName}}(header{{#isListContainer}}.clone(){{/isListContainer}}))); +{{/required}}{{/isMapContainer}}{{#isMapContainer}} let param_{{paramName}}: Option<{{{dataType}}}> = None; +{{/isMapContainer}}{{/headerParams}} + + let request = request.headers(custom_headers);{{#vendorExtensions}}{{#hasFile}} + let request = request.header(ContentType(multipart_header)) + .body(&body_string); +{{/hasFile}}{{/vendorExtensions}} + + // Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists). + fn parse_response(mut response: hyper::client::response::Response) -> Result<{{operationId}}Response, ApiError> { + match response.status.to_u16() { +{{#responses}} + {{code}} => { +{{#dataType}}{{^isFile}} let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;{{#vendorExtensions}}{{#producesXml}} + // ToDo: this will move to swagger-rs and become a standard From conversion trait + // once https://github.com/RReverser/serde-xml-rs/pull/45 is accepted upstream + let body = serde_xml_rs::from_str::<{{{dataType}}}>(&buf) + .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?;{{/producesXml}}{{^producesXml}} + let body = serde_json::from_str::<{{{dataType}}}>(&buf)?; +{{/producesXml}}{{/vendorExtensions}}{{/isFile}}{{#isFile}} let mut buf = Vec::new(); + response.read_to_end(&mut buf).map_err(|e| ApiError(format!("Received error reading response: {}", e)))?; + let body = Box::new(stream::once(Ok(buf)));{{/isFile}} +{{/dataType}} + +{{#headers}} header! { (Response{{nameInCamelCase}}, "{{baseName}}") => [{{{datatype}}}] } + let response_{{name}} = response.headers.get::().ok_or_else(|| "Required response header {{baseName}} for response {{code}} was not found.")?; +{{/headers}} + +{{#dataType}} Ok({{operationId}}Response::{{message}}{{^headers}}(body){{/headers}}{{#headers}}{{#-first}}{ body: body, {{/-first}}{{name}}: response_{{name}}.0.clone(){{^-last}}, {{/-last}}{{#-last}} }{{/-last}}{{/headers}}) +{{/dataType}}{{^dataType}} Ok({{operationId}}Response::{{message}}{{#headers}}{{#-first}}{ {{/-first}}{{^-first}}, {{/-first}}{{name}}: response_{{name}}.0.clone(){{#-last}} }{{/-last}}{{/headers}}) +{{/dataType}} + }, +{{/responses}} + code => { + let mut buf = [0; 100]; + let debug_body = match response.read(&mut buf) { + Ok(len) => match str::from_utf8(&buf[..len]) { + Ok(body) => Cow::from(body), + Err(_) => Cow::from(format!("", &buf[..len].to_vec())), + }, + Err(e) => Cow::from(format!("", e)), + }; + Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}", + code, + response.headers, + debug_body))) + } + } + }{{#vendorExtensions}}{{#hasFile}} + + // Helper function to convert a Stream into a String. The String can then be used to build the HTTP body. + fn convert_stream_to_string(stream: Box, Error=Error> + Send>) -> Result { + + stream.fold(Vec::new(), |mut body, chunk| { + body.extend(chunk.iter()); + future::ok::,Error>(body) + }).wait() + .map_err(|e| ApiError(format!("Unable to fold stream: {}", e))) + .and_then(|body| String::from_utf8(body) + .map_err(|e| ApiError(format!("Failed to convert utf8 stream to String: {}", e)))) + }{{/hasFile}}{{/vendorExtensions}} + + let result = request.send().map_err(|e| ApiError(format!("No response received: {}", e))).and_then(parse_response); + Box::new(futures::done(result)) + } +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +} + +#[derive(Debug)] +pub enum ClientInitError { + InvalidScheme, + InvalidUrl(hyper::error::ParseError), + MissingHost, + SslError(openssl::error::ErrorStack) +} + +impl From for ClientInitError { + fn from(err: hyper::error::ParseError) -> ClientInitError { + ClientInitError::InvalidUrl(err) + } +} + +impl From for ClientInitError { + fn from(err: openssl::error::ErrorStack) -> ClientInitError { + ClientInitError::SslError(err) + } +} + +impl fmt::Display for ClientInitError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + (self as &fmt::Debug).fmt(f) + } +} + +impl error::Error for ClientInitError { + fn description(&self) -> &str { + "Failed to produce a hyper client." + } +} diff --git a/modules/swagger-codegen/src/main/resources/rust-server/example-ca.pem b/modules/swagger-codegen/src/main/resources/rust-server/example-ca.pem new file mode 100644 index 00000000000..d2317fb5db7 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust-server/example-ca.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICtjCCAZ4CCQDpKecRERZ0xDANBgkqhkiG9w0BAQsFADAdMQswCQYDVQQGEwJV +UzEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwNTIzMTYwMDIzWhcNMTcwNjIyMTYwMDIz +WjAdMQswCQYDVQQGEwJVUzEOMAwGA1UEAxMFTXkgQ0EwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCt66py3x7sCSASRF2D05L5wkNDxAUjQKYx23W8Gbwv +GMGykk89BIdU5LX1JB1cKiUOkoIxfwAYuWc2V/wzTvVV7+11besnk3uX1c9KiqUF +LIX7kn/z5hzS4aelhKvH+MJlSZCSlp1ytpZbwo5GB5Pi2SGH56jDBiBoDRNBVdWL +z4wH7TdrQjqWwNxIZumD5OGMtcfJyuX08iPiEOaslOeoMqzObhvjc9aUgjVjhqyA +FkJGTXsi0oaD7oml+NE+mTNfEeZvEJQpLSjBY0OvQHzuHkyGBShBnfu/9x7/NRwd +WaqsLiF7/re9KDGYdJwP7Cu6uxYfKAyWarp6h2mG/GIdAgMBAAEwDQYJKoZIhvcN +AQELBQADggEBAGIl/VVIafeq/AJOQ9r7TzzB2ABJYr7NZa6bTu5O1jSp1Fonac15 +SZ8gvRxODgH22ZYSqghPG4xzq4J3hkytlQqm57ZEt2I2M3OqIp17Ndcc1xDYzpLl +tA0FrVn6crQTM8vQkTDtGesaCWX+7Fir5dK7HnYWzfpSmsOpST07PfbNisEXKOxG +Dj4lBL1OnhTjsJeymVS1pFvkKkrcEJO+IxFiHL3CDsWjcXB0Z+E1zBtPoYyYsNsO +rBrjUxcZewF4xqWZhpW90Mt61fY2nRgU0uUwHcvDQUqvmzKcsqYa4mPKzfBI5mxo +01Ta96cDD6pS5Y1hOflZ0g84f2g/7xBLLDA= +-----END CERTIFICATE----- diff --git a/modules/swagger-codegen/src/main/resources/rust-server/example-client.mustache b/modules/swagger-codegen/src/main/resources/rust-server/example-client.mustache new file mode 100644 index 00000000000..2d76fcb47d4 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust-server/example-client.mustache @@ -0,0 +1,59 @@ +#![allow(missing_docs, unused_variables, trivial_casts)] + +extern crate {{externCrateName}}; +#[allow(unused_extern_crates)] +extern crate futures; +#[allow(unused_extern_crates)] +extern crate swagger; +#[allow(unused_extern_crates)] +extern crate uuid; +extern crate clap; + +#[allow(unused_imports)] +use futures::{Future, future, Stream, stream}; +#[allow(unused_imports)] +use {{externCrateName}}::{ApiNoContext, ContextWrapperExt, + ApiError{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}, + {{operationId}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + }; +use clap::{App, Arg}; + +fn main() { + let matches = App::new("client") + .arg(Arg::with_name("operation") + .help("Sets the operation to run") + .possible_values(&[ +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}{{#vendorExtensions}}{{^noClientExample}} "{{operationId}}", +{{/noClientExample}}{{/vendorExtensions}}{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}]) + .required(true) + .index(1)) + .arg(Arg::with_name("https") + .long("https") + .help("Whether to use HTTPS or not")) + .get_matches(); + + let client = if matches.is_present("https") { + // Using Simple HTTPS + {{externCrateName}}::Client::try_new_https("https://localhost:{{serverPort}}", "examples/ca.pem").expect("Failed to create HTTPS client") + } else { + // Using HTTP + {{externCrateName}}::Client::try_new_http("http://localhost:{{serverPort}}").expect("Failed to create HTTP client") + }; + + // Using a non-default `Context` is not required; this is just an example! + let client = client.with_context({{externCrateName}}::Context::new_with_span_id(self::uuid::Uuid::new_v4().to_string())); + + match matches.value_of("operation") { +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} + {{#vendorExtensions}}{{#noClientExample}}// Disabled because there's no example. + // {{/noClientExample}}Some("{{operationId}}") => { + {{#noClientExample}}// {{/noClientExample}} let result = client.{{operation_id}}{{/vendorExtensions}}({{#allParams}}{{^-first}}, {{/-first}}{{#vendorExtensions}}{{{example}}}{{/vendorExtensions}}{{/allParams}}).wait(); + {{#vendorExtensions}}{{#noClientExample}}// {{/noClientExample}}{{/vendorExtensions}} println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from(""))); + {{#vendorExtensions}}{{#noClientExample}}// {{/noClientExample}}{{/vendorExtensions}} }, +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + _ => { + panic!("Invalid operation provided") + } + } +} + diff --git a/modules/swagger-codegen/src/main/resources/rust-server/example-server-chain.pem b/modules/swagger-codegen/src/main/resources/rust-server/example-server-chain.pem new file mode 100644 index 00000000000..47d7e201404 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust-server/example-server-chain.pem @@ -0,0 +1,66 @@ +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 4096 (0x1000) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, CN=My CA + Validity + Not Before: May 23 16:00:23 2017 GMT + Not After : Apr 29 16:00:23 2117 GMT + Subject: CN=localhost, C=US + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c9:d4:43:60:50:fc:d6:0f:38:4d:5d:5e:aa:7c: + c0:5e:a9:ec:d9:93:78:d3:93:72:28:41:f5:08:a5: + ea:ac:67:07:d7:1f:f7:7d:74:69:7e:46:89:20:4b: + 7a:2d:9b:02:08:e7:6f:0f:1d:0c:0f:c7:60:69:19: + 4b:df:7e:ca:75:94:0b:49:71:e3:6d:f2:e8:79:fd: + ed:0a:94:67:55:f3:ca:6b:61:ba:58:b7:2e:dd:7b: + ca:b9:02:9f:24:36:ac:26:8f:04:8f:81:c8:35:10: + f4:aa:33:b2:24:16:f8:f7:1e:ea:f7:16:fe:fa:34: + c3:dd:bb:2c:ba:7a:df:4d:e2:da:1e:e5:d2:28:44: + 6e:c8:96:e0:fd:09:0c:14:0c:31:dc:e0:ca:c1:a7: + 9b:bf:16:8c:f7:36:3f:1b:2e:dd:90:eb:45:78:51: + bf:59:22:1e:c6:8c:0a:69:88:e5:03:5e:73:b7:fc: + 93:7f:1b:46:1b:97:68:c5:c0:8b:35:1f:bb:1e:67: + 7f:55:b7:3b:55:3f:ea:f2:ca:db:cc:52:cd:16:89: + db:15:47:bd:f2:cd:6c:7a:d7:b4:1a:ac:c8:15:6c: + 6a:fb:77:c4:e9:f2:30:e0:14:24:66:65:6f:2a:e5: + 2d:cc:f6:81:ae:57:c8:d1:9b:38:90:dc:60:93:02: + 5e:cb + Exponent: 65537 (0x10001) + Signature Algorithm: sha256WithRSAEncryption + 1c:7c:39:e8:3d:49:b2:09:1e:68:5a:2f:74:18:f4:63:b5:8c: + f6:e6:a1:e3:4d:95:90:99:ef:32:5c:34:40:e8:55:13:0e:e0: + 1c:be:cd:ab:3f:64:38:99:5e:2b:c1:81:53:a0:18:a8:f6:ee: + 6a:33:73:6c:9a:73:9d:86:08:5d:c7:11:38:46:4c:cd:a0:47: + 37:8f:fe:a6:50:a9:02:21:99:42:86:5e:47:fe:65:56:60:1d: + 16:53:86:bd:e4:63:c5:69:cf:fa:30:51:ab:a1:c3:50:53:cc: + 66:1c:4c:ff:3f:2a:39:4d:a2:8f:9d:d1:a7:8b:22:e4:78:69: + 24:06:83:4d:cc:0a:c0:87:69:9b:bc:80:a9:d2:b7:a5:23:84: + 7e:a2:32:26:7c:78:0e:bd:db:cd:3b:69:18:33:b8:44:ef:96: + b4:99:86:ee:06:bd:51:1c:c7:a1:a4:0c:c4:4c:51:a0:df:ac: + 14:07:88:8e:d7:39:45:fe:52:e0:a3:4c:db:5d:7a:ab:4d:e4: + ca:06:e8:bd:74:6f:46:e7:93:4a:4f:1b:67:e7:a5:9f:ef:9c: + 02:49:d1:f2:d5:e9:53:ee:09:21:ac:08:c8:15:f7:af:35:b9: + 4f:11:0f:43:ae:46:8e:fd:5b:8d:a3:4e:a7:2c:b7:25:ed:e4: + e5:94:1d:e3 +-----BEGIN CERTIFICATE----- +MIICtTCCAZ0CAhAAMA0GCSqGSIb3DQEBCwUAMB0xCzAJBgNVBAYTAlVTMQ4wDAYD +VQQDEwVNeSBDQTAgFw0xNzA1MjMxNjAwMjNaGA8yMTE3MDQyOTE2MDAyM1owITES +MBAGA1UEAxMJbG9jYWxob3N0MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMnUQ2BQ/NYPOE1dXqp8wF6p7NmTeNOTcihB9Qil6qxn +B9cf9310aX5GiSBLei2bAgjnbw8dDA/HYGkZS99+ynWUC0lx423y6Hn97QqUZ1Xz +ymthuli3Lt17yrkCnyQ2rCaPBI+ByDUQ9KozsiQW+Pce6vcW/vo0w927LLp6303i +2h7l0ihEbsiW4P0JDBQMMdzgysGnm78WjPc2Pxsu3ZDrRXhRv1kiHsaMCmmI5QNe +c7f8k38bRhuXaMXAizUfux5nf1W3O1U/6vLK28xSzRaJ2xVHvfLNbHrXtBqsyBVs +avt3xOnyMOAUJGZlbyrlLcz2ga5XyNGbOJDcYJMCXssCAwEAATANBgkqhkiG9w0B +AQsFAAOCAQEAHHw56D1JsgkeaFovdBj0Y7WM9uah402VkJnvMlw0QOhVEw7gHL7N +qz9kOJleK8GBU6AYqPbuajNzbJpznYYIXccROEZMzaBHN4/+plCpAiGZQoZeR/5l +VmAdFlOGveRjxWnP+jBRq6HDUFPMZhxM/z8qOU2ij53Rp4si5HhpJAaDTcwKwIdp +m7yAqdK3pSOEfqIyJnx4Dr3bzTtpGDO4RO+WtJmG7ga9URzHoaQMxExRoN+sFAeI +jtc5Rf5S4KNM2116q03kygbovXRvRueTSk8bZ+eln++cAknR8tXpU+4JIawIyBX3 +rzW5TxEPQ65Gjv1bjaNOpyy3Je3k5ZQd4w== +-----END CERTIFICATE----- diff --git a/modules/swagger-codegen/src/main/resources/rust-server/example-server-key.pem b/modules/swagger-codegen/src/main/resources/rust-server/example-server-key.pem new file mode 100644 index 00000000000..29c00682922 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust-server/example-server-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJ1ENgUPzWDzhN +XV6qfMBeqezZk3jTk3IoQfUIpeqsZwfXH/d9dGl+RokgS3otmwII528PHQwPx2Bp +GUvffsp1lAtJceNt8uh5/e0KlGdV88prYbpYty7de8q5Ap8kNqwmjwSPgcg1EPSq +M7IkFvj3Hur3Fv76NMPduyy6et9N4toe5dIoRG7IluD9CQwUDDHc4MrBp5u/Foz3 +Nj8bLt2Q60V4Ub9ZIh7GjAppiOUDXnO3/JN/G0Ybl2jFwIs1H7seZ39VtztVP+ry +ytvMUs0WidsVR73yzWx617QarMgVbGr7d8Tp8jDgFCRmZW8q5S3M9oGuV8jRmziQ +3GCTAl7LAgMBAAECggEBAKEd1q9j14KWYc64s6KLthGbutyxsinMMbxbct11fdIk +6YhdF3fJ35ETg9IJDr6rWEN9ZRX+jStncNpVfFEs6ThVd3Eo/nI+EEGaaIkikR93 +X2a7fEPn7/yVHu70XdBN6L1bPDvHUeiy4W2hmRrgT90OjGm1rNRWHOm7yugOwIZu +HclzbR9Ca7EInFnotUiDQm9sw9VKHbJHqWx6OORdZrxR2ytYs0Qkq0XpGMvti2HW +7WAmKTg5QM8myXW7+/4iqb/u68wVBR2BBalShKmIf7lim9O3W2a1RjDdsvm/wNe9 +I+D+Iq825vpqkKXcrxYlpVg7hYiaQaW/MNsEb7lQRjECgYEA/RJYby0POW+/k0Jn +jO8UmJVEMiuGa8WIUu/JJWMOmzRCukjSRNQOkt7niQrZPJYE8W6clM6RJTolWf9L +IL6mIb+mRaoudUk8SHGDq7ho1iMg9GK8lhYxvKh1Q6uv8EyVSkgLknAEY0NANKC1 +zNdU5Dhven9aRX2gq9vP4XwMz2MCgYEAzCogQ7IFk+gkp3k491dOZnrGRoRCfuzo +4CJtyKFgOSd7BjmpcKkj0IPfVBjw6GjMIxfQRMTQmxAjjWevH45vG8l0Iiwz/gSp +81b5nsDEX5uv2Olcmcz5zxRFy36jOZ9ihMWinxcIlT2oDbyCdbruDKZq9ieJ9S8g +4qGx0OkwE3kCgYEA7CmAiU89U9YqqttfEq/RQoqY91CSwmO10d+ej9seuEtOsdRf +FIfnibulycdr7hP5TOxyBpO1802NqayJiWcgVYIpQf2MGTtcnCYCP+95NcvWZvj1 +EAJqK6nwtFO1fcOZ1ZXh5qfOEGujsPkAbsXLnKXlsiTCMvMHSxl3pu5Cbg0CgYBf +JjbZNctRrjv+7Qj2hPLd4dQsIxGWc7ToWENP4J2mpVa5hQAJqFovoHXhjKohtk2F +AWEn243Y5oGbMjo0e74edhmwn2cvuF64MM2vBem/ISCn98IXT6cQskMA3qkVfsl8 +VVs/x41ReGWs2TD3y0GMFbb9t1mdMfSiincDhNnKCQKBgGfeT4jKyYeCoCw4OLI1 +G75Gd0METt/IkppwODPpNwj3Rp9I5jctWZFA/3wCX/zk0HgBeou5AFNS4nQZ/X/L +L9axbSdR7UJTGkT1r4gu3rLkPV4Tk+8XM03/JT2cofMlzQBuhvl1Pn4SgKowz7hl +lS76ECw4Av3T0S34VW9Z5oye +-----END PRIVATE KEY----- diff --git a/modules/swagger-codegen/src/main/resources/rust-server/example-server.mustache b/modules/swagger-codegen/src/main/resources/rust-server/example-server.mustache new file mode 100644 index 00000000000..7da74ee26e7 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust-server/example-server.mustache @@ -0,0 +1,58 @@ +#![allow(missing_docs)] + +extern crate {{externCrateName}}; +extern crate iron; +extern crate futures; +extern crate hyper_openssl; +extern crate clap; +extern crate swagger; + +use hyper_openssl::OpensslServer; +use hyper_openssl::openssl::x509::X509_FILETYPE_PEM; +use hyper_openssl::openssl::ssl::{SslAcceptorBuilder, SslMethod}; +use hyper_openssl::openssl::error::ErrorStack; +use clap::{App, Arg}; +use iron::{Iron, Chain}; +use swagger::auth::AllowAllMiddleware; + +// Import the module that defines the Server struct. +mod server_lib; + +/// Builds an SSL implementation for Simple HTTPS from some hard-coded file names +fn ssl() -> Result { + let mut ssl = SslAcceptorBuilder::mozilla_intermediate_raw(SslMethod::tls())?; + + // Server authentication + ssl.builder_mut().set_private_key_file("examples/server-key.pem", X509_FILETYPE_PEM)?; + ssl.builder_mut().set_certificate_chain_file("examples/server-chain.pem")?; + ssl.builder_mut().check_private_key()?; + + Ok(OpensslServer::from(ssl.build())) +} + +/// Create custom server, wire it to the autogenerated router, +/// and pass it to the web server. +fn main() { + let matches = App::new("server") + .arg(Arg::with_name("https") + .long("https") + .help("Whether to use HTTPS or not")) + .get_matches(); + + let server = server_lib::Server{}; + let router = {{externCrateName}}::router(server); + + let mut chain = Chain::new(router); + chain.link_before({{externCrateName}}::server::ExtractAuthData); + // add authentication middlewares into the chain here + // for the purpose of this example, pretend we have authenticated a user + chain.link_before(AllowAllMiddleware::new("cosmo")); + + if matches.is_present("https") { + // Using Simple HTTPS + Iron::new(chain).https("localhost:{{serverPort}}", ssl().expect("Failed to load SSL keys")).expect("Failed to start HTTPS server"); + } else { + // Using HTTP + Iron::new(chain).http("localhost:{{serverPort}}").expect("Failed to start HTTP server"); + } +} diff --git a/modules/swagger-codegen/src/main/resources/rust-server/example-server_lib.mustache b/modules/swagger-codegen/src/main/resources/rust-server/example-server_lib.mustache new file mode 100644 index 00000000000..b9288a38cc8 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust-server/example-server_lib.mustache @@ -0,0 +1,31 @@ +#![allow(missing_docs, unused_extern_crates)] +extern crate chrono; +extern crate swagger; + +use futures::{self, Future}; +{{#apiHasFile}}use futures::Stream;{{/apiHasFile}} + +#[allow(unused_imports)] +use std::collections::HashMap; +{{#apiHasFile}}use std::io::Error;{{/apiHasFile}} + +use {{externCrateName}}::{Api, ApiError, Context{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}, + {{operationId}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +}; +#[allow(unused_imports)] +use {{externCrateName}}::models; + +#[derive(Copy, Clone)] +pub struct Server; + +impl Api for Server { +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} +{{#summary}} /// {{{summary}}}{{/summary}} + fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("{{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}({{#allParams}}{{^isFile}}{{#vendorExtensions}}{{{formatString}}}{{/vendorExtensions}}{{#hasMore}}, {{/hasMore}}{{/isFile}}{{/allParams}}) - X-Span-ID: {:?}"{{#allParams}}{{^isFile}}, {{paramName}}{{/isFile}}{{/allParams}}, context.x_span_id.unwrap_or(String::from("")).clone());{{#allParams}}{{#isFile}} + let _ = {{paramName}}; //Suppresses unused param warning{{/isFile}}{{/allParams}} + Box::new(futures::failed("Generic failure".into())) + } +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +} diff --git a/modules/swagger-codegen/src/main/resources/rust-server/gitignore b/modules/swagger-codegen/src/main/resources/rust-server/gitignore new file mode 100644 index 00000000000..a9d37c560c6 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust-server/gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/modules/swagger-codegen/src/main/resources/rust-server/lib.mustache b/modules/swagger-codegen/src/main/resources/rust-server/lib.mustache new file mode 100644 index 00000000000..2c913125401 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust-server/lib.mustache @@ -0,0 +1,96 @@ +#![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)] +extern crate serde; +#[macro_use] +extern crate serde_derive; +extern crate serde_json; +{{#usesXml}}extern crate serde_xml_rs;{{/usesXml}} +extern crate futures; +extern crate chrono; +{{#apiHasFile}}extern crate multipart;{{/apiHasFile}} +#[macro_use] +extern crate lazy_static; +#[macro_use] +extern crate log; + +// Logically this should be in the client and server modules, but rust doesn't allow `macro_use` from a module. +#[cfg(any(feature = "client", feature = "server"))] +#[macro_use] +extern crate hyper; + +extern crate swagger; + +use futures::Stream; +use std::io::Error; + +#[allow(unused_imports)] +use std::collections::HashMap; + +pub use futures::Future; + +#[cfg(any(feature = "client", feature = "server"))] +mod mimetypes; + +pub use swagger::{ApiError, Context, ContextWrapper}; + +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} +{{^isResponseFile}} +#[derive(Debug, PartialEq)] +{{/isResponseFile}} +pub enum {{operationId}}Response { +{{#responses}} {{message}} {{#dataType}}{{^headers}}( {{{dataType}}} ) {{/headers}}{{#headers}}{{#-first}}{ body: {{{dataType}}}{{/-first}}{{/headers}}{{/dataType}}{{#headers}}{{#-first}}{{^dataType}} { {{/dataType}}{{#dataType}}, {{/dataType}}{{/-first}}{{^-first}}, {{/-first}}{{name}}: {{{datatype}}}{{#-last}} } {{/-last}}{{/headers}}, +{{/responses}} +} +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + +/// API +pub trait Api { +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} +{{#summary}} /// {{{summary}}}{{/summary}} + fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &Context) -> Box + Send>; +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +} + +/// API without a `Context` +pub trait ApiNoContext { +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} +{{#summary}} /// {{{summary}}}{{/summary}} + fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}) -> Box + Send>; +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +} + +/// Trait to extend an API to make it easy to bind it to a context. +pub trait ContextWrapperExt<'a> where Self: Sized { + /// Binds this API to a context. + fn with_context(self: &'a Self, context: Context) -> ContextWrapper<'a, Self>; +} + +impl<'a, T: Api + Sized> ContextWrapperExt<'a> for T { + fn with_context(self: &'a T, context: Context) -> ContextWrapper<'a, T> { + ContextWrapper::::new(self, context) + } +} + +impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> { +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} +{{#summary}} /// {{{summary}}}{{/summary}} + fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}) -> Box + Send> { + self.api().{{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}({{#allParams}}{{paramName}}, {{/allParams}}&self.context()) + } +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +} + +#[cfg(feature = "client")] +pub mod client; + +// Re-export Client as a top-level name +#[cfg(feature = "client")] +pub use self::client::Client; + +#[cfg(feature = "server")] +pub mod server; + +// Re-export router() as a top-level name +#[cfg(feature = "server")] +pub use self::server::router; + +pub mod models; diff --git a/modules/swagger-codegen/src/main/resources/rust-server/mimetypes.mustache b/modules/swagger-codegen/src/main/resources/rust-server/mimetypes.mustache new file mode 100644 index 00000000000..336e12dfe64 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust-server/mimetypes.mustache @@ -0,0 +1,21 @@ +/// mime types for requests and responses + +pub mod responses { + use hyper::mime::*; + + // The macro is called per-operation to beat the recursion limit +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}{{#responses}}{{#produces}}{{#-first}}{{#dataType}} /// Create Mime objects for the response content types for {{operationId}} + lazy_static! { + pub static ref {{#vendorExtensions}}{{uppercase_operation_id}}_{{uppercase_message}}{{/vendorExtensions}}: Mime = mime!({{{mediaType}}}); + } +{{/dataType}}{{/-first}}{{/produces}}{{/responses}}{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +} + +pub mod requests { + use hyper::mime::*; +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}{{#bodyParam}} /// Create Mime objects for the request content types for {{operationId}} + lazy_static! { + pub static ref {{#vendorExtensions}}{{uppercase_operation_id}}{{/vendorExtensions}}: Mime = mime!({{#consumes}}{{#-first}}{{{mediaType}}}{{/-first}}{{/consumes}}{{^consumes}}Application/Json{{/consumes}}); + } +{{/bodyParam}}{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +} diff --git a/modules/swagger-codegen/src/main/resources/rust-server/models.mustache b/modules/swagger-codegen/src/main/resources/rust-server/models.mustache new file mode 100755 index 00000000000..4c31931dde0 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust-server/models.mustache @@ -0,0 +1,175 @@ +#![allow(unused_imports, unused_qualifications, unused_extern_crates)] +extern crate chrono; +extern crate uuid; + +{{#usesXml}}use serde_xml_rs;{{/usesXml}} +use serde::ser::Serializer; + +use std::collections::HashMap; +use models; +use swagger; + +{{#models}}{{#model}} +{{#description}}/// {{{description}}} +{{/description}}{{#isEnum}}/// Enumeration of values. +/// Since this enum's variants do not hold data, we can easily define them them as `#[repr(C)]` +/// which helps with FFI. +#[allow(non_camel_case_types)] +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]{{#xmlName}} +#[serde(rename = "{{xmlName}}")]{{/xmlName}} +pub enum {{classname}} { {{#allowableValues}}{{#enumVars}} + #[serde(rename = {{{value}}})] + {{name}},{{/enumVars}}{{/allowableValues}} +} + +impl ::std::fmt::Display for {{classname}} { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { {{#allowableValues}}{{#enumVars}} + {{classname}}::{{name}} => write!(f, "{}", {{{value}}}),{{/enumVars}}{{/allowableValues}} + } + } +} + +impl ::std::str::FromStr for {{classname}} { + type Err = (); + fn from_str(s: &str) -> Result { + match s { +{{#allowableValues}}{{#enumVars}} {{{value}}} => Ok({{classname}}::{{name}}), +{{/enumVars}}{{/allowableValues}} _ => Err(()), + } + } +} +{{/isEnum}}{{^isEnum}}{{#dataType}}{{! newtype}}#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)] +{{#xmlName}}#[serde(rename = "{{xmlName}}")]{{/xmlName}} +pub struct {{classname}}({{{dataType}}}); + +impl ::std::convert::From<{{dataType}}> for {{classname}} { + fn from(x: {{dataType}}) -> Self { + {{classname}}(x) + } +} + +impl ::std::convert::From<{{classname}}> for {{dataType}} { + fn from(x: {{classname}}) -> Self { + x.0 + } +} + +impl ::std::ops::Deref for {{classname}} { + type Target = {{{dataType}}}; + fn deref(&self) -> &{{{dataType}}} { + &self.0 + } +} + +impl ::std::ops::DerefMut for {{classname}} { + fn deref_mut(&mut self) -> &mut {{{dataType}}} { + &mut self.0 + } +} + +{{/dataType}}{{^dataType}}{{#arrayModelType}}{{#vendorExtensions}}{{#itemXmlName}}// Utility function for wrapping list elements when serializing xml +fn wrap_in_{{itemXmlName}}(item: &Vec<{{arrayModelType}}>, serializer: S) -> Result +where + S: Serializer, +{ + serde_xml_rs::wrap_primitives(item, serializer, "{{itemXmlName}}") +} + +{{/itemXmlName}}{{/vendorExtensions}}{{! vec}}#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct {{classname}}(Vec<{{{arrayModelType}}}>); + +impl ::std::convert::From> for {{classname}} { + fn from(x: Vec<{{arrayModelType}}>) -> Self { + {{classname}}(x) + } +} + +impl ::std::convert::From<{{classname}}> for Vec<{{arrayModelType}}> { + fn from(x: {{classname}}) -> Self { + x.0 + } +} + +impl ::std::iter::FromIterator<{{arrayModelType}}> for {{classname}} { + fn from_iter>(u: U) -> Self { + {{classname}}(Vec::<{{arrayModelType}}>::from_iter(u)) + } +} + +impl ::std::iter::IntoIterator for {{classname}} { + type Item = {{arrayModelType}}; + type IntoIter = ::std::vec::IntoIter<{{arrayModelType}}>; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a> ::std::iter::IntoIterator for &'a {{classname}} { + type Item = &'a {{arrayModelType}}; + type IntoIter = ::std::slice::Iter<'a, {{arrayModelType}}>; + + fn into_iter(self) -> Self::IntoIter { + (&self.0).into_iter() + } +} + +impl<'a> ::std::iter::IntoIterator for &'a mut {{classname}} { + type Item = &'a mut {{arrayModelType}}; + type IntoIter = ::std::slice::IterMut<'a, {{arrayModelType}}>; + + fn into_iter(self) -> Self::IntoIter { + (&mut self.0).into_iter() + } +} + +impl ::std::ops::Deref for {{classname}} { + type Target = Vec<{{{arrayModelType}}}>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl ::std::ops::DerefMut for {{classname}} { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +{{/arrayModelType}}{{^arrayModelType}}{{! general struct}}#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]{{#xmlName}} +#[serde(rename = "{{xmlName}}")]{{/xmlName}} +pub struct {{classname}} { +{{#vars}}{{#description}} /// {{{description}}} +{{/description}}{{#isEnum}} // Note: inline enums are not fully supported by swagger-codegen +{{/isEnum}} #[serde(rename = "{{baseName}}")]{{#vendorExtensions}}{{#itemXmlName}} + #[serde(serialize_with = "wrap_in_{{itemXmlName}}")]{{/itemXmlName}}{{/vendorExtensions}}{{#required}} + pub {{name}}: {{#vendorExtensions}}{{#x-nullable}}swagger::Nullable<{{/x-nullable}}{{/vendorExtensions}}{{{datatype}}}{{#vendorExtensions}}{{#x-nullable}}>{{/x-nullable}}{{/vendorExtensions}}, +{{/required}}{{^required}}{{#vendorExtensions}}{{#x-nullable}} #[serde(deserialize_with = "swagger::nullable_format::deserialize_optional_nullable")] + #[serde(default = "swagger::nullable_format::default_optional_nullable")] +{{/x-nullable}}{{/vendorExtensions}} + #[serde(skip_serializing_if="Option::is_none")] + pub {{name}}: Option<{{#vendorExtensions}}{{#x-nullable}}swagger::Nullable<{{/x-nullable}}{{/vendorExtensions}}{{#isListContainer}}Vec<{{#items}}{{{datatype}}}{{/items}}>{{/isListContainer}}{{^isListContainer}}{{{datatype}}}{{/isListContainer}}{{#vendorExtensions}}{{#x-nullable}}>{{/x-nullable}}{{/vendorExtensions}}>, +{{/required}} + +{{/vars}} +} + +impl {{classname}} { + pub fn new({{#vars}}{{^defaultValue}}{{name}}: {{#vendorExtensions}}{{#x-nullable}}swagger::Nullable<{{/x-nullable}}{{/vendorExtensions}}{{{datatype}}}{{#vendorExtensions}}{{#x-nullable}}>{{/x-nullable}}{{/vendorExtensions}}, {{/defaultValue}}{{/vars}}) -> {{classname}} { + {{classname}} { +{{#vars}} {{name}}: {{#defaultValue}}{{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}{{name}}{{/defaultValue}}, +{{/vars}} + } + } +} +{{/arrayModelType}}{{/dataType}}{{/isEnum}}{{/model}}{{/models}}{{#usesXmlNamespaces}} +//XML namespaces +pub mod namespaces { + lazy_static!{ + {{#models}}{{#model}}{{#xmlNamespace}}pub static ref {{#vendorExtensions}}{{upperCaseName}}{{/vendorExtensions}}: String = "{{xmlNamespace}}".to_string(); + {{/xmlNamespace}}{{/model}}{{/models}} + } +} +{{/usesXmlNamespaces}} diff --git a/modules/swagger-codegen/src/main/resources/rust-server/server.mustache b/modules/swagger-codegen/src/main/resources/rust-server/server.mustache new file mode 100644 index 00000000000..6091527455f --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust-server/server.mustache @@ -0,0 +1,338 @@ +#![allow(unused_extern_crates)] +extern crate serde_ignored; +extern crate iron; +extern crate router; +extern crate bodyparser; +extern crate urlencoded; +extern crate uuid; +extern crate chrono; +{{#apiHasFile}}extern crate multipart;{{/apiHasFile}} + +use futures::Future; +use futures::future; +use futures::{stream, Stream}; +use hyper; +use hyper::header::{Headers, ContentType}; +use self::iron::prelude::*; +use self::iron::{status, modifiers, BeforeMiddleware}; +use self::iron::url::percent_encoding::percent_decode; +use self::router::Router; +use self::urlencoded::UrlEncodedQuery; +use mimetypes; +{{#apiHasFile}}use multipart::server::{Multipart, SaveResult};{{/apiHasFile}} + +use serde_json; +{{#usesXml}}use serde_xml_rs;{{/usesXml}} + +#[allow(unused_imports)] +use std::collections::{HashMap, BTreeMap}; +#[allow(unused_imports)] +use swagger; +use std::io::Error; + +#[allow(unused_imports)] +use std::collections::BTreeSet; + +use swagger::auth::{Authorization, AuthData, Scopes}; +use swagger::{ApiError, Context, XSpanId}; + +use {Api{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}, + {{operationId}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + }; +#[allow(unused_imports)] +use models; + +header! { (Warning, "Warning") => [String] } + +/// Create a new router for `Api` +pub fn router(api: T) -> Router where T: Api + Send + Sync + Clone + 'static { + let mut router = Router::new(); + add_routes(&mut router, api); + router +} + +/// Add routes for `Api` to a provided router. +/// +/// Note that these routes are added straight onto the router. This means that if the router +/// already has a route for an endpoint which clashes with those provided by this API, then the +/// old route will be lost. +/// +/// It is generally a bad idea to add routes in this way to an existing router, which may have +/// routes on it for other APIs. Distinct APIs should be behind distinct paths to encourage +/// separation of interfaces, which this function does not enforce. APIs should not overlap. +/// +/// Alternative approaches include: +/// +/// - generate an `iron::middleware::Handler` (usually a `router::Router` or +/// `iron::middleware::chain`) for each interface, and add those handlers inside an existing +/// router, mounted at different paths - so the interfaces are separated by path +/// - use a different instance of `iron::Iron` for each interface - so the interfaces are +/// separated by the address/port they listen on +/// +/// This function exists to allow legacy code, which doesn't separate its APIs properly, to make +/// use of this crate. +#[deprecated(note="APIs should not overlap - only for use in legacy code.")] +pub fn route(router: &mut Router, api: T) where T: Api + Send + Sync + Clone + 'static { + add_routes(router, api) +} + +/// Add routes for `Api` to a provided router +fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone + 'static { +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} + let api_clone = api.clone(); + router.{{#vendorExtensions}}{{httpmethod}}{{/vendorExtensions}}( + "{{#vendorExtensions}}{{basePathWithoutHost}}{{path}}{{/vendorExtensions}}", + move |req: &mut Request| { + let mut context = Context::default(); + + // Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists). + fn handle_request(req: &mut Request, api: &T, context: &mut Context) -> Result where T: Api { + + context.x_span_id = Some(req.headers.get::().map(XSpanId::to_string).unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string())); + context.auth_data = req.extensions.remove::(); + context.authorization = req.extensions.remove::(); + + {{#hasAuthMethods}} + let authorization = context.authorization.as_ref().ok_or_else(|| { + Response::with(( + status::Forbidden, + "Unauthenticated".to_string() + )) + })?; + + {{#authMethods}} + {{#isOAuth}} + // Authorization + if let Scopes::Some(ref scopes) = authorization.scopes { + let required_scopes: BTreeSet = vec![ + {{#scopes}} + "{{scope}}".to_string(), // {{description}} + {{/scopes}} + ].into_iter().collect(); + + if !required_scopes.is_subset(scopes) { + let missing_scopes = required_scopes.difference(scopes); + return Err(Response::with(( + status::Forbidden, + missing_scopes.fold( + "Insufficient authorization, missing scopes".to_string(), + |s, scope| format!("{} {}", s, scope) + ) + ))); + } + } + {{/isOAuth}} + {{/authMethods}} + {{/hasAuthMethods}} + +{{#pathParams}}{{#-first}} + // Path parameters +{{/-first}} let param_{{paramName}} = { + let param = req.extensions.get::().ok_or_else(|| Response::with((status::InternalServerError, "An internal error occurred".to_string())))? + .find("{{baseName}}").ok_or_else(|| Response::with((status::BadRequest, "Missing path parameter {{baseName}}".to_string())))?; + percent_decode(param.as_bytes()).decode_utf8() + .map_err(|_| Response::with((status::BadRequest, format!("Couldn't percent-decode path parameter as UTF-8: {}", param))))? + .parse().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse path parameter {{baseName}}: {}", e))))? + }; +{{/pathParams}} +{{#headerParams}}{{#-first}} + // Header parameters +{{/-first}} header! { (Request{{vendorExtensions.typeName}}, "{{baseName}}") => {{#isListContainer}}({{{baseType}}})*{{/isListContainer}}{{^isListContainer}}[{{{dataType}}}]{{/isListContainer}} } +{{#required}} let param_{{paramName}} = req.headers.get::().ok_or_else(|| Response::with((status::BadRequest, "Missing or invalid required header {{baseName}}".to_string())))?.0.clone(); +{{/required}}{{^required}} let param_{{paramName}} = req.headers.get::().map(|header| header.0.clone()); +{{/required}}{{/headerParams}} +{{#queryParams}}{{#-first}} + // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response) + let query_params = req.get::().unwrap_or_default(); +{{/-first}} let param_{{paramName}} = query_params.get("{{baseName}}") +{{#required}} .ok_or_else(|| Response::with((status::BadRequest, "Missing required query parameter {{baseName}}".to_string())))? +{{#isListContainer}} .iter().flat_map(|x| x.parse::<{{{baseType}}}>()).collect::>(); +{{/isListContainer}}{{^isListContainer}} .first().ok_or_else(|| Response::with((status::BadRequest, "Required query parameter {{baseName}} was empty".to_string())))? + .parse::<{{{dataType}}}>().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse query parameter {{baseName}} - doesn't match schema: {}", e))))?; +{{/isListContainer}}{{/required}}{{^required}}{{#isListContainer}} .map(|list| list.iter().flat_map(|x| x.parse::<{{{baseType}}}>()).collect::>()); +{{/isListContainer}}{{^isListContainer}} .and_then(|list| list.first()).and_then(|x| x.parse::<{{{dataType}}}>().ok()); +{{/isListContainer}}{{/required}}{{/queryParams}} +{{#bodyParams}}{{#-first}} // Body parameters (note that non-required body parameters will ignore garbage + // values, rather than causing a 400 response). Produce warning header and logs for + // any unused fields. +{{/-first}}{{#required}} + let param_{{paramName}}_raw = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter {{baseName}} - not valid UTF-8: {}", e))))?; +{{/required}}{{^required}} + let param_{{paramName}}_raw = req.get::().unwrap_or(None); +{{/required}} + let mut unused_elements = Vec::new(); + + let param_{{paramName}} = if let Some(param_{{paramName}}_raw) = param_{{paramName}}_raw { {{#vendorExtensions}}{{#consumesXml}} + let deserializer = &mut serde_xml_rs::de::Deserializer::new_from_reader(param_{{paramName}}_raw.as_bytes());{{/consumesXml}}{{^consumesXml}} + let deserializer = &mut serde_json::Deserializer::from_str(¶m_{{paramName}}_raw);{{/consumesXml}}{{/vendorExtensions}} + + let param_{{paramName}}: Option<{{{dataType}}}> = serde_ignored::deserialize(deserializer, |path| { + warn!("Ignoring unknown field in body: {}", path); + unused_elements.push(path.to_string()); + }){{#required}}.map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter {{baseName}} - doesn't match schema: {}", e))))?{{/required}}{{^required}}.unwrap_or(None){{/required}}; + + param_{{paramName}} + } else { + None + };{{#required}} + let param_{{paramName}} = param_{{paramName}}.ok_or_else(|| Response::with((status::BadRequest, "Missing required body parameter {{baseName}}".to_string())))?{{/required}}; + +{{/bodyParams}} +{{#formParams}} + {{#-first}} + // Form parameters +{{/-first}}{{/formParams}}{{#vendorExtensions}}{{#hasFile}} + // Expecting a multipart form, extract and parse it now. + let mut entries = match Multipart::from_request(req) { + Ok(mut multipart) => { + + match multipart.save().temp() { + SaveResult::Full(entries) => { + Ok(entries) + }, + _ => { + Err(Response::with((status::InternalServerError, format!("Unable to process all message parts")))) + }, + } + }, + Err(e) => { + // Unable to parse as multipart + Err(Response::with((status::BadRequest, format!("Couldn't parse body as multipart")))) + } + }?; + +{{/hasFile}}{{/vendorExtensions}}{{#allParams}}{{#isFormParam}}{{#isFile}} + + let param_{{paramName}} = entries.fields.remove("{{paramName}}"); + + let param_{{paramName}} = match param_{{paramName}} { + Some(body) => { + Ok({let bytes = body.as_bytes(); + {{^required}}Some({{/required}} + Box::new(stream::once(Ok(bytes.to_vec()))) as Box, Error=Error> + Send> + {{^required}}){{/required}}} + ) + } + None => {Err(Response::with((status::BadRequest, format!("Body part not found!"))))} + }?; +{{/isFile}} + let param_{{paramName}} = {{#isFile}}{{^required}}Box::new(future::ok({{/required}}param_{{paramName}}{{^required}})){{/required}};{{/isFile}}{{^isFile}}{{^isContainer}}{{#vendorExtensions}}{{{example}}};{{/vendorExtensions}}{{/isContainer}}{{#isListContainer}}{{#required}}Vec::new();{{/required}}{{^required}}None;{{/required}}{{/isListContainer}}{{#isMapContainer}}None;{{/isMapContainer}}{{/isFile}} + {{/isFormParam}} +{{/allParams}} + + match api.{{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}({{#allParams}}param_{{paramName}}{{#isListContainer}}.as_ref(){{/isListContainer}}, {{/allParams}}context).wait() { + Ok(rsp) => match rsp { +{{#responses}} + {{operationId}}Response::{{message}}{{#dataType}}{{^headers}}(body){{/headers}}{{#headers}}{{#-first}}{ body{{/-first}}{{/headers}}{{/dataType}}{{#headers}}{{#-first}}{{^dataType}}{ {{/dataType}}{{#dataType}}, {{/dataType}}{{/-first}}{{^-first}}, {{/-first}}{{name}}{{#-last}} }{{/-last}}{{/headers}} => { +{{^isFile}} +{{#dataType}}{{#vendorExtensions}}{{#producesXml}} +{{^has_namespace}} let body_string = serde_xml_rs::to_string(&body).expect("impossible to fail to serialize");{{/has_namespace}}{{#has_namespace}} + let mut namespaces = BTreeMap::new(); + // An empty string is used to indicate a global namespace in xmltree. + namespaces.insert("".to_string(), models::namespaces::{{uppercase_data_type}}.clone()); + let body_string = serde_xml_rs::to_string_with_namespaces(&body, namespaces).expect("impossible to fail to serialize");{{/has_namespace}}{{/producesXml}}{{^producesXml}} + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize");{{/producesXml}}{{/vendorExtensions}}{{/dataType}} + + let mut response = Response::with((status::Status::from_u16({{code}}){{#dataType}}, body_string{{/dataType}}));{{/isFile}}{{#isFile}} body.fold(Vec::new(), |mut body, chunk| { + body.extend(chunk.iter()); + future::ok::, Error>(body) + }) + + // Block whilst waiting for the stream to complete + .wait() + + // It's possible that errors were received in the stream, if this is the case then we can't return a success response to the client and instead must return an internal error. + .map_err(|_| Response::with((status::InternalServerError, "An internal error occurred".to_string()))) + + // Assuming no errors then create an Iron response. + .map(|rsp| { + let mut response = Response::new(); + response.status = Some(status::Status::from_u16({{code}})); + response.body = Some(Box::new(rsp)); +{{/isFile}} +{{#headers}}{{#isFile}} {{/isFile}} header! { (Response{{nameInCamelCase}}, "{{baseName}}") => [{{{datatype}}}] } +{{#isFile}} {{/isFile}} response.headers.set(Response{{nameInCamelCase}}({{name}})); +{{/headers}} + {{#produces}}{{#-first}} +{{#dataType}}{{#isFile}} {{/isFile}} response.headers.set(ContentType(mimetypes::responses::{{#vendorExtensions}}{{uppercase_operation_id}}_{{uppercase_message}}{{/vendorExtensions}}.clone()));{{/dataType}} +{{/-first}}{{/produces}} +{{#isFile}} {{/isFile}} context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); +{{#bodyParams}} if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + }{{/bodyParams}} + {{^isFile}}Ok(response){{/isFile}}{{#isFile}} response + }){{/isFile}} + }, +{{/responses}} + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + Err(Response::with((status::InternalServerError, "An internal error occurred".to_string()))) + } + } + } + + handle_request(req, &api_clone, &mut context).or_else(|mut response| { + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + Ok(response) + }) + }, + "{{operationId}}"); +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +} + +/// Middleware to extract authentication data from request +pub struct ExtractAuthData; + +impl BeforeMiddleware for ExtractAuthData { + fn before(&self, req: &mut Request) -> IronResult<()> { + {{#authMethods}} + {{#isBasic}} + { + use hyper::header::{Authorization, Basic, Bearer}; + use std::ops::Deref; + if let Some(basic) = req.headers.get::>() { + req.extensions.insert::(AuthData::Basic(basic.deref().clone())); + return Ok(()); + } + } + {{/isBasic}} + {{#isOAuth}} + { + use hyper::header::{Authorization, Basic, Bearer}; + use std::ops::Deref; + if let Some(bearer) = req.headers.get::>() { + req.extensions.insert::(AuthData::Bearer(bearer.deref().clone())); + return Ok(()); + } + } + {{/isOAuth}} + {{#isApiKey}} + {{#isKeyInHeader}} + { + header! { (ApiKey{{-index}}, "{{keyParamName}}") => [String] } + if let Some(header) = req.headers.get::() { + req.extensions.insert::(AuthData::ApiKey(header.0.clone())); + return Ok(()); + } + } + {{/isKeyInHeader}} + {{#isKeyInQuery}} + { + let header = match req.get_ref::() { + Ok(query) => query.get("{{keyParamName}}").map(|v| v[0].clone()), + _ => None + }; + if let Some(key) = header { + req.extensions.insert::(AuthData::ApiKey(key)); + return Ok(()); + } + } + {{/isKeyInQuery}} + {{/isApiKey}} + {{/authMethods}} + + Ok(()) + } +} diff --git a/modules/swagger-codegen/src/main/resources/rust-server/swagger.mustache b/modules/swagger-codegen/src/main/resources/rust-server/swagger.mustache new file mode 100644 index 00000000000..51560926bba --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust-server/swagger.mustache @@ -0,0 +1 @@ +{{{swagger-yaml}}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/rust/.travis.yml b/modules/swagger-codegen/src/main/resources/rust/.travis.yml new file mode 100644 index 00000000000..22761ba7ee1 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust/.travis.yml @@ -0,0 +1 @@ +language: rust diff --git a/modules/swagger-codegen/src/main/resources/rust/Cargo.mustache b/modules/swagger-codegen/src/main/resources/rust/Cargo.mustache new file mode 100644 index 00000000000..f3b94581c17 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust/Cargo.mustache @@ -0,0 +1,17 @@ +[package] +name = "{{{packageName}}}" +version = "{{{packageVersion}}}" +authors = ["Swagger Codegen team and contributors"] + +[dependencies] +serde = "1.0" +serde_derive = "1.0" +serde_yaml = "0.7" +serde_json = "1.0" +base64 = "~0.7.0" +futures = "0.1.16" +hyper = "0.11.6" +url = "1.5" + +[dev-dependencies] +tokio-core = "*" diff --git a/modules/swagger-codegen/src/main/resources/rust/README.mustache b/modules/swagger-codegen/src/main/resources/rust/README.mustache new file mode 100644 index 00000000000..67cc0280e2c --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust/README.mustache @@ -0,0 +1,96 @@ +# Rust API client for {{packageName}} + +{{#appDescription}} +{{{appDescription}}} +{{/appDescription}} + +## Overview +This API client was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the [swagger-spec](https://github.com/swagger-api/swagger-spec) from a remote server, you can easily generate an API client. + +- API version: {{appVersion}} +- Package version: {{packageVersion}} +{{^hideGenerationTimestamp}} +- Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} +- Build package: {{generatorClass}} +{{#infoUrl}} +For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + +## Installation +Put the package under your project folder and add the following in import: +``` + "./{{packageName}}" +``` + +## Documentation for API Endpoints + +All URIs are relative to *{{basePath}}* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + +## Documentation For Models + +{{#models}}{{#model}} - [{{{classname}}}]({{modelDocPath}}{{{classname}}}.md) +{{/model}}{{/models}} + +## Documentation For Authorization +{{^authMethods}} Endpoints do not require authorization. +{{/authMethods}}{{#authMethods}}{{#last}} Authentication schemes defined for the API:{{/last}}{{/authMethods}} +{{#authMethods}} +## {{{name}}} +{{#isApiKey}}- **Type**: API key + +Example +``` + auth := context.WithValue(context.TODO(), sw.ContextAPIKey, sw.APIKey{ + Key: "APIKEY", + Prefix: "Bearer", // Omit if not necessary. + }) + r, err := client.Service.Operation(auth, args) +``` +{{/isApiKey}} +{{#isBasic}}- **Type**: HTTP basic authentication + +Example +``` + auth := context.WithValue(context.TODO(), sw.ContextBasicAuth, sw.BasicAuth{ + UserName: "username", + Password: "password", + }) + r, err := client.Service.Operation(auth, args) +``` +{{/isBasic}} +{{#isOAuth}}- **Type**: OAuth +- **Flow**: {{{flow}}} +- **Authorization URL**: {{{authorizationUrl}}} +- **Scopes**: {{^scopes}}N/A{{/scopes}} +{{#scopes}} - **{{{scope}}}**: {{{description}}} +{{/scopes}} + +Example +``` + auth := context.WithValue(context.TODO(), sw.ContextAccessToken, "ACCESSTOKENSTRING") + r, err := client.Service.Operation(auth, args) +``` + +Or via OAuth2 module to automaticly refresh tokens and perform user authentication. +``` + import "golang.org/x/oauth2" + + / .. Perform OAuth2 round trip request and obtain a token .. // + + tokenSource := oauth2cfg.TokenSource(createContext(httpClient), &token) + auth := context.WithValue(oauth2.NoContext, sw.ContextOAuth2, tokenSource) + r, err := client.Service.Operation(auth, args) +``` +{{/isOAuth}} +{{/authMethods}} + +## Author + +{{#apiInfo}}{{#apis}}{{^hasMore}}{{infoEmail}} +{{/hasMore}}{{/apis}}{{/apiInfo}} diff --git a/modules/swagger-codegen/src/main/resources/rust/api.mustache b/modules/swagger-codegen/src/main/resources/rust/api.mustache new file mode 100644 index 00000000000..811f8870448 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust/api.mustache @@ -0,0 +1,96 @@ +{{>partial_header}} +use std::rc::Rc; +use std::borrow::Borrow; + +use hyper; +use serde_json; +use futures; +use futures::{Future, Stream}; + +use super::{Error, configuration}; + +pub struct {{{classname}}}Client { + configuration: Rc>, +} + +impl {{{classname}}}Client { + pub fn new(configuration: Rc>) -> {{{classname}}}Client { + {{{classname}}}Client { + configuration: configuration, + } + } +} + +pub trait {{classname}} { +{{#operations}} +{{#operation}} + fn {{{operationId}}}(&self, {{#allParams}}{{paramName}}: {{#isString}}&str{{/isString}}{{^isString}}{{^isPrimitiveType}}{{^isContainer}}::models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isString}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) -> Box>; +{{/operation}} +{{/operations}} +} + + +impl{{classname}} for {{classname}}Client { +{{#operations}} +{{#operation}} + fn {{{operationId}}}(&self, {{#allParams}}{{paramName}}: {{#isString}}&str{{/isString}}{{^isString}}{{^isPrimitiveType}}{{^isContainer}}::models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isString}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) -> Box> { + let configuration: &configuration::Configuration = self.configuration.borrow(); + + let method = hyper::Method::{{httpMethod}}; + + {{^hasQueryParams}} + let uri_str = format!("{}{{{path}}}", configuration.base_path{{#pathParams}}, {{baseName}}={{paramName}}{{#isListContainer}}.join(",").as_ref(){{/isListContainer}}{{/pathParams}}); + {{/hasQueryParams}} + {{#hasQueryParams}} + let query = ::url::form_urlencoded::Serializer::new(String::new()) + {{#queryParams}} + .append_pair("{{baseName}}", &{{paramName}}{{#isListContainer}}.join(","){{/isListContainer}}.to_string()) + {{/queryParams}} + .finish(); + let uri_str = format!("{}{{{path}}}{}", configuration.base_path, query{{#pathParams}}, {{baseName}}={{paramName}}{{#isListContainer}}.join(",").as_ref(){{/isListContainer}}{{/pathParams}}); + {{/hasQueryParams}} + + let uri = uri_str.parse(); + // TODO(farcaller): handle error + // if let Err(e) = uri { + // return Box::new(futures::future::err(e)); + // } + let mut req = hyper::Request::new(method, uri.unwrap()); + + {{#hasHeaderParams}} + { + let mut headers = req.headers_mut(); + {{#headerParams}} + headers.set_raw("{{baseName}}", {{paramName}}{{#isListContainer}}.join(",").as_ref(){{/isListContainer}}); + {{/headerParams}} + } + {{/hasHeaderParams}} + + {{#hasBodyParam}} + {{#bodyParams}} + let serialized = serde_json::to_string(&body).unwrap(); + req.headers_mut().set(hyper::header::ContentType::json()); + req.headers_mut().set(hyper::header::ContentLength(serialized.len() as u64)); + req.set_body(serialized); + {{/bodyParams}} + {{/hasBodyParam}} + + // send request + Box::new( + configuration.client.request(req).and_then(|res| { res.body().concat2() }) + .map_err(|e| Error::from(e)) + {{^returnType}} + .and_then(|_| futures::future::ok(())) + {{/returnType}} + {{#returnType}} + .and_then(|body| { + let parsed: Result<{{{returnType}}}, _> = serde_json::from_slice(&body); + parsed.map_err(|e| Error::from(e)) + }).map_err(|e| Error::from(e)) + {{/returnType}} + ) + } + +{{/operation}} +{{/operations}} +} diff --git a/modules/swagger-codegen/src/main/resources/rust/api_doc.mustache b/modules/swagger-codegen/src/main/resources/rust/api_doc.mustache new file mode 100644 index 00000000000..70d0b96ce86 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust/api_doc.mustache @@ -0,0 +1,50 @@ +# {{invokerPackage}}\{{classname}}{{#description}} +{{description}}{{/description}} + +All URIs are relative to *{{basePath}}* + +Method | HTTP request | Description +------------- | ------------- | ------------- +{{#operations}}{{#operation}}[**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} +{{/operation}}{{/operations}} + +{{#operations}} +{{#operation}} +# **{{{operationId}}}** +> {{#returnType}}{{{returnType}}} {{/returnType}}{{{operationId}}}({{#authMethods}}ctx, {{/authMethods}}{{#allParams}}{{#required}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/required}}{{/allParams}}{{#hasOptionalParams}}optional{{/hasOptionalParams}}) +{{{summary}}}{{#notes}} + +{{{notes}}}{{/notes}} + +### Required Parameters +{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}} +Name | Type | Description | Notes +------------- | ------------- | ------------- | -------------{{#authMethods}} + **ctx** | **context.Context** | context containing the authentication | nil if no authentication{{/authMethods}}{{/-last}}{{/allParams}}{{#allParams}}{{#required}} + **{{paramName}}** | {{#isFile}}**{{dataType}}**{{/isFile}}{{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}[**{{dataType}}**]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} | {{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}}{{/required}}{{/allParams}}{{#hasOptionalParams}} + **optional** | **map[string]interface{}** | optional parameters | nil if no parameters + +### Optional Parameters +Optional parameters are passed through a map[string]interface{}. +{{#allParams}}{{#-last}} +Name | Type | Description | Notes +------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}}{{#allParams}} + **{{paramName}}** | {{#isFile}}**{{dataType}}**{{/isFile}}{{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}[**{{dataType}}**]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} | {{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}}{{/allParams}}{{/hasOptionalParams}} + +### Return type + +{{#returnType}}{{#returnTypeIsPrimitive}}**{{{returnType}}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{{returnType}}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}} (empty response body){{/returnType}} + +### Authorization + +{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{{name}}}](../README.md#{{{name}}}){{^-last}}, {{/-last}}{{/authMethods}} + +### HTTP request headers + + - **Content-Type**: {{#consumes}}{{{mediaType}}}{{#hasMore}}, {{/hasMore}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} + - **Accept**: {{#produces}}{{{mediaType}}}{{#hasMore}}, {{/hasMore}}{{/produces}}{{^produces}}Not defined{{/produces}} + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +{{/operation}} +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/rust/api_mod.mustache b/modules/swagger-codegen/src/main/resources/rust/api_mod.mustache new file mode 100644 index 00000000000..5b62b897cdd --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust/api_mod.mustache @@ -0,0 +1,38 @@ +use hyper; +use serde_json; + +#[derive(Debug)] +pub enum Error { + Hyper(hyper::Error), + Serde(serde_json::Error), +} + +impl From for Error { + fn from(e: hyper::Error) -> Self { + return Error::Hyper(e) + } +} + +impl From for Error { + fn from(e: serde_json::Error) -> Self { + return Error::Serde(e) + } +} + +use super::models::*; + +{{#apiInfo}} +{{#apis}} +mod {{classFilename}}; +{{#operations}} +{{#operation}} +{{#-last}} +pub use self::{{classFilename}}::{ {{classname}}, {{classname}}Client }; +{{/-last}} +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} + +pub mod configuration; +pub mod client; diff --git a/modules/swagger-codegen/src/main/resources/rust/client.mustache b/modules/swagger-codegen/src/main/resources/rust/client.mustache new file mode 100644 index 00000000000..faaea0dbd2f --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust/client.mustache @@ -0,0 +1,56 @@ +use std::rc::Rc; + +use hyper; +use super::configuration::Configuration; + +pub struct APIClient { + configuration: Rc>, +{{#apiInfo}} +{{#apis}} +{{#operations}} +{{#operation}} + {{#-last}} + {{classFilename}}: Box<::apis::{{classname}}>, + {{/-last}} +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} +} + +impl APIClient { + pub fn new(configuration: Configuration) -> APIClient { + let rc = Rc::new(configuration); + + APIClient { + configuration: rc.clone(), +{{#apiInfo}} +{{#apis}} +{{#operations}} +{{#operation}} + {{#-last}} + {{classFilename}}: Box::new(::apis::{{classname}}Client::new(rc.clone())), + {{/-last}} +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} + } + } + +{{#apiInfo}} +{{#apis}} +{{#operations}} +{{#operation}} +{{#-last}} + pub fn {{classFilename}}(&self) -> &::apis::{{classname}}{ + self.{{classFilename}}.as_ref() + } + +{{/-last}} +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} + +} diff --git a/modules/swagger-codegen/src/main/resources/rust/configuration.mustache b/modules/swagger-codegen/src/main/resources/rust/configuration.mustache new file mode 100644 index 00000000000..59e82af56b1 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust/configuration.mustache @@ -0,0 +1,16 @@ +{{>partial_header}} +use hyper; + +pub struct Configuration { + pub base_path: String, + pub client: hyper::client::Client, +} + +impl Configuration { + pub fn new(client: hyper::client::Client) -> Configuration { + Configuration { + base_path: "{{{basePath}}}".to_owned(), + client: client, + } + } +} diff --git a/modules/swagger-codegen/src/main/resources/rust/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/rust/git_push.sh.mustache new file mode 100755 index 00000000000..a2d75234837 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust/git_push.sh.mustache @@ -0,0 +1,52 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 swagger-petstore-perl "minor update" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 + +if [ "$git_user_id" = "" ]; then + git_user_id="{{{gitUserId}}}" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="{{{gitRepoId}}}" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="{{{releaseNote}}}" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=`git remote` +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://github.com/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' + diff --git a/modules/swagger-codegen/src/main/resources/rust/gitignore.mustache b/modules/swagger-codegen/src/main/resources/rust/gitignore.mustache new file mode 100644 index 00000000000..6aa106405a4 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust/gitignore.mustache @@ -0,0 +1,3 @@ +/target/ +**/*.rs.bk +Cargo.lock diff --git a/modules/swagger-codegen/src/main/resources/rust/lib.rs b/modules/swagger-codegen/src/main/resources/rust/lib.rs new file mode 100644 index 00000000000..2ad195c6fb1 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust/lib.rs @@ -0,0 +1,10 @@ +#[macro_use] +extern crate serde_derive; + +extern crate hyper; +extern crate serde_json; +extern crate futures; +extern crate url; + +pub mod apis; +pub mod models; diff --git a/modules/swagger-codegen/src/main/resources/rust/model.mustache b/modules/swagger-codegen/src/main/resources/rust/model.mustache new file mode 100644 index 00000000000..d49cc8dd4dd --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust/model.mustache @@ -0,0 +1,71 @@ +{{>partial_header}} +{{#models}} +{{#model}} +{{#description}} +/// {{{classname}}} : {{{description}}} +{{/description}} + +#[allow(unused_imports)] +use serde_json::Value; + +#[derive(Debug, Serialize, Deserialize)] +pub struct {{classname}} { +{{#vars}} + {{#description}} + /// {{{description}}} + {{/description}} + #[serde(rename = "{{baseName}}")] + {{name}}: {{^required}}Option<{{/required}}{{{datatype}}}{{^required}}>{{/required}}{{#hasMore}},{{/hasMore}} +{{/vars}} +} + +impl {{classname}} { + {{#description}} + /// {{{description}}} + {{/description}} + pub fn new({{#requiredVars}}{{name}}: {{{datatype}}}{{^-last}}, {{/-last}}{{/requiredVars}}) -> {{classname}} { + {{classname}} { + {{#vars}} + {{name}}: {{#required}}{{name}}{{/required}}{{^required}}{{#isListContainer}}None{{/isListContainer}}{{#isMapContainer}}None{{/isMapContainer}}{{^isContainer}}None{{/isContainer}}{{/required}}{{#hasMore}},{{/hasMore}} + {{/vars}} + } + } + + {{#vars}} + pub fn set_{{name}}(&mut self, {{name}}: {{{datatype}}}) { + self.{{name}} = {{^required}}Some({{name}}){{/required}}{{#required}}{{name}}{{/required}}; + } + + pub fn with_{{name}}(mut self, {{name}}: {{{datatype}}}) -> {{classname}} { + self.{{name}} = {{^required}}Some({{name}}){{/required}}{{#required}}{{name}}{{/required}}; + self + } + + pub fn {{name}}(&self) -> {{^required}}Option<{{/required}}&{{{datatype}}}{{^required}}>{{/required}} { + {{#required}}&{{/required}}self.{{name}}{{^required}}.as_ref(){{/required}} + } + + {{^required}} + pub fn reset_{{name}}(&mut self) { + self.{{name}} = None; + } + {{/required}} + + {{/vars}} +} + +{{#isEnum}} +// TODO enum +// List of {{{name}}} +//const ( +// {{#allowableValues}} +// {{#enumVars}} +// {{name}} {{{classname}}} = "{{{value}}}" +// {{/enumVars}} +// {{/allowableValues}} +//) +{{/isEnum}} + + +{{/model}} +{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/rust/model_doc.mustache b/modules/swagger-codegen/src/main/resources/rust/model_doc.mustache new file mode 100644 index 00000000000..25537b2c5ed --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust/model_doc.mustache @@ -0,0 +1,11 @@ +{{#models}}{{#model}}# {{classname}} + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +{{#vars}}**{{name}}** | {{#isPrimitiveType}}**{{{datatype}}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{^isContainer}}{{^isDateTime}}*{{/isDateTime}}{{/isContainer}}{{{datatype}}}**]({{complexType}}.md){{/isPrimitiveType}} | {{description}} | {{^required}}[optional] {{/required}}{{#readOnly}}[readonly] {{/readOnly}}{{#defaultValue}}[default to {{{.}}}]{{/defaultValue}} +{{/vars}} + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + +{{/model}}{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/rust/model_mod.mustache b/modules/swagger-codegen/src/main/resources/rust/model_mod.mustache new file mode 100644 index 00000000000..d2b7578f324 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust/model_mod.mustache @@ -0,0 +1,9 @@ +{{#models}} +{{#model}} +mod {{{classFilename}}}; +pub use self::{{{classFilename}}}::{{{classname}}}; +{{/model}} +{{/models}} + +// TODO(farcaller): sort out files +pub struct File; diff --git a/modules/swagger-codegen/src/main/resources/rust/partial_header.mustache b/modules/swagger-codegen/src/main/resources/rust/partial_header.mustache new file mode 100644 index 00000000000..b655ebb8269 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust/partial_header.mustache @@ -0,0 +1,13 @@ +/* + {{#appName}} + * {{{appName}}} + * + {{/appName}} + {{#appDescription}} + * {{{appDescription}}} + * + {{/appDescription}} + * {{#version}}OpenAPI spec version: {{{version}}}{{/version}} + * {{#infoEmail}}Contact: {{{infoEmail}}}{{/infoEmail}} + * Generated by: https://github.com/swagger-api/swagger-codegen.git + */ diff --git a/modules/swagger-codegen/src/main/resources/scala-lagom-server/README.mustache b/modules/swagger-codegen/src/main/resources/scala-lagom-server/README.mustache new file mode 100644 index 00000000000..938d7331cb7 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/scala-lagom-server/README.mustache @@ -0,0 +1,13 @@ +# Swagger generated scala-lagomApi + +## Overview +This server was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the +[OpenAPI-Spec](https://github.com/swagger-api/swagger-core/wiki) from a remote server, you can easily generate a server stub. This +is an example of building a swagger-enabled lagon-api. + +This example uses the [lagomframework](https://www.lagomframework.com) lagomframework. + +# Features which are not supported +- Form Parameters +- Seq in query parameters +- File Handling diff --git a/modules/swagger-codegen/src/main/resources/scala-lagom-server/api.mustache b/modules/swagger-codegen/src/main/resources/scala-lagom-server/api.mustache new file mode 100644 index 00000000000..cf79ddabd37 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/scala-lagom-server/api.mustache @@ -0,0 +1,62 @@ +{{>licenseInfo}} +package {{package}} + +import akka.{Done, NotUsed} +import com.lightbend.lagom.scaladsl.api.transport.Method +import com.lightbend.lagom.scaladsl.api.{Service, ServiceCall} +import play.api.libs.json._ +import com.lightbend.lagom.scaladsl.api.deser.PathParamSerializer + +{{#imports}}import {{import}} +{{/imports}} + +trait {{classname}} extends Service { + + + final override def descriptor = { + import Service._ + named("{{classname}}").withCalls( +{{#operations}} + {{#operation}} + restCall(Method.{{httpMethod}}, "{{path}}{{#queryParams.0}}?{{/queryParams.0}}{{#queryParams}}{{paramName}}{{#hasMore}}&{{/hasMore}}{{/queryParams}}", {{operationId}} _){{#hasMore}}, {{/hasMore}} + {{/operation}} +{{/operations}} + ).withAutoAcl(true) + } + +{{#operations}} + {{#operation}} + {{#hasQueryParams}} + {{#queryParams}}{{#isContainer}} + // {{{paramName}}}:{{dataType}} -- not yet supported Seq PathParamSerializers for multi value query parameters https://github.com/lagom/lagom/issues/643 + {{/isContainer}}{{/queryParams}}{{/hasQueryParams}}{{#hasFormParams}}{{#allParams}} + {{#isFormParam}} + // {{{paramName}}}:{{dataType}} -- not yet supported x-www-form-urlencoded{{/isFormParam}}{{/allParams}}{{/hasFormParams}}{{#hasHeaderParams}}{{#allParams}} + {{#isHeaderParam}} + // {{{paramName}}}:{{dataType}} -- not yet supported heder params{{/isHeaderParam}}{{/allParams}}{{/hasHeaderParams}} + /** + * {{summary}} + * {{notes}} + * {{#allParams}} {{^isBodyParam}} + * @param {{paramName}} {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/isBodyParam}}{{/allParams}} + * @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}{{#bodyParams}} Body Parameter {{description}} {{/bodyParams}} + */ + def {{operationId}}({{#queryParams}}{{^isContainer}}{{^isEnum}}{{paramName}}:{{#required}}{{dataType}}{{#defaultValue}} /* = {{{defaultValue}}}*/{{/defaultValue}}{{/required}} {{^required}} Option[{{dataType}}]{{#defaultValue}} /* = {{{defaultValue}}}*/{{/defaultValue}}{{^defaultValue}} = None{{/defaultValue}}{{/required}}{{#hasMore}},{{/hasMore}}{{/isEnum}}{{/isContainer}}{{#isEnum}}{{{paramName}}}: Option[{{classname}}{{enumName}}.{{classname}}{{enumName}}]{{/isEnum}}{{/queryParams}}{{#pathParams}}{{^isEnum}}{{paramName}}: {{#required}}{{dataType}}{{#defaultValue}} /* = {{{defaultValue}}}*/{{/defaultValue}}{{/required}}{{^required}}Option[{{dataType}}]{{#defaultValue}} /* = {{{defaultValue}}}*/{{/defaultValue}}{{^defaultValue}} = None{{/defaultValue}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/isEnum}}{{#isEnum}}{{{paramName}}}: Option[{{classname}}{{enumName}}.{{classname}}{{enumName}}]{{/isEnum}}{{/pathParams}}): ServiceCall[{{#bodyParams}}{{dataType}}{{/bodyParams}}{{^hasBodyParam}}NotUsed{{/hasBodyParam}} ,{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Done{{/returnType}}] + {{/operation}}{{/operations}} + +{{#operations}} + {{#operation}} + {{#allParams}} + {{#isEnum}} + object {{classname}}{{enumName}} extends Enumeration { + val {{#allowableValues}} {{#values}}{{.}}{{^-last}}, {{/-last}}{{/values}} = Value {{/allowableValues}} + type {{classname}}{{enumName}} = Value + implicit val format: Format[Value] = Format(Reads.enumNameReads(this), Writes.enumNameWrites[{{classname}}{{enumName}}.type]) + implicit val pathParamSerializer: PathParamSerializer[{{classname}}{{enumName}}] = PathParamSerializer.required("{{classname}}{{enumName}}")(withName)(_.toString) + } + {{/isEnum}} + {{/allParams}} + {{/operation}} + } +{{/operations}} + diff --git a/modules/swagger-codegen/src/main/resources/scala-lagom-server/build.properties.mustache b/modules/swagger-codegen/src/main/resources/scala-lagom-server/build.properties.mustache new file mode 100644 index 00000000000..c091b86ca46 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/scala-lagom-server/build.properties.mustache @@ -0,0 +1 @@ +sbt.version=0.13.16 diff --git a/modules/swagger-codegen/src/main/resources/scala-lagom-server/build.sbt.mustache b/modules/swagger-codegen/src/main/resources/scala-lagom-server/build.sbt.mustache new file mode 100644 index 00000000000..f6cede96bb3 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/scala-lagom-server/build.sbt.mustache @@ -0,0 +1,16 @@ +version := "{{artifactVersion}}" + +name := "{{artifactId}}" + +organization := "{{groupId}}" + +scalaVersion := "2.11.8" + +val playJsonDerivedCodecs = "org.julienrf" %% "play-json-derived-codecs" % "3.3" + +libraryDependencies ++= Seq( +lagomScaladslApi, +playJsonDerivedCodecs +) + + diff --git a/modules/swagger-codegen/src/main/resources/scala-lagom-server/gitignore.mustache b/modules/swagger-codegen/src/main/resources/scala-lagom-server/gitignore.mustache new file mode 100644 index 00000000000..eacfd7da6b1 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/scala-lagom-server/gitignore.mustache @@ -0,0 +1,30 @@ +*.class +*.log + +# sbt specific +.cache +.history +.lib/ +dist/* +target/ +lib_managed/ +src_managed/ +project/boot/ +project/plugins/project/ + +# Scala-IDE specific +.scala_dependencies +.worksheet +target +ideatarget +.target +bin +.cache-main +.cache-tests +.classpath +.project +.tmpBin +.factorypath +.settings +logs +.idea diff --git a/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/licenseInfo.mustache b/modules/swagger-codegen/src/main/resources/scala-lagom-server/licenseInfo.mustache similarity index 100% rename from modules/swagger-codegen/src/main/resources/TypeScript-Fetch/licenseInfo.mustache rename to modules/swagger-codegen/src/main/resources/scala-lagom-server/licenseInfo.mustache diff --git a/modules/swagger-codegen/src/main/resources/scala-lagom-server/model.mustache b/modules/swagger-codegen/src/main/resources/scala-lagom-server/model.mustache new file mode 100644 index 00000000000..2027c731c32 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/scala-lagom-server/model.mustache @@ -0,0 +1,38 @@ +{{>licenseInfo}} +package {{package}} +import play.api.libs.json._ +{{#imports}} +import {{import}} +{{/imports}} + +{{#models}} +{{#model}} +case class {{classname}} ( +{{#vars}} + {{#isEnum}} + {{{name}}}: Option[{{classname}}{{datatypeWithEnum}}.{{classname}}{{datatypeWithEnum}}]{{#hasMore}},{{/hasMore}} + {{/isEnum}} + {{^isEnum}} + {{#description}} + /* {{{description}}} */ + {{/description}} + {{{name}}}: {{^required}}Option[{{/required}}{{datatype}}{{^required}}]{{/required}}{{#hasMore}},{{/hasMore}} + {{/isEnum}} +{{/vars}} +) + +object {{classname}} { +implicit val format: Format[{{classname}}] = Json.format +} + +{{#vars}} + {{#isEnum}} +object {{classname}}{{datatypeWithEnum}} extends Enumeration { + val {{#allowableValues}} {{#values}}{{.}}{{^-last}}, {{/-last}}{{/values}} = Value{{/allowableValues}} + type {{classname}}{{datatypeWithEnum}} = Value + implicit val format: Format[Value] = Format(Reads.enumNameReads(this), Writes.enumNameWrites[{{classname}}{{datatypeWithEnum}}.type]) +} + {{/isEnum}} +{{/vars}} +{{/model}} +{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/scala-lagom-server/plugins.sbt.mustache b/modules/swagger-codegen/src/main/resources/scala-lagom-server/plugins.sbt.mustache new file mode 100644 index 00000000000..2e1dfa4e058 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/scala-lagom-server/plugins.sbt.mustache @@ -0,0 +1,5 @@ +addSbtPlugin("com.lightbend.lagom" % "lagom-sbt-plugin" % "1.3.8") +// Needed for importing the project into Eclipse +addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.1.0") +// The ConductR plugin +addSbtPlugin("com.lightbend.conductr" % "sbt-conductr" % "2.3.5") diff --git a/modules/swagger-codegen/src/main/resources/scala/README.mustache b/modules/swagger-codegen/src/main/resources/scala/README.mustache new file mode 100644 index 00000000000..85c72cd1d16 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/scala/README.mustache @@ -0,0 +1,129 @@ +# NAME + +{{appName}} + +{{#appDescription}}{{{appDescription}}}{{/appDescription}} + +# VERSION + +Automatically generated by the [Swagger Codegen](https://github.com/swagger-api/swagger-codegen) project: + +- API version: {{appVersion}} +- Package version: {{moduleVersion}} +{{^hideGenerationTimestamp}} +- Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} +- Build package: {{generatorClass}} +{{#infoUrl}} +For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + +# Requirements + +Building the API client library requires [Maven](https://maven.apache.org/) to be installed. + +## Installation + +To install the API client library to your local Maven repository, simply execute: + +```shell +mvn install +``` + +To deploy it to a remote Maven repository instead, configure the settings of the repository and execute: + +```shell +mvn deploy +``` + +Refer to the [official documentation](https://maven.apache.org/plugins/maven-deploy-plugin/usage.html) for more information. + +### Maven users + +Add this dependency to your project's POM: + +```xml + + {{{groupId}}} + {{{artifactId}}} + {{{artifactVersion}}} + compile + +``` + +### Gradle users + +Add this dependency to your project's build file: + +```groovy +compile "{{{groupId}}}:{{{artifactId}}}:{{{artifactVersion}}}" +``` + +### SBT users + +```scala +libraryDependencies += "{{{groupId}}}" % "{{{artifactId}}}" % "{{{artifactVersion}}}" +``` + +## Documentation for API Endpoints + +All URIs are relative to *{{basePath}}* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + +## Documentation for Models + +{{#models}}{{#model}} - [{{classname}}]({{modelDocPath}}{{classname}}.md) +{{/model}}{{/models}} + +## Documentation for Authorization + +{{^authMethods}}All endpoints do not require authorization. +{{/authMethods}}Authentication schemes defined for the API: +{{#authMethods}}### {{name}} + +{{#isApiKey}}- **Type**: API key +- **API key parameter name**: {{keyParamName}} +- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}} +{{/isApiKey}} +{{#isBasic}}- **Type**: HTTP basic authentication +{{/isBasic}} +{{#isOAuth}}- **Type**: OAuth +- **Flow**: {{flow}} +- **Authorization URL**: {{authorizationUrl}} +- **Scopes**: {{^scopes}}N/A{{/scopes}} +{{#scopes}} - {{scope}}: {{description}} +{{/scopes}} +{{/isOAuth}} + +{{/authMethods}} + + +# BUILDING YOUR LIBRARY + +See the homepage `https://github.com/swagger-api/swagger-codegen` for full details. +But briefly, clone the git repository, build the codegen codebase, set up your build +config file, then run the API build script. You will need git, Java 7 or 8 and Apache +maven 3.0.3 or better already installed. + +Your library files will be built under `WWW::MyProjectName`. + + $ git clone https://github.com/swagger-api/swagger-codegen.git + $ cd swagger-codegen + $ mvn package + $ java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jar generate \ + -i [URL or file path to JSON swagger API spec] \ + -l akka-scala \ + -c /path/to/config/file.json \ + -o /path/to/output/folder + +Bang, all done. Run the `autodoc` script in the `bin` directory to see the API +you just built. + +## Author + +{{#apiInfo}}{{#apis}}{{^hasMore}}{{infoEmail}} +{{/hasMore}}{{/apis}}{{/apiInfo}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/scala/api.mustache b/modules/swagger-codegen/src/main/resources/scala/api.mustache index c3dd3effe7d..ea4f035b6b5 100644 --- a/modules/swagger-codegen/src/main/resources/scala/api.mustache +++ b/modules/swagger-codegen/src/main/resources/scala/api.mustache @@ -1,10 +1,11 @@ {{>licenseInfo}} package {{package}} +import java.text.SimpleDateFormat + {{#imports}}import {{import}} {{/imports}} -import {{invokerPackage}}.ApiInvoker -import {{invokerPackage}}.ApiException +import {{invokerPackage}}.{ApiInvoker, ApiException} import com.sun.jersey.multipart.FormDataMultiPart import com.sun.jersey.multipart.file.FileDataBodyPart @@ -16,13 +17,46 @@ import java.util.Date import scala.collection.mutable.HashMap +import com.wordnik.swagger.client._ +import scala.concurrent.Future +import collection.mutable + +import java.net.URI + +import com.wordnik.swagger.client.ClientResponseReaders.Json4sFormatsReader._ +import com.wordnik.swagger.client.RequestWriters.Json4sFormatsWriter._ + +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent._ +import scala.concurrent.duration._ +import scala.util.{Failure, Success, Try} + {{#operations}} -class {{classname}}(val defBasePath: String = "{{{basePath}}}", - defApiInvoker: ApiInvoker = ApiInvoker) { - var basePath = defBasePath - var apiInvoker = defApiInvoker +class {{classname}}( + val defBasePath: String = "{{{basePath}}}", + defApiInvoker: ApiInvoker = ApiInvoker +) { - def addHeader(key: String, value: String) = apiInvoker.defaultHeaders += key -> value + implicit val formats = new org.json4s.DefaultFormats { + override def dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS+0000") + } + implicit val stringReader = ClientResponseReaders.StringReader + implicit val unitReader = ClientResponseReaders.UnitReader + implicit val jvalueReader = ClientResponseReaders.JValueReader + implicit val jsonReader = JsonFormatsReader + implicit val stringWriter = RequestWriters.StringWriter + implicit val jsonWriter = JsonFormatsWriter + + var basePath: String = defBasePath + var apiInvoker: ApiInvoker = defApiInvoker + + def addHeader(key: String, value: String): mutable.HashMap[String, String] = { + apiInvoker.defaultHeaders += key -> value + } + + val config = SwaggerConfig.forUrl(new URI(defBasePath)) + val client = new RestClient(config) + val helper = new {{classname}}AsyncHelper(client, config) {{#operation}} /** @@ -31,98 +65,82 @@ class {{classname}}(val defBasePath: String = "{{{basePath}}}", {{#allParams}} * @param {{paramName}} {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} {{/allParams}} * @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} */ - def {{operationId}}({{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{#defaultValue}} /* = {{{defaultValue}}}*/{{/defaultValue}}{{/required}}{{^required}}Option[{{dataType}}]{{#defaultValue}} /* = {{{defaultValue}}}*/{{/defaultValue}}{{^defaultValue}} = None{{/defaultValue}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{#returnType}}: Option[{{returnType}}]{{/returnType}} = { - // create path and map variables - val path = "{{{path}}}".replaceAll("\\{format\\}", "json"){{#pathParams}}.replaceAll("\\{" + "{{baseName}}" + "\\}",apiInvoker.escape({{paramName}})){{/pathParams}} + def {{operationId}}({{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{#defaultValue}} /* = {{{defaultValue}}}*/{{/defaultValue}}{{/required}}{{^required}}Option[{{dataType}}]{{#defaultValue}} = None /* = {{{defaultValue}}}*/{{/defaultValue}}{{^defaultValue}} = None{{/defaultValue}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{#returnType}}: Option[{{returnType}}]{{/returnType}} = { + val await = Try(Await.result({{operationId}}Async({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}), Duration.Inf)) + await match { + case Success(i) => Some(await.get) + case Failure(t) => None + } + } - val contentTypes = List({{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}}{{^consumes}}"application/json"{{/consumes}}) - val contentType = contentTypes(0) + /** + * {{summary}} asynchronously + * {{notes}} +{{#allParams}} * @param {{paramName}} {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} +{{/allParams}} * @return Future({{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}) + */ + def {{operationId}}Async({{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{#defaultValue}} /* = {{{defaultValue}}}*/{{/defaultValue}}{{/required}}{{^required}}Option[{{dataType}}]{{#defaultValue}} = None /* = {{{defaultValue}}}*/{{/defaultValue}}{{^defaultValue}} = None{{/defaultValue}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{#returnType}}: Future[{{returnType}}]{{/returnType}} = { + helper.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) + } - val queryParams = new HashMap[String, String] - val headerParams = new HashMap[String, String] - val formParams = new HashMap[String, String] + {{/operation}} +} + +class {{classname}}AsyncHelper(client: TransportClient, config: SwaggerConfig) extends ApiClient(client, config) { + +{{#operation}} + def {{operationId}}({{#allParams}}{{^required}}{{paramName}}: Option[{{dataType}}] = {{#defaultValue}}Some({{defaultValue}}){{/defaultValue}}{{^defaultValue}}None{{/defaultValue}}{{#hasMore}},{{/hasMore}} + {{/required}}{{#required}}{{paramName}}: {{dataType}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{#hasMore}}, + {{/hasMore}}{{/required}}{{/allParams}})(implicit reader: ClientResponseReader[{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}]{{#bodyParams}}, writer: RequestWriter[{{dataType}}]{{/bodyParams}}){{#returnType}}: Future[{{returnType}}]{{/returnType}}{{^returnType}}: Future[Unit]{{/returnType}} = { + // create path and map variables + val path = (addFmt("{{path}}"){{#pathParams}} + replaceAll ("\\{" + "{{baseName}}" + "\\}",{{paramName}}.toString){{/pathParams}}) + + // query params + val queryParams = new mutable.HashMap[String, String] + val headerParams = new mutable.HashMap[String, String] {{#allParams}} - {{#required}} - {{^isPrimitiveType}} + {{#required}} + {{^isPrimitiveType}} if ({{paramName}} == null) throw new Exception("Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}") - - {{/isPrimitiveType}} - {{#isString}} + {{/isPrimitiveType}} + {{#isString}} if ({{paramName}} == null) throw new Exception("Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}") - {{/isString}} - {{/required}} + {{/isString}} + {{/required}} {{/allParams}} {{#queryParams}} - {{#required}} - queryParams += "{{baseName}}" -> {{paramName}}.toString - {{/required}} - {{^required}} - {{paramName}}.map(paramVal => queryParams += "{{baseName}}" -> paramVal.toString) - {{/required}} - {{/queryParams}} - - {{#headerParams}} - {{#required}} - headerParams += "{{baseName}}" -> {{paramName}} - {{/required}} - {{^required}} - {{paramName}}.map(paramVal => headerParams += "{{baseName}}" -> paramVal) - {{/required}} - {{/headerParams}} - - var postBody: AnyRef = {{#bodyParam}}{{#required}}{{paramName}}{{/required}}{{^required}}{{paramName}}.map(paramVal => paramVal){{/required}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}} - - if (contentType.startsWith("multipart/form-data")) { - val mp = new FormDataMultiPart - {{#formParams}} - {{#notFile}} - {{#required}} - mp.field("{{baseName}}", {{paramName}}.toString, MediaType.MULTIPART_FORM_DATA_TYPE) - {{/required}} {{^required}} - {{paramName}}.map(paramVal => mp.field("{{baseName}}", paramVal.toString, MediaType.MULTIPART_FORM_DATA_TYPE)) + {{paramName}} match { + case Some(param) => queryParams += "{{baseName}}" -> param.toString + case _ => queryParams + } {{/required}} - {{/notFile}} - {{#isFile}} {{#required}} - mp.field("{{baseName}}", file.getName) - mp.bodyPart(new FileDataBodyPart("{{baseName}}", {{paramName}}, MediaType.MULTIPART_FORM_DATA_TYPE)) + queryParams += "{{baseName}}" -> {{paramName}}.toString {{/required}} + {{/queryParams}} + {{#headerParams}} {{^required}} - file.map(fileVal => mp.field("{{baseName}}", fileVal.getName)) - {{paramName}}.map(paramVal => mp.bodyPart(new FileDataBodyPart("{{baseName}}", paramVal, MediaType.MULTIPART_FORM_DATA_TYPE))) + {{paramName}} match { + case Some(param) => headerParams += "{{baseName}}" -> param.toString + case _ => headerParams + } {{/required}} - {{/isFile}} - {{/formParams}} - postBody = mp - } else { - {{#formParams}} - {{#notFile}} {{#required}} - formParams += "{{baseName}}" -> {{paramName}}.toString - {{/required}} - {{^required}} - {{paramName}}.map(paramVal => formParams += "{{baseName}}" -> paramVal.toString) + headerParams += "{{baseName}}" -> {{paramName}}.toString {{/required}} - {{/notFile}} - {{/formParams}} - } + {{/headerParams}} - try { - apiInvoker.invokeApi(basePath, path, "{{httpMethod}}", queryParams.toMap, formParams.toMap, postBody, headerParams.toMap, contentType) match { - case s: String => - {{#returnType}} Some(apiInvoker.deserialize(s, "{{returnContainer}}", classOf[{{returnBaseType}}]).asInstanceOf[{{returnType}}]) - {{/returnType}} - case _ => None - } - } catch { - case ex: ApiException if ex.code == 404 => None - case ex: ApiException => throw ex + val resFuture = client.submit("{{httpMethod}}", path, queryParams.toMap, headerParams.toMap, {{#bodyParam}}writer.write({{paramName}}){{/bodyParam}}{{^bodyParam}}"{{emptyBodyParam}}"{{/bodyParam}}) + resFuture flatMap { resp => + process(reader.read(resp)) } } - {{/operation}} +{{/operation}} + } {{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/scala/apiInvoker.mustache b/modules/swagger-codegen/src/main/resources/scala/apiInvoker.mustache index e1e420d7c6d..ccb74ec2f4d 100644 --- a/modules/swagger-codegen/src/main/resources/scala/apiInvoker.mustache +++ b/modules/swagger-codegen/src/main/resources/scala/apiInvoker.mustache @@ -17,7 +17,7 @@ import java.util.UUID import javax.ws.rs.core.MediaType import scala.collection.JavaConverters._ -import scala.collection.mutable.HashMap +import scala.collection.mutable import com.fasterxml.jackson.module.scala.DefaultScalaModule import com.fasterxml.jackson.datatype.joda.JodaModule @@ -30,8 +30,8 @@ object ScalaJsonUtil { def getJsonMapper = { val mapper = new ObjectMapper() mapper.registerModule(new DefaultScalaModule()) - mapper.registerModule(new JodaModule()); - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + mapper.registerModule(new JodaModule()) + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL) mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT) mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) @@ -41,17 +41,21 @@ object ScalaJsonUtil { } class ApiInvoker(val mapper: ObjectMapper = ScalaJsonUtil.getJsonMapper, - httpHeaders: HashMap[String, String] = HashMap(), - hostMap: HashMap[String, Client] = HashMap(), - asyncHttpClient: Boolean = false, - authScheme: String = "", - authPreemptive: Boolean = false) { + httpHeaders: mutable.HashMap[String, String] = mutable.HashMap(), + hostMap: mutable.HashMap[String, Client] = mutable.HashMap(), + asyncHttpClient: Boolean = false, + authScheme: String = "", + authPreemptive: Boolean = false +) { - var defaultHeaders: HashMap[String, String] = httpHeaders + var defaultHeaders: mutable.HashMap[String, String] = httpHeaders def escape(value: String): String = { URLEncoder.encode(value, "utf-8").replaceAll("\\+", "%20") } + def escape(values: List[String]): String = { + values.map(escape).mkString(",") + } def escape(value: Long): String = value.toString def escape(value: Double): String = value.toString @@ -61,30 +65,29 @@ class ApiInvoker(val mapper: ObjectMapper = ScalaJsonUtil.getJsonMapper, def deserialize(json: String, containerType: String, cls: Class[_]) = { if (cls == classOf[String]) { json match { - case s: String => { - if (s.startsWith("\"") && s.endsWith("\"") && s.length > 1) s.substring(1, s.length - 1) - else s - } + case s: String => + if (s.startsWith("\"") && s.endsWith("\"") && s.length > 1) { + s.substring(1, s.length - 1) + } else { + s + } case _ => null } } else { containerType.toLowerCase match { - case "array" => { - val typeInfo = mapper.getTypeFactory().constructCollectionType(classOf[java.util.List[_]], cls) + case "array" => + val typeInfo = mapper.getTypeFactory.constructCollectionType(classOf[java.util.List[_]], cls) val response = mapper.readValue(json, typeInfo).asInstanceOf[java.util.List[_]] response.asScala.toList - } - case "list" => { - val typeInfo = mapper.getTypeFactory().constructCollectionType(classOf[java.util.List[_]], cls) + case "list" => + val typeInfo = mapper.getTypeFactory.constructCollectionType(classOf[java.util.List[_]], cls) val response = mapper.readValue(json, typeInfo).asInstanceOf[java.util.List[_]] response.asScala.toList - } - case _ => { + case _ => json match { - case e: String if ("\"\"" == e) => null + case e: String if "\"\"" == e => null case _ => mapper.readValue(json, cls) } - } } } } @@ -95,89 +98,104 @@ class ApiInvoker(val mapper: ObjectMapper = ScalaJsonUtil.getJsonMapper, case e: List[_] => mapper.writeValueAsString(obj.asInstanceOf[List[_]].asJava) case _ => mapper.writeValueAsString(obj) } - } else null + } else { + null + } } - def invokeApi(host: String, path: String, method: String, queryParams: Map[String, String], formParams: Map[String, String], body: AnyRef, headerParams: Map[String, String], contentType: String): String = { + def invokeApi( + host: String, + path: String, + method: String, + queryParams: Map[String, String], + formParams: Map[String, String], + body: AnyRef, + headerParams: Map[String, String], + contentType: String +): String = { val client = getClient(host) - val querystring = queryParams.filter(k => k._2 != null).map(k => (escape(k._1) + "=" + escape(k._2))).mkString("?", "&", "") + val querystring = queryParams.filter(k => k._2 != null).map(k => escape(k._1) + "=" + escape(k._2)).mkString("?", "&", "") val builder = client.resource(host + path + querystring).accept(contentType) headerParams.map(p => builder.header(p._1, p._2)) - defaultHeaders.map(p => { + defaultHeaders.foreach(p => { headerParams.contains(p._1) match { case true => // override default with supplied header case false => if (p._2 != null) builder.header(p._1, p._2) } }) var formData: MultivaluedMapImpl = null - if(contentType == "application/x-www-form-urlencoded") { + if (contentType == "application/x-www-form-urlencoded") { formData = new MultivaluedMapImpl() - formParams.map(p => formData.add(p._1, p._2)) + formParams.foreach(p => formData.add(p._1, p._2)) } val response: ClientResponse = method match { - case "GET" => { - builder.get(classOf[ClientResponse]).asInstanceOf[ClientResponse] - } - case "POST" => { - if(formData != null) builder.post(classOf[ClientResponse], formData) - else if(body != null && body.isInstanceOf[File]) { + case "GET" => builder.get(classOf[ClientResponse]).asInstanceOf[ClientResponse] + case "POST" => + if (formData != null && formData.size() > 0) { + builder.post(classOf[ClientResponse], formData) + } else if (body != null && body.isInstanceOf[File]) { val file = body.asInstanceOf[File] val form = new FormDataMultiPart() - form.field("filename", file.getName()) + form.field("filename", file.getName) form.bodyPart(new FileDataBodyPart("file", file, MediaType.MULTIPART_FORM_DATA_TYPE)) builder.post(classOf[ClientResponse], form) + } else { + if (body == null) { + builder.post(classOf[ClientResponse], serialize(body)) + } else { + builder.`type`(contentType).post(classOf[ClientResponse], serialize(body)) + } } - else { - if(body == null) builder.post(classOf[ClientResponse], serialize(body)) - else builder.`type`(contentType).post(classOf[ClientResponse], serialize(body)) + case "PUT" => + if (formData != null) { + builder.post(classOf[ClientResponse], formData) + } else if (body == null) { + builder.put(classOf[ClientResponse], null) + } else { + builder.`type`(contentType).put(classOf[ClientResponse], serialize(body)) + } + case "DELETE" => builder.delete(classOf[ClientResponse]) + case "PATCH" => + if(formData != null) { + builder.header("X-HTTP-Method-Override", "PATCH").post(classOf[ClientResponse], formData) + } else if(body == null) { + builder.header("X-HTTP-Method-Override", "PATCH").post(classOf[ClientResponse], null) + } else { + builder.header("X-HTTP-Method-Override", "PATCH").`type`(contentType).post(classOf[ClientResponse], serialize(body)) } - } - case "PUT" => { - if(formData != null) builder.post(classOf[ClientResponse], formData) - else if(body == null) builder.put(classOf[ClientResponse], null) - else builder.`type`(contentType).put(classOf[ClientResponse], serialize(body)) - } - case "DELETE" => { - builder.delete(classOf[ClientResponse]) - } case _ => null } - response.getStatusInfo().getStatusCode() match { + response.getStatusInfo.getStatusCode match { case 204 => "" - case code: Int if (Range(200, 299).contains(code)) => { - response.hasEntity() match { + case code: Int if Range(200, 299).contains(code) => + response.hasEntity match { case true => response.getEntity(classOf[String]) case false => "" } - } - case _ => { - val entity = response.hasEntity() match { + case _ => + val entity = response.hasEntity match { case true => response.getEntity(classOf[String]) case false => "no data" } - throw new ApiException( - response.getStatusInfo().getStatusCode(), - entity) - } + throw new ApiException(response.getStatusInfo.getStatusCode, entity) } } def getClient(host: String): Client = { hostMap.contains(host) match { case true => hostMap(host) - case false => { + case false => val client = newClient(host) // client.addFilter(new LoggingFilter()) hostMap += host -> client client } - } } def newClient(host: String): Client = asyncHttpClient match { - case true => { + case true => import org.sonatype.spice.jersey.client.ahc.config.DefaultAhcConfig import org.sonatype.spice.jersey.client.ahc.AhcHttpClient import com.ning.http.client.Realm @@ -185,21 +203,23 @@ class ApiInvoker(val mapper: ObjectMapper = ScalaJsonUtil.getJsonMapper, val config: DefaultAhcConfig = new DefaultAhcConfig() if (!authScheme.isEmpty) { val authSchemeEnum = Realm.AuthScheme.valueOf(authScheme) - config.getAsyncHttpClientConfigBuilder + config + .getAsyncHttpClientConfigBuilder .setRealm(new Realm.RealmBuilder().setScheme(authSchemeEnum) .setUsePreemptiveAuth(authPreemptive).build) } AhcHttpClient.create(config) - } case _ => Client.create() } } -object ApiInvoker extends ApiInvoker(mapper = ScalaJsonUtil.getJsonMapper, - httpHeaders = HashMap(), - hostMap = HashMap(), +object ApiInvoker extends ApiInvoker( + mapper = ScalaJsonUtil.getJsonMapper, + httpHeaders = mutable.HashMap(), + hostMap = mutable.HashMap(), asyncHttpClient = {{asyncHttpClient}}, authScheme = "{{authScheme}}", - authPreemptive = {{authPreemptive}}) + authPreemptive = {{authPreemptive}} +) class ApiException(val code: Int, msg: String) extends RuntimeException(msg) diff --git a/modules/swagger-codegen/src/main/resources/scala/build.gradle.mustache b/modules/swagger-codegen/src/main/resources/scala/build.gradle.mustache index 88faa3610b2..e124eb0a5c7 100644 --- a/modules/swagger-codegen/src/main/resources/scala/build.gradle.mustache +++ b/modules/swagger-codegen/src/main/resources/scala/build.gradle.mustache @@ -104,6 +104,12 @@ ext { jackson_version = "2.4.2" junit_version = "4.8.1" scala_test_version = "2.2.4" + swagger_async_httpclient_version = "0.3.5" +} + +repositories { + mavenLocal() + mavenCentral() } dependencies { @@ -117,4 +123,5 @@ dependencies { testCompile "junit:junit:$junit_version" compile "joda-time:joda-time:$jodatime_version" compile "org.joda:joda-convert:$joda_version" + compile "com.wordnik.swagger:swagger-async-httpclient_2.10:$swagger_async_httpclient_version" } diff --git a/modules/swagger-codegen/src/main/resources/scala/build.sbt.mustache b/modules/swagger-codegen/src/main/resources/scala/build.sbt.mustache index 6a198acb832..24ee01b6fd5 100644 --- a/modules/swagger-codegen/src/main/resources/scala/build.sbt.mustache +++ b/modules/swagger-codegen/src/main/resources/scala/build.sbt.mustache @@ -1,33 +1,34 @@ -lazy val root = (project in file(".")). - settings( - version := "{{artifactVersion}}", - name := "{{artifactId}}", - organization := "{{groupId}}", - scalaVersion := "2.11.8", - - libraryDependencies ++= Seq( - "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.4.2", - "com.sun.jersey" % "jersey-core" % "1.19", - "com.sun.jersey" % "jersey-client" % "1.19", - "com.sun.jersey.contribs" % "jersey-multipart" % "1.19", - "org.jfarcand" % "jersey-ahc-client" % "1.0.5", - "io.swagger" % "swagger-core" % "1.5.8", - "joda-time" % "joda-time" % "2.2", - "org.joda" % "joda-convert" % "1.2", - "org.scalatest" %% "scalatest" % "2.2.4" % "test", - "junit" % "junit" % "4.8.1" % "test" - ), - - resolvers ++= Seq( - Resolver.jcenterRepo, - Resolver.mavenLocal - ), - - scalacOptions := Seq( - "-unchecked", - "-deprecation", - "-feature" - ), - - publishArtifact in (Compile, packageDoc) := false - ) \ No newline at end of file +version := "{{artifactVersion}}" + +name := "{{artifactId}}" + +organization := "{{groupId}}" + +scalaVersion := "2.11.8" + +libraryDependencies ++= Seq( + "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.4.2", + "com.sun.jersey" % "jersey-core" % "1.19", + "com.sun.jersey" % "jersey-client" % "1.19", + "com.sun.jersey.contribs" % "jersey-multipart" % "1.19", + "org.jfarcand" % "jersey-ahc-client" % "1.0.5", + "io.swagger" % "swagger-core" % "1.5.8", + "joda-time" % "joda-time" % "2.2", + "org.joda" % "joda-convert" % "1.2", + "org.scalatest" %% "scalatest" % "2.2.4" % "test", + "junit" % "junit" % "4.8.1" % "test", + "com.wordnik.swagger" %% "swagger-async-httpclient" % "0.3.5" +) + +resolvers ++= Seq( + Resolver.mavenLocal +) + +scalacOptions := Seq( + "-unchecked", + "-deprecation", + "-feature" +) + +publishArtifact in (Compile, packageDoc) := false + diff --git a/modules/swagger-codegen/src/main/resources/scala/client.mustache b/modules/swagger-codegen/src/main/resources/scala/client.mustache new file mode 100644 index 00000000000..8098b73c6bb --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/scala/client.mustache @@ -0,0 +1,22 @@ +package {{invokerPackage}} + +{{#imports}}import {{import}} +{{/imports}} +import {{apiPackage}}._ + +import com.wordnik.swagger.client._ + +import java.io.Closeable + +class {{clientName}}(config: SwaggerConfig) extends Closeable { + val locator = config.locator + val name = config.name + + private[this] val client = transportClient + + protected def transportClient: TransportClient = new RestClient(config) + + def close() { + client.close() + } +} diff --git a/modules/swagger-codegen/src/main/resources/scala/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/scala/git_push.sh.mustache index e153ce23ecf..a2d75234837 100755 --- a/modules/swagger-codegen/src/main/resources/scala/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/scala/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/scala/model.mustache b/modules/swagger-codegen/src/main/resources/scala/model.mustache index 12b635bfb3e..f44fc1e5a78 100644 --- a/modules/swagger-codegen/src/main/resources/scala/model.mustache +++ b/modules/swagger-codegen/src/main/resources/scala/model.mustache @@ -12,7 +12,7 @@ case class {{classname}} ( {{#description}} /* {{{description}}} */ {{/description}} - {{{name}}}: {{^required}}Option[{{/required}}{{datatype}}{{^required}}]{{/required}}{{#hasMore}},{{/hasMore}} + {{{name}}}: {{^required}}Option[{{/required}}{{datatype}}{{^required}}] = None{{/required}}{{#hasMore}},{{/hasMore}} {{/vars}} ) diff --git a/modules/swagger-codegen/src/main/resources/scala/pom.mustache b/modules/swagger-codegen/src/main/resources/scala/pom.mustache index 1c72f7d4fde..202b4c82446 100644 --- a/modules/swagger-codegen/src/main/resources/scala/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/scala/pom.mustache @@ -1,229 +1,255 @@ - 4.0.0 - {{groupId}} - {{artifactId}} - jar - {{artifactId}} - {{artifactVersion}} - - 2.2.0 - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + 4.0.0 + {{groupId}} + {{artifactId}} + jar + {{artifactId}} + {{artifactVersion}} - - - maven-mongodb-plugin-repo - maven mongodb plugin repository - http://maven-mongodb-plugin.googlecode.com/svn/maven/repo - default - - + + + maven-mongodb-plugin-repo + maven mongodb plugin repository + http://maven-mongodb-plugin.googlecode.com/svn/maven/repo + default + + - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.12 - - - - loggerPath - conf/log4j.properties - - - -Xms512m -Xmx1500m - methods - pertest - - - - maven-dependency-plugin - - - package - - copy-dependencies - - - ${project.build.directory}/lib - - - - + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + pertest + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + - - - org.apache.maven.plugins - maven-jar-plugin - 2.2 - - - - jar - test-jar - - - - - - + + + org.apache.maven.plugins + maven-jar-plugin + 2.2 + + + + jar + test-jar + + + + + + - - org.codehaus.mojo - build-helper-maven-plugin - 1.9.1 - - - add_sources - generate-sources - - add-source - - - - src/main/java - - - - - add_test_sources - generate-test-sources - - add-test-source - - - - src/test/java - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.6.1 - - 1.7 - 1.7 - - - - net.alchim31.maven - scala-maven-plugin - ${scala-maven-plugin-version} - - - scala-compile-first - process-resources - - add-source - compile - - - - scala-test-compile - process-test-resources - - testCompile - - - - - - -Xms128m - -Xmx1500m - - - - - - - - - org.scala-tools - maven-scala-plugin - - ${scala-version} - - - - - - - com.fasterxml.jackson.module - jackson-module-scala_2.10 - ${jackson-version} - - - com.fasterxml.jackson.datatype - jackson-datatype-joda - ${jackson-version} - - - com.sun.jersey - jersey-client - ${jersey-version} - - - com.sun.jersey.contribs - jersey-multipart - ${jersey-version} - - - org.jfarcand - jersey-ahc-client - ${jersey-async-version} - compile - - - org.scala-lang - scala-library - ${scala-version} - - - io.swagger - swagger-core - ${swagger-core-version} - - - org.scalatest - scalatest_2.10 - ${scala-test-version} - test - - - junit - junit - ${junit-version} - test - - - joda-time - joda-time - ${joda-time-version} - - - org.joda - joda-convert - ${joda-version} - - - - 2.10.4 - 1.2 - 2.2 - 1.19 - 1.5.15 - 1.0.5 - 1.0.0 - 2.8.9 + + org.codehaus.mojo + build-helper-maven-plugin + 1.9.1 + + + add_sources + generate-sources + + add-source + + + + + src/main/java + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + + src/test/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + + 1.7 + 1.7 + + + + net.alchim31.maven + scala-maven-plugin + ${scala-maven-plugin-version} + + + scala-compile-first + process-resources + + add-source + compile + + + + scala-test-compile + process-test-resources + + testCompile + + + + + + -Xms128m + -Xmx1500m + + + + + + + + + org.scala-tools + maven-scala-plugin + + ${scala-version} + + + + + + + com.fasterxml.jackson.module + jackson-module-scala_2.11 + ${jackson-version} + + + com.fasterxml.jackson.datatype + jackson-datatype-joda + ${jackson-version} + + + com.sun.jersey + jersey-client + ${jersey-version} + + + com.sun.jersey.contribs + jersey-multipart + ${jersey-version} + + + org.jfarcand + jersey-ahc-client + ${jersey-async-version} + compile + + + org.scala-lang + scala-library + ${scala-version} + + + io.swagger + swagger-core + ${swagger-core-version} + + + org.scalatest + scalatest_2.11 + ${scala-test-version} + test + + + junit + junit + ${junit-version} + test + + + joda-time + joda-time + ${joda-time-version} + + + org.joda + joda-convert + ${joda-version} + + + com.wordnik.swagger + swagger-async-httpclient_2.11 + ${swagger-async-httpclient-version} + + + + 2.11.11 + 1.2 + 2.2 + 1.19 + 1.5.16 + 1.0.5 + 1.0.0 + 2.8.9 - 4.8.1 - 3.1.5 - 2.2.4 + 4.8.1 + 3.1.5 + 2.2.4 + 0.3.5 - UTF-8 - + UTF-8 + diff --git a/modules/swagger-codegen/src/main/resources/scalaz/HelperCodecs.mustache b/modules/swagger-codegen/src/main/resources/scalaz/HelperCodecs.mustache new file mode 100644 index 00000000000..9bb400b9bda --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/scalaz/HelperCodecs.mustache @@ -0,0 +1,15 @@ +package {{apiPackage}} + +import argonaut._ +import argonaut.EncodeJson._ +import argonaut.DecodeJson._ + +import org.http4s._ +import org.http4s.{EntityDecoder, EntityEncoder} +import org.http4s.argonaut._ + +import org.joda.time.DateTime + +object HelperCodecs { + implicit def returnTypeDecoder[A: EncodeJson]: EntityEncoder[List[A]] = jsonEncoderOf[List[A]] +} diff --git a/modules/swagger-codegen/src/main/resources/scalaz/QueryParamTypeclass.mustache b/modules/swagger-codegen/src/main/resources/scalaz/QueryParamTypeclass.mustache new file mode 100644 index 00000000000..6ce8ee71291 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/scalaz/QueryParamTypeclass.mustache @@ -0,0 +1,15 @@ +package {{apiPackage}} + +trait QueryParam[A] { + def toParamString(a: A): String +} + +object QueryParam { + implicit def strQueryParam: QueryParam[String] = new QueryParam[String] { + def toParamString(s: String): String = s + } + + implicit def listStrQueryParam: QueryParam[List[String]] = new QueryParam[List[String]] { + def toParamString(s: List[String]): String = s.mkString(",") + } +} diff --git a/modules/swagger-codegen/src/main/resources/scalaz/api.mustache b/modules/swagger-codegen/src/main/resources/scalaz/api.mustache new file mode 100644 index 00000000000..9eeba0a96ee --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/scalaz/api.mustache @@ -0,0 +1,83 @@ +package {{package}} + +import argonaut._ +import argonaut.EncodeJson._ +import argonaut.DecodeJson._ + +import java.io.File +import java.net.URLEncoder +import java.util.UUID + +import org.http4s._ +import org.http4s.{EntityDecoder, EntityEncoder} +import org.http4s.argonaut._ +import org.http4s.client._ +import org.http4s.client.blaze.PooledHttp1Client +import org.http4s.headers._ + +import org.joda.time.DateTime + +import scalaz.concurrent.Task + +import HelperCodecs._ + +{{#operations}} +object {{classname}} { + + val client = PooledHttp1Client() + + def escape(value: String): String = URLEncoder.encode(value, "utf-8").replaceAll("\\+", "%20") + + {{#operation}} + def {{nickname}}(host: String{{#allParams}}{{#-first}}, {{/-first}}{{paramName}}: {{dataType}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{#queryParams}}{{#-first}}(implicit {{/-first}}{{paramName}}Query: QueryParam[{{dataType}}]{{^-last}}, {{/-last}}{{#-last}}){{/-last}}{{/queryParams}}: {{>operationReturnType}}{{#returnType}} implicit val returnTypeDecoder: EntityDecoder[{{returnType}}] = jsonOf[{{returnType}}] + +{{/returnType}} + val path = "{{path}}"{{#pathParams}}.replaceAll("\\{" + "{{baseName}}" + "\\}",escape({{paramName}}.toString)){{/pathParams}} + + val httpMethod = Method.{{httpMethod}} + val contentType = `Content-Type`(MediaType.`application/json`) + val headers = Headers( + {{#headerParams}}Header("{{baseName}}", {{paramName}}){{^-last}}, {{/-last}}{{/headerParams}}) + val queryParams = Query( + {{#queryParams}}("{{paramName}}", Some({{baseName}}Query.toParamString({{baseName}}))){{^-last}}, {{/-last}}{{/queryParams}}) + + for { + uri <- Task.fromDisjunction(Uri.fromString(host + path)) + uriWithParams = uri.copy(query = queryParams) + req = Request(method = httpMethod, uri = uriWithParams, headers = headers.put(contentType)){{#bodyParam}}.withBody({{paramName}}){{/bodyParam}} + {{>clientFunction}} + } yield resp + } + + {{/operation}} +} + +class HttpService{{classname}}(service: HttpService) { + val client = Client.fromHttpService(service) + + def escape(value: String): String = URLEncoder.encode(value, "utf-8").replaceAll("\\+", "%20") + + {{#operation}} + def {{nickname}}({{#allParams}}{{paramName}}: {{dataType}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{#queryParams}}{{#-first}}(implicit {{/-first}}{{paramName}}Query: QueryParam[{{dataType}}]{{^-last}}, {{/-last}}{{#-last}}){{/-last}}{{/queryParams}}: {{>operationReturnType}}{{#returnType}} implicit val returnTypeDecoder: EntityDecoder[{{returnType}}] = jsonOf[{{returnType}}] + +{{/returnType}} + val path = "{{path}}"{{#pathParams}}.replaceAll("\\{" + "{{baseName}}" + "\\}",escape({{paramName}}.toString)){{/pathParams}} + + val httpMethod = Method.{{httpMethod}} + val contentType = `Content-Type`(MediaType.`application/json`) + val headers = Headers( + {{#headerParams}}Header("{{baseName}}", {{paramName}}){{^-last}}, {{/-last}}{{/headerParams}}) + val queryParams = Query( + {{#queryParams}}("{{paramName}}", Some({{baseName}}Query.toParamString({{baseName}}))){{^-last}}, {{/-last}}{{/queryParams}}) + + for { + uri <- Task.fromDisjunction(Uri.fromString(path)) + uriWithParams = uri.copy(query = queryParams) + req = Request(method = httpMethod, uri = uriWithParams, headers = headers.put(contentType)){{#bodyParam}}.withBody({{paramName}}){{/bodyParam}} + {{>clientFunction}} + } yield resp + } + + {{/operation}} +} +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/scalaz/build.sbt.mustache b/modules/swagger-codegen/src/main/resources/scalaz/build.sbt.mustache new file mode 100644 index 00000000000..0153bb64d34 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/scalaz/build.sbt.mustache @@ -0,0 +1,19 @@ +scalaVersion := "2.11.8" + +val http4sVersion = "0.15.12a" +val scalazVersion = "7.2.12" +val shapelessVersion = "2.3.2" +val argonautShapelessVersion = "1.2.0-M4" + +libraryDependencies ++= Seq( + "joda-time" % "joda-time" % "2.2", + "com.chuusai" %% "shapeless" % shapelessVersion, + "org.http4s" %% "http4s-argonaut" % http4sVersion, + "org.http4s" %% "http4s-dsl" % http4sVersion, + "org.http4s" %% "http4s-server-metrics" % http4sVersion, + "org.http4s" %% "http4s-blaze-client" % http4sVersion, + "org.scalaz" %% "scalaz-concurrent" % scalazVersion, + "org.scalaz" %% "scalaz-core" % scalazVersion, + "io.argonaut" %% "argonaut-scalaz" % "6.2", + "com.github.alexarchambault" %% "argonaut-shapeless_6.2" % argonautShapelessVersion +) diff --git a/modules/swagger-codegen/src/main/resources/scalaz/clientFunction.mustache b/modules/swagger-codegen/src/main/resources/scalaz/clientFunction.mustache new file mode 100644 index 00000000000..bd7c75b38d9 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/scalaz/clientFunction.mustache @@ -0,0 +1 @@ +{{#returnType}}resp <- client.expect[{{returnType}}](req){{/returnType}}{{^returnType}}resp <- client.fetch[Unit](req)(_ => Task.now(())){{/returnType}} diff --git a/modules/swagger-codegen/src/main/resources/scalaz/dateTimeCodecs.mustache b/modules/swagger-codegen/src/main/resources/scalaz/dateTimeCodecs.mustache new file mode 100644 index 00000000000..fa73adb562e --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/scalaz/dateTimeCodecs.mustache @@ -0,0 +1,19 @@ +package {{apiPackage}} + +import argonaut._ +import argonaut.EncodeJson._ +import argonaut.DecodeJson._ + +import org.http4s._ +import org.http4s.{EntityDecoder, EntityEncoder} +import org.http4s.argonaut._ + +import org.joda.time.DateTime + +object DateTimeCodecs { + implicit def dateTimeEncodeJson: EncodeJson[DateTime] = + EncodeJson[DateTime](dt => StringEncodeJson(dt.toString)) + + implicit def dateTimeDecodeJson: DecodeJson[DateTime] = + DecodeJson.of[String].map(DateTime.parse(_)) setName "org.joda.time.DateTime" +} diff --git a/modules/swagger-codegen/src/main/resources/scalaz/model.mustache b/modules/swagger-codegen/src/main/resources/scalaz/model.mustache new file mode 100644 index 00000000000..e0725cb819d --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/scalaz/model.mustache @@ -0,0 +1,56 @@ +package {{package}} + +import argonaut._ +import argonaut.EncodeJson._ +import argonaut.DecodeJson._ + +import org.http4s.{EntityDecoder, EntityEncoder} +import org.http4s.argonaut._ +import org.joda.time.DateTime +{{#models}} +{{#model}} +import {{classname}}._ + +case class {{classname}} ( + {{#vars}}{{#description}}/* {{{description}}} */ + {{/description}}{{name}}: {{^required}}Option[{{/required}}{{^isEnum}}{{datatype}}{{/isEnum}}{{#isEnum}}{{datatypeWithEnum}}{{/isEnum}}{{^required}}]{{/required}}{{#hasMore}},{{/hasMore}}{{^hasMore}}){{/hasMore}} + {{/vars}} + +object {{classname}} { + import DateTimeCodecs._ + {{#hasEnums}} + {{#vars}} + {{#isEnum}} + sealed trait {{datatypeWithEnum}} + {{#_enum}} + case object {{#fnEnumEntry}}{{.}}{{/fnEnumEntry}} extends {{datatypeWithEnum}} + {{/_enum}} + + object {{datatypeWithEnum}} { + def to{{datatypeWithEnum}}(s: String): Option[{{datatypeWithEnum}}] = s match { +{{#_enum}} case "{{#fnEnumEntry}}{{.}}{{/fnEnumEntry}}" => Some({{#fnEnumEntry}}{{.}}{{/fnEnumEntry}}) +{{/_enum}} + case _ => None + } + + def from{{datatypeWithEnum}}(x: {{datatypeWithEnum}}): String = x match { +{{#_enum}} case {{#fnEnumEntry}}{{.}}{{/fnEnumEntry}} => "{{#fnEnumEntry}}{{.}}{{/fnEnumEntry}}" +{{/_enum}} + } + } + + implicit val {{datatypeWithEnum}}EnumEncoder: EncodeJson[{{datatypeWithEnum}}] = + EncodeJson[{{datatypeWithEnum}}](is => StringEncodeJson({{datatypeWithEnum}}.from{{datatypeWithEnum}}(is))) + + implicit val {{datatypeWithEnum}}EnumDecoder: DecodeJson[{{datatypeWithEnum}}] = + DecodeJson.optionDecoder[{{datatypeWithEnum}}](n => n.string.flatMap(jStr => {{datatypeWithEnum}}.to{{datatypeWithEnum}}(jStr)), "{{datatypeWithEnum}} failed to de-serialize") + {{/isEnum}} + {{/vars}} + {{/hasEnums}} + + implicit val {{classname}}CodecJson: CodecJson[{{classname}}] = CodecJson.derive[{{classname}}] + implicit val {{classname}}Decoder: EntityDecoder[{{classname}}] = jsonOf[{{classname}}] + implicit val {{classname}}Encoder: EntityEncoder[{{classname}}] = jsonEncoderOf[{{classname}}] +} +{{/model}} +{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/scalaz/operationReturnType.mustache b/modules/swagger-codegen/src/main/resources/scalaz/operationReturnType.mustache new file mode 100644 index 00000000000..f224ea59ad0 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/scalaz/operationReturnType.mustache @@ -0,0 +1 @@ +Task[{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}] = { diff --git a/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache b/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache index 66971688891..0536080ca72 100644 --- a/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache @@ -88,7 +88,7 @@ class AlamofireRequestBuilder: RequestBuilder { } self.processRequest(uploadRequest, managerId, completion) case .Failure(let encodingError): - completion(response: nil, error: ErrorResponse.Error(415, nil, encodingError)) + completion(response: nil, error: ErrorResponse.error(415, nil, encodingError)) } } ) @@ -121,7 +121,7 @@ class AlamofireRequestBuilder: RequestBuilder { if stringResponse.result.isFailure { completion( response: nil, - error: ErrorResponse.Error(stringResponse.response?.statusCode ?? 500, stringResponse.data, stringResponse.result.error!) + error: ErrorResponse.error(stringResponse.response?.statusCode ?? 500, stringResponse.data, stringResponse.result.error!) ) return } @@ -141,7 +141,7 @@ class AlamofireRequestBuilder: RequestBuilder { if voidResponse.result.isFailure { completion( response: nil, - error: ErrorResponse.Error(voidResponse.response?.statusCode ?? 500, voidResponse.data, voidResponse.result.error!) + error: ErrorResponse.error(voidResponse.response?.statusCode ?? 500, voidResponse.data, voidResponse.result.error!) ) return } @@ -158,10 +158,10 @@ class AlamofireRequestBuilder: RequestBuilder { validatedRequest.responseData(completionHandler: { (dataResponse) in cleanupRequest() - if (dataResponse.result.isFailure) { + if dataResponse.result.isFailure { completion( response: nil, - error: ErrorResponse.Error(dataResponse.response?.statusCode ?? 500, dataResponse.data, dataResponse.result.error!) + error: ErrorResponse.error(dataResponse.response?.statusCode ?? 500, dataResponse.data, dataResponse.result.error!) ) return } @@ -179,7 +179,7 @@ class AlamofireRequestBuilder: RequestBuilder { cleanupRequest() if response.result.isFailure { - completion(response: nil, error: ErrorResponse.Error(response.response?.statusCode ?? 500, response.data, response.result.error!)) + completion(response: nil, error: ErrorResponse.error(response.response?.statusCode ?? 500, response.data, response.result.error!)) return } @@ -198,7 +198,7 @@ class AlamofireRequestBuilder: RequestBuilder { return } - completion(response: nil, error: ErrorResponse.Error(500, nil, NSError(domain: "localhost", code: 500, userInfo: ["reason": "unreacheable code"]))) + completion(response: nil, error: ErrorResponse.error(500, nil, NSError(domain: "localhost", code: 500, userInfo: ["reason": "unreacheable code"]))) } } } diff --git a/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache b/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache index 27253b6d95f..40b7e813657 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache @@ -84,6 +84,99 @@ extension NSUUID: JSONEncodable { } } +/// Represents an ISO-8601 full-date (RFC-3339). +/// ex: 12-31-1999 +/// https://xml2rfc.tools.ietf.org/public/rfc/html/rfc3339.html#anchor14 +public final class ISOFullDate: CustomStringConvertible { + + public let year: Int + public let month: Int + public let day: Int + + public init(year year: Int, month: Int, day: Int) { + self.year = year + self.month = month + self.day = day + } + + /** + Converts an NSDate to an ISOFullDate. Only interested in the year, month, day components. + + - parameter date: The date to convert. + + - returns: An ISOFullDate constructed from the year, month, day of the date. + */ + public static func from(date date: NSDate) -> ISOFullDate? { + guard let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian) else { + return nil + } + + let components = calendar.components( + [ + .Year, + .Month, + .Day, + ], + fromDate: date + ) + return ISOFullDate( + year: components.year, + month: components.month, + day: components.day + ) + } + + /** + Converts a ISO-8601 full-date string to an ISOFullDate. + + - parameter string: The ISO-8601 full-date format string to convert. + + - returns: An ISOFullDate constructed from the string. + */ + public static func from(string string: String) -> ISOFullDate? { + let components = string + .characters + .split("-") + .map(String.init) + .flatMap { Int($0) } + guard components.count == 3 else { return nil } + + return ISOFullDate( + year: components[0], + month: components[1], + day: components[2] + ) + } + + /** + Converts the receiver to an NSDate, in the default time zone. + + - returns: An NSDate from the components of the receiver, in the default time zone. + */ + public func toDate() -> NSDate? { + let components = NSDateComponents() + components.year = year + components.month = month + components.day = day + components.timeZone = NSTimeZone.defaultTimeZone() + let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian) + return calendar?.dateFromComponents(components) + } + + // MARK: CustomStringConvertible + + public var description: String { + return "\(year)-\(month)-\(day)" + } + +} + +extension ISOFullDate: JSONEncodable { + public func encodeToJSON() -> AnyObject { + return "\(year)-\(month)-\(day)" + } +} + {{#usePromiseKit}}extension RequestBuilder { public func execute() -> Promise> { let deferred = Promise>.pendingPromise() diff --git a/modules/swagger-codegen/src/main/resources/swift/Models.mustache b/modules/swagger-codegen/src/main/resources/swift/Models.mustache index b91e2727b8a..66b172f342e 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Models.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Models.mustache @@ -28,8 +28,8 @@ public class Response { public convenience init(response: NSHTTPURLResponse, body: T?) { let rawHeader = response.allHeaderFields var header = [String:String]() - for (key, value) in rawHeader { - header[key as! String] = value as? String + for case let (key, value) as (String, String) in rawHeader { + header[key] = value } self.init(statusCode: response.statusCode, header: header, body: body) } @@ -140,7 +140,16 @@ class Decoders { return NSDate(timeIntervalSince1970: Double(sourceInt / 1000) ) } fatalError("formatter failed to parse \(source)") - } {{#models}}{{#model}} + } + + // Decoder for ISOFullDate + Decoders.addDecoder(clazz: ISOFullDate.self, decoder: { (source: AnyObject) -> ISOFullDate in + if let string = source as? String, + let isoDate = ISOFullDate.from(string: string) { + return isoDate + } + fatalError("formatter failed to parse \(source)") + }) {{#models}}{{#model}} // Decoder for [{{{classname}}}] Decoders.addDecoder(clazz: [{{{classname}}}].self) { (source: AnyObject) -> [{{{classname}}}] in diff --git a/modules/swagger-codegen/src/main/resources/swift/Podspec.mustache b/modules/swagger-codegen/src/main/resources/swift/Podspec.mustache index 526efb165bc..98704427681 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Podspec.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Podspec.mustache @@ -2,6 +2,7 @@ Pod::Spec.new do |s| s.name = '{{projectName}}' s.ios.deployment_target = '8.0' s.osx.deployment_target = '10.9' + s.tvos.deployment_target = '9.0' s.version = '{{#podVersion}}{{podVersion}}{{/podVersion}}{{^podVersion}}0.0.1{{/podVersion}}' s.source = {{#podSource}}{{& podSource}}{{/podSource}}{{^podSource}}{ :git => 'git@github.com:swagger-api/swagger-mustache.git', :tag => 'v1.0.0' }{{/podSource}} {{#podAuthors}} @@ -25,7 +26,7 @@ Pod::Spec.new do |s| {{#podDocumentationURL}} s.documentation_url = '{{podDocumentationURL}}' {{/podDocumentationURL}} - s.source_files = '{{projectName}}/Classes/Swaggers/**/*.swift' + s.source_files = '{{projectName}}/Classes/**/*.swift' {{#usePromiseKit}} s.dependency 'PromiseKit', '~> 3.5.3' {{/usePromiseKit}} diff --git a/modules/swagger-codegen/src/main/resources/swift/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/swift/git_push.sh.mustache index e153ce23ecf..a2d75234837 100755 --- a/modules/swagger-codegen/src/main/resources/swift/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/swift/model.mustache b/modules/swagger-codegen/src/main/resources/swift/model.mustache index a42ff74a644..d182a5f0962 100644 --- a/modules/swagger-codegen/src/main/resources/swift/model.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/model.mustache @@ -33,10 +33,10 @@ public class {{classname}}: JSONEncodable { public init() {} {{/unwrapRequired}} {{#unwrapRequired}} - public init({{#requiredVars}}{{^-first}}, {{/-first}}{{name}}: {{#isEnum}}{{datatypeWithEnum}}{{/isEnum}}{{^isEnum}}{{datatype}}{{/isEnum}}{{/requiredVars}}) { - {{#requiredVars}} + public init({{#allVars}}{{^-first}}, {{/-first}}{{name}}: {{#isEnum}}{{datatypeWithEnum}}{{/isEnum}}{{^isEnum}}{{datatype}}{{/isEnum}}{{^required}}?=nil{{/required}}{{/allVars}}) { + {{#allVars}} self.{{name}} = {{name}} - {{/requiredVars}} + {{/allVars}} } {{/unwrapRequired}} diff --git a/modules/swagger-codegen/src/main/resources/swift3/APIHelper.mustache b/modules/swagger-codegen/src/main/resources/swift3/APIHelper.mustache index b612ff90921..f5a6b477952 100644 --- a/modules/swagger-codegen/src/main/resources/swift3/APIHelper.mustache +++ b/modules/swagger-codegen/src/main/resources/swift3/APIHelper.mustache @@ -49,17 +49,27 @@ class APIHelper { return destination } - static func mapValuesToQueryItems(values: [String:Any?]) -> [URLQueryItem]? { let returnValues = values .filter { $0.1 != nil } - .map { (item: (_key: String, _value: Any?)) -> URLQueryItem in - URLQueryItem(name: item._key, value:"\(item._value!)") + .map { (item: (_key: String, _value: Any?)) -> [URLQueryItem] in + if let value = item._value as? Array { + return value.map { (v) -> URLQueryItem in + URLQueryItem( + name: item._key, + value: v + ) + } + } else { + return [URLQueryItem( + name: item._key, + value: "\(item._value!)" + )] + } } - if returnValues.count == 0 { - return nil - } + .flatMap { $0 } + + if returnValues.isEmpty { return nil } return returnValues } - } diff --git a/modules/swagger-codegen/src/main/resources/swift3/APIs.mustache b/modules/swagger-codegen/src/main/resources/swift3/APIs.mustache index 65294aafe56..557ba31efb6 100644 --- a/modules/swagger-codegen/src/main/resources/swift3/APIs.mustache +++ b/modules/swagger-codegen/src/main/resources/swift3/APIs.mustache @@ -32,39 +32,39 @@ open class APIBase { open class RequestBuilder { var credential: URLCredential? var headers: [String:String] - let parameters: [String:Any]? - let isBody: Bool - let method: String - let URLString: String - + public let parameters: Any? + public let isBody: Bool + public let method: String + public let URLString: String + /// Optional block to obtain a reference to the request's progress instance when available. public var onProgressReady: ((Progress) -> ())? - required public init(method: String, URLString: String, parameters: [String:Any]?, isBody: Bool, headers: [String:String] = [:]) { + required public init(method: String, URLString: String, parameters: Any?, isBody: Bool, headers: [String:String] = [:]) { self.method = method self.URLString = URLString self.parameters = parameters self.isBody = isBody self.headers = headers - + addHeaders({{projectName}}API.customHeaders) } - + open func addHeaders(_ aHeaders:[String:String]) { for (header, value) in aHeaders { - headers[header] = value + addHeader(name: header, value: value) } } - - open func execute(_ completion: @escaping (_ response: Response?, _ error: Error?) -> Void) { } - public func addHeader(name: String, value: String) -> Self { + open func execute(_ completion: @escaping (_ response: Response?, _ error: ErrorResponse?) -> Void) { } + + @discardableResult public func addHeader(name: String, value: String) -> Self { if !value.isEmpty { headers[name] = value } return self } - + open func addCredential() -> Self { self.credential = {{projectName}}API.credential return self diff --git a/modules/swagger-codegen/src/main/resources/swift3/AlamofireImplementations.mustache b/modules/swagger-codegen/src/main/resources/swift3/AlamofireImplementations.mustache index 35d187923f4..2b001505c23 100644 --- a/modules/swagger-codegen/src/main/resources/swift3/AlamofireImplementations.mustache +++ b/modules/swagger-codegen/src/main/resources/swift3/AlamofireImplementations.mustache @@ -13,11 +13,54 @@ class AlamofireRequestBuilderFactory: RequestBuilderFactory { } } +private struct SynchronizedDictionary { + + private var dictionary = [K: V]() + private let queue = DispatchQueue( + label: "SynchronizedDictionary", + qos: DispatchQoS.userInitiated, + attributes: [DispatchQueue.Attributes.concurrent], + autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, + target: nil + ) + + public subscript(key: K) -> V? { + get { + var value: V? + + queue.sync { + value = self.dictionary[key] + } + + return value + } + set { + queue.sync(flags: DispatchWorkItemFlags.barrier) { + self.dictionary[key] = newValue + } + } + } + +} + +class JSONEncodingWrapper: ParameterEncoding { + var bodyParameters: Any? + var encoding: JSONEncoding = JSONEncoding() + + public init(parameters: Any?) { + self.bodyParameters = parameters + } + + public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { + return try encoding.encode(urlRequest, withJSONObject: bodyParameters) + } +} + // Store manager to retain its reference -private var managerStore: [String: Alamofire.SessionManager] = [:] +private var managerStore = SynchronizedDictionary() open class AlamofireRequestBuilder: RequestBuilder { - required public init(method: String, URLString: String, parameters: [String : Any]?, isBody: Bool, headers: [String : String] = [:]) { + required public init(method: String, URLString: String, parameters: Any?, isBody: Bool, headers: [String : String] = [:]) { super.init(method: method, URLString: URLString, parameters: parameters, isBody: isBody, headers: headers) } @@ -47,24 +90,26 @@ open class AlamofireRequestBuilder: RequestBuilder { configuration (e.g. to override the cache policy). */ open func makeRequest(manager: SessionManager, method: HTTPMethod, encoding: ParameterEncoding, headers: [String:String]) -> DataRequest { - return manager.request(URLString, method: method, parameters: parameters, encoding: encoding, headers: headers) + return manager.request(URLString, method: method, parameters: parameters as? Parameters, encoding: encoding, headers: headers) } - override open func execute(_ completion: @escaping (_ response: Response?, _ error: Error?) -> Void) { + override open func execute(_ completion: @escaping (_ response: Response?, _ error: ErrorResponse?) -> Void) { let managerId:String = UUID().uuidString // Create a new manager for each request to customize its request header let manager = createSessionManager() managerStore[managerId] = manager - let encoding:ParameterEncoding = isBody ? JSONEncoding() : URLEncoding() + let encoding:ParameterEncoding = isBody ? JSONEncodingWrapper(parameters: parameters) : URLEncoding() let xMethod = Alamofire.HTTPMethod(rawValue: method) - let fileKeys = parameters == nil ? [] : parameters!.filter { $1 is NSURL } + + let param = parameters as? Parameters + let fileKeys = param == nil ? [] : param!.filter { $1 is NSURL } .map { $0.0 } if fileKeys.count > 0 { manager.upload(multipartFormData: { mpForm in - for (k, v) in self.parameters! { + for (k, v) in param! { switch v { case let fileURL as URL: if let mimeType = self.contentTypeForFormPart(fileURL: fileURL) { @@ -73,16 +118,12 @@ open class AlamofireRequestBuilder: RequestBuilder { else { mpForm.append(fileURL, withName: k) } - break case let string as String: mpForm.append(string.data(using: String.Encoding.utf8)!, withName: k) - break case let number as NSNumber: mpForm.append(number.stringValue.data(using: String.Encoding.utf8)!, withName: k) - break default: fatalError("Unprocessable value \(v) with key \(k)") - break } } }, to: URLString, method: xMethod!, headers: nil, encodingCompletion: { encodingResult in @@ -93,7 +134,7 @@ open class AlamofireRequestBuilder: RequestBuilder { } self.processRequest(request: upload, managerId, completion) case .failure(let encodingError): - completion(nil, ErrorResponse.Error(415, nil, encodingError)) + completion(nil, ErrorResponse.HttpError(statusCode: 415, data: nil, error: encodingError)) } }) } else { @@ -106,13 +147,13 @@ open class AlamofireRequestBuilder: RequestBuilder { } - private func processRequest(request: DataRequest, _ managerId: String, _ completion: @escaping (_ response: Response?, _ error: Error?) -> Void) { + private func processRequest(request: DataRequest, _ managerId: String, _ completion: @escaping (_ response: Response?, _ error: ErrorResponse?) -> Void) { if let credential = self.credential { request.authenticate(usingCredential: credential) } let cleanupRequest = { - _ = managerStore.removeValue(forKey: managerId) + managerStore[managerId] = nil } let validatedRequest = request.validate() @@ -125,7 +166,7 @@ open class AlamofireRequestBuilder: RequestBuilder { if stringResponse.result.isFailure { completion( nil, - ErrorResponse.Error(stringResponse.response?.statusCode ?? 500, stringResponse.data, stringResponse.result.error as Error!) + ErrorResponse.HttpError(statusCode: stringResponse.response?.statusCode ?? 500, data: stringResponse.data, error: stringResponse.result.error as Error!) ) return } @@ -145,7 +186,7 @@ open class AlamofireRequestBuilder: RequestBuilder { if voidResponse.result.isFailure { completion( nil, - ErrorResponse.Error(voidResponse.response?.statusCode ?? 500, voidResponse.data, voidResponse.result.error!) + ErrorResponse.HttpError(statusCode: voidResponse.response?.statusCode ?? 500, data: voidResponse.data, error: voidResponse.result.error!) ) return } @@ -161,10 +202,10 @@ open class AlamofireRequestBuilder: RequestBuilder { validatedRequest.responseData(completionHandler: { (dataResponse) in cleanupRequest() - if (dataResponse.result.isFailure) { + if dataResponse.result.isFailure { completion( nil, - ErrorResponse.Error(dataResponse.response?.statusCode ?? 500, dataResponse.data, dataResponse.result.error!) + ErrorResponse.HttpError(statusCode: dataResponse.response?.statusCode ?? 500, data: dataResponse.data, error: dataResponse.result.error!) ) return } @@ -177,12 +218,62 @@ open class AlamofireRequestBuilder: RequestBuilder { nil ) }) + case is URL.Type: + validatedRequest.responseData(completionHandler: { (dataResponse) in + cleanupRequest() + + do { + + guard !dataResponse.result.isFailure else { + throw DownloadException.responseFailed + } + + guard let data = dataResponse.data else { + throw DownloadException.responseDataMissing + } + + guard let request = request.request else { + throw DownloadException.requestMissing + } + + let fileManager = FileManager.default + let urlRequest = try request.asURLRequest() + let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0] + let requestURL = try self.getURL(from: urlRequest) + + var requestPath = try self.getPath(from: requestURL) + + if let headerFileName = self.getFileName(fromContentDisposition: dataResponse.response?.allHeaderFields["Content-Disposition"] as? String) { + requestPath = requestPath.appending("/\(headerFileName)") + } + + let filePath = documentsDirectory.appendingPathComponent(requestPath) + let directoryPath = filePath.deletingLastPathComponent().path + + try fileManager.createDirectory(atPath: directoryPath, withIntermediateDirectories: true, attributes: nil) + try data.write(to: filePath, options: .atomic) + + completion( + Response( + response: dataResponse.response!, + body: (filePath as! T) + ), + nil + ) + + } catch let requestParserError as DownloadException { + completion(nil, ErrorResponse.HttpError(statusCode: 400, data: dataResponse.data, error: requestParserError)) + } catch let error { + completion(nil, ErrorResponse.HttpError(statusCode: 400, data: dataResponse.data, error: error)) + } + return + }) default: validatedRequest.responseJSON(options: .allowFragments) { response in cleanupRequest() if response.result.isFailure { - completion(nil, ErrorResponse.Error(response.response?.statusCode ?? 500, response.data, response.result.error!)) + completion(nil, ErrorResponse.HttpError(statusCode: response.response?.statusCode ?? 500, data: response.data, error: response.result.error!)) return } @@ -190,7 +281,7 @@ open class AlamofireRequestBuilder: RequestBuilder { // NSNull would crash decoders if response.response?.statusCode == 204 && response.result.value is NSNull{ completion(nil, nil) - return; + return } if () is T { @@ -198,8 +289,11 @@ open class AlamofireRequestBuilder: RequestBuilder { return } if let json: Any = response.result.value { - let body = Decoders.decode(clazz: T.self, source: json as AnyObject, instance: nil) - completion(Response(response: response.response!, body: body), nil) + let decoded = Decoders.decode(clazz: T.self, source: json as AnyObject, instance: nil) + switch decoded { + case let .success(object): completion(Response(response: response.response!, body: object), nil) + case let .failure(error): completion(nil, ErrorResponse.DecodeError(response: response.data, decodeError: error)) + } return } else if "" is T { // swagger-parser currently doesn't support void, which will be fixed in future swagger-parser release @@ -208,7 +302,7 @@ open class AlamofireRequestBuilder: RequestBuilder { return } - completion(nil, ErrorResponse.Error(500, nil, NSError(domain: "localhost", code: 500, userInfo: ["reason": "unreacheable code"]))) + completion(nil, ErrorResponse.HttpError(statusCode: 500, data: nil, error: NSError(domain: "localhost", code: 500, userInfo: ["reason": "unreacheable code"]))) } } } @@ -220,4 +314,63 @@ open class AlamofireRequestBuilder: RequestBuilder { } return httpHeaders } + + fileprivate func getFileName(fromContentDisposition contentDisposition : String?) -> String? { + + guard let contentDisposition = contentDisposition else { + return nil + } + + let items = contentDisposition.components(separatedBy: ";") + + var filename : String? = nil + + for contentItem in items { + + let filenameKey = "filename=" + guard let range = contentItem.range(of: filenameKey) else { + break + } + + filename = contentItem + return filename? + .replacingCharacters(in: range, with:"") + .replacingOccurrences(of: "\"", with: "") + .trimmingCharacters(in: .whitespacesAndNewlines) + } + + return filename + + } + + fileprivate func getPath(from url : URL) throws -> String { + + guard var path = NSURLComponents(url: url, resolvingAgainstBaseURL: true)?.path else { + throw DownloadException.requestMissingPath + } + + if path.hasPrefix("/") { + path.remove(at: path.startIndex) + } + + return path + + } + + fileprivate func getURL(from urlRequest : URLRequest) throws -> URL { + + guard let url = urlRequest.url else { + throw DownloadException.requestMissingURL + } + + return url + } } + +fileprivate enum DownloadException : Error { + case responseDataMissing + case responseFailed + case requestMissing + case requestMissingPath + case requestMissingURL +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/swift3/Cartfile.mustache b/modules/swagger-codegen/src/main/resources/swift3/Cartfile.mustache index 101df9a7e04..523ab5db209 100644 --- a/modules/swagger-codegen/src/main/resources/swift3/Cartfile.mustache +++ b/modules/swagger-codegen/src/main/resources/swift3/Cartfile.mustache @@ -1,3 +1,3 @@ -github "Alamofire/Alamofire" >= 3.1.0{{#usePromiseKit}} -github "mxcl/PromiseKit" >=1.5.3{{/usePromiseKit}}{{#useRxSwift}} -github "ReactiveX/RxSwift" ~> 2.0{{/useRxSwift}} +github "Alamofire/Alamofire" ~> 4.5{{#usePromiseKit}} +github "mxcl/PromiseKit" ~> 4.4{{/usePromiseKit}}{{#useRxSwift}} +github "ReactiveX/RxSwift" "rxswift-3.0"{{/useRxSwift}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/swift3/Extensions.mustache b/modules/swagger-codegen/src/main/resources/swift3/Extensions.mustache index 19a3dd7364c..4d91fa2e443 100644 --- a/modules/swagger-codegen/src/main/resources/swift3/Extensions.mustache +++ b/modules/swagger-codegen/src/main/resources/swift3/Extensions.mustache @@ -54,7 +54,7 @@ extension Dictionary: JSONEncodable { func encodeToJSON() -> Any { var dictionary = [AnyHashable: Any]() for (key, value) in self { - dictionary[key as! NSObject] = encodeIfPossible(value) + dictionary[key] = encodeIfPossible(value) } return dictionary as Any } @@ -85,6 +85,106 @@ extension UUID: JSONEncodable { } } +/// Represents an ISO-8601 full-date (RFC-3339). +/// ex: 12-31-1999 +/// https://xml2rfc.tools.ietf.org/public/rfc/html/rfc3339.html#anchor14 +public final class ISOFullDate: CustomStringConvertible { + + public let year: Int + public let month: Int + public let day: Int + + public init(year: Int, month: Int, day: Int) { + self.year = year + self.month = month + self.day = day + } + + /** + Converts a Date to an ISOFullDate. Only interested in the year, month, day components. + + - parameter date: The date to convert. + + - returns: An ISOFullDate constructed from the year, month, day of the date. + */ + public static func from(date: Date) -> ISOFullDate? { + let calendar = Calendar(identifier: .gregorian) + + let components = calendar.dateComponents( + [ + .year, + .month, + .day, + ], + from: date + ) + + guard + let year = components.year, + let month = components.month, + let day = components.day + else { + return nil + } + + return ISOFullDate( + year: year, + month: month, + day: day + ) + } + + /** + Converts a ISO-8601 full-date string to an ISOFullDate. + + - parameter string: The ISO-8601 full-date format string to convert. + + - returns: An ISOFullDate constructed from the string. + */ + public static func from(string: String) -> ISOFullDate? { + let components = string + .characters + .split(separator: "-") + .map(String.init) + .flatMap { Int($0) } + guard components.count == 3 else { return nil } + + return ISOFullDate( + year: components[0], + month: components[1], + day: components[2] + ) + } + + /** + Converts the receiver to a Date, in the default time zone. + + - returns: A Date from the components of the receiver, in the default time zone. + */ + public func toDate() -> Date? { + var components = DateComponents() + components.year = year + components.month = month + components.day = day + components.timeZone = TimeZone.ReferenceType.default + let calendar = Calendar(identifier: .gregorian) + return calendar.date(from: components) + } + + // MARK: CustomStringConvertible + + public var description: String { + return "\(year)-\(month)-\(day)" + } + +} + +extension ISOFullDate: JSONEncodable { + public func encodeToJSON() -> Any { + return "\(year)-\(month)-\(day)" + } +} + {{#usePromiseKit}}extension RequestBuilder { public func execute() -> Promise> { let deferred = Promise>.pending() diff --git a/modules/swagger-codegen/src/main/resources/swift3/Models.mustache b/modules/swagger-codegen/src/main/resources/swift3/Models.mustache index 1e6d3ceab98..11a6f4fb0f1 100644 --- a/modules/swagger-codegen/src/main/resources/swift3/Models.mustache +++ b/modules/swagger-codegen/src/main/resources/swift3/Models.mustache @@ -11,7 +11,8 @@ protocol JSONEncodable { } public enum ErrorResponse : Error { - case Error(Int, Data?, Error) + case HttpError(statusCode: Int, data: Data?, error: Error) + case DecodeError(response: Data?, decodeError: DecodeError) } open class Response { @@ -28,61 +29,137 @@ open class Response { public convenience init(response: HTTPURLResponse, body: T?) { let rawHeader = response.allHeaderFields var header = [String:String]() - for (key, value) in rawHeader { - header[key as! String] = value as? String + for case let (key, value) as (String, String) in rawHeader { + header[key] = value } self.init(statusCode: response.statusCode, header: header, body: body) } } +public enum Decoded { + case success(ValueType) + case failure(DecodeError) +} + +public extension Decoded { + var value: ValueType? { + switch self { + case let .success(value): + return value + case .failure: + return nil + } + } +} + +public enum DecodeError { + case typeMismatch(expected: String, actual: String) + case missingKey(key: String) + case parseError(message: String) +} + private var once = Int() class Decoders { static fileprivate var decoders = Dictionary AnyObject)>() - static func addDecoder(clazz: T.Type, decoder: @escaping ((AnyObject, AnyObject?) -> T)) { + static func addDecoder(clazz: T.Type, decoder: @escaping ((AnyObject, AnyObject?) -> Decoded)) { let key = "\(T.self)" decoders[key] = { decoder($0, $1) as AnyObject } } - static func decode(clazz: T.Type, discriminator: String, source: AnyObject) -> T { - let key = discriminator; - if let decoder = decoders[key] { - return decoder(source, nil) as! T + static func decode(clazz: T.Type, discriminator: String, source: AnyObject) -> Decoded { + let key = discriminator + if let decoder = decoders[key], let value = decoder(source, nil) as? Decoded { + return value + } else { + return .failure(.typeMismatch(expected: String(describing: clazz), actual: String(describing: source))) + } + } + + static func decode(clazz: [T].Type, source: AnyObject) -> Decoded<[T]> { + if let sourceArray = source as? [AnyObject] { + var values = [T]() + for sourceValue in sourceArray { + switch Decoders.decode(clazz: T.self, source: sourceValue, instance: nil) { + case let .success(value): + values.append(value) + case let .failure(error): + return .failure(error) + } + } + return .success(values) + } else { + return .failure(.typeMismatch(expected: String(describing: clazz), actual: String(describing: source))) + } + } + + static func decode(clazz: T.Type, source: AnyObject) -> Decoded { + switch Decoders.decode(clazz: T.self, source: source, instance: nil) { + case let .success(value): + return .success(value) + case let .failure(error): + return .failure(error) + } + } + + static open func decode(clazz: T.Type, source: AnyObject) -> Decoded { + if let value = source as? T.RawValue { + if let enumValue = T.init(rawValue: value) { + return .success(enumValue) + } else { + return .failure(.typeMismatch(expected: "A value from the enumeration \(T.self)", actual: "\(value)")) + } } else { - fatalError("Source \(source) is not convertible to type \(clazz): Maybe swagger file is insufficient") + return .failure(.typeMismatch(expected: "\(T.RawValue.self) matching a case from the enumeration \(T.self)", actual: String(describing: type(of: source)))) } } - static func decode(clazz: [T].Type, source: AnyObject) -> [T] { - let array = source as! [AnyObject] - return array.map { Decoders.decode(clazz: T.self, source: $0, instance: nil) } + static func decode(clazz: [Key:T].Type, source: AnyObject) -> Decoded<[Key:T]> { + if let sourceDictionary = source as? [Key: AnyObject] { + var dictionary = [Key:T]() + for (key, value) in sourceDictionary { + switch Decoders.decode(clazz: T.self, source: value, instance: nil) { + case let .success(value): + dictionary[key] = value + case let .failure(error): + return .failure(error) + } + } + return .success(dictionary) + } else { + return .failure(.typeMismatch(expected: String(describing: clazz), actual: String(describing: source))) + } } - static func decode(clazz: [Key:T].Type, source: AnyObject) -> [Key:T] { - let sourceDictionary = source as! [Key: AnyObject] - var dictionary = [Key:T]() - for (key, value) in sourceDictionary { - dictionary[key] = Decoders.decode(clazz: T.self, source: value, instance: nil) + static func decodeOptional(clazz: T.Type, source: AnyObject?) -> Decoded { + guard !(source is NSNull), source != nil else { return .success(nil) } + if let value = source as? T.RawValue { + if let enumValue = T.init(rawValue: value) { + return .success(enumValue) + } else { + return .failure(.typeMismatch(expected: "A value from the enumeration \(T.self)", actual: "\(value)")) + } + } else { + return .failure(.typeMismatch(expected: "\(T.RawValue.self) matching a case from the enumeration \(T.self)", actual: String(describing: type(of: source)))) } - return dictionary } - static func decode(clazz: T.Type, source: AnyObject, instance: AnyObject?) -> T { + static func decode(clazz: T.Type, source: AnyObject, instance: AnyObject?) -> Decoded { initialize() - if T.self is Int32.Type && source is NSNumber { - return (source as! NSNumber).int32Value as! T + if let sourceNumber = source as? NSNumber, let value = sourceNumber.int32Value as? T, T.self is Int32.Type { + return .success(value) } - if T.self is Int64.Type && source is NSNumber { - return (source as! NSNumber).int64Value as! T + if let sourceNumber = source as? NSNumber, let value = sourceNumber.int32Value as? T, T.self is Int64.Type { + return .success(value) } - if T.self is UUID.Type && source is String { - return UUID(uuidString: source as! String) as! T + if let intermediate = source as? String, let value = UUID(uuidString: intermediate) as? T, source is String, T.self is UUID.Type { + return .success(value) } - if source is T { - return source as! T + if let value = source as? T { + return .success(value) } - if T.self is Data.Type && source is String { - return Data(base64Encoded: source as! String) as! T + if let intermediate = source as? String, let value = Data(base64Encoded: intermediate) as? T { + return .success(value) } {{#lenientTypeCast}} if T.self is Int32.Type && source is String { @@ -100,40 +177,87 @@ class Decoders { {{/lenientTypeCast}} let key = "\(T.self)" - if let decoder = decoders[key] { - return decoder(source, instance) as! T + if let decoder = decoders[key], let value = decoder(source, instance) as? Decoded { + return value } else { - fatalError("Source \(source) is not convertible to type \(clazz): Maybe swagger file is insufficient") + return .failure(.typeMismatch(expected: String(describing: clazz), actual: String(describing: source))) } } - static func decodeOptional(clazz: T.Type, source: AnyObject?) -> T? { - if source is NSNull { - return nil + //Convert a Decoded so that its value is optional. DO WE STILL NEED THIS? + static func toOptional(decoded: Decoded) -> Decoded { + return .success(decoded.value) + } + + static func decodeOptional(clazz: T.Type, source: AnyObject?) -> Decoded { + if let source = source, !(source is NSNull) { + switch Decoders.decode(clazz: clazz, source: source, instance: nil) { + case let .success(value): return .success(value) + case let .failure(error): return .failure(error) + } + } else { + return .success(nil) } - return source.map { (source: AnyObject) -> T in - Decoders.decode(clazz: clazz, source: source, instance: nil) + } + + static func decodeOptional(clazz: [T].Type, source: AnyObject?) -> Decoded<[T]?> where T: RawRepresentable { + if let source = source as? [AnyObject] { + var values = [T]() + for sourceValue in source { + switch Decoders.decodeOptional(clazz: T.self, source: sourceValue) { + case let .success(value): if let value = value { values.append(value) } + case let .failure(error): return .failure(error) + } + } + return .success(values) + } else { + return .success(nil) } } - static func decodeOptional(clazz: [T].Type, source: AnyObject?) -> [T]? { - if source is NSNull { - return nil - } - return source.map { (someSource: AnyObject) -> [T] in - Decoders.decode(clazz: clazz, source: someSource) + static func decodeOptional(clazz: [T].Type, source: AnyObject?) -> Decoded<[T]?> { + if let source = source as? [AnyObject] { + var values = [T]() + for sourceValue in source { + switch Decoders.decode(clazz: T.self, source: sourceValue, instance: nil) { + case let .success(value): values.append(value) + case let .failure(error): return .failure(error) + } + } + return .success(values) + } else { + return .success(nil) } } - static func decodeOptional(clazz: [Key:T].Type, source: AnyObject?) -> [Key:T]? { - if source is NSNull { - return nil + static func decodeOptional(clazz: [Key:T].Type, source: AnyObject?) -> Decoded<[Key:T]?> { + if let sourceDictionary = source as? [Key: AnyObject] { + var dictionary = [Key:T]() + for (key, value) in sourceDictionary { + switch Decoders.decode(clazz: T.self, source: value, instance: nil) { + case let .success(value): dictionary[key] = value + case let .failure(error): return .failure(error) + } + } + return .success(dictionary) + } else { + return .success(nil) } - return source.map { (someSource: AnyObject) -> [Key:T] in - Decoders.decode(clazz: clazz, source: someSource) + } + + static func decodeOptional(clazz: T, source: AnyObject) -> Decoded where T.RawValue == U { + if let value = source as? U { + if let enumValue = T.init(rawValue: value) { + return .success(enumValue) + } else { + return .failure(.typeMismatch(expected: "A value from the enumeration \(T.self)", actual: "\(value)")) + } + } else { + return .failure(.typeMismatch(expected: "String", actual: String(describing: type(of: source)))) } } + private static var __once: () = { let formatters = [ "yyyy-MM-dd", @@ -149,100 +273,126 @@ class Decoders { return formatter } // Decoder for Date - Decoders.addDecoder(clazz: Date.self) { (source: AnyObject, instance: AnyObject?) -> Date in + Decoders.addDecoder(clazz: Date.self) { (source: AnyObject, instance: AnyObject?) -> Decoded in if let sourceString = source as? String { for formatter in formatters { if let date = formatter.date(from: sourceString) { - return date + return .success(date) } } } - if let sourceInt = source as? Int64 { + if let sourceInt = source as? Int { // treat as a java date - return Date(timeIntervalSince1970: Double(sourceInt / 1000) ) + return .success(Date(timeIntervalSince1970: Double(sourceInt / 1000) )) + } + if source is String || source is Int { + return .failure(.parseError(message: "Could not decode date")) + } else { + return .failure(.typeMismatch(expected: "String or Int", actual: "\(source)")) } - fatalError("formatter failed to parse \(source)") - } {{#models}}{{#model}} + } + + // Decoder for ISOFullDate + Decoders.addDecoder(clazz: ISOFullDate.self) { (source: AnyObject, instance: AnyObject?) -> Decoded in + if let string = source as? String, + let isoDate = ISOFullDate.from(string: string) { + return .success(isoDate) + } else { + return .failure(.typeMismatch(expected: "ISO date", actual: "\(source)")) + } + } + {{#models}} + {{#model}} + {{^isArrayModel}} // Decoder for [{{{classname}}}] - Decoders.addDecoder(clazz: [{{{classname}}}].self) { (source: AnyObject, instance: AnyObject?) -> [{{{classname}}}] in + Decoders.addDecoder(clazz: [{{{classname}}}].self) { (source: AnyObject, instance: AnyObject?) -> Decoded<[{{{classname}}}]> in return Decoders.decode(clazz: [{{{classname}}}].self, source: source) } + // Decoder for {{{classname}}} - Decoders.addDecoder(clazz: {{{classname}}}.self) { (source: AnyObject, instance: AnyObject?) -> {{{classname}}} in -{{#isArrayModel}} - let sourceArray = source as! [AnyObject] - return sourceArray.map({ Decoders.decode(clazz: {{{arrayModelType}}}.self, source: $0, instance: nil) }) -{{/isArrayModel}} -{{^isArrayModel}} + Decoders.addDecoder(clazz: {{{classname}}}.self) { (source: AnyObject, instance: AnyObject?) -> Decoded<{{{classname}}}> in {{#isEnum}} - if let source = source as? {{dataType}} { - if let result = {{classname}}(rawValue: source) { - return result - } - } - fatalError("Source \(source) is not convertible to enum type {{classname}}: Maybe swagger file is insufficient") + //TODO: I don't think we need this anymore + return Decoders.decode(clazz: {{{classname}}}.self, source: source, instance: instance) {{/isEnum}} {{^isEnum}} {{#allVars.isEmpty}} - if let source = source as? {{dataType}} { - return source + if let source = source as? {{classname}} { + return .success(source) + } else { + return .failure(.typeMismatch(expected: "Typealias {{classname}}", actual: "\(source)")) } - fatalError("Source \(source) is not convertible to typealias {{classname}}: Maybe swagger file is insufficient") {{/allVars.isEmpty}} {{^allVars.isEmpty}} - let sourceDictionary = source as! [AnyHashable: Any] + if let sourceDictionary = source as? [AnyHashable: Any] { {{#discriminator}} - // Check discriminator to support inheritance - if let discriminator = sourceDictionary["{{discriminator}}"] as? String, instance == nil && discriminator != "{{classname}}" { - return Decoders.decode(clazz: {{classname}}.self, discriminator: discriminator, source: source) - } + // Check discriminator to support inheritance + if let discriminator = sourceDictionary["{{discriminator}}"] as? String, instance == nil && discriminator != "{{classname}}"{ + return Decoders.decode(clazz: {{classname}}.self, discriminator: discriminator, source: source) + } {{/discriminator}} {{#additionalPropertiesType}} var propsDictionary = sourceDictionary let keys : [AnyHashable] = [{{#allVars}}{{^-last}}"{{baseName}}", {{/-last}}{{#-last}}"{{baseName}}"{{/-last}}{{/allVars}}] {{/additionalPropertiesType}} {{#unwrapRequired}} - let result = {{classname}}({{#requiredVars}}{{^-first}}, {{/-first}}{{#isEnum}}{{name}}: {{classname}}.{{datatypeWithEnum}}(rawValue: (sourceDictionary["{{baseName}}"] as! {{datatype}}))! {{/isEnum}}{{^isEnum}}{{name}}: Decoders.decode(clazz: {{{baseType}}}.self, source: sourceDictionary["{{baseName}}"]! as AnyObject, instance: nil){{/isEnum}}{{/requiredVars}}) - {{#optionalVars}}{{#isEnum}} - if let {{name}} = sourceDictionary["{{baseName}}"] as? {{datatype}} { {{^isContainer}} - result.{{name}} = {{classname}}.{{datatypeWithEnum}}(rawValue: ({{name}})){{/isContainer}}{{#isListContainer}} - result.{{name}} = {{name}}.map ({ {{classname}}.{{enumName}}(rawValue: $0)! }){{/isListContainer}} - }{{/isEnum}}{{^isEnum}} - result.{{name}} = Decoders.decodeOptional(clazz: {{{baseType}}}.self, source: sourceDictionary["{{baseName}}"] as AnyObject?){{/isEnum}} - {{/optionalVars}} + {{#requiredVars}} + guard let {{name}}Source = sourceDictionary["{{baseName}}"] as AnyObject? else { + return .failure(.missingKey(key: "{{baseName}}")) + } + guard let {{name}} = Decoders.decode(clazz: {{#isEnum}}{{^isListContainer}}{{classname}}.{{enumName}}.self{{/isListContainer}}{{#isListContainer}}Array<{{classname}}.{{enumName}}>.self{{/isListContainer}}{{/isEnum}}{{^isEnum}}{{{datatype}}}.self{{/isEnum}}.self, source: {{name}}Source).value else { + return .failure(.typeMismatch(expected: "{{classname}}", actual: "\({{name}}Source)")) + } + {{/requiredVars}} + let _result = {{classname}}({{#requiredVars}}{{^-first}}, {{/-first}}{{name}}: {{name}}{{/requiredVars}}) + {{#optionalVars}} + switch Decoders.decodeOptional(clazz: {{#isEnum}}{{^isListContainer}}{{classname}}.{{enumName}}.self{{/isListContainer}}{{#isListContainer}}Array<{{classname}}.{{enumName}}>.self{{/isListContainer}}{{/isEnum}}{{^isEnum}}{{{datatype}}}.self{{/isEnum}}, source: sourceDictionary["{{baseName}}"] as AnyObject?) { + case let .success(value): _result.{{name}} = value + case let .failure(error): break + } + {{/optionalVars}} {{/unwrapRequired}} {{^unwrapRequired}} - let result = instance == nil ? {{classname}}() : instance as! {{classname}} - {{#parent}} - if decoders["\({{parent}}.self)"] != nil { - _ = Decoders.decode(clazz: {{parent}}.self, source: source, instance: result) - } - {{/parent}} - {{#allVars}}{{#isEnum}} - if let {{name}} = sourceDictionary["{{baseName}}"] as? {{datatype}} { {{^isContainer}} - result.{{name}} = {{classname}}.{{datatypeWithEnum}}(rawValue: ({{name}})){{/isContainer}}{{#isListContainer}} - result.{{name}} = {{name}}.map ({ {{classname}}.{{enumName}}(rawValue: $0)! }){{/isListContainer}}{{#isMapContainer}}//TODO: handle enum map scenario{{/isMapContainer}} - }{{/isEnum}} - {{^isEnum}}result.{{name}} = Decoders.decodeOptional(clazz: {{{baseType}}}.self, source: sourceDictionary["{{baseName}}"] as AnyObject?){{/isEnum}}{{/allVars}} + let _result = instance == nil ? {{classname}}() : instance as! {{classname}} + {{#parent}} + if decoders["\({{parent}}.self)"] != nil { + _ = Decoders.decode(clazz: {{parent}}.self, source: source, instance: _result) + } + {{/parent}} + {{#allVars}} + switch Decoders.decodeOptional(clazz: {{#isEnum}}{{^isListContainer}}{{classname}}.{{enumName}}.self{{/isListContainer}}{{#isListContainer}}Array<{{classname}}.{{enumName}}>.self{{/isListContainer}}{{/isEnum}}{{^isEnum}}{{{datatype}}}.self{{/isEnum}}, source: sourceDictionary["{{baseName}}"] as AnyObject?) { + {{#isEnum}}{{#isMapContainer}}/*{{/isMapContainer}}{{/isEnum}} + case let .success(value): _result.{{name}} = value + case let .failure(error): break + {{#isEnum}}{{#isMapContainer}}*/ default: break //TODO: handle enum map scenario{{/isMapContainer}}{{/isEnum}} + } + {{/allVars}} {{/unwrapRequired}} {{#additionalPropertiesType}} - for key in keys { - propsDictionary.removeValue(forKey: key) - } + for key in keys { + propsDictionary.removeValue(forKey: key) + } + + for key in propsDictionary.keys { + switch Decoders.decodeOptional(clazz: String.self, source: propsDictionary[key] as AnyObject?) { + + case let .success(value): _result[key] = value + default: continue - for key in propsDictionary.keys { - if let decodedValue = Decoders.decodeOptional(clazz: String.self, source: propsDictionary[key] as AnyObject?) { - result[key] = decodedValue + } } - } {{/additionalPropertiesType}} - return result + return .success(_result) + } else { + return .failure(.typeMismatch(expected: "{{classname}}", actual: "\(source)")) + } {{/allVars.isEmpty}} {{/isEnum}} -{{/isArrayModel}} - }{{/model}} + } + {{/isArrayModel}} + {{/model}} {{/models}} }() diff --git a/modules/swagger-codegen/src/main/resources/swift3/Podspec.mustache b/modules/swagger-codegen/src/main/resources/swift3/Podspec.mustache index 97bb64e229d..de8b1241985 100644 --- a/modules/swagger-codegen/src/main/resources/swift3/Podspec.mustache +++ b/modules/swagger-codegen/src/main/resources/swift3/Podspec.mustache @@ -3,6 +3,7 @@ Pod::Spec.new do |s| s.summary = '{{projectDescription}}'{{/projectDescription}} s.ios.deployment_target = '9.0' s.osx.deployment_target = '10.11' + s.tvos.deployment_target = '9.0' s.version = '{{#podVersion}}{{podVersion}}{{/podVersion}}{{^podVersion}}0.0.1{{/podVersion}}' s.source = {{#podSource}}{{& podSource}}{{/podSource}}{{^podSource}}{ :git => 'git@github.com:swagger-api/swagger-mustache.git', :tag => 'v1.0.0' }{{/podSource}}{{#podAuthors}} s.authors = '{{podAuthors}}'{{/podAuthors}}{{#podSocialMediaURL}} @@ -14,8 +15,8 @@ Pod::Spec.new do |s| s.description = '{{podDescription}}'{{/podDescription}}{{#podScreenshots}} s.screenshots = {{& podScreenshots}}{{/podScreenshots}}{{#podDocumentationURL}} s.documentation_url = '{{podDocumentationURL}}'{{/podDocumentationURL}} - s.source_files = '{{projectName}}/Classes/Swaggers/**/*.swift'{{#usePromiseKit}} - s.dependency 'PromiseKit', '~> 4.2.2'{{/usePromiseKit}}{{#useRxSwift}} - s.dependency 'RxSwift', '~> 3.4.1'{{/useRxSwift}} - s.dependency 'Alamofire', '~> 4.0' + s.source_files = '{{projectName}}/Classes/**/*.swift'{{#usePromiseKit}} + s.dependency 'PromiseKit/CorePromise', '~> 4.4.0'{{/usePromiseKit}}{{#useRxSwift}} + s.dependency 'RxSwift', '3.6.1'{{/useRxSwift}} + s.dependency 'Alamofire', '~> 4.5.0' end diff --git a/modules/swagger-codegen/src/main/resources/swift3/api.mustache b/modules/swagger-codegen/src/main/resources/swift3/api.mustache index e85028db04b..f32b668c64f 100644 --- a/modules/swagger-codegen/src/main/resources/swift3/api.mustache +++ b/modules/swagger-codegen/src/main/resources/swift3/api.mustache @@ -15,7 +15,8 @@ extension {{projectName}}API { {{/swiftUseApiNamespace}} {{#description}} -/** {{description}} */{{/description}} +/** {{description}} */ +{{/description}} open class {{classname}}: APIBase { {{#operation}} {{#allParams}} @@ -32,13 +33,15 @@ open class {{classname}}: APIBase { /** {{#summary}} {{{summary}}} - {{/summary}}{{#allParams}} - - parameter {{paramName}}: ({{#isFormParam}}form{{/isFormParam}}{{#isQueryParam}}query{{/isQueryParam}}{{#isPathParam}}path{{/isPathParam}}{{#isHeaderParam}}header{{/isHeaderParam}}{{#isBodyParam}}body{{/isBodyParam}}) {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}} + {{/summary}} + {{#allParams}} + - parameter {{paramName}}: ({{#isFormParam}}form{{/isFormParam}}{{#isQueryParam}}query{{/isQueryParam}}{{#isPathParam}}path{{/isPathParam}}{{#isHeaderParam}}header{{/isHeaderParam}}{{#isBodyParam}}body{{/isBodyParam}}) {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} + {{/allParams}} - parameter completion: completion handler to receive the data and the error objects */ - open class func {{operationId}}({{#allParams}}{{paramName}}: {{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}{{{datatypeWithEnum}}}_{{operationId}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}? = nil{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}{{#hasParams}}, {{/hasParams}}completion: @escaping ((_ {{#returnType}}data: {{{returnType}}}?,_ {{/returnType}}error: Error?) -> Void)) { + open class func {{operationId}}({{#allParams}}{{paramName}}: {{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}{{{datatypeWithEnum}}}_{{operationId}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}? = nil{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}{{#hasParams}}, {{/hasParams}}completion: @escaping ((_ {{#returnType}}data: {{{returnType}}}?, _ {{/returnType}}error: ErrorResponse?) -> Void)) { {{operationId}}WithRequestBuilder({{#allParams}}{{paramName}}: {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}).execute { (response, error) -> Void in - completion({{#returnType}}response?.body, {{/returnType}}error); + completion({{#returnType}}response?.body, {{/returnType}}error) } } @@ -46,8 +49,10 @@ open class {{classname}}: APIBase { /** {{#summary}} {{{summary}}} - {{/summary}}{{#allParams}} - - parameter {{paramName}}: ({{#isFormParam}}form{{/isFormParam}}{{#isQueryParam}}query{{/isQueryParam}}{{#isPathParam}}path{{/isPathParam}}{{#isHeaderParam}}header{{/isHeaderParam}}{{#isBodyParam}}body{{/isBodyParam}}) {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}} + {{/summary}} + {{#allParams}} + - parameter {{paramName}}: ({{#isFormParam}}form{{/isFormParam}}{{#isQueryParam}}query{{/isQueryParam}}{{#isPathParam}}path{{/isPathParam}}{{#isHeaderParam}}header{{/isHeaderParam}}{{#isBodyParam}}body{{/isBodyParam}}) {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} + {{/allParams}} - returns: Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> */ open class func {{operationId}}({{#allParams}} {{paramName}}: {{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}{{{datatypeWithEnum}}}_{{operationId}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}? = nil{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) -> Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> { @@ -66,8 +71,10 @@ open class {{classname}}: APIBase { /** {{#summary}} {{{summary}}} - {{/summary}}{{#allParams}} - - parameter {{paramName}}: ({{#isFormParam}}form{{/isFormParam}}{{#isQueryParam}}query{{/isQueryParam}}{{#isPathParam}}path{{/isPathParam}}{{#isHeaderParam}}header{{/isHeaderParam}}{{#isBodyParam}}body{{/isBodyParam}}) {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}} + {{/summary}} + {{#allParams}} + - parameter {{paramName}}: ({{#isFormParam}}form{{/isFormParam}}{{#isQueryParam}}query{{/isQueryParam}}{{#isPathParam}}path{{/isPathParam}}{{#isHeaderParam}}header{{/isHeaderParam}}{{#isBodyParam}}body{{/isBodyParam}}) {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} + {{/allParams}} - returns: Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> */ open class func {{operationId}}({{#allParams}}{{paramName}}: {{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}{{{datatypeWithEnum}}}_{{operationId}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}? = nil{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) -> Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> { @@ -89,27 +96,36 @@ open class {{classname}}: APIBase { {{#summary}} {{{summary}}} {{/summary}} - - {{httpMethod}} {{{path}}}{{#notes}} - - {{{notes}}}{{/notes}}{{#subresourceOperation}} - - subresourceOperation: {{subresourceOperation}}{{/subresourceOperation}}{{#defaultResponse}} - - defaultResponse: {{defaultResponse}}{{/defaultResponse}}{{#authMethods}} + - {{httpMethod}} {{{path}}} + {{#notes}} + - {{{notes}}} + {{/notes}} + {{#subresourceOperation}} + - subresourceOperation: {{subresourceOperation}} + {{/subresourceOperation}} + {{#defaultResponse}} + - defaultResponse: {{defaultResponse}} + {{/defaultResponse}} + {{#authMethods}} - {{#isBasic}}BASIC{{/isBasic}}{{#isOAuth}}OAuth{{/isOAuth}}{{#isApiKey}}API Key{{/isApiKey}}: - type: {{type}}{{#keyParamName}} {{keyParamName}} {{#isKeyInQuery}}(QUERY){{/isKeyInQuery}}{{#isKeyInHeaer}}(HEADER){{/isKeyInHeaer}}{{/keyParamName}} - name: {{name}}{{/authMethods}}{{#responseHeaders}} - responseHeaders: {{responseHeaders}}{{/responseHeaders}}{{#examples}} - examples: {{{examples}}}{{/examples}}{{#externalDocs}} - - externalDocs: {{externalDocs}}{{/externalDocs}}{{#hasParams}} - {{/hasParams}}{{#allParams}} - - parameter {{paramName}}: ({{#isFormParam}}form{{/isFormParam}}{{#isQueryParam}}query{{/isQueryParam}}{{#isPathParam}}path{{/isPathParam}}{{#isHeaderParam}}header{{/isHeaderParam}}{{#isBodyParam}}body{{/isBodyParam}}) {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}} - + - externalDocs: {{externalDocs}}{{/externalDocs}} + {{#allParams}} + - parameter {{paramName}}: ({{#isFormParam}}form{{/isFormParam}}{{#isQueryParam}}query{{/isQueryParam}}{{#isPathParam}}path{{/isPathParam}}{{#isHeaderParam}}header{{/isHeaderParam}}{{#isBodyParam}}body{{/isBodyParam}}) {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} + {{/allParams}} - returns: RequestBuilder<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{description}} */ open class func {{operationId}}WithRequestBuilder({{#allParams}}{{paramName}}: {{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}{{{datatypeWithEnum}}}_{{operationId}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}? = nil{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) -> RequestBuilder<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> { {{^pathParams}}let{{/pathParams}}{{#pathParams}}{{^secondaryParam}}var{{/secondaryParam}}{{/pathParams}} path = "{{{path}}}"{{#pathParams}} - path = path.replacingOccurrences(of: "{{=<% %>=}}{<%baseName%>}<%={{ }}=%>", with: "\({{paramName}}{{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}.rawValue{{/isContainer}}{{/isEnum}})", options: .literal, range: nil){{/pathParams}} + let {{paramName}}PreEscape = "\({{paramName}}{{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}.rawValue{{/isContainer}}{{/isEnum}})" + let {{paramName}}PostEscape = {{paramName}}PreEscape.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? "" + path = path.replacingOccurrences(of: "{{=<% %>=}}{<%baseName%>}<%={{ }}=%>", with: {{paramName}}PostEscape, options: .literal, range: nil){{/pathParams}} let URLString = {{projectName}}API.basePath + path {{#bodyParam}} - let parameters = {{paramName}}{{^required}}?{{/required}}.encodeToJSON() as? [String:AnyObject] + let parameters = {{paramName}}{{^required}}?{{/required}}.encodeToJSON() {{/bodyParam}} {{^bodyParam}} {{#hasFormParams}} @@ -134,11 +150,17 @@ open class {{classname}}: APIBase { {{> _param}}{{#hasMore}}, {{/hasMore}} {{/queryParams}} ]) - {{/hasQueryParams}}{{#headerParams}}{{^secondaryParam}} - let nillableHeaders: [String: Any?] = [{{/secondaryParam}} - {{> _param}}{{#hasMore}},{{/hasMore}}{{^hasMore}} + {{/hasQueryParams}} + {{#headerParams}} + {{^secondaryParam}} + let nillableHeaders: [String: Any?] = [ + {{/secondaryParam}} + {{> _param}}{{#hasMore}},{{/hasMore}} + {{^hasMore}} ] - let headerParameters = APIHelper.rejectNilHeaders(nillableHeaders){{/hasMore}}{{/headerParams}} + let headerParameters = APIHelper.rejectNilHeaders(nillableHeaders) + {{/hasMore}} + {{/headerParams}} let requestBuilder: RequestBuilder<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}>.Type = {{projectName}}API.requestBuilderFactory.getBuilder() diff --git a/modules/swagger-codegen/src/main/resources/swift3/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/swift3/git_push.sh.mustache index e153ce23ecf..a2d75234837 100755 --- a/modules/swagger-codegen/src/main/resources/swift3/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/swift3/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/swift3/model.mustache b/modules/swagger-codegen/src/main/resources/swift3/model.mustache index c1bd6ebdb87..129ecc15275 100644 --- a/modules/swagger-codegen/src/main/resources/swift3/model.mustache +++ b/modules/swagger-codegen/src/main/resources/swift3/model.mustache @@ -1,4 +1,6 @@ -{{#models}}{{#model}}// +{{#models}} +{{#model}} +// // {{classname}}.swift // // Generated by swagger-codegen @@ -22,10 +24,6 @@ public enum {{classname}}: {{dataType}} { } {{/isEnum}} {{^isEnum}} -{{#vars.isEmpty}} -public typealias {{classname}} = {{dataType}} -{{/vars.isEmpty}} -{{^vars.isEmpty}} open class {{classname}}: {{#parent}}{{{parent}}}{{/parent}}{{^parent}}JSONEncodable{{/parent}} { {{#vars}} @@ -42,7 +40,12 @@ open class {{classname}}: {{#parent}}{{{parent}}}{{/parent}}{{^parent}}JSONEncod {{/isEnum}} {{^isEnum}} {{#description}}/** {{description}} */ - {{/description}}public var {{name}}: {{{datatype}}}{{^unwrapRequired}}?{{/unwrapRequired}}{{#unwrapRequired}}{{^required}}?{{/required}}{{/unwrapRequired}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}} + {{/description}}public var {{name}}: {{{datatype}}}{{^unwrapRequired}}?{{/unwrapRequired}}{{#unwrapRequired}}{{^required}}?{{/required}}{{/unwrapRequired}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{#objcCompatible}}{{#vendorExtensions.x-swift-optional-scalar}} + public var {{name}}Num: NSNumber? { + get { + return {{name}}.map({ return NSNumber(value: $0) }) + } + }{{/vendorExtensions.x-swift-optional-scalar}}{{/objcCompatible}} {{/isEnum}} {{/vars}} @@ -95,7 +98,7 @@ open class {{classname}}: {{#parent}}{{{parent}}}{{/parent}}{{^parent}}JSONEncod return dictionary } } -{{/vars.isEmpty}} + {{/isEnum}} {{/isArrayModel}} {{/model}} diff --git a/modules/swagger-codegen/src/main/resources/swift4/AlamofireImplementations.mustache b/modules/swagger-codegen/src/main/resources/swift4/AlamofireImplementations.mustache index b9d2a2727de..24351523736 100644 --- a/modules/swagger-codegen/src/main/resources/swift4/AlamofireImplementations.mustache +++ b/modules/swagger-codegen/src/main/resources/swift4/AlamofireImplementations.mustache @@ -77,16 +77,12 @@ open class AlamofireRequestBuilder: RequestBuilder { else { mpForm.append(fileURL, withName: k) } - break case let string as String: mpForm.append(string.data(using: String.Encoding.utf8)!, withName: k) - break case let number as NSNumber: mpForm.append(number.stringValue.data(using: String.Encoding.utf8)!, withName: k) - break default: fatalError("Unprocessable value \(v) with key \(k)") - break } } }, to: URLString, method: xMethod!, headers: nil, encodingCompletion: { encodingResult in @@ -97,7 +93,7 @@ open class AlamofireRequestBuilder: RequestBuilder { } self.processRequest(request: upload, managerId, completion) case .failure(let encodingError): - completion(nil, ErrorResponse.Error(415, nil, encodingError)) + completion(nil, ErrorResponse.error(415, nil, encodingError)) } }) } else { @@ -129,7 +125,7 @@ open class AlamofireRequestBuilder: RequestBuilder { if stringResponse.result.isFailure { completion( nil, - ErrorResponse.Error(stringResponse.response?.statusCode ?? 500, stringResponse.data, stringResponse.result.error as Error!) + ErrorResponse.error(stringResponse.response?.statusCode ?? 500, stringResponse.data, stringResponse.result.error as Error!) ) return } @@ -142,6 +138,56 @@ open class AlamofireRequestBuilder: RequestBuilder { nil ) }) + case is URL.Type: + validatedRequest.responseData(completionHandler: { (dataResponse) in + cleanupRequest() + + do { + + guard !dataResponse.result.isFailure else { + throw DownloadException.responseFailed + } + + guard let data = dataResponse.data else { + throw DownloadException.responseDataMissing + } + + guard let request = request.request else { + throw DownloadException.requestMissing + } + + let fileManager = FileManager.default + let urlRequest = try request.asURLRequest() + let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0] + let requestURL = try self.getURL(from: urlRequest) + + var requestPath = try self.getPath(from: requestURL) + + if let headerFileName = self.getFileName(fromContentDisposition: dataResponse.response?.allHeaderFields["Content-Disposition"] as? String) { + requestPath = requestPath.appending("/\(headerFileName)") + } + + let filePath = documentsDirectory.appendingPathComponent(requestPath) + let directoryPath = filePath.deletingLastPathComponent().path + + try fileManager.createDirectory(atPath: directoryPath, withIntermediateDirectories: true, attributes: nil) + try data.write(to: filePath, options: .atomic) + + completion( + Response( + response: dataResponse.response!, + body: (filePath as! T) + ), + nil + ) + + } catch let requestParserError as DownloadException { + completion(nil, ErrorResponse.error(400, dataResponse.data, requestParserError)) + } catch let error { + completion(nil, ErrorResponse.error(400, dataResponse.data, error)) + } + return + }) case is Void.Type: validatedRequest.responseData(completionHandler: { (voidResponse) in cleanupRequest() @@ -149,7 +195,7 @@ open class AlamofireRequestBuilder: RequestBuilder { if voidResponse.result.isFailure { completion( nil, - ErrorResponse.Error(voidResponse.response?.statusCode ?? 500, voidResponse.data, voidResponse.result.error!) + ErrorResponse.error(voidResponse.response?.statusCode ?? 500, voidResponse.data, voidResponse.result.error!) ) return } @@ -165,10 +211,10 @@ open class AlamofireRequestBuilder: RequestBuilder { validatedRequest.responseData(completionHandler: { (dataResponse) in cleanupRequest() - if (dataResponse.result.isFailure) { + if dataResponse.result.isFailure { completion( nil, - ErrorResponse.Error(dataResponse.response?.statusCode ?? 500, dataResponse.data, dataResponse.result.error!) + ErrorResponse.error(dataResponse.response?.statusCode ?? 500, dataResponse.data, dataResponse.result.error!) ) return } @@ -191,6 +237,66 @@ open class AlamofireRequestBuilder: RequestBuilder { } return httpHeaders } + + fileprivate func getFileName(fromContentDisposition contentDisposition : String?) -> String? { + + guard let contentDisposition = contentDisposition else { + return nil + } + + let items = contentDisposition.components(separatedBy: ";") + + var filename : String? = nil + + for contentItem in items { + + let filenameKey = "filename=" + guard let range = contentItem.range(of: filenameKey) else { + break + } + + filename = contentItem + return filename? + .replacingCharacters(in: range, with:"") + .replacingOccurrences(of: "\"", with: "") + .trimmingCharacters(in: .whitespacesAndNewlines) + } + + return filename + + } + + fileprivate func getPath(from url : URL) throws -> String { + + guard var path = NSURLComponents(url: url, resolvingAgainstBaseURL: true)?.path else { + throw DownloadException.requestMissingPath + } + + if path.hasPrefix("/") { + path.remove(at: path.startIndex) + } + + return path + + } + + fileprivate func getURL(from urlRequest : URLRequest) throws -> URL { + + guard let url = urlRequest.url else { + throw DownloadException.requestMissingURL + } + + return url + } + +} + +fileprivate enum DownloadException : Error { + case responseDataMissing + case responseFailed + case requestMissing + case requestMissingPath + case requestMissingURL } public enum AlamofireDecodableRequestBuilderError: Error { @@ -221,7 +327,7 @@ open class AlamofireDecodableRequestBuilder: AlamofireRequestBuilde if stringResponse.result.isFailure { completion( nil, - ErrorResponse.Error(stringResponse.response?.statusCode ?? 500, stringResponse.data, stringResponse.result.error as Error!) + ErrorResponse.error(stringResponse.response?.statusCode ?? 500, stringResponse.data, stringResponse.result.error as Error!) ) return } @@ -241,7 +347,7 @@ open class AlamofireDecodableRequestBuilder: AlamofireRequestBuilde if voidResponse.result.isFailure { completion( nil, - ErrorResponse.Error(voidResponse.response?.statusCode ?? 500, voidResponse.data, voidResponse.result.error!) + ErrorResponse.error(voidResponse.response?.statusCode ?? 500, voidResponse.data, voidResponse.result.error!) ) return } @@ -257,10 +363,10 @@ open class AlamofireDecodableRequestBuilder: AlamofireRequestBuilde validatedRequest.responseData(completionHandler: { (dataResponse) in cleanupRequest() - if (dataResponse.result.isFailure) { + if dataResponse.result.isFailure { completion( nil, - ErrorResponse.Error(dataResponse.response?.statusCode ?? 500, dataResponse.data, dataResponse.result.error!) + ErrorResponse.error(dataResponse.response?.statusCode ?? 500, dataResponse.data, dataResponse.result.error!) ) return } @@ -278,17 +384,17 @@ open class AlamofireDecodableRequestBuilder: AlamofireRequestBuilde cleanupRequest() guard dataResponse.result.isSuccess else { - completion(nil, ErrorResponse.Error(dataResponse.response?.statusCode ?? 500, dataResponse.data, dataResponse.result.error!)) + completion(nil, ErrorResponse.error(dataResponse.response?.statusCode ?? 500, dataResponse.data, dataResponse.result.error!)) return } guard let data = dataResponse.data, !data.isEmpty else { - completion(nil, ErrorResponse.Error(-1, nil, AlamofireDecodableRequestBuilderError.emptyDataResponse)) + completion(nil, ErrorResponse.error(-1, nil, AlamofireDecodableRequestBuilderError.emptyDataResponse)) return } guard let httpResponse = dataResponse.response else { - completion(nil, ErrorResponse.Error(-2, nil, AlamofireDecodableRequestBuilderError.nilHTTPResponse)) + completion(nil, ErrorResponse.error(-2, nil, AlamofireDecodableRequestBuilderError.nilHTTPResponse)) return } diff --git a/modules/swagger-codegen/src/main/resources/swift4/Cartfile.mustache b/modules/swagger-codegen/src/main/resources/swift4/Cartfile.mustache index 101df9a7e04..b8548fc548c 100644 --- a/modules/swagger-codegen/src/main/resources/swift4/Cartfile.mustache +++ b/modules/swagger-codegen/src/main/resources/swift4/Cartfile.mustache @@ -1,3 +1,3 @@ -github "Alamofire/Alamofire" >= 3.1.0{{#usePromiseKit}} -github "mxcl/PromiseKit" >=1.5.3{{/usePromiseKit}}{{#useRxSwift}} -github "ReactiveX/RxSwift" ~> 2.0{{/useRxSwift}} +github "Alamofire/Alamofire" ~> 4.5.0{{#usePromiseKit}} +github "mxcl/PromiseKit" ~> 4.4{{/usePromiseKit}}{{#useRxSwift}} +github "ReactiveX/RxSwift" ~> 4.0{{/useRxSwift}} diff --git a/modules/swagger-codegen/src/main/resources/swift4/CodableHelper.mustache b/modules/swagger-codegen/src/main/resources/swift4/CodableHelper.mustache index 7603003824f..323715c5f94 100644 --- a/modules/swagger-codegen/src/main/resources/swift4/CodableHelper.mustache +++ b/modules/swagger-codegen/src/main/resources/swift4/CodableHelper.mustache @@ -11,14 +11,23 @@ public typealias EncodeResult = (data: Data?, error: Error?) open class CodableHelper { + open static var dateformatter: DateFormatter? + open class func decode(_ type: T.Type, from data: Data) -> (decodableObj: T?, error: Error?) where T : Decodable { var returnedDecodable: T? = nil var returnedError: Error? = nil let decoder = JSONDecoder() - decoder.dataDecodingStrategy = .base64Decode - if #available(iOS 10.0, *) { - decoder.dateDecodingStrategy = .iso8601 + if let df = self.dateformatter { + decoder.dateDecodingStrategy = .formatted(df) + } else { + decoder.dataDecodingStrategy = .base64 + let formatter = DateFormatter() + formatter.calendar = Calendar(identifier: .iso8601) + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = TimeZone(secondsFromGMT: 0) + formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX" + decoder.dateDecodingStrategy = .formatted(formatter) } do { @@ -35,11 +44,16 @@ open class CodableHelper { var returnedError: Error? = nil let encoder = JSONEncoder() - encoder.outputFormatting = (prettyPrint ? .prettyPrinted : .compact) - encoder.dataEncodingStrategy = .base64Encode - if #available(iOS 10.0, *) { - encoder.dateEncodingStrategy = .iso8601 + if prettyPrint { + encoder.outputFormatting = .prettyPrinted } + encoder.dataEncodingStrategy = .base64 + let formatter = DateFormatter() + formatter.calendar = Calendar(identifier: .iso8601) + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = TimeZone(secondsFromGMT: 0) + formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX" + encoder.dateEncodingStrategy = .formatted(formatter) do { returnedData = try encoder.encode(value) diff --git a/modules/swagger-codegen/src/main/resources/swift4/Extensions.mustache b/modules/swagger-codegen/src/main/resources/swift4/Extensions.mustache index 19a3dd7364c..d5a43b407de 100644 --- a/modules/swagger-codegen/src/main/resources/swift4/Extensions.mustache +++ b/modules/swagger-codegen/src/main/resources/swift4/Extensions.mustache @@ -54,7 +54,7 @@ extension Dictionary: JSONEncodable { func encodeToJSON() -> Any { var dictionary = [AnyHashable: Any]() for (key, value) in self { - dictionary[key as! NSObject] = encodeIfPossible(value) + dictionary[key] = encodeIfPossible(value) } return dictionary as Any } @@ -85,6 +85,92 @@ extension UUID: JSONEncodable { } } +extension String: CodingKey { + + public var stringValue: String { + return self + } + + public init?(stringValue: String) { + self.init(stringLiteral: stringValue) + } + + public var intValue: Int? { + return nil + } + + public init?(intValue: Int) { + return nil + } + +} + +extension KeyedEncodingContainerProtocol { + + public mutating func encodeArray(_ values: [T], forKey key: Self.Key) throws where T : Encodable { + var arrayContainer = nestedUnkeyedContainer(forKey: key) + try arrayContainer.encode(contentsOf: values) + } + + public mutating func encodeArrayIfPresent(_ values: [T]?, forKey key: Self.Key) throws where T : Encodable { + if let values = values { + try encodeArray(values, forKey: key) + } + } + + public mutating func encodeMap(_ pairs: [Self.Key: T]) throws where T : Encodable { + for (key, value) in pairs { + try encode(value, forKey: key) + } + } + + public mutating func encodeMapIfPresent(_ pairs: [Self.Key: T]?) throws where T : Encodable { + if let pairs = pairs { + try encodeMap(pairs) + } + } + +} + +extension KeyedDecodingContainerProtocol { + + public func decodeArray(_ type: T.Type, forKey key: Self.Key) throws -> [T] where T : Decodable { + var tmpArray = [T]() + + var nestedContainer = try nestedUnkeyedContainer(forKey: key) + while !nestedContainer.isAtEnd { + let arrayValue = try nestedContainer.decode(T.self) + tmpArray.append(arrayValue) + } + + return tmpArray + } + + public func decodeArrayIfPresent(_ type: T.Type, forKey key: Self.Key) throws -> [T]? where T : Decodable { + var tmpArray: [T]? = nil + + if contains(key) { + tmpArray = try decodeArray(T.self, forKey: key) + } + + return tmpArray + } + + public func decodeMap(_ type: T.Type, excludedKeys: Set) throws -> [Self.Key: T] where T : Decodable { + var map: [Self.Key : T] = [:] + + for key in allKeys { + if !excludedKeys.contains(key) { + let value = try decode(T.self, forKey: key) + map[key] = value + } + } + + return map + } + +} + {{#usePromiseKit}}extension RequestBuilder { public func execute() -> Promise> { let deferred = Promise>.pending() diff --git a/modules/swagger-codegen/src/main/resources/swift4/Models.mustache b/modules/swagger-codegen/src/main/resources/swift4/Models.mustache index 2c19b321582..4962405f029 100644 --- a/modules/swagger-codegen/src/main/resources/swift4/Models.mustache +++ b/modules/swagger-codegen/src/main/resources/swift4/Models.mustache @@ -11,7 +11,7 @@ protocol JSONEncodable { } public enum ErrorResponse : Error { - case Error(Int, Data?, Error) + case error(Int, Data?, Error) } open class Response { @@ -28,8 +28,8 @@ open class Response { public convenience init(response: HTTPURLResponse, body: T?) { let rawHeader = response.allHeaderFields var header = [String:String]() - for (key, value) in rawHeader { - header[key as! String] = value as? String + for case let (key, value) as (String, String) in rawHeader { + header[key] = value } self.init(statusCode: response.statusCode, header: header, body: body) } diff --git a/modules/swagger-codegen/src/main/resources/swift4/Podspec.mustache b/modules/swagger-codegen/src/main/resources/swift4/Podspec.mustache index 173be12b43b..8c9689ea9ea 100644 --- a/modules/swagger-codegen/src/main/resources/swift4/Podspec.mustache +++ b/modules/swagger-codegen/src/main/resources/swift4/Podspec.mustache @@ -3,6 +3,7 @@ Pod::Spec.new do |s| s.summary = '{{projectDescription}}'{{/projectDescription}} s.ios.deployment_target = '9.0' s.osx.deployment_target = '10.11' + s.tvos.deployment_target = '9.0' s.version = '{{#podVersion}}{{podVersion}}{{/podVersion}}{{^podVersion}}0.0.1{{/podVersion}}' s.source = {{#podSource}}{{& podSource}}{{/podSource}}{{^podSource}}{ :git => 'git@github.com:swagger-api/swagger-mustache.git', :tag => 'v1.0.0' }{{/podSource}}{{#podAuthors}} s.authors = '{{podAuthors}}'{{/podAuthors}}{{#podSocialMediaURL}} @@ -14,8 +15,8 @@ Pod::Spec.new do |s| s.description = '{{podDescription}}'{{/podDescription}}{{#podScreenshots}} s.screenshots = {{& podScreenshots}}{{/podScreenshots}}{{#podDocumentationURL}} s.documentation_url = '{{podDocumentationURL}}'{{/podDocumentationURL}} - s.source_files = '{{projectName}}/Classes/Swaggers/**/*.swift'{{#usePromiseKit}} - s.dependency 'PromiseKit', '~> 4.2.2'{{/usePromiseKit}}{{#useRxSwift}} - s.dependency 'RxSwift', '~> 3.4.1'{{/useRxSwift}} - s.dependency 'Alamofire', '~> 4.5' + s.source_files = '{{projectName}}/Classes/**/*.swift'{{#usePromiseKit}} + s.dependency 'PromiseKit/CorePromise', '~> 4.4.0'{{/usePromiseKit}}{{#useRxSwift}} + s.dependency 'RxSwift', '~> 4.0'{{/useRxSwift}} + s.dependency 'Alamofire', '~> 4.5.0' end diff --git a/modules/swagger-codegen/src/main/resources/swift4/api.mustache b/modules/swagger-codegen/src/main/resources/swift4/api.mustache index fbce8ed7489..cc7684afbde 100644 --- a/modules/swagger-codegen/src/main/resources/swift4/api.mustache +++ b/modules/swagger-codegen/src/main/resources/swift4/api.mustache @@ -74,9 +74,9 @@ open class {{classname}} { return Observable.create { observer -> Disposable in {{operationId}}({{#allParams}}{{paramName}}: {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) { {{#returnType}}data, {{/returnType}}error in if let error = error { - observer.on(.error(error as Error)) + observer.on(.error(error)) } else { - observer.on(.next({{#returnType}}data!{{/returnType}})) + observer.on(.next({{#returnType}}data!{{/returnType}}{{^returnType}}(){{/returnType}})) } observer.on(.completed) } diff --git a/modules/swagger-codegen/src/main/resources/swift4/git_push.sh.mustache b/modules/swagger-codegen/src/main/resources/swift4/git_push.sh.mustache index e153ce23ecf..a2d75234837 100755 --- a/modules/swagger-codegen/src/main/resources/swift4/git_push.sh.mustache +++ b/modules/swagger-codegen/src/main/resources/swift4/git_push.sh.mustache @@ -36,7 +36,7 @@ git_remote=`git remote` if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git crediential in your environment." + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git diff --git a/modules/swagger-codegen/src/main/resources/swift4/model.mustache b/modules/swagger-codegen/src/main/resources/swift4/model.mustache index 12bc82a54d5..eb51bc23d09 100644 --- a/modules/swagger-codegen/src/main/resources/swift4/model.mustache +++ b/modules/swagger-codegen/src/main/resources/swift4/model.mustache @@ -11,7 +11,7 @@ import Foundation /** {{description}} */{{/description}} {{#isArrayModel}} -public typealias {{classname}} = [{{arrayModelType}}] +public typealias {{classname}} = {{parent}} {{/isArrayModel}} {{^isArrayModel}} {{#isEnum}} @@ -21,10 +21,7 @@ public enum {{classname}}: {{dataType}}, Codable { } {{/isEnum}} {{^isEnum}} -{{#vars.isEmpty}} -public typealias {{classname}} = {{dataType}} -{{/vars.isEmpty}} -{{^vars.isEmpty}} + open class {{classname}}: {{#parent}}{{{parent}}}{{/parent}}{{^parent}}Codable{{/parent}} { {{#vars}} @@ -37,28 +34,23 @@ open class {{classname}}: {{#parent}}{{{parent}}}{{/parent}}{{^parent}}Codable{{ {{#vars}} {{#isEnum}} {{#description}}/** {{description}} */ - {{/description}}public var {{name}}: {{{datatypeWithEnum}}}{{^unwrapRequired}}?{{/unwrapRequired}}{{#unwrapRequired}}{{^required}}?{{/required}}{{/unwrapRequired}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}} + {{/description}}public var {{name}}: {{{datatypeWithEnum}}}{{^required}}?{{/required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}} {{/isEnum}} {{^isEnum}} {{#description}}/** {{description}} */ - {{/description}}public var {{name}}: {{{datatype}}}{{^unwrapRequired}}?{{/unwrapRequired}}{{#unwrapRequired}}{{^required}}?{{/required}}{{/unwrapRequired}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}} + {{/description}}public var {{name}}: {{{datatype}}}{{^required}}?{{/required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{#objcCompatible}}{{#vendorExtensions.x-swift-optional-scalar}} + public var {{name}}Num: NSNumber? { + get { + return {{name}}.map({ return NSNumber(value: $0) }) + } + }{{/vendorExtensions.x-swift-optional-scalar}}{{/objcCompatible}} {{/isEnum}} {{/vars}} {{#additionalPropertiesType}} - public var additionalProperties: [AnyHashable:{{{additionalPropertiesType}}}] = [:] + public var additionalProperties: [String:{{{additionalPropertiesType}}}] = [:] -{{/additionalPropertiesType}} -{{^unwrapRequired}} - {{^parent}}public init() {}{{/parent}}{{/unwrapRequired}} -{{#unwrapRequired}} - public init({{#allVars}}{{^-first}}, {{/-first}}{{name}}: {{#isEnum}}{{datatypeWithEnum}}{{/isEnum}}{{^isEnum}}{{datatype}}{{/isEnum}}{{^required}}?=nil{{/required}}{{/allVars}}) { - {{#vars}} - self.{{name}} = {{name}} - {{/vars}} - }{{/unwrapRequired}} -{{#additionalPropertiesType}} - public subscript(key: AnyHashable) -> {{{additionalPropertiesType}}}? { + public subscript(key: String) -> {{{additionalPropertiesType}}}? { get { if let value = additionalProperties[key] { return value @@ -71,8 +63,50 @@ open class {{classname}}: {{#parent}}{{{parent}}}{{/parent}}{{^parent}}Codable{{ } } {{/additionalPropertiesType}} + + {{^parent}}{{#hasVars}} + public init({{#vars}}{{name}}: {{{datatypeWithEnum}}}{{^required}}?{{/required}}{{#hasMore}}, {{/hasMore}}{{/vars}}) { + {{#vars}} + self.{{name}} = {{name}} + {{/vars}} + } + {{/hasVars}}{{/parent}} + + // Encodable protocol methods + + public {{#parent}}override {{/parent}}func encode(to encoder: Encoder) throws { + + var container = encoder.container(keyedBy: String.self) + + {{#vars}} + try container.encode{{^required}}IfPresent{{/required}}({{{name}}}, forKey: "{{{baseName}}}") + {{/vars}} + {{#additionalPropertiesType}} + try container.encodeMap(additionalProperties) + {{/additionalPropertiesType}} + } + + // Decodable protocol methods + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: String.self) + + {{#vars}} + {{name}} = try container.decode{{^required}}IfPresent{{/required}}({{{datatypeWithEnum}}}.self, forKey: "{{{baseName}}}") + {{/vars}} + {{#additionalPropertiesType}} + var nonAdditionalPropertyKeys = Set() + {{#vars}} + nonAdditionalPropertyKeys.insert("{{{baseName}}}") + {{/vars}} + additionalProperties = try container.decodeMap({{{additionalPropertiesType}}}.self, excludedKeys: nonAdditionalPropertyKeys) + {{/additionalPropertiesType}} + {{#parent}} + try super.init(from: decoder) + {{/parent}} + } } -{{/vars.isEmpty}} + {{/isEnum}} {{/isArrayModel}} {{/model}} diff --git a/modules/swagger-codegen/src/main/resources/tizen/Doxyfile.mustache b/modules/swagger-codegen/src/main/resources/tizen/Doxyfile.mustache new file mode 100644 index 00000000000..af114f74cdb --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/tizen/Doxyfile.mustache @@ -0,0 +1,2313 @@ +# Doxyfile 1.8.6 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = {{#swagger}}{{#info}}"{{title}} {{version}} Tizen SDK"{{/info}}{{/swagger}} + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = {{#swagger}}{{#info}}{{version}}{{/info}}{{/swagger}} + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "An SDK for creating client applications for {{#swagger}}{{#info}}{{title}}{{/info}}{{/swagger}} on Tizen Platform (http://tizen.org/)" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = "./doc/logo.png" + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = "./doc/SDK" + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = NO + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = YES + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. Do not use file names with spaces, bibtex cannot handle them. See +# also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = ./src ./include + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = *.h *.cpp + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- +# defined cascading style sheet that is included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet file to the output directory. For an example +# see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = YES + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /