diff --git a/.gitignore b/.gitignore index 0d24b8c983a..8cc98ef5e5b 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/ diff --git a/.travis.objc_swift_test.yml b/.travis.objc_swift_test.yml new file mode 100644 index 00000000000..225bea18288 --- /dev/null +++ b/.travis.objc_swift_test.yml @@ -0,0 +1,57 @@ +sudo: required +language: objective-c +osx_image: xcode9 + +cache: + directories: + - $HOME/.m2 + - $HOME/.gem + - $HOME/.rvm + - $HOME/.cocoapods + - swagger-api/swagger-codegen/samples/client/petstore/objc/default/SwaggerClientTests/Pods + - swagger-api/swagger-codegen/samples/client/petstore/objc/core-data/SwaggerClientTests/Pods + - swagger-api/swagger-codegen/samples/client/petstore/swift/default/SwaggerClientTests/Pods + - swagger-api/swagger-codegen/samples/client/petstore/swift/promisekit/SwaggerClientTests/Pods + +services: + - docker + +addons: + hosts: + - petstore.swagger.io + +before_install: + - export SW=`pwd` + # show host table to confirm petstore.swagger.io is mapped to localhost + - cat /private/etc/hosts + #- rvm install 2.2.2 > /dev/null 2>&1 + - rvm use 2.2.4 + - gem environment + - gem install cocoapods -v 1.0.1 -N --no-ri --no-rdoc + - gem install xcpretty -N --no-ri --no-rdoc + - pod --version + - pod setup --silent > /dev/null + # xctool already pre-installed + #- brew install xctool + - git clone https://github.com/wing328/swagger-samples + - cd swagger-samples/java/java-jersey-jaxrs && sudo mvn -q jetty:run & + +install: + +script: + # test default objc client + - cd $SW/samples/client/petstore/objc/default/SwaggerClientTests && pod install && xctool -workspace "SwaggerClient.xcworkspace" -scheme "SwaggerClient-Example" -destination platform='iOS Simulator',OS=8.4,name='iPhone 6' test -test-sdk iphonesimulator CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO + # test objc client with coredata + - cd $SW/samples/client/petstore/objc/core-data/SwaggerClientTests && pod install && xctool -workspace "SwaggerClient.xcworkspace" -scheme "SwaggerClient-Example" -destination platform='iOS Simulator',OS=8.4,name='iPhone 6' test -test-sdk iphonesimulator CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO + - set -o pipefail + # test swift client with promisekit + - cd $SW/samples/client/petstore/swift/promisekit/SwaggerClientTests && pod install && xcodebuild clean test -workspace "SwaggerClient.xcworkspace" -scheme "SwaggerClient" -sdk iphonesimulator GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES | xcpretty + # test default swift client + - cd $SW/samples/client/petstore/swift/default/SwaggerClientTests && pod install && xcodebuild clean test -workspace "SwaggerClient.xcworkspace" -scheme "SwaggerClient" -sdk iphonesimulator GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES | xcpretty + # test swift3 client with promisekit + - cd $SW/samples/client/petstore/swift3/promisekit/SwaggerClientTests && pod install && xcodebuild clean test -workspace "SwaggerClient.xcworkspace" -scheme "SwaggerClient" -sdk iphonesimulator GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES | xcpretty + # test default swift3 client + - cd $SW/samples/client/petstore/swift3/default/SwaggerClientTests && pod install && xcodebuild clean test -workspace "SwaggerClient.xcworkspace" -scheme "SwaggerClient" -sdk iphonesimulator GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES | xcpretty + +env: + - DOCKER_IMAGE_NAME=swaggerapi/swagger-generator diff --git a/.travis.yml b/.travis.yml index 0657f524586..b1880348c76 100644 --- a/.travis.yml +++ b/.travis.yml @@ -104,7 +104,7 @@ script: after_success: # push a snapshot version to maven repo - - if [ $SONATYPE_USERNAME ] && [ -z $TRAVIS_TAG ] && [ "$TRAVIS_BRANCH" = "master" ]; then + - if [ $SONATYPE_USERNAME ] && [ -z $TRAVIS_TAG ] && [ "$TRAVIS_BRANCH" = "2.3.0" ]; then mvn clean deploy --settings .travis/settings.xml; - echo "Finished mvn clean deploy"; + echo "Finished mvn clean deploy for 2.3.0"; fi; 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/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/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/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/security/typescript-angular2.sh b/bin/security/typescript-angular2.sh index cdf5b44b695..4bb954bae85 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 -t modules/swagger-codegen/src/main/resources/typescript-angular2 -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/swift3-petstore-all.sh b/bin/swift3-petstore-all.sh index 4f38026418d..8eb4a7c5f4f 100755 --- a/bin/swift3-petstore-all.sh +++ b/bin/swift3-petstore-all.sh @@ -38,3 +38,7 @@ 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 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/typescript-angular-petstore.sh b/bin/typescript-angular-petstore.sh index 60c10790c90..32cdba067eb 100755 --- a/bin/typescript-angular-petstore.sh +++ b/bin/typescript-angular-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-angular -o samples/client/petstore/typescript-angular" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.json -l typescript-angularjs -o samples/client/petstore/typescript-angular" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-angular2-petstore-all.sh b/bin/typescript-angular2-petstore-all.sh index 1c805738b52..d60082e2ec2 100755 --- a/bin/typescript-angular2-petstore-all.sh +++ b/bin/typescript-angular2-petstore-all.sh @@ -28,13 +28,13 @@ fi 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" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular -o samples/client/petstore/typescript-angular2/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-angular2 -c bin/typescript-petstore-npm.json -o samples/client/petstore/typescript-angular2/npm" +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-angular2/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-angular2 -o samples/client/petstore/typescript-angular2/with-interfaces -D withInterfaces=true" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular -o samples/client/petstore/typescript-angular2/with-interfaces -D withInterfaces=true --additional-properties ngVersion=2" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-angular2-petstore-interfaces.sh b/bin/typescript-angular2-petstore-interfaces.sh index 8fc81f13d68..2ada8e7f803 100755 --- a/bin/typescript-angular2-petstore-interfaces.sh +++ b/bin/typescript-angular2-petstore-interfaces.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 typescript-angular2 -o samples/client/petstore/typescript-angular2/with-interfaces -D withInterfaces=true" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular -o samples/client/petstore/typescript-angular2/with-interfaces -D withInterfaces=true --additional-properties ngVersion=2" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-angular2-petstore-with-npm.sh b/bin/typescript-angular2-petstore-with-npm.sh index 0b96d7bd732..ae148bc88f5 100755 --- a/bin/typescript-angular2-petstore-with-npm.sh +++ b/bin/typescript-angular2-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.yaml -l typescript-angular2 -c bin/typescript-petstore-npm.json -o samples/client/petstore/typescript-angular2/npm" +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-angular2/npm --additional-properties ngVersion=2" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-angular2-petstore.sh b/bin/typescript-angular2-petstore.sh index dbb00a91344..63e5d6dd2d8 100755 --- a/bin/typescript-angular2-petstore.sh +++ b/bin/typescript-angular2-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 typescript-angular2 -o samples/client/petstore/typescript-angular2/default" +ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l typescript-angular -o samples/client/petstore/typescript-angular2/default --additional-properties ngVersion=2" java $JAVA_OPTS -jar $executable $ags diff --git a/bin/typescript-angular4-petstore-with-npm.sh b/bin/typescript-angular4-petstore-with-npm.sh new file mode 100755 index 00000000000..597a83630e1 --- /dev/null +++ b/bin/typescript-angular4-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-angular4/npm --additional-properties ngVersion=4" + +java $JAVA_OPTS -jar $executable $ags 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.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/windows/typescript-angular.bat b/bin/windows/typescript-angular.bat index 47e575494f0..9a3215c6c79 100755 --- a/bin/windows/typescript-angular.bat +++ b/bin/windows/typescript-angular.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 typescript-angular -o samples\client\petstore\typescript-angular +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l typescript-angularjs -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-interfaces.bat b/bin/windows/typescript-angular2-interfaces.bat new file mode 100644 index 00000000000..6185232b704 --- /dev/null +++ b/bin/windows/typescript-angular2-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-angular2\with-interfaces -D withInterfaces=true --additional-properties ngVersion=2 + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-angular2-petstore-all.bat b/bin/windows/typescript-angular2-petstore-all.bat new file mode 100755 index 00000000000..3378de085de --- /dev/null +++ b/bin/windows/typescript-angular2-petstore-all.bat @@ -0,0 +1,3 @@ +call .\bin\windows\typescript-angular2-with-npm.bat +call .\bin\windows\typescript-angular2-interfaces.bat +call .\bin\windows\typescript-angular2.bat diff --git a/bin/windows/typescript-angular2-with-npm.bat b/bin/windows/typescript-angular2-with-npm.bat index 0bf7a88ad11..0125441fac3 100644 --- a/bin/windows/typescript-angular2-with-npm.bat +++ b/bin/windows/typescript-angular2-with-npm.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 -c bin\typescript-petstore-npm.json -l typescript-angular2 -o samples\client\petstore\typescript-angular2\npm +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-angular2\npm --additional-properties ngVersion=2 java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-angular2.bat b/bin/windows/typescript-angular2.bat index 81be77b6bff..b94528ba6d5 100755 --- a/bin/windows/typescript-angular2.bat +++ b/bin/windows/typescript-angular2.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 typescript-angular2 -o samples\client\petstore\typescript-angular2\default +set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l typescript-angular -o samples\client\petstore\typescript-angular2\default --additional-properties ngVersion=2 java %JAVA_OPTS% -jar %executable% %ags% diff --git a/bin/windows/typescript-angular4-with-npm.bat b/bin/windows/typescript-angular4-with-npm.bat new file mode 100644 index 00000000000..f91d22dca41 --- /dev/null +++ b/bin/windows/typescript-angular4-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-angular2\npm --additional-properties ngVersion=4 + +java %JAVA_OPTS% -jar %executable% %ags% 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/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..658e8524fa4 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 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..90a88df5fa7 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; @@ -28,14 +29,14 @@ public class CodegenOperation { public List headerParams = new ArrayList(); public List formParams = 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 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; 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..daa0551e31d 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 @@ -2022,7 +2022,6 @@ public class DefaultCodegen { op.summary = escapeText(operation.getSummary()); op.unescapedNotes = operation.getDescription(); op.notes = escapeText(operation.getDescription()); - op.tags = operation.getTags(); op.hasConsumes = false; op.hasProduces = false; 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..2e161da54f8 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 @@ -776,11 +776,37 @@ public class DefaultGenerator extends AbstractGenerator implements Generator { 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 +829,11 @@ public class DefaultGenerator extends AbstractGenerator implements Generator { } } - 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) { 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..067e0dde664 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); @@ -203,7 +181,7 @@ public class ExampleGenerator { 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); @@ -221,6 +199,7 @@ public class ExampleGenerator { Property property = impl.getProperties().get(propertyName); values.put(propertyName, resolvePropertyToExample(mediaType, property, processedModels)); } + impl.setExample(values); } return values; } 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..ff632789da0 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 @@ -49,9 +49,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 +93,9 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code 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", @@ -153,10 +154,11 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code 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 +376,6 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code // 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 +390,26 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code } } - 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)) { 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..be53ddf68a6 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 @@ package io.swagger.codegen.languages; 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; @@ -43,7 +43,7 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp // 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 +57,10 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp "Date", "number", "any", - "Error" - )); + "File", + "Error", + "Map" + )); languageGenericTypes = new HashSet(Arrays.asList( "Array" @@ -82,12 +84,15 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp 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 +119,7 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp } @Override - public String escapeReservedWord(String name) { + public String escapeReservedWord(String name) { if(this.reservedWordsMappings().containsKey(name)) { return this.reservedWordsMappings().get(name); } @@ -188,6 +193,7 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp 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); 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 692c0004cac..80c1cbbbf41 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 @@ -114,6 +114,7 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen { 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")); 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 8fe760ff00d..6a0cc7fa055 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 @@ -212,6 +212,7 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen { additionalProperties.remove("supportsAsync"); } additionalProperties.put("validatable", false); + additionalProperties.put("net35", true); } else if (NETSTANDARD.equals(this.targetFramework)){ setTargetFrameworkNuget("netstandard1.3"); setSupportsAsync(Boolean.TRUE); @@ -314,6 +315,7 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen { clientPackageDir, "ExceptionFactory.cs")); supportingFiles.add(new SupportingFile("SwaggerDateConverter.mustache", clientPackageDir, "SwaggerDateConverter.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")); @@ -326,6 +328,11 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen { 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)) { // shell script to run the nunit test 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 class GoClientCodegen extends DefaultCodegen implements CodegenConfig { 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 class GoClientCodegen extends DefaultCodegen implements CodegenConfig { 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 class GoClientCodegen extends DefaultCodegen implements CodegenConfig { // - 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 class GoClientCodegen extends DefaultCodegen implements CodegenConfig { 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 class GoClientCodegen extends DefaultCodegen implements CodegenConfig { parameter.vendorExtensions.put("x-exportParamName", sb.toString()); } - @Override public String apiDocFileFolder() { return (outputFolder + "/" + apiDocPath).replace('/', File.separatorChar); @@ -406,9 +403,10 @@ public class GoClientCodegen extends DefaultCodegen implements CodegenConfig { 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 class GoClientCodegen extends DefaultCodegen implements CodegenConfig { } } - // recursively add import for mapping one type to multiple imports List> recursiveImports = (List>) objs.get("imports"); if (recursiveImports == null) @@ -468,7 +465,7 @@ public class GoClientCodegen extends DefaultCodegen implements CodegenConfig { } } - return objs; + return postProcessModelsEnum(objs); } @Override @@ -502,4 +499,65 @@ public class GoClientCodegen extends DefaultCodegen implements CodegenConfig { 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..a827ed15e77 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 com.google.common.collect.Multimap; 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 class GoServerCodegen extends DefaultCodegen implements CodegenConfig { */ 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 class GoServerCodegen extends DefaultCodegen implements CodegenConfig { 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 class GoServerCodegen extends DefaultCodegen implements CodegenConfig { // 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 class GoServerCodegen extends DefaultCodegen implements CodegenConfig { ); 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")); } @@ -211,7 +223,7 @@ public class GoServerCodegen extends DefaultCodegen implements CodegenConfig { 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 class GoServerCodegen extends DefaultCodegen implements CodegenConfig { @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 class GoServerCodegen extends DefaultCodegen implements CodegenConfig { 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 class GoServerCodegen extends DefaultCodegen implements CodegenConfig { 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/JavaClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java index 8d3892ee536..cf3fd691499 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 @@ -202,6 +202,9 @@ public class JavaClientCodegen extends AbstractJavaCodegen 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())) { + 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"); @@ -243,6 +246,9 @@ public class JavaClientCodegen extends AbstractJavaCodegen if (additionalProperties.containsKey("jackson") ) { supportingFiles.add(new SupportingFile("RFC3339DateFormat.mustache", invokerFolder, "RFC3339DateFormat.java")); + if ("threetenbp".equals(dateLibrary)) { + supportingFiles.add(new SupportingFile("CustomInstantDeserializer.mustache", invokerFolder, "CustomInstantDeserializer.java")); + } } } 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 class JavaInflectorServerCodegen extends AbstractJavaCodegen { 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 class JavaInflectorServerCodegen extends AbstractJavaCodegen { 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 class JavaInflectorServerCodegen extends AbstractJavaCodegen { 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 class JavaInflectorServerCodegen extends AbstractJavaCodegen { 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/JavascriptClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavascriptClientCodegen.java index 548155035f2..dc5835dfdd2 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 @@ -96,7 +96,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(); @@ -269,7 +269,7 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo if (additionalProperties.containsKey(USE_ES6)) { setUseES6(convertPropertyToBooleanAndWriteBack(USE_ES6)); } else { - setUseES6(false); + setUseES6(true); // default to ES6 } } 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.io.IOException; 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 class NodeJSServerCodegen extends DefaultCodegen implements CodegenConfig */ 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 class NodeJSServerCodegen extends DefaultCodegen implements CodegenConfig 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 class NodeJSServerCodegen extends DefaultCodegen implements CodegenConfig 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 class NodeJSServerCodegen extends DefaultCodegen implements CodegenConfig // "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 class NodeJSServerCodegen extends DefaultCodegen implements CodegenConfig 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 class NodeJSServerCodegen extends DefaultCodegen implements CodegenConfig } } - @Override + @Override public Map postProcessSupportingFileData(Map objs) { Swagger swagger = (Swagger)objs.get("swagger"); if(swagger != null) { @@ -370,7 +391,7 @@ public class NodeJSServerCodegen extends DefaultCodegen implements CodegenConfig 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/PhpClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/PhpClientCodegen.java index a9286f58fe8..c80ee27a3d3 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 @@ -300,12 +300,11 @@ public class PhpClientCodegen extends DefaultCodegen implements CodegenConfig { // 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("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")); 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..ca0f59ec76b 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 @@ -459,7 +459,7 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig // 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 +649,11 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig 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..89c77195ed1 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 @@ -257,7 +257,7 @@ public class Qt5CPPGenerator extends DefaultCodegen implements CodegenConfig { @Override public String toModelFilename(String name) { - return PREFIX + initialCaps(name); + return modelNamePrefix + initialCaps(name); } @Override @@ -372,7 +372,7 @@ public class Qt5CPPGenerator extends DefaultCodegen implements CodegenConfig { 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/ScalaClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ScalaClientCodegen.java index 2044b80fc80..81d55226fb4 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 @@ -9,6 +9,7 @@ import io.swagger.codegen.SupportingFile; import java.io.File; import java.util.Arrays; import java.util.HashMap; +import java.util.Map; import org.apache.commons.lang3.StringUtils; @@ -20,6 +21,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 +53,13 @@ public class ScalaClientCodegen extends AbstractScalaCodegen implements CodegenC 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,8 +80,8 @@ public class ScalaClientCodegen extends AbstractScalaCodegen implements CodegenC importMapping.remove("Set"); importMapping.remove("Map"); - importMapping.put("DateTime", "org.joda.time.DateTime"); - importMapping.put("ListBuffer", "scala.collection.mutable.ListBuffer"); + importMapping.put("Date", "java.util.Date"); + importMapping.put("ListBuffer", "scala.collections.mutable.ListBuffer"); typeMapping = new HashMap(); typeMapping.put("enum", "NSString"); @@ -90,7 +95,6 @@ public class ScalaClientCodegen extends AbstractScalaCodegen implements CodegenC 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 +102,10 @@ public class ScalaClientCodegen extends AbstractScalaCodegen implements CodegenC // 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 +116,6 @@ public class ScalaClientCodegen extends AbstractScalaCodegen implements CodegenC @Override public void processOpts() { super.processOpts(); - if (additionalProperties.containsKey(CodegenConstants.MODEL_PROPERTY_NAMING)) { setModelPropertyNaming((String) additionalProperties.get(CodegenConstants.MODEL_PROPERTY_NAMING)); } @@ -183,7 +190,7 @@ public class ScalaClientCodegen extends AbstractScalaCodegen implements CodegenC @Override public String getHelp() { - return "Generates a Scala client library."; + return "Generates a Scala client library (beta)."; } @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..34aa46cd3f2 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() { @@ -75,6 +77,7 @@ public class SpringCodegen extends AbstractJavaCodegen 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,19 @@ public class SpringCodegen extends AbstractJavaCodegen @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"); + } + } + super.processOpts(); // clear model and api doc template as this codegen @@ -171,6 +187,10 @@ public class SpringCodegen extends AbstractJavaCodegen 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 +198,15 @@ public class SpringCodegen extends AbstractJavaCodegen 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")); @@ -234,8 +254,20 @@ public class SpringCodegen extends AbstractJavaCodegen 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); } @@ -576,6 +608,10 @@ public class SpringCodegen extends AbstractJavaCodegen 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..4461fc4ea01 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 @@ -132,8 +132,7 @@ public class Swift3Codegen extends DefaultCodegen implements CodegenConfig { 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"); 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..9082ede37af 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 @@ -119,8 +119,8 @@ public class SwiftCodegen extends DefaultCodegen implements CodegenConfig { 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/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.HashSet; 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 class TizenClientCodegen extends DefaultCodegen implements CodegenConfig 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 class TizenClientCodegen extends DefaultCodegen implements CodegenConfig @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 class TizenClientCodegen extends DefaultCodegen implements CodegenConfig 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 class TizenClientCodegen extends DefaultCodegen implements CodegenConfig @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 class TizenClientCodegen extends DefaultCodegen implements CodegenConfig 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 class TizenClientCodegen extends DefaultCodegen implements CodegenConfig return "null"; } + @Override public String apiFileFolder() { return outputFolder + File.separator + sourceFolder; @@ -241,24 +241,27 @@ public class TizenClientCodegen extends DefaultCodegen implements CodegenConfig @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 class TizenClientCodegen extends DefaultCodegen implements CodegenConfig @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 index 8fe2e2d849b..10429ce4bd1 100644 --- 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 @@ -2,9 +2,12 @@ package io.swagger.codegen.languages; import java.io.File; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import io.swagger.codegen.CliOption; import io.swagger.codegen.CodegenModel; @@ -27,10 +30,12 @@ public class TypeScriptAngular2ClientCodegen extends AbstractTypeScriptClientCod 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; + protected String ngVersion = "4"; public TypeScriptAngular2ClientCodegen() { super(); @@ -38,8 +43,9 @@ public class TypeScriptAngular2ClientCodegen extends AbstractTypeScriptClientCod embeddedTemplateDir = templateDir = "typescript-angular2"; modelTemplateFiles.put("model.mustache", ".ts"); - apiTemplateFiles.put("api.mustache", ".ts"); - typeMapping.put("Date","Date"); + apiTemplateFiles.put("api.service.mustache", ".ts"); + languageSpecificPrimitives.add("Blob"); + typeMapping.put("file","Blob"); apiPackage = "api"; modelPackage = "model"; @@ -49,6 +55,7 @@ public class TypeScriptAngular2ClientCodegen extends AbstractTypeScriptClientCod 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 (2 or 4). Default is '4'")); } @Override @@ -59,12 +66,12 @@ public class TypeScriptAngular2ClientCodegen extends AbstractTypeScriptClientCod @Override public String getName() { - return "typescript-angular2"; + return "typescript-angular"; } @Override public String getHelp() { - return "Generates a TypeScript Angular2 client library."; + return "Generates a TypeScript Angular (2.x or 4.x) client library."; } @Override @@ -73,6 +80,8 @@ public class TypeScriptAngular2ClientCodegen extends AbstractTypeScriptClientCod 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("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("gitignore", "", ".gitignore")); @@ -88,6 +97,23 @@ public class TypeScriptAngular2ClientCodegen extends AbstractTypeScriptClientCod apiTemplateFiles.put("apiInterface.mustache", "Interface.ts"); } } + + // determine NG version + if (additionalProperties.containsKey(NG_VERSION)) { + if ("2".equals(additionalProperties.get(NG_VERSION).toString())) { + additionalProperties.put("isNg2x", true); + setNgVersion("2"); + } else if ("4".equals(additionalProperties.get(NG_VERSION).toString())) { + additionalProperties.put("isNg4x", true); + setNgVersion("4"); + } else { + throw new IllegalArgumentException("Invalid ngVersion, which must be either '2' or '4'"); + } + } else { + // default to 4 + additionalProperties.put("isNg4x", true); + setNgVersion("4"); + } } private void addNpmPackageGeneration() { @@ -120,6 +146,11 @@ public class TypeScriptAngular2ClientCodegen extends AbstractTypeScriptClientCod return indexPackage.replace('.', File.separatorChar); } + @Override + public boolean isDataTypeFile(final String dataType) { + return dataType != null && dataType.equals("Blob"); + } + @Override public String getTypeDeclaration(Property p) { Property inner; @@ -131,7 +162,9 @@ public class TypeScriptAngular2ClientCodegen extends AbstractTypeScriptClientCod MapProperty mp = (MapProperty)p; inner = mp.getAdditionalProperties(); return "{ [key: string]: " + this.getTypeDeclaration(inner) + "; }"; - } else if(p instanceof FileProperty || p instanceof ObjectProperty) { + } else if(p instanceof FileProperty) { + return "Blob"; + } else if(p instanceof ObjectProperty) { return "any"; } else { return super.getTypeDeclaration(p); @@ -144,19 +177,13 @@ public class TypeScriptAngular2ClientCodegen extends AbstractTypeScriptClientCod if(isLanguagePrimitive(swaggerType) || isLanguageGenericType(swaggerType)) { return swaggerType; } - return addModelPrefix(swaggerType); + applyLocalTypeMapping(swaggerType); + return 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; + private String applyLocalTypeMapping(String type) { + if (typeMapping.containsKey(type)) { + type = typeMapping.get(type); } return type; } @@ -177,12 +204,16 @@ public class TypeScriptAngular2ClientCodegen extends AbstractTypeScriptClientCod @Override public void postProcessParameter(CodegenParameter parameter) { super.postProcessParameter(parameter); - parameter.dataType = addModelPrefix(parameter.dataType); + 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) { // Convert httpMethod to Angular's RequestMethod enum @@ -217,9 +248,81 @@ public class TypeScriptAngular2ClientCodegen extends AbstractTypeScriptClientCod op.path = op.path.replaceAll("\\{(.*?)\\}", "\\$\\{$1\\}"); } + // 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.imports)); + } + + return result; + } + + private List> toTsImports(Set imports) { + List> tsImports = new ArrayList<>(); + for(String im : imports) { + 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 getNgVersion() { + return ngVersion; + } + + public void setNgVersion(String ngVersion) { + this.ngVersion = ngVersion; + } + public String getNpmName() { return npmName; } @@ -244,4 +347,14 @@ public class TypeScriptAngular2ClientCodegen extends AbstractTypeScriptClientCod 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/TypeScriptAngularClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAngularClientCodegen.java index 8c042f1220b..e1c58e894af 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 @@ -10,7 +10,7 @@ public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCode @Override public String getName() { - return "typescript-angular"; + return "typescript-angularjs"; } @Override 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..a5a3470fdc3 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 @@ -42,6 +42,7 @@ public class TypeScriptFetchClientCodegen extends AbstractTypeScriptClientCodege supportingFiles.add(new SupportingFile("tsconfig.json.mustache", "", "tsconfig.json")); supportingFiles.add(new SupportingFile("tslint.json.mustache", "", "tslint.json")); supportingFiles.add(new SupportingFile("gitignore", "", ".gitignore")); + supportingFiles.add(new SupportingFile("configuration.mustache", "", "configuration.ts")); if(additionalProperties.containsKey(NPM_NAME)) { this.setNpmName(additionalProperties.get(NPM_NAME).toString()); 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..ab12f1a5b60 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,7 +13,6 @@ import java.util.Date; 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); @@ -19,6 +22,7 @@ public class TypeScriptJqueryClientCodegen extends AbstractTypeScriptClientCodeg 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 class TypeScriptJqueryClientCodegen extends AbstractTypeScriptClientCodeg 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()); } @@ -77,6 +140,12 @@ public class TypeScriptJqueryClientCodegen extends AbstractTypeScriptClientCodeg 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/resources/Java/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Java/ApiClient.mustache index 9fe8d556135..4bc3c86d741 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))); + 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(); + + // preconditions + if (name == null || name.isEmpty() || value == null) { return params; } - if (valueCollection.isEmpty()){ - 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; } @@ -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(); @@ -640,6 +681,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 +692,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..aee7a27cfe5 100644 --- a/modules/swagger-codegen/src/main/resources/Java/api.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/api.mustache @@ -67,11 +67,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 +97,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/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/build.gradle.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/feign/build.gradle.mustache index 51ac6819fd4..f9475efb6fd 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" @@ -117,7 +120,15 @@ 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" - 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/pom.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/feign/pom.mustache index 20d8f9b8886..a077cb0febb 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 @@ -222,11 +222,27 @@ {{/withXml}} + {{#joda}} com.fasterxml.jackson.datatype - jackson-datatype-{{^java8}}joda{{/java8}}{{#java8}}jsr310{{/java8}} + 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 @@ -261,6 +277,9 @@ 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/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/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..5e409099601 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 @@ -232,6 +232,13 @@ {{/withXml}} + {{#joda}} + + com.fasterxml.jackson.datatype + jackson-datatype-joda + ${jackson-version} + + {{/joda}} {{#java8}} com.fasterxml.jackson.datatype @@ -239,18 +246,14 @@ ${jackson-version} {{/java8}} - {{^java8}} + {{#threetenbp}} - com.fasterxml.jackson.datatype - jackson-datatype-joda + com.github.joschi.jackson + jackson-datatype-threetenbp ${jackson-version} - - joda-time - joda-time - ${jodatime-version} - - + {{/threetenbp}} + {{^java8}} com.brsanthu @@ -258,21 +261,18 @@ 2.2 {{/java8}} - {{#supportJava6}} org.apache.commons commons-lang3 ${commons_lang3_version} - commons-io commons-io ${commons_io_version} {{/supportJava6}} - junit @@ -284,14 +284,11 @@ 1.5.15 2.25.1 - 2.8.9 - {{^java8}} - 2.9.9 - {{/java8}} {{#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/libraries/okhttp-gson/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache index b3cb0221ac6..24d0c89d10b 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 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 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 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); - } + public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) { + this.json.setLocalDateFormat(dateFormat); + return this; } - /** - * 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); - } + {{/joda}} + {{#jsr310}} + public ApiClient setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { + this.json.setOffsetDateTimeFormat(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); + public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) { + this.json.setLocalDateFormat(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); - } - - /** - * 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. * @@ -644,8 +466,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 +485,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))); + 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(); + + // preconditions + if (name == null || name.isEmpty() || value == null || value.isEmpty()) { return params; } - if (valueCollection.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; } @@ -1076,6 +908,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 +917,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 +929,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 +938,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 +987,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 +1011,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 +1115,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..5ee0a44b58b 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,62 @@ 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}} +{{#joda}} import org.joda.time.DateTime; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormatter; 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 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; 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; + public JSON() { 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()) + .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 +76,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 +101,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 +119,326 @@ 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 { - /** - * Serialize - * - * @param src Date - * @param typeOfSrc Type - * @param context Json Serialization Context - * @return Json Element - */ - @Override - public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) { - if (src == null) { - return JsonNull.INSTANCE; - } else { - return new JsonPrimitive(apiClient.formatDatetime(src)); + private final DateTimeFormatter parseFormatter = ISODateTimeFormat.dateOptionalTimeParser(); + private final DateTimeFormatter printFormatter = ISODateTimeFormat.dateTime(); + + public DateTimeTypeAdapter() { + this(ISODateTimeFormat.dateTime().withOffsetParsed()); + } + + 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(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); + } } } /** - * Deserialize - * - * @param json Json element - * @param date Type - * @param context Json Serialization Context - * @return Date - * @throws JsonParseException if fail to parse + * Gson TypeAdapter for Joda LocalDate 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 class LocalDateTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public LocalDateTypeAdapter() { + this(ISODateTimeFormat.date()); } - } -} -{{^java8}} -/** - * Gson TypeAdapter for Joda DateTime type - */ -class DateTimeTypeAdapter extends TypeAdapter { + public LocalDateTypeAdapter(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, 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); + } } } - @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); - } + public JSON setDateTimeFormat(DateTimeFormatter dateFormat) { + dateTimeTypeAdapter.setFormat(dateFormat); + return this; } -} -/** - * Gson TypeAdapter for Joda LocalDate type - */ -class LocalDateTypeAdapter extends TypeAdapter { + public JSON setLocalDateFormat(DateTimeFormatter dateFormat) { + localDateTypeAdapter.setFormat(dateFormat); + return this; + } - private final DateTimeFormatter formatter = ISODateTimeFormat.date(); + {{/joda}} + {{#jsr310}} + /** + * Gson TypeAdapter for JSR310 OffsetDateTime type + */ + public static class OffsetDateTimeTypeAdapter extends TypeAdapter { - @Override - public void write(JsonWriter out, LocalDate date) throws IOException { - if (date == null) { - out.nullValue(); - } else { - out.value(formatter.print(date)); + 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); + } } } - @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); + /** + * Gson TypeAdapter for JSR310 LocalDate type + */ + public class LocalDateTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public LocalDateTypeAdapter() { + this(DateTimeFormatter.ISO_LOCAL_DATE); } - } -} -{{/java8}} -{{#java8}} -/** - * Gson TypeAdapter for jsr310 OffsetDateTime type - */ -class OffsetDateTimeTypeAdapter extends TypeAdapter { - private final DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME; + public LocalDateTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } - @Override - public void write(JsonWriter out, OffsetDateTime date) throws IOException { - if (date == null) { - out.nullValue(); - } else { - out.value(formatter.format(date)); + 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); + } } } - @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 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; + } + } - -/** - * 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/okhttp-gson/api.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/okhttp-gson/api.mustache index 49f9ea0a6a6..9dfa86cf9ad 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 @@ -78,9 +78,10 @@ public class {{classname}} { 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,7 +116,7 @@ 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); } @SuppressWarnings("rawtypes") @@ -225,4 +226,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..f058fe0c345 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,11 @@ 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}} + {{#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..ee3030b0232 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,12 @@ 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}} "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..f58a28269b7 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 @@ -189,13 +189,20 @@ gson ${gson-version} - {{^java8}} + {{#joda}} joda-time joda-time ${jodatime-version} - {{/java8}} + {{/joda}} + {{#threetenbp}} + + org.threeten + threetenbp + ${threetenbp-version} + + {{/threetenbp}} {{#useBeanValidation}} @@ -242,7 +249,12 @@ 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/resttemplate/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/resttemplate/ApiClient.mustache index f3c9b9dde25..0177d25b784 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; @@ -312,6 +319,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; } @@ -567,6 +582,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 +667,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/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..1ef02d8d6f8 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 @@ -235,7 +235,7 @@ ${jackson-version} {{/java8}} - {{^java8}} + {{#joda}} com.fasterxml.jackson.datatype jackson-datatype-joda @@ -246,7 +246,14 @@ joda-time ${jodatime-version} - {{/java8}} + {{/joda}} + {{#threetenbp}} + + com.github.joschi.jackson + jackson-datatype-threetenbp + ${jackson-threetenbp-version} + + {{/threetenbp}} @@ -261,9 +268,12 @@ 1.5.15 4.3.9.RELEASE 2.8.9 - {{^java8}} + {{#joda}} 2.9.9 - {{/java8}} + {{/joda}} + {{#threetenbp}} + 2.6.4 + {{/threetenbp}} 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..2aa743a12eb 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,25 +1,19 @@ 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 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}} @@ -30,29 +24,28 @@ import com.jakewharton.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; + public class ApiClient { private Map apiAuthorizations; private OkHttpClient.Builder okBuilder; private Retrofit.Builder adapterBuilder; + private JSON json; public ApiClient() { apiAuthorizations = new LinkedHashMap(); @@ -65,18 +58,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 +126,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 +138,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 +150,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 +256,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 +274,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 +287,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 +368,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 +387,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 +416,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..79c398fc1b9 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/JSON.mustache @@ -0,0 +1,399 @@ +{{>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; +{{#joda}} +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormatter; +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 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; + +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 JSON() { + gson = new GsonBuilder() + .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(ISODateTimeFormat.dateTime().withOffsetParsed()); + } + + 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..ecadd1592e4 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,9 +2,15 @@ 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; 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/build.gradle.mustache b/modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/build.gradle.mustache index 9e0ad8a572c..d34028237a8 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 @@ -117,9 +117,12 @@ 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}} } dependencies { @@ -136,9 +139,12 @@ dependencies { {{/useRxJava2}} compile "io.swagger:swagger-annotations:$swagger_annotations_version" compile "org.apache.oltu.oauth2:org.apache.oltu.oauth2.client:$oltu_version" - {{^java8}} + {{#joda}} compile "joda-time:joda-time:$jodatime_version" - {{/java8}} + {{/joda}} + {{#threetenbp}} + compile "org.threeten:threetenbp:$threetenbp_version" + {{/threetenbp}} {{#usePlay24WS}} compile "com.typesafe.play:play-java-ws_2.11:$play_version" compile "com.squareup.retrofit2:converter-jackson:$retrofit_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..9b755a354dc 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 @@ -25,7 +25,7 @@ lazy val root = (project in file(".")). "com.fasterxml.jackson.core" % "jackson-databind" % "2.8.9" % "compile", {{/usePlay24WS}} {{#useRxJava}} - "com.squareup.retrofit2" % "adapter-rxjava" % "{{^usePlay24WS}}2.2.0{{/usePlay24WS}}{{#usePlay24WS}}2.1.0{{/usePlay24WS}}" % "compile", + "com.squareup.retrofit2" % "adapter-rxjava" % "{{^usePlay24WS}}2.3.0{{/usePlay24WS}}{{#usePlay24WS}}2.1.0{{/usePlay24WS}}" % "compile", "io.reactivex" % "rxjava" % "1.3.0" % "compile", {{/useRxJava}} {{#useRxJava2}} @@ -34,9 +34,12 @@ lazy val root = (project in file(".")). {{/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}} "junit" % "junit" % "4.12" % "test", "com.novocode" % "junit-interface" % "0.11" % "test" ) 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..881f1aadd36 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 @@ -194,13 +194,20 @@ org.apache.oltu.oauth2.client ${oltu-version} - {{^java8}} + {{#joda}} joda-time joda-time ${jodatime-version} - {{/java8}} + {{/joda}} + {{#threetenbp}} + + org.threeten + threetenbp + ${threetenbp-version} + + {{/threetenbp}} {{#useRxJava}} io.reactivex @@ -305,9 +312,12 @@ {{#useRxJava2}} 2.1.1 {{/useRxJava2}} - {{^java8}} + {{#joda}} 2.9.9 - {{/java8}} + {{/joda}} + {{#threetenbp}} + 1.3.5 + {{/threetenbp}} 1.0.1 4.12 diff --git a/modules/swagger-codegen/src/main/resources/Java/pom.mustache b/modules/swagger-codegen/src/main/resources/Java/pom.mustache index bcc0cc4b13e..fd6f7480c11 100644 --- a/modules/swagger-codegen/src/main/resources/Java/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/pom.mustache @@ -233,6 +233,13 @@ {{/withXml}} + {{#joda}} + + com.fasterxml.jackson.datatype + jackson-datatype-joda + ${jackson-version} + + {{/joda}} {{#java8}} com.fasterxml.jackson.datatype @@ -240,18 +247,14 @@ ${jackson-version} {{/java8}} - {{^java8}} + {{#threetenbp}} - com.fasterxml.jackson.datatype - jackson-datatype-joda + com.github.joschi.jackson + jackson-datatype-threetenbp ${jackson-version} - - joda-time - joda-time - ${jodatime-version} - - + {{/threetenbp}} + {{^java8}} com.brsanthu @@ -259,14 +262,12 @@ 2.2 {{/java8}} - {{#supportJava6}} org.apache.commons commons-lang3 ${commons_lang3_version} - commons-io commons-io @@ -282,7 +283,6 @@ provided {{/useBeanValidation}} - {{#parcelableModel}} @@ -292,7 +292,6 @@ provided {{/parcelableModel}} - junit @@ -305,14 +304,11 @@ UTF-8 1.5.15 1.19.4 - 2.8.9 - {{^java8}} - 2.9.9 - {{/java8}} {{#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/JavaJaxRS/cxf/server/pom.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/cxf/server/pom.mustache index ddf991b6a37..2bc2739b1fe 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 @@ -241,7 +241,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/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/pom.mustache b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/pom.mustache index 7d37136fbd4..106f78a60f5 100644 --- a/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaJaxRS/resteasy/pom.mustache @@ -49,6 +49,11 @@ + + io.swagger + swagger-annotations + ${swagger-core-version} + org.slf4j slf4j-log4j12 diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/api.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/api.mustache index 9b1acf30d1b..a1dd83d7657 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/api.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/api.mustache @@ -21,6 +21,9 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.multipart.MultipartFile; +{{^useSpringCloudClient}} +import java.io.IOException; +{{/useSpringCloudClient}} import java.util.List; {{#useOptional}} @@ -48,20 +51,31 @@ public interface {{classname}} { }{{/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({ + {{#implicitHeaders}} + @ApiImplicitParams({ {{#headerParams}}{{>implicitHeader}}{{/headerParams}} - }){{/implicitHeaders}} + }) + {{/implicitHeaders}} @RequestMapping(value = "{{{path}}}",{{#singleContentTypes}} produces = "{{{vendorExtensions.x-accepts}}}", consumes = "{{{vendorExtensions.x-contentType}}}",{{/singleContentTypes}}{{^singleContentTypes}}{{#hasProduces}} produces = { {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }, {{/hasProduces}}{{#hasConsumes}} consumes = { {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} },{{/hasConsumes}}{{/singleContentTypes}} method = RequestMethod.{{httpMethod}}) +{{#useSpringCloudClient}} {{#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}}; }{{/jdk8}} +{{/useSpringCloudClient}} +{{^useSpringCloudClient}} + {{#jdk8}}default {{/jdk8}}{{#responseWrapper}}{{.}}<{{/responseWrapper}}ResponseEntity<{{>returnTypes}}>{{#responseWrapper}}>{{/responseWrapper}} {{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}},{{/allParams}} @RequestHeader(value = "Accept", required = false) String accept) throws Exception{{^jdk8}};{{/jdk8}}{{#jdk8}} { + // do some magic! + return {{#async}}CompletableFuture.completedFuture({{/async}}new ResponseEntity<{{>returnTypes}}>(HttpStatus.OK){{#async}}){{/async}}; + }{{/jdk8}} + +{{/useSpringCloudClient}} {{/operation}} } {{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/JavaSpring/apiController.mustache b/modules/swagger-codegen/src/main/resources/JavaSpring/apiController.mustache index cb76830b77a..019c7618e06 100644 --- a/modules/swagger-codegen/src/main/resources/JavaSpring/apiController.mustache +++ b/modules/swagger-codegen/src/main/resources/JavaSpring/apiController.mustache @@ -24,7 +24,12 @@ import java.util.Optional; {{/useOptional}} {{#async}} import java.util.concurrent.Callable; -{{/async}}{{/jdk8-no-delegate}} +{{/async}} +{{/jdk8-no-delegate}} +{{^useSpringCloudClient}} +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +{{/useSpringCloudClient}} {{#useBeanValidation}} import javax.validation.constraints.*; import javax.validation.Valid; @@ -33,27 +38,72 @@ import javax.validation.Valid; @Controller {{#operations}} public class {{classname}}Controller implements {{classname}} { + private final ObjectMapper objectMapper; + + public {{classname}}Controller(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + {{#isDelegate}} private final {{classname}}Delegate delegate; @org.springframework.beans.factory.annotation.Autowired public {{classname}}Controller({{classname}}Delegate delegate) { this.delegate = delegate; - }{{/isDelegate}} + } -{{^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}} +{{/isDelegate}} +{{^jdk8-no-delegate}} +{{#operation}} + public {{#async}}Callable<{{/async}}ResponseEntity<{{>returnTypes}}>{{#async}}>{{/async}} {{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}, + {{/allParams}}@RequestHeader(value = "Accept", required = false) String accept) throws Exception { + // do some magic! +{{#useSpringCloudClient}} + {{^isDelegate}} + {{^async}} + return new ResponseEntity<{{>returnTypes}}>(HttpStatus.OK); + {{/async}} + {{#async}} return new CallablereturnTypes}}>>() { @Override public ResponseEntity<{{>returnTypes}}> call() throws Exception { return new ResponseEntity<{{>returnTypes}}>(HttpStatus.OK); } - };{{/async}}{{/isDelegate}}{{#isDelegate}} - return delegate.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{/isDelegate}} + }; + {{/async}} + {{/isDelegate}} + {{#isDelegate}} + return delegate.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + {{/isDelegate}} +{{/useSpringCloudClient}} +{{^useSpringCloudClient}} + {{^isDelegate}} + {{^async}} + {{#examples}} + + if (accept != null && accept.contains("{{{contentType}}}")) { + return new ResponseEntity<{{>returnTypes}}>(objectMapper.readValue("{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{example}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{>exampleReturnTypes}}.class), HttpStatus.OK); + } + + {{/examples}} + return new ResponseEntity<{{>returnTypes}}>(HttpStatus.OK); + {{/async}} + {{#async}} + return new CallablereturnTypes}}>>() { + @Override + public ResponseEntity<{{>returnTypes}}> call() throws Exception { + return new ResponseEntity<{{>returnTypes}}>(HttpStatus.OK); + } + }; + {{/async}} + {{/isDelegate}} + {{#isDelegate}} + return delegate.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + {{/isDelegate}} +{{/useSpringCloudClient}} } -{{/operation}}{{/jdk8-no-delegate}} + +{{/operation}} +{{/jdk8-no-delegate}} } {{/operations}} 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/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/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/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/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/Javascript/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/Javascript/ApiClient.mustache index 01660219ed8..aa7fdc70c71 100644 --- a/modules/swagger-codegen/src/main/resources/Javascript/ApiClient.mustache +++ b/modules/swagger-codegen/src/main/resources/Javascript/ApiClient.mustache @@ -357,6 +357,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 +370,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 +380,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(); diff --git a/modules/swagger-codegen/src/main/resources/Javascript/api.mustache b/modules/swagger-codegen/src/main/resources/Javascript/api.mustache index b012bfe458c..0de415ad049 100644 --- a/modules/swagger-codegen/src/main/resources/Javascript/api.mustache +++ b/modules/swagger-codegen/src/main/resources/Javascript/api.mustache @@ -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/META-INF/services/io.swagger.codegen.CodegenConfig b/modules/swagger-codegen/src/main/resources/META-INF/services/io.swagger.codegen.CodegenConfig index d0feabe6507..e0cd25f8358 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 @@ -2,7 +2,6 @@ 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 diff --git a/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/api.mustache b/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/api.mustache index 23be9a91356..deb227d02fd 100644 --- a/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/api.mustache +++ b/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/api.mustache @@ -7,6 +7,8 @@ import * as isomorphicFetch from "isomorphic-fetch"; import * as assign from "core-js/library/fn/object/assign"; {{/supportsES6}} +import { Configuration } from "./configuration"; + interface Dictionary { [index: string]: T; } export interface FetchAPI { (url: string, init?: any): Promise; } @@ -20,10 +22,12 @@ export interface FetchArgs { export class BaseAPI { basePath: string; fetch: FetchAPI; + public configuration: Configuration; - constructor(fetch: FetchAPI = isomorphicFetch, basePath: string = BASE_PATH) { + constructor(fetch: FetchAPI = isomorphicFetch, basePath: string = BASE_PATH, configuration: Configuration = new Configuration()) { this.basePath = basePath; this.fetch = fetch; + this.configuration = configuration; } }; @@ -81,7 +85,7 @@ export const {{classname}}FetchParamCreator = { * @param {{paramName}} {{description}} {{/allParams}} */ - {{nickname}}({{#hasParams}}params: { {{#allParams}} "{{paramName}}"{{^required}}?{{/required}}: {{{dataType}}};{{/allParams}} }, {{/hasParams}}options?: any): FetchArgs { + {{nickname}}({{#hasParams}}params: { {{#allParams}} {{paramName}}{{^required}}?{{/required}}: {{{dataType}}};{{/allParams}} }, {{/hasParams}}{{#hasAuthMethods}}configuration: Configuration, {{/hasAuthMethods}}options: any = {}): FetchArgs { {{#allParams}} {{#required}} // verify required parameter "{{paramName}}" is set @@ -127,6 +131,42 @@ export const {{classname}}FetchParamCreator = { fetchOptions.headers = {{#supportsES6}}Object.{{/supportsES6}}assign({}, contentTypeHeader, fetchOptions.headers); } {{/hasHeaderParams}} +{{#authMethods}} + // authentication ({{name}}) required +{{#isApiKey}} +{{#isKeyInHeader}} + if (configuration.apiKey && configuration.apiKey.{{keyParamName}}) { + fetchOptions.headers = {{#supportsES6}}Object.{{/supportsES6}}assign({ + "{{keyParamName}}": configuration.apiKey.{{keyParamName}}, + }, contentTypeHeader); + } +{{/isKeyInHeader}} +{{#isKeyInQuery}} + if (configuration.apiKey && configuration.apiKey.{{keyParamName}}) { + urlObj.query = {{#supportsES6}}Object.{{/supportsES6}}assign({}, urlObj.query, { + "{{keyParamName}}": configuration.apiKey.{{keyParamName}}, + }); + } +{{/isKeyInQuery}} +{{/isApiKey}} +{{#isBasic}} + // http basic authentication required + if (configuration.username || configuration.password) { + fetchOptions.headers = {{#supportsES6}}Object.{{/supportsES6}}assign({ + "Authorization": "Basic " + btoa(configuration.username + ":" + configuration.password), + }, contentTypeHeader); + } +{{/isBasic}} +{{#isOAuth}} + // oauth required + if (configuration.accessToken) { + fetchOptions.headers = {{#supportsES6}}Object.{{/supportsES6}}assign({ + "Authorization": "Bearer " + configuration.accessToken, + }, contentTypeHeader); + } +{{/isOAuth}} +{{/authMethods}} + return { url: url.format(urlObj), options: fetchOptions, @@ -150,8 +190,8 @@ export const {{classname}}Fp = { * @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); + {{nickname}}({{#hasParams}}params: { {{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}; {{/allParams}} }, {{/hasParams}}{{#hasAuthMethods}}configuration: Configuration, {{/hasAuthMethods}}options: any = {}): (fetch: FetchAPI, basePath?: string) => Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}any{{/returnType}}> { + const fetchArgs = {{classname}}FetchParamCreator.{{nickname}}({{#hasParams}}params, {{/hasParams}}{{#hasAuthMethods}}configuration, {{/hasAuthMethods}}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) { @@ -180,8 +220,8 @@ export class {{classname}} extends BaseAPI { * @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); + {{nickname}}({{#hasParams}}params: { {{#allParams}} {{paramName}}{{^required}}?{{/required}}: {{{dataType}}};{{/allParams}} }, {{/hasParams}}options: any = {}) { + return {{classname}}Fp.{{nickname}}({{#hasParams}}params, {{/hasParams}}{{#hasAuthMethods}}this.configuration, {{/hasAuthMethods}}options)(this.fetch, this.basePath); } {{/operation}} }; @@ -202,8 +242,8 @@ export const {{classname}}Factory = function (fetch?: FetchAPI, basePath?: strin * @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); + {{nickname}}({{#hasParams}}params: { {{#allParams}} {{paramName}}{{^required}}?{{/required}}: {{{dataType}}};{{/allParams}} }, {{/hasParams}}{{#hasAuthMethods}}configuration: Configuration, {{/hasAuthMethods}}options: any = {}) { + return {{classname}}Fp.{{nickname}}({{#hasParams}}params, {{/hasParams}}{{#hasAuthMethods}}configuration, {{/hasAuthMethods}}options)(fetch, basePath); }, {{/operation}} }; diff --git a/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/configuration.mustache b/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/configuration.mustache new file mode 100644 index 00000000000..f9ef24d8266 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/TypeScript-Fetch/configuration.mustache @@ -0,0 +1,12 @@ +export class Configuration { + apiKey: { +{{#authMethods}} +{{#isApiKey}} + {{keyParamName}}: string; +{{/isApiKey}} +{{/authMethods}} + }; + username: string; + password: string; + accessToken: string; +} \ No newline at end of file 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..7e6a12ed022 100644 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/Program.mustache +++ b/modules/swagger-codegen/src/main/resources/aspnetcore/Program.mustache @@ -1,15 +1,17 @@ -using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Builder; namespace {{packageName}} { + /// + /// Program + /// public class Program { + /// + /// Main + /// + /// public static void Main(string[] args) { var host = new WebHostBuilder() 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/Startup.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/Startup.mustache index 80709bd068f..cb3a3bb9288 100644 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/Startup.mustache +++ b/modules/swagger-codegen/src/main/resources/aspnetcore/Startup.mustache @@ -1,27 +1,31 @@ {{>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 IConfigurationRoot Configuration { get; } + /// + /// Constructor + /// + /// public Startup(IHostingEnvironment env) { _hostingEnv = env; @@ -33,49 +37,60 @@ namespace {{packageName}} .AddEnvironmentVariables(); Configuration = builder.Build(); } - - - // 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 1.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(); + loggerFactory + .AddConsole(Configuration.GetSection("Logging")) + .AddDebug(); - app.UseMvc(); - - app.UseDefaultFiles(); - app.UseStaticFiles(); - - app.UseSwagger(); - app.UseSwaggerUi(); + app + .UseMvc() + .UseDefaultFiles() + .UseStaticFiles() + .UseSwagger() + .UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1/swagger.json", "{{packageName}}"); + }); } } } diff --git a/modules/swagger-codegen/src/main/resources/aspnetcore/controller.mustache b/modules/swagger-codegen/src/main/resources/aspnetcore/controller.mustache index e7cbb54cfda..a6cdff4cdef 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,8 +30,9 @@ namespace {{packageName}}.Controllers /// {{message}}{{/responses}} [{{httpMethod}}] [Route("{{{basePathWithoutHost}}}{{{path}}}")] - [SwaggerOperation("{{operationId}}")]{{#returnType}} - [SwaggerResponse(200, type: typeof({{&returnType}}))]{{/returnType}} + [ValidateModelState] + [SwaggerOperation("{{operationId}}")]{{#responses}}{{#returnType}} + [SwaggerResponse({{code}}, typeof({{&returnType}}), "{{message}}")]{{/returnType}}{{/responses}} public virtual {{#returnType}}IActionResult{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}}({{#allParams}}{{>pathParam}}{{>queryParam}}{{>bodyParam}}{{>formParam}}{{>headerParam}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) { {{#returnType}} string exampleJson = null; @@ -39,7 +41,7 @@ namespace {{packageName}}.Controllers 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..3c1d6e15d6a 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}}, {{/-last}}{{/enumVars}}{{/allowableValues}} - } \ 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..4a876dd1600 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}}; } @@ -155,17 +108,18 @@ namespace {{packageName}}.Models // credit: http://stackoverflow.com/a/263416/677735 unchecked // Overflow is fine, just wrap { - int hash = 41; + var hash = 41; // Suitable nullity checks etc, of course :) {{#vars}} - if (this.{{name}} != null) - hash = hash * 59 + this.{{name}}.GetHashCode(); + if ({{name}} != null) + hash = hash * 59 + {{name}}.GetHashCode(); {{/vars}} return hash; } } #region Operators + #pragma warning disable 1591 public static bool operator ==({{classname}} left, {{classname}} right) { @@ -177,8 +131,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 index 46a9071fde4..e415d80d728 100644 --- a/modules/swagger-codegen/src/main/resources/aspnetcore/project.json.mustache +++ b/modules/swagger-codegen/src/main/resources/aspnetcore/project.json.mustache @@ -21,8 +21,7 @@ "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", + "Swashbuckle.AspNetCore": "1.0.0-rc3", "Newtonsoft.Json": "9.0.1" }, 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/client.mustache b/modules/swagger-codegen/src/main/resources/asyncscala/client.mustache index d48d031860e..87d830e3e6a 100644 --- a/modules/swagger-codegen/src/main/resources/asyncscala/client.mustache +++ b/modules/swagger-codegen/src/main/resources/asyncscala/client.mustache @@ -23,3 +23,4 @@ class {{clientName}}(config: SwaggerConfig) extends Closeable { client.close() } } + diff --git a/modules/swagger-codegen/src/main/resources/csharp/ApiClient.mustache b/modules/swagger-codegen/src/main/resources/csharp/ApiClient.mustache index 536e660a94d..6bef6f4ec66 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) @@ -200,7 +202,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 +212,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 +249,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 +337,7 @@ namespace {{packageName}}.Client return response.RawBytes; } + // TODO: ? if (type.IsAssignableFrom(typeof(Stream))) if (type == typeof(Stream)) { if (headers != null) @@ -543,5 +547,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,10 +14,147 @@ namespace {{packageName}}.Client /// /// Represents a set of configuration settings /// - {{>visibility}} class Configuration + {{>visibility}} class Configuration : IReadableConfiguration { + #region Constants + /// - /// Initializes a new instance of the Configuration class with different settings + /// Version of the package. + /// + /// Version of the package. + public const string Version = "{{packageVersion}}"; + + /// + /// Identifier for ISO 8601 DateTime Format + /// + /// 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) => + { + 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 default Configuration. + /// + /// Configuration. + public static Configuration Default + { + get { return _globalConfiguration; } + set + { + lock (GlobalConfigSync) + { + _globalConfiguration = value; + } + } + } + + #endregion Static Members + + #region Private Members + + /// + /// Gets or sets the API key based on the authentication name. + /// + /// The API key. + private IDictionary _apiKey = null; + + /// + /// Gets or sets the prefix (e.g. Token) of the API key based on the authentication name. + /// + /// The prefix of the API key. + private IDictionary _apiKeyPrefix = null; + + private string _dateTimeFormat = ISO8601_DATETIME_FORMAT; + private string _tempFolderPath = Path.GetTempPath(); + + #endregion Private Members + + #region Constructors + + static Configuration() + { + _globalConfiguration = new GlobalConfiguration(); + } + + /// + /// 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; + } + + /// + /// Initializes a new instance of the class + /// + public Configuration( + IDictionary defaultHeader, + IDictionary apiKey, + IDictionary apiKeyPrefix, + string basePath = "{{{basePath}}}") : this() + { + if (string.{{^net35}}IsNullOrWhiteSpace{{/net35}}{{#net35}}IsNullOrEmpty{{/net35}}(basePath)) + throw new ArgumentException("The provided basePath is invalid.", "basePath"); + if (defaultHeader == null) + throw new ArgumentNullException("defaultHeader"); + if (apiKey == null) + throw new ArgumentNullException("apiKey"); + if (apiKeyPrefix == null) + throw new ArgumentNullException("apiKeyPrefix"); + + BasePath = basePath; + + foreach (var keyValuePair in defaultHeader) + { + DefaultHeader.Add(keyValuePair); + } + + foreach (var keyValuePair in apiKey) + { + ApiKey.Add(keyValuePair); + } + + foreach (var keyValuePair in apiKeyPrefix) + { + ApiKeyPrefix.Add(keyValuePair); + } + } + + /// + /// Initializes a new instance of the class with different settings /// /// Api client /// Dictionary of default HTTP header @@ -27,131 +167,225 @@ namespace {{packageName}}.Client /// 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}}" - ) + [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 + ) { - 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. + [Obsolete("This constructor caused unexpected sharing of static data. It is no longer supported.", true)] + // ReSharper disable once UnusedParameter.Local public Configuration(ApiClient apiClient) { - setApiClientUsingDefault(apiClient); + } - /// - /// Version of the package. - /// - /// Version of the package. - public const string Version = "{{packageVersion}}"; + #endregion Constructors - /// - /// Gets or sets the default Configuration. - /// - /// Configuration. - public static Configuration Default = new Configuration(); + #region Properties + + private ApiClient _apiClient = null; /// - /// Default creation of exceptions for a given method name and response object + /// Gets an instance of an ApiClient for this configuration /// - public static readonly ExceptionFactory DefaultExceptionFactory = (methodName, response) => + public virtual ApiClient ApiClient { - 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}} - return null; - }; - - /// - /// Gets or sets the HTTP timeout (milliseconds) of ApiClient. Default to 100000 milliseconds. - /// - /// Timeout. - public {{^netStandard}}int{{/netStandard}}{{#netStandard}}TimeSpan?{{/netStandard}} Timeout - { - get { return ApiClient.RestClient.Timeout; } - - set + get { - if (ApiClient != null) - ApiClient.RestClient.Timeout = value; + if (_apiClient == null) _apiClient = CreateApiClient(); + return _apiClient; } } + private String _basePath = null; /// - /// Gets or sets the default API client for making HTTP calls. + /// Gets or sets the base path for API access. /// - /// The API client. - public ApiClient ApiClient; - - /// - /// Set the ApiClient using Default or ApiClient instance. - /// - /// An instance of ApiClient. - /// - public void setApiClientUsingDefault (ApiClient apiClient = null) - { - if (apiClient == null) - { - if (Default != null && Default.ApiClient == null) - Default.ApiClient = new ApiClient(); - - ApiClient = Default != null ? Default.ApiClient : new ApiClient(); - } - else - { - if (Default != null && Default.ApiClient == null) - Default.ApiClient = apiClient; - - ApiClient = apiClient; + 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); + } } } - private Dictionary _defaultHeaderMap = new Dictionary(); - /// /// Gets or sets the default header. /// - public Dictionary DefaultHeader + public virtual IDictionary DefaultHeader { get; set; } + + /// + /// Gets or sets the HTTP timeout (milliseconds) of ApiClient. Default to 100000 milliseconds. + /// + public virtual int Timeout { - get { return _defaultHeaderMap; } + {{#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 HTTP user agent. + /// + /// Http user agent. + public virtual string UserAgent { get; set; } + + /// + /// Gets or sets the username (HTTP basic authentication). + /// + /// The username. + public virtual string Username { get; set; } + + /// + /// Gets or sets the password (HTTP basic authentication). + /// + /// The password. + public virtual string Password { get; set; } + + /// + /// Gets the API key with prefix. + /// + /// API key identifier (authentication scheme). + /// API key with prefix. + public string GetApiKeyWithPrefix(string apiKeyIdentifier) + { + var apiKeyValue = ""; + ApiKey.TryGetValue (apiKeyIdentifier, out apiKeyValue); + var apiKeyPrefix = ""; + if (ApiKeyPrefix.TryGetValue (apiKeyIdentifier, out apiKeyPrefix)) + return apiKeyPrefix + " " + apiKeyValue; + else + return apiKeyValue; + } + + /// + /// 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 virtual string TempFolderPath + { + get { return _tempFolderPath; } set { - _defaultHeaderMap = value; + if (string.IsNullOrEmpty(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; + } } } + /// + /// 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 virtual 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; + } + } + + /// + /// 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. /// @@ -160,7 +394,38 @@ namespace {{packageName}}.Client /// public void AddDefaultHeader(string key, string value) { - _defaultHeaderMap[key] = 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. + /// + public static String ToDebugReport() + { + String report = "C# SDK ({{{packageName}}}) Debug Report:\n"; + {{^netStandard}} + {{^supportsUWP}} + report += " OS: " + System.Environment.OSVersion + "\n"; + report += " .NET Framework Version: " + System.Environment.Version + "\n"; + {{/supportsUWP}} + {{/netStandard}} + {{#netStandard}} + report += " OS: " + System.Runtime.InteropServices.RuntimeInformation.OSDescription + "\n"; + {{/netStandard}} + report += " Version of the API: {{{version}}}\n"; + report += " SDK Package Version: {{{packageVersion}}}\n"; + + return report; } /// @@ -184,151 +449,6 @@ namespace {{packageName}}.Client ApiKeyPrefix[key] = value; } - /// - /// Gets or sets the HTTP user agent. - /// - /// Http user agent. - public String UserAgent { get; set; } - - /// - /// Gets or sets the username (HTTP basic authentication). - /// - /// The username. - public String Username { get; set; } - - /// - /// Gets or sets the password (HTTP basic authentication). - /// - /// The password. - public String Password { get; set; } - - /// - /// Gets or sets the access token for OAuth2 authentication. - /// - /// The access token. - public String AccessToken { get; set; } - - /// - /// Gets or sets the API key based on the authentication name. - /// - /// The API key. - public 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 Dictionary ApiKeyPrefix = new Dictionary(); - - /// - /// Get the API key with prefix. - /// - /// API key identifier (authentication scheme). - /// API key with prefix. - public string GetApiKeyWithPrefix (string apiKeyIdentifier) - { - var apiKeyValue = ""; - ApiKey.TryGetValue (apiKeyIdentifier, out apiKeyValue); - var apiKeyPrefix = ""; - if (ApiKeyPrefix.TryGetValue (apiKeyIdentifier, out apiKeyPrefix)) - return apiKeyPrefix + " " + apiKeyValue; - else - return apiKeyValue; - } - - private string _tempFolderPath; - - /// - /// Gets or sets the temporary folder path to store the files downloaded from the server. - /// - /// Folder path. - public String TempFolderPath - { - get - { - // default to Path.GetTempPath() if _tempFolderPath is not set - if (String.IsNullOrEmpty(_tempFolderPath)) - { - _tempFolderPath = Path.GetTempPath(); - } - 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 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 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"; - {{^netStandard}} - {{^supportsUWP}} - report += " OS: " + Environment.OSVersion + "\n"; - report += " .NET Framework Version: " + Assembly - .GetExecutingAssembly() - .GetReferencedAssemblies() - .Where(x => x.Name == "System.Core").First().Version.ToString() + "\n"; - {{/supportsUWP}} - {{/netStandard}} - {{#netStandard}} - report += " OS: " + System.Runtime.InteropServices.RuntimeInformation.OSDescription + "\n"; - {{/netStandard}} - report += " Version of the API: {{{version}}}\n"; - report += " SDK Package Version: {{{packageVersion}}}\n"; - - return report; - } + #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/api.mustache b/modules/swagger-codegen/src/main/resources/csharp/api.mustache index a16b99c9f68..9a9f99f26b8 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 @@ -350,7 +338,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 +366,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 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/go/README.mustache b/modules/swagger-codegen/src/main/resources/go/README.mustache index 190beb68810..0e89ca61846 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.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}}} @@ -55,8 +70,24 @@ Class | Method | HTTP request | Description - **Scopes**: {{^scopes}}N/A{{/scopes}} {{#scopes}} - **{{{scope}}}**: {{{description}}} {{/scopes}} -{{/isOAuth}} +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 diff --git a/modules/swagger-codegen/src/main/resources/go/api.mustache b/modules/swagger-codegen/src/main/resources/go/api.mustache index 7c2e31ecd41..d02bb0a75e0 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 -} +// Linger please +var ( + _ context.Context +) -func New{{classname}}() *{{classname}} { - configuration := NewConfiguration() - return &{{classname}}{ - Configuration: configuration, - } -} +type {{classname}}Service service -func New{{classname}}WithBasePath(basePath string) *{{classname}} { - configuration := NewConfiguration() - configuration.BasePath = basePath - - 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}} +{{#hasAuthMethods}} * @param ctx context.Context Authentication Context {{/hasAuthMethods}} +{{#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}}}({{#hasAuthMethods}}ctx context.Context, {{/hasAuthMethods}}{{#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() + localVarFormParams := url.Values{} + + {{#allParams}} + {{^required}} + {{#isPrimitiveType}} + if err := typeCheckParameter(localVarOptionals["{{paramName}}"], "{{{dataType}}}", "{{paramName}}"); err != nil { + return {{#returnType}}successPayload, {{/returnType}}nil, err } -{{/isBasic}} -{{#isOAuth}} - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken + {{/isPrimitiveType}} + {{/required}} + {{#required}} + {{#minItems}} + if len({{paramName}}) < {{minItems}} { + return {{#returnType}}successPayload, {{/returnType}}nil, reportError("{{paramName}} must have at least {{minItems}} elements") } -{{/isOAuth}} -{{/authMethods}} - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] + {{/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['{{keyParamName}}'] = key{{/isKeyInQuery}} + } } - +{{/isApiKey}} +{{/authMethods}} + r, err := a.client.prepareRequest({{#hasAuthMethods}}ctx, {{/hasAuthMethods}}{{^hasAuthMethods}}nil, {{/hasAuthMethods}}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..eac78242445 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 +// 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 } - return nil, fmt.Errorf("invalid method %v", method) + // 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 } -func (c *APIClient) ParameterToString(obj interface{}, collectionFormat string) string { - delimiter := "" +// 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,277 @@ 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) - } - - if c.config.Timeout != nil { - rClient.SetTimeout(*c.config.Timeout) - } - rClient.SetLogger(ioutil.Discard) - return rClient +// callAPI do the request. +func (c *APIClient) callAPI(request *http.Request) (*http.Response, error) { + return c.cfg.HTTPClient.Do(request) } -func (c *APIClient) prepareRequest( - rClient *resty.Client, +// 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 map[string]string, + formParams url.Values, fileName string, - fileBytes []byte) *resty.Request { + fileBytes []byte) (localVarRequest *http.Request, err error) { + var body *bytes.Buffer - request := rClient.R() - request.SetBody(postBody) + // Detect postBody type and post. + if postBody != nil { + contentType := headerParams["Content-Type"] + if contentType == "" { + contentType = detectContentType(postBody) + headerParams["Content-Type"] = contentType + } - if c.config.UserAgent != "" { - request.SetHeader("User-Agent", c.config.UserAgent) + body, err = setBody(postBody, contentType) + if err != nil { + return nil, err + } } - // add header parameter, if any + // 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 { - request.SetHeaders(headerParams) + headers := http.Header{} + for h, v := range headerParams { + headers.Set(h, v) + } + localVarRequest.Header = headers + } + + // 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) + } } - // add query parameter, if any - if len(queryParams) > 0 { - request.SetMultiValueQueryParams(queryParams) + for header, value := range c.cfg.DefaultHeader { + localVarRequest.Header.Add(header, value) } - - // add form parameter, if any - if len(formParams) > 0 { - request.SetFormData(formParams) - } - - if len(fileBytes) > 0 && fileName != "" { - _, fileNm := filepath.Split(fileName) - request.SetFileReader("file", fileNm, bytes.NewReader(fileBytes)) - } - 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..70d0b96ce86 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}}}({{#authMethods}}ctx, {{/authMethods}}{{#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}} +------------- | ------------- | ------------- | -------------{{#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}}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..97bfa891d40 100644 --- a/modules/swagger-codegen/src/main/resources/go/configuration.mustache +++ b/modules/swagger-codegen/src/main/resources/go/configuration.mustache @@ -2,57 +2,42 @@ package {{packageName}} import ( - "encoding/base64" "net/http" - "time" ) +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 { - 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/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/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/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}} + +/** + {{#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}} - * returns {{{returnType}}} - {{/returnType}} - **/ - {{#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..7350b54371f --- /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, code); +} \ No newline at end of file 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/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/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}}; -{{#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}} -{{/hasAuthMethods}} -my $api_instance = {{moduleName}}::{{classname}}->new(); +my $api_instance = {{moduleName}}::{{classname}}->new( +{{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#operation}}{{#-first}}{{#hasAuthMethods}}{{#authMethods}}{{#isBasic}} + # 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}} +); + {{#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 $class = shift; + my $api_client; - #my $self = { - # #api_client => $options->{api_client} - # api_client => $default_api_client - #}; + if ($_[0] && ref $_[0] && ref $_[0] eq '{{moduleName}}::ApiClient' ) { + $api_client = $_[0]; + } else { + $api_client = {{moduleName}}::ApiClient->new(@_); + } - bless \%self, $class; + 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/php/ApiException.mustache b/modules/swagger-codegen/src/main/resources/php/ApiException.mustache index 31dc76fe3fb..e8115d2238c 100644 --- a/modules/swagger-codegen/src/main/resources/php/ApiException.mustache +++ b/modules/swagger-codegen/src/main/resources/php/ApiException.mustache @@ -41,7 +41,7 @@ class ApiException extends Exception /** * The HTTP header of the server response. * - * @var string[] + * @var string[]|null */ protected $responseHeaders; @@ -55,10 +55,10 @@ class ApiException extends Exception /** * Constructor * - * @param string $message Error message - * @param int $code HTTP status code - * @param string[] $responseHeaders HTTP response header - * @param mixed $responseBody HTTP decoded body of the server response either as \stdClass or string + * @param string $message Error message + * @param int $code HTTP status code + * @param string[]|null $responseHeaders HTTP response header + * @param mixed $responseBody HTTP decoded body of the server response either as \stdClass or string */ public function __construct($message = "", $code = 0, $responseHeaders = [], $responseBody = null) { @@ -70,7 +70,7 @@ class ApiException extends Exception /** * Gets the HTTP response header * - * @return string[] HTTP response headers + * @return string[]|null HTTP response header */ public function getResponseHeaders() { diff --git a/modules/swagger-codegen/src/main/resources/php/configuration.mustache b/modules/swagger-codegen/src/main/resources/php/Configuration.mustache similarity index 55% rename from modules/swagger-codegen/src/main/resources/php/configuration.mustache rename to modules/swagger-codegen/src/main/resources/php/Configuration.mustache index eee903c0da7..cdbba93a000 100644 --- a/modules/swagger-codegen/src/main/resources/php/configuration.mustache +++ b/modules/swagger-codegen/src/main/resources/php/Configuration.mustache @@ -66,33 +66,12 @@ class Configuration */ 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; + protected $host = '{{basePath}}'; /** * User agent of the HTTP request, set to "PHP-Swagger" by default @@ -122,58 +101,6 @@ class Configuration */ 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 */ @@ -303,48 +230,6 @@ class Configuration 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 * @@ -396,199 +281,6 @@ class Configuration 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 * @@ -658,29 +350,6 @@ class Configuration 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 * @@ -725,4 +394,29 @@ class Configuration 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/ObjectSerializer.mustache b/modules/swagger-codegen/src/main/resources/php/ObjectSerializer.mustache index 86ca232a6cc..d8ca2d904d2 100644 --- a/modules/swagger-codegen/src/main/resources/php/ObjectSerializer.mustache +++ b/modules/swagger-codegen/src/main/resources/php/ObjectSerializer.mustache @@ -95,9 +95,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 +110,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 +128,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 +142,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 +160,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 +179,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 +254,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 +263,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()); - } - return $deserialized; + $file = fopen($filename, 'w'); + while ($chunk = $data->read(200)) { + fwrite($file, $chunk); + } + fclose($file); + + 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..eeff364b156 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 diff --git a/modules/swagger-codegen/src/main/resources/php/api.mustache b/modules/swagger-codegen/src/main/resources/php/api.mustache index 1efbe254d8f..b473bdcd04f 100644 --- a/modules/swagger-codegen/src/main/resources/php/api.mustache +++ b/modules/swagger-codegen/src/main/resources/php/api.mustache @@ -18,10 +18,15 @@ 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 {{invokerPackage}}\ApiException; +use {{invokerPackage}}\Configuration; +use {{invokerPackage}}\HeaderSelector; +use {{invokerPackage}}\ObjectSerializer; /** * {{classname}} Class Doc Comment @@ -34,50 +39,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(); - } + protected $config; - $this->apiClient = $apiClient; + /** + * @param ClientInterface $client + * @param Configuration $config + * @param HeaderSelector $selector + */ + 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(); } /** - * Get API client - * - * @return \{{invokerPackage}}\ApiClient get the API client + * @return Configuration */ - public function getApiClient() + public function getConfig() { - return $this->apiClient; + return $this->config; } - /** - * Set the API client - * - * @param \{{invokerPackage}}\ApiClient $apiClient set the API client - * - * @return {{classname}} - */ - public function setApiClient(\{{invokerPackage}}\ApiClient $apiClient) - { - $this->apiClient = $apiClient; - return $this; - } - {{#operation}} - +{{#operation}} /** * Operation {{{operationId}}} {{#summary}} @@ -93,12 +87,13 @@ use \{{invokerPackage}}\ObjectSerializer; * @param {{dataType}} ${{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}} } /** @@ -116,9 +111,158 @@ use \{{invokerPackage}}\ObjectSerializer; * @param {{dataType}} ${{paramName}} {{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 { + + try { + $response = $this->client->send($request); + } catch (RequestException $e) { + throw new ApiException( + "[{$e->getCode()}] {$e->getMessage()}", + $e->getCode(), + $e->getResponse() ? $e->getResponse()->getHeaders() : null + ); + } + + $statusCode = $response->getStatusCode(); + + if ($statusCode < 200 || $statusCode > 299) { + throw new ApiException( + "[$statusCode] Error connecting to the API ({$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}} {{#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}} {{#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)->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( + "[$statusCode] Error connecting to the API ({$exception->getRequest()->getUri()})", + $statusCode, + $response->getHeaders(), + $response->getBody() + ); + }); + } + + /** + * Create request for operation '{{{operationId}}}' + * +{{#allParams}} + * @param {{dataType}} ${{paramName}} {{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}} @@ -129,106 +273,95 @@ use \{{invokerPackage}}\ObjectSerializer; {{/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}}), - $resourcePath - ); + $resourcePath = str_replace('{' . '{{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['file'] = \GuzzleHttp\Psr7\try_fopen(ObjectSerializer::toFormValue($file), 'rb'); {{/isFile}} {{^isFile}} - $formParams['{{baseName}}'] = $this->apiClient->getSerializer()->toFormValue(${{paramName}}); + $formParams['{{baseName}}'] = ObjectSerializer::toFormValue(${{paramName}}); {{/isFile}} } {{/formParams}} @@ -240,71 +373,84 @@ use \{{invokerPackage}}\ObjectSerializer; } {{/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 + } elseif (count($formParams) > 0) { - $httpBody = $formParams; // for HTTP post (form) + if ($multipart) { + $multipartContents = []; + foreach ($formParams as $formParamName => $formParamValue) { + $multipartContents[] = [ + 'name' => $formParamName, + 'contents' => $formParamValue + ]; + } + $httpBody = new MultipartStream($multipartContents); // for HTTP post (form) + + } elseif ($headers['Content-Type'] === 'application/json') { + $httpBody = \GuzzleHttp\json_encode($formParams); + + } else { + $httpBody = \GuzzleHttp\Psr7\build_query($formParams); // for HTTP post (form) + } } + {{#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}} - } + $query = \GuzzleHttp\Psr7\build_query($queryParams); + $url = $this->config->getHost() . $resourcePath . ($query ? '?' . $query : ''); - throw $e; + $defaultHeaders = []; + if ($this->config->getUserAgent()) { + $defaultHeaders['User-Agent'] = $this->config->getUserAgent(); } + + $headers = array_merge( + $defaultHeaders, + $headerParams, + $headers + ); + + return new Request( + '{{httpMethod}}', + $url, + $headers, + $httpBody + ); } + {{/operation}} } {{/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..6d0ad8ebbb4 100644 --- a/modules/swagger-codegen/src/main/resources/php/api_doc.mustache +++ b/modules/swagger-codegen/src/main/resources/php/api_doc.mustache @@ -33,7 +33,7 @@ require_once(__DIR__ . '/vendor/autoload.php'); {{{invokerPackage}}}\Configuration::getDefaultConfiguration()->setAccessToken('YOUR_ACCESS_TOKEN');{{/isOAuth}}{{/authMethods}} {{/hasAuthMethods}} -$api_instance = new {{invokerPackage}}\Api\{{classname}}(); +$api_instance = new {{invokerPackage}}\Api\{{classname}}(new \Http\Adapter\Guzzle6\Client()); {{#allParams}}${{paramName}} = {{{example}}}; // {{{dataType}}} | {{{description}}} {{/allParams}} 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/python/__init__package.mustache b/modules/swagger-codegen/src/main/resources/python/__init__package.mustache index 6528c480eed..2289de872ef 100644 --- a/modules/swagger-codegen/src/main/resources/python/__init__package.mustache +++ b/modules/swagger-codegen/src/main/resources/python/__init__package.mustache @@ -14,5 +14,3 @@ from __future__ import absolute_import from .api_client import ApiClient from .configuration import Configuration - -configuration = Configuration() diff --git a/modules/swagger-codegen/src/main/resources/python/api.mustache b/modules/swagger-codegen/src/main/resources/python/api.mustache index eb6d1fc68cb..354bb6fd66b 100644 --- a/modules/swagger-codegen/src/main/resources/python/api.mustache +++ b/modules/swagger-codegen/src/main/resources/python/api.mustache @@ -11,7 +11,6 @@ import re # python 2 and python 3 compatibility library from six import iteritems -from ..configuration import Configuration from ..api_client import ApiClient @@ -24,13 +23,9 @@ class {{classname}}(object): """ 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): @@ -42,20 +37,16 @@ class {{classname}}(object): {{{notes}}} {{/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,7 +55,7 @@ class {{classname}}(object): returns the request thread. """ kwargs['_return_http_data_only'] = True - if kwargs.get('callback'): + if kwargs.get('async'): return self.{{operationId}}_with_http_info({{#sortParamsByRequiredFlag}}{{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}{{/sortParamsByRequiredFlag}}**kwargs) else: (data) = self.{{operationId}}_with_http_info({{#sortParamsByRequiredFlag}}{{#allParams}}{{#required}}{{paramName}}, {{/required}}{{/allParams}}{{/sortParamsByRequiredFlag}}**kwargs) @@ -79,20 +70,16 @@ class {{classname}}(object): {{{notes}}} {{/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}} @@ -102,7 +89,7 @@ class {{classname}}(object): """ all_params = [{{#allParams}}'{{paramName}}'{{#hasMore}}, {{/hasMore}}{{/allParams}}] - all_params.append('callback') + all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') all_params.append('_request_timeout') @@ -219,7 +206,7 @@ class {{classname}}(object): files=local_var_files, response_type={{#returnType}}'{{returnType}}'{{/returnType}}{{^returnType}}None{{/returnType}}, auth_settings=auth_settings, - callback=params.get('callback'), + 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'), 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..06148fef489 100644 --- a/modules/swagger-codegen/src/main/resources/python/api_client.mustache +++ b/modules/swagger-codegen/src/main/resources/python/api_client.mustache @@ -7,7 +7,7 @@ import re import json import mimetypes import tempfile -import threading +from multiprocessing.pool import ThreadPool from datetime import date, datetime @@ -50,18 +50,16 @@ class ApiClient(object): '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 = 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}}' @@ -86,7 +84,7 @@ 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, + response_type=None, auth_settings=None, _return_http_data_only=None, collection_formats=None, _preload_content=True, _request_timeout=None): @@ -133,7 +131,7 @@ 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, @@ -153,12 +151,7 @@ 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: + if _return_http_data_only: return (return_data) else: return (return_data, response_data.status, response_data.getheaders()) @@ -272,7 +265,7 @@ 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, + response_type=None, auth_settings=None, async=None, _return_http_data_only=None, collection_formats=None, _preload_content=True, _request_timeout=None): """ @@ -292,9 +285,7 @@ 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 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. @@ -309,22 +300,20 @@ class ApiClient(object): If parameter callback is None, 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, + 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, @@ -490,13 +479,11 @@ class ApiClient(object): :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 @@ -517,9 +504,7 @@ class ApiClient(object): :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) 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..a496f63bfd1 100644 --- a/modules/swagger-codegen/src/main/resources/python/api_doc.mustache +++ b/modules/swagger-codegen/src/main/resources/python/api_doc.mustache @@ -26,20 +26,30 @@ 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: {{#summary}} # {{{.}}} diff --git a/modules/swagger-codegen/src/main/resources/python/configuration.mustache b/modules/swagger-codegen/src/main/resources/python/configuration.mustache index 6c055604665..41c9778087c 100644 --- a/modules/swagger-codegen/src/main/resources/python/configuration.mustache +++ b/modules/swagger-codegen/src/main/resources/python/configuration.mustache @@ -13,17 +13,6 @@ from six import iteritems from six.moves import http_client as httplib -def singleton(cls, *args, **kw): - instances = {} - - def _singleton(): - if cls not in instances: - instances[cls] = cls(*args, **kw) - return instances[cls] - return _singleton - - -@singleton class Configuration(object): """ NOTE: This class is auto generated by the swagger code generator program. @@ -37,8 +26,6 @@ class Configuration(object): """ # Default Base url self.host = "{{{basePath}}}" - # Default api client - self.api_client = None # Temp file folder for downloading files self.temp_folder_path = None diff --git a/modules/swagger-codegen/src/main/resources/python/model.mustache b/modules/swagger-codegen/src/main/resources/python/model.mustache index 308eeb8071e..78d3925f197 100644 --- a/modules/swagger-codegen/src/main/resources/python/model.mustache +++ b/modules/swagger-codegen/src/main/resources/python/model.mustache @@ -65,7 +65,7 @@ class {{classname}}(object): def {{name}}(self): """ Gets the {{name}} of this {{classname}}. -{{#description}} +{{#description}} {{{description}}} {{/description}} diff --git a/modules/swagger-codegen/src/main/resources/python/rest.mustache b/modules/swagger-codegen/src/main/resources/python/rest.mustache index b801938dc52..b421c80c841 100644 --- a/modules/swagger-codegen/src/main/resources/python/rest.mustache +++ b/modules/swagger-codegen/src/main/resources/python/rest.mustache @@ -15,8 +15,6 @@ import re from six import PY3 from six.moves.urllib.parse import urlencode -from .configuration import Configuration - try: import urllib3 except ImportError: @@ -49,7 +47,7 @@ class RESTResponse(io.IOBase): class RESTClientObject(object): - def __init__(self, pools_size=4, maxsize=4): + def __init__(self, configuration, pools_size=4, maxsize=4): # 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 @@ -58,37 +56,28 @@ class RESTClientObject(object): # http://stackoverflow.com/a/23957365/2985775 # 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 - - # key file - key_file = Configuration().key_file - - # proxy - proxy = Configuration().proxy - # 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 ) else: self.pool_manager = urllib3.PoolManager( @@ -96,11 +85,10 @@ 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 ) - def request(self, method, url, query_params=None, headers=None, body=None, post_params=None, _preload_content=True, _request_timeout=None): """ diff --git a/modules/swagger-codegen/src/main/resources/scala/api.mustache b/modules/swagger-codegen/src/main/resources/scala/api.mustache index c3dd3effe7d..5c8c5ec07b4 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,42 @@ 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) { + + 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 = defBasePath var apiInvoker = defApiInvoker - def addHeader(key: String, value: String) = apiInvoker.defaultHeaders += key -> value + def addHeader(key: String, value: 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}} /** @@ -32,97 +62,82 @@ class {{classname}}(val defBasePath: String = "{{{basePath}}}", {{/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}} - - val contentTypes = List({{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}}{{^consumes}}"application/json"{{/consumes}}) - val contentType = contentTypes(0) - - val queryParams = new HashMap[String, String] - val headerParams = new HashMap[String, String] - val formParams = new 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}} - 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)) - {{/required}} - {{/notFile}} - {{#isFile}} - {{#required}} - mp.field("{{baseName}}", file.getName) - mp.bodyPart(new FileDataBodyPart("{{baseName}}", {{paramName}}, MediaType.MULTIPART_FORM_DATA_TYPE)) - {{/required}} - {{^required}} - file.map(fileVal => mp.field("{{baseName}}", fileVal.getName)) - {{paramName}}.map(paramVal => mp.bodyPart(new FileDataBodyPart("{{baseName}}", paramVal, MediaType.MULTIPART_FORM_DATA_TYPE))) - {{/required}} - {{/isFile}} - {{/formParams}} - postBody = mp - } else { - {{#formParams}} - {{#notFile}} - {{#required}} - formParams += "{{baseName}}" -> {{paramName}}.toString - {{/required}} - {{^required}} - {{paramName}}.map(paramVal => formParams += "{{baseName}}" -> paramVal.toString) - {{/required}} - {{/notFile}} - {{/formParams}} - } - - 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 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 } } + /** + * {{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}} /* = {{{defaultValue}}}*/{{/defaultValue}}{{^defaultValue}} = None{{/defaultValue}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{#returnType}}: Future[{{returnType}}]{{/returnType}} = { + helper.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) + } + + {{/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}} + 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/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", +version := "{{artifactVersion}}" - 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" - ), +name := "{{artifactId}}" - resolvers ++= Seq( - Resolver.jcenterRepo, - Resolver.mavenLocal - ), +organization := "{{groupId}}" - scalacOptions := Seq( - "-unchecked", - "-deprecation", - "-feature" - ), +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 - publishArtifact in (Compile, packageDoc) := false - ) \ No newline at end of file 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/pom.mustache b/modules/swagger-codegen/src/main/resources/scala/pom.mustache index 1c72f7d4fde..05ab94c16b8 100644 --- a/modules/swagger-codegen/src/main/resources/scala/pom.mustache +++ b/modules/swagger-codegen/src/main/resources/scala/pom.mustache @@ -209,6 +209,11 @@ joda-convert ${joda-version} + + com.wordnik.swagger + swagger-async-httpclient_2.10 + ${swagger-async-httpclient-version} + 2.10.4 @@ -223,6 +228,7 @@ 4.8.1 3.1.5 2.2.4 + 0.3.5 UTF-8 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..3892a4fd318 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Models.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Models.mustache @@ -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/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..b0bdb8a6c78 100644 --- a/modules/swagger-codegen/src/main/resources/swift3/APIHelper.mustache +++ b/modules/swagger-codegen/src/main/resources/swift3/APIHelper.mustache @@ -49,16 +49,30 @@ 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!)" + )] + } } + .flatMap { $0 } + if returnValues.count == 0 { 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..340f5b66fd8 100644 --- a/modules/swagger-codegen/src/main/resources/swift3/APIs.mustache +++ b/modules/swagger-codegen/src/main/resources/swift3/APIs.mustache @@ -36,7 +36,7 @@ open class RequestBuilder { let isBody: Bool let method: String let URLString: String - + /// Optional block to obtain a reference to the request's progress instance when available. public var onProgressReady: ((Progress) -> ())? @@ -46,17 +46,17 @@ open class RequestBuilder { 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 } } - - open func execute(_ completion: @escaping (_ response: Response?, _ error: Error?) -> Void) { } + + open func execute(_ completion: @escaping (_ response: Response?, _ error: ErrorResponse?) -> Void) { } public func addHeader(name: String, value: String) -> Self { if !value.isEmpty { @@ -64,7 +64,7 @@ open class RequestBuilder { } 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..fd981a05b05 100644 --- a/modules/swagger-codegen/src/main/resources/swift3/AlamofireImplementations.mustache +++ b/modules/swagger-codegen/src/main/resources/swift3/AlamofireImplementations.mustache @@ -13,8 +13,38 @@ 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 + } + } + } + +} + // 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] = [:]) { @@ -50,7 +80,7 @@ open class AlamofireRequestBuilder: RequestBuilder { return manager.request(URLString, method: method, parameters: 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() @@ -93,7 +123,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 +136,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 +155,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 +175,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 } @@ -164,7 +194,7 @@ open class AlamofireRequestBuilder: RequestBuilder { 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 } @@ -182,7 +212,7 @@ open class AlamofireRequestBuilder: RequestBuilder { 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 +220,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 +228,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 +241,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"]))) } } } diff --git a/modules/swagger-codegen/src/main/resources/swift3/Extensions.mustache b/modules/swagger-codegen/src/main/resources/swift3/Extensions.mustache index 19a3dd7364c..e6a33fee0fc 100644 --- a/modules/swagger-codegen/src/main/resources/swift3/Extensions.mustache +++ b/modules/swagger-codegen/src/main/resources/swift3/Extensions.mustache @@ -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..e8fa94d0e01 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 { @@ -35,54 +36,109 @@ open class Response { } } +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 { - 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 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) -> [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 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))) } - return dictionary } - static func decode(clazz: T.Type, source: AnyObject, instance: AnyObject?) -> T { + 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 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)))) + } + } + + 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 value = source.int32Value as? T, source is NSNumber, 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 value = source.int32Value as? T, source is NSNumber, 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 +156,72 @@ 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 - } - return source.map { (source: AnyObject) -> T in - Decoders.decode(clazz: clazz, source: source, instance: 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) } } - 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 - } - return source.map { (someSource: AnyObject) -> [Key:T] in - Decoders.decode(clazz: clazz, source: someSource) + 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) } } + static func decodeOptional(clazz: T, source: AnyObject) -> Decoded { + 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 +237,118 @@ 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 [{{{classname}}}] - Decoders.addDecoder(clazz: [{{{classname}}}].self) { (source: AnyObject, instance: AnyObject?) -> [{{{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}} -{{#isEnum}} - if let source = source as? {{dataType}} { - if let result = {{classname}}(rawValue: source) { - return result - } + + // 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)")) } - fatalError("Source \(source) is not convertible to enum type {{classname}}: Maybe swagger file is insufficient") + } + + {{#models}} + {{#model}} + {{^isArrayModel}} + // Decoder for {{{classname}}} + Decoders.addDecoder(clazz: {{{classname}}}.self) { (source: AnyObject, instance: AnyObject?) -> Decoded<{{{classname}}}> in +{{#isEnum}} + //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 + 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): instance.{{name}} = value + case let .failure(error): return .failure(error) + } + {{/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): return .failure(error) + {{#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 propsDictionary.keys { - if let decodedValue = Decoders.decodeOptional(clazz: String.self, source: propsDictionary[key] as AnyObject?) { - result[key] = decodedValue + for key in keys { + propsDictionary.removeValue(forKey: key) + } + + 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/api.mustache b/modules/swagger-codegen/src/main/resources/swift3/api.mustache index e85028db04b..fda7fadb373 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,19 +96,26 @@ 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}}> { @@ -134,11 +148,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/model.mustache b/modules/swagger-codegen/src/main/resources/swift3/model.mustache index c1bd6ebdb87..cd6a8cd82d1 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 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 +# , /