Compare commits

..

76 Commits

Author SHA1 Message Date
Tino Fuhrmann
9b8e4c27c2 Fixed bugs in blob handling of jquery 2020-05-24 21:08:31 +02:00
Tino Fuhrmann
277e4ed49d Fixed compile issues in ts-refactor jquery http test 2020-05-24 15:05:11 +02:00
Tino Fuhrmann
e2aaf66917 Renamed ts-refactor samples & tests in pom.xmls 2020-05-24 14:22:58 +02:00
Tino Fuhrmann
a7ac715e80 Update pom to use OAIv3 paths for Typescript-refactor 2020-05-24 13:07:06 +02:00
Tino Fuhrmann
c4cc0f705b Updated package-lock 2020-05-24 11:24:36 +02:00
Tino Fuhrmann
91f4e9fb8f Moved samples to oaiv3 folder 2020-05-24 11:16:28 +02:00
Tino Fuhrmann
6bd05358db Change to OAIv3 spec for TS-Refactor 2020-05-24 11:11:46 +02:00
Tino Fuhrmann
dee6ed420e Replaced isSuccessCode with is2xx 2020-05-23 19:01:24 +02:00
Tino Fuhrmann
41d1864d02 Updated versions in ts-default/jquery and ts docs 2020-05-22 23:45:25 +02:00
Tino Fuhrmann
56f9737b2d Merge master 2020-05-22 22:06:06 +02:00
Bodo Graumann
e315d48636 Support media types other than json (#6177)
List of changes:

* Add */* as fallback to accept header

* Use more sophisticated media type selection

* Handle object stringify in ObjectSerializer

* Parse response with ObejctSerializer

* Fix: Correctly extract response headers in browser

* Create HttpFile objects from responses

* Handle binary responses

* Clean up dependencies and replace isomorphic-fetch

Instead of isomorphic-fetch, which is unmaintained, we directly use
node-fetch and whatwg-fetch polyfills.
2020-05-22 21:54:52 +02:00
Bodo Graumann
659369c3ea Typescript refactor fixes (#6027)
Fixes a handful of issues identified in https://github.com/OpenAPITools/openapi-generator/issues/802#issuecomment-617262139

List of changes

* Clean: Remove redundant cliOption definition

* Remove redundant directory structure in templates

If we need to have different index.ts files for the different
frameworks, we can mostly do that in the one mustache file. In the cases
where that is not possible, we can still add a new override file later.

* Use File.separator consistently

* Only export selected api type

* Simplify promise polyfill import

The behaviour should be the same, according to the es6-promise docs.
Previously tsc would report the error:
> error TS2307: Cannot find module 'es6-promise'.

* Import HttpFile in all models

* Export server configurations

* Use undefined as default body value

The empty string is not interpreted as "no body" by the browser fetch
api and thus leads to an exception during get requests

* Improve codestyle: prefer guards to nesting

* Remove verbose debug output

This should not be commited, because every developer has very different
requirements what debug information he needs to see.

* Fix: Use cleaned model names for imports

* Fix: do not call toString on undefined

* Fix typo in doc comment

* Introduce RequestBody type and remove method check
2020-05-21 15:34:14 +02:00
Tino Fuhrmann
303ec6c04b [TS-Refactor] Added options for npm version, repository, name and updated readme (#6139)
* Added options for npm version, repository, name and updated readme

* Removed `this`  where not required

* Updated typescript docs
2020-05-16 19:01:08 +02:00
Bodo Graumann
391a191ecf Allow browsers File type for files (#5521)
* Allow passing file parameters as File objects
* Add test for jquery upload
* Use HttpFile object for node platform
* Regenerate samples

This is by far the most common use case. A `File` object already
contains the name attribute. This commit allows that information to be
used directly.
When sending a `Blob`, in most browsers the `File` constructor can be
used to assign a file name. In all other browsers the alternative is
```typescript
Object.assign(data, { name: "foobar.txt" });
```
That is why we explicitely pass the name as third parameter to
`FormData.append`. This `Object.assign` method also works for `Buffer`
objects in node.

If one really does not want to touch the data object in the browser it
is possible to define another reference to the data with
```typescript
new Blob([data], { type: data.type })
```
or in node via
```typescript
Buffer.from(data)
```
2020-05-05 22:53:07 +02:00
Tino Fuhrmann
934f226098 [TS-Refactor] Top-level exports for fetch & jquery (#6138)
* Added top-level exports
* Updated generator README
* Updated typescript generator docs
2020-05-04 23:58:38 +02:00
Tino Fuhrmann
07dd1efb7a Removed whitespace 2020-05-03 20:58:07 +02:00
Tino Fuhrmann
5a2561f520 Mark typescript client codegen as experimental 2020-05-02 22:03:56 +02:00
Tino Fuhrmann
d704a4ffba Readded missing change 2020-05-02 19:21:57 +02:00
Tino Fuhrmann
bfc14c82e5 Removed tab from DefaultCodegen 2020-04-27 01:12:35 +02:00
Bodo Graumann
9afb8ff12a Typescript refactor: Platform select for browser and node (#4500)
* Use string form of filename parameter

This works for the form-data library and is also compatible with the
browser FormData object.

* Add new option to select platform node or browser

When no platform is selected, a default is chosen by the framework
option and likewise the file data type option is implied by the
platform.

* Remove redundant import of node dns module

* Only use form-data library for node platform

* Generate npm package from npmName option

* Use method convertPropertyToBooleanAndWriteBack

* Generate typescript samples with ensure-up-to-date
2020-04-27 00:57:36 +02:00
Bodo Graumann
0000342d77 Typescript refactor: stub rxjs (#4424)
* Remove unused supportsES6 field from codegen

* Add a new switch for RXJS

* Remove redundant npm dependency on rxjs4 types

* Fix return type of PromiseMiddleware methods

* Install webpack dependency to run jquery tests

* Update form-data to 2.5 which includes typings

* Add missing dependency on node typings

* Fix test artifact name typo

* Stub rxjs when it is not explicitly enabled
2020-04-27 00:54:36 +02:00
Bodo Graumann
0f3ad99d72 Refactor typescript merge master (#4319)
Merge master into ts-refactor
2020-04-27 00:54:36 +02:00
Tino Fuhrmann
4f461f948a Updated typescript docs 2020-04-27 00:09:32 +02:00
Tino Fuhrmann
68241f8c33 Fixed missing fetch definition in TS default tests 2020-04-27 00:09:32 +02:00
Tino Fuhrmann
5fd3be29bd Ensured up to date 2020-04-27 00:09:32 +02:00
Tino Fuhrmann
e4a0855db9 Fixed a couple issues with pom.xml 2020-04-27 00:09:31 +02:00
Tino Fuhrmann
9297e59053 Removed tabs in TypeScriptClientCodegen 2020-04-27 00:09:31 +02:00
Tino Fuhrmann
6c37c7180d Added pom.xmls, fixed packagejsons and hopefully webppack 2020-04-27 00:09:31 +02:00
Tino Fuhrmann
fb6f8c5344 Added jquery library 2020-04-27 00:09:31 +02:00
Tino Fuhrmann
8d8e57f1f9 Added gitignore and git_push 2020-04-27 00:09:31 +02:00
Tino Fuhrmann
495ce938f8 Updated docs 2020-04-27 00:09:31 +02:00
Tino Fuhrmann
99c3dceae7 Added typescript to docs/generators 2020-04-27 00:09:18 +02:00
Tino Fuhrmann
fcbecc4dbd Fixed compilation issues in TypeScriptClientCodegen 2020-04-27 00:08:46 +02:00
Tino Fuhrmann
40f3c4f4dd Removed accidentally created generated code 2020-04-27 00:08:46 +02:00
Tino Fuhrmann
c330a9f872 Ignore openapi-generator-cli/bin 2020-04-27 00:08:46 +02:00
Tino Fuhrmann
a481d0ce83 Added comments 2020-04-27 00:08:46 +02:00
Tino Fuhrmann
35d3cc20c9 Added comments & license info 2020-04-27 00:08:46 +02:00
Tino Fuhrmann
7a372acbed Fixed date-time and date handling 2020-04-27 00:08:46 +02:00
Tino Fuhrmann
56ca583b2e Set discriminator value automatically 2020-04-27 00:08:29 +02:00
Tino Fuhrmann
c8c58b4f5e Configure discriminator correctly 2020-04-27 00:08:28 +02:00
Tino Fuhrmann
861f774c56 Made discriminator and attributeTypeMap readonly 2020-04-27 00:08:28 +02:00
Tino Fuhrmann
df970ae8b1 Added promise based middleware 2020-04-27 00:08:28 +02:00
Tino Fuhrmann
1d27563a41 Use observables internally 2020-04-27 00:08:28 +02:00
Tino Fuhrmann
aeef285190 Restructured module layout 2020-04-27 00:08:28 +02:00
Tino Fuhrmann
8068315f79 Remove tab 2020-04-27 00:08:28 +02:00
Tino Fuhrmann
e11a5a9395 Use string union for enums 2020-04-27 00:08:28 +02:00
Tino Fuhrmann
a00e342505 Made api call configuration separately settable 2020-04-27 00:08:28 +02:00
Tino Fuhrmann
7786f2e9fb Fixed file uploads 2020-04-27 00:08:28 +02:00
Tino Fuhrmann
1c2943dcc8 Additional tests for pet store api 2020-04-27 00:08:28 +02:00
Tino Fuhrmann
c5c9a59060 Removed tabs in DefaultCodegen 2020-04-27 00:08:28 +02:00
Tino Fuhrmann
8bfb88cd76 Removed tabs in TypeScriptClientCodegen 2020-04-27 00:08:02 +02:00
Tino Fuhrmann
35b98cf2bc Added handling for different http status codes and test for deletePet 2020-04-27 00:08:02 +02:00
Tino Fuhrmann
9b0bb9a399 Fixed ObjectSerializer test 2020-04-27 00:07:33 +02:00
Tino Fuhrmann
e41df36261 Added simple test for PetApi 2020-04-27 00:07:33 +02:00
Tino Fuhrmann
c2b7422a81 [TS-Refactor] Added tests for Object Serializer 2020-04-27 00:07:33 +02:00
Tino Fuhrmann
525f48d694 Added server variable configuration to ts-refactor 2020-04-27 00:07:33 +02:00
Tino Fuhrmann
0867522b0a Added ts client codegen to root pom.xml and travis 2020-04-27 00:07:33 +02:00
Tino Fuhrmann
988df1f7a5 Removed tabs from TypeScriptClientCodegen 2020-04-27 00:07:21 +02:00
Tino Fuhrmann
4c11314a7b Added pom.xml files to TypeScript PetStore client samples 2020-04-27 00:07:21 +02:00
Tino Fuhrmann
3eaa5e54ef Removed TODOs 2020-04-27 00:07:21 +02:00
Tino Fuhrmann
7909cbaec7 Reverted: http library.send returns string again 2020-04-27 00:07:21 +02:00
Tino Fuhrmann
a6560e5530 Restructured TypeScript generator 2020-04-27 00:07:21 +02:00
Tino Fuhrmann
61a1bbdabe Added middleware to fetch 2020-04-27 00:07:21 +02:00
Tino Fuhrmann
a7de49110e Ignore dist folder in typescript client sample 2020-04-27 00:07:21 +02:00
Tino Fuhrmann
e40d94984f Implemented fetch client 2020-04-27 00:07:21 +02:00
Tino Fuhrmann
a8ec866117 Implemented RequestFactory and Processor completely 2020-04-27 00:07:21 +02:00
Tino Fuhrmann
f5b062957d WIP: api modeling 2020-04-27 00:07:21 +02:00
Tino Fuhrmann
d4fa8c7f72 Updated auth 2020-04-27 00:07:21 +02:00
Tino Fuhrmann
6b2a2289f2 WIP: Models & API 2020-04-27 00:07:20 +02:00
Tino Fuhrmann
b89646a223 Added sample for typescript client 2020-04-27 00:07:20 +02:00
Tino Fuhrmann
276d7d47e5 Added servers 2020-04-27 00:07:20 +02:00
Tino Fuhrmann
1cc6fb0421 Added auth module 2020-04-27 00:07:20 +02:00
Tino Fuhrmann
1a31c48ceb Added model generation with imports 2020-04-27 00:07:20 +02:00
Tino Fuhrmann
6638cef37a Modified http lib, added config & middleware definition to ts-fetch 2020-04-27 00:07:20 +02:00
Tino Fuhrmann
05f64c6732 Added generic enum 2020-04-27 00:07:20 +02:00
Tino Fuhrmann
06d9556f13 Added http module draft 2020-04-27 00:07:20 +02:00
783 changed files with 22566 additions and 30932 deletions

View File

@@ -21,6 +21,7 @@ cache:
- $HOME/samples/client/petstore/php/OpenAPIToolsClient-php/vendor
- $HOME/samples/client/petstore/ruby/vendor/bundle
- $HOME/samples/client/petstore/python/.venv/
- $HOME/samples/client/petstore/typescript/tests/default/node_modules
- $HOME/samples/client/petstore/typescript-node/npm/node_modules
- $HOME/samples/client/petstore/typescript-node/npm/typings/
- $HOME/samples/client/petstore/typescript-fetch/tests/default/node_modules

View File

@@ -2,11 +2,6 @@ kind: pipeline
name: default
steps:
# test ue4 cpp client
- name: cpp-ue4
image: adamrehn/ue4-full:4.21.0
commands:
- (cd samples/client/petstore/cpp-ue4/ && ue4 build)
# test aspnetcore 3.x
- name: aspnetcore-test
image: mcr.microsoft.com/dotnet/core/sdk:3.1

View File

@@ -122,6 +122,7 @@ public class ApiClientTest {
}
}
@Ignore("There is no more basic auth in petstore security definitions")
@Test
public void testSetUsernameAndPassword() {
HttpBasicAuth auth = null;

View File

@@ -122,6 +122,7 @@ public class ApiClientTest {
}
}
@Ignore("There is no more basic auth in petstore security definitions")
@Test
public void testSetUsernameAndPassword() {
HttpBasicAuth auth = null;

View File

@@ -128,6 +128,7 @@ public class ApiClientTest {
}
}
@Ignore("There is no more basic auth in petstore security definitions")
@Test
public void testSetUsernameAndPassword() {
HttpBasicAuth auth = null;

View File

@@ -745,11 +745,9 @@ Here are some companies/projects (alphabetical order) using OpenAPI Generator in
- 2020-03-15 - [Load Testing Your API with Swagger/OpenAPI and k6](https://k6.io/blog/load-testing-your-api-with-swagger-openapi-and-k6)
- 2020-04-13 - [俺的【OAS】との向き合い方 (爆速でOpenAPIと友達になろう)](https://tech-blog.optim.co.jp/entry/2020/04/13/100000) in [OPTim Blog](https://tech-blog.optim.co.jp/)
- 2020-04-22 - [Introduction to OpenAPI Generator](https://nordicapis.com/introduction-to-openapi-generator/) by [Kristopher Sandoval](https://nordicapis.com/author/sandovaleffect/) in [Nordic APIs](https://nordicapis.com/)
- 2020-04-27 - [How we use Open API v3 specification to auto-generate API documentation, code-snippets and clients](https://medium.com/pdf-generator-api/how-we-use-open-api-v3-specification-to-auto-generate-api-documentation-code-snippets-and-clients-d127a3cea784) by [Tanel Tähepõld](https://medium.com/@tanel.tahepold)
- 2020-05-09 - [OpenAPIでお手軽にモックAPIサーバーを動かす](https://qiita.com/kasa_le/items/97ca6a8dd4605695c25c) by [Sachie Kamba](https://qiita.com/kasa_le)
- 2020-05-18 - [Spring Boot REST with OpenAPI 3](https://dev.to/alfonzjanfrithz/spring-boot-rest-with-openapi-3-59jm) by [Alfonz Jan Frithz](https://dev.to/alfonzjanfrithz)
- 2020-05-19 - [Dead Simple APIs with Open API](https://www.youtube.com/watch?v=sIaXmR6xRAw) by [Chris Tankersley](https://github.com/dragonmantank) at [Nexmo](https://developer.nexmo.com/)
- 2020-05-22 - [TypeScript REST API Client](https://dev.to/unhurried/typescript-rest-api-client-4in3) by ["unhurried"](https://dev.to/unhurried)
## [6 - About Us](#table-of-contents)
@@ -779,7 +777,6 @@ Here is a list of template creators:
* Bash: @bkryza
* C: @PowerOfCreation @zhemant [:heart:](https://www.patreon.com/zhemant)
* C++ REST: @Danielku15
* C++ UE4: @Kahncode
* C# (.NET 2.0): @who
* C# (.NET Standard 1.3 ): @Gronsak
* C# (.NET 4.5 refactored): @jimschubert [:heart:](https://www.patreon.com/jimschubert)

View File

@@ -27,6 +27,6 @@ fi
# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties"
ags="generate -t modules/openapi-generator/src/main/resources/nodejs-express-server -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g nodejs-express-server -o samples/server/petstore/nodejs-express-server $@"
ags="generate -t modules/openapi-generator/src/main/resources/nodejs-express-server -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g nodejs-express-server -o samples/server/petstore/nodejs-express-server -Dservice $@"
java $JAVA_OPTS -jar $executable $ags

View File

@@ -28,7 +28,7 @@ fi
# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties"
args="generate -t modules/openapi-generator/src/main/resources/ocaml -i modules/openapi-generator/src/test/resources/3_0/petstore.yaml -g ocaml -o samples/client/petstore/ocaml --additional-properties packageName=petstore_client $@"
args="generate -t modules/openapi-generator/src/main/resources/ocaml -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g ocaml -o samples/client/petstore/ocaml --additional-properties packageName=petstore_client $@"
echo "java ${JAVA_OPTS} -jar ${executable} ${args}"
java $JAVA_OPTS -jar $executable $args

View File

@@ -26,7 +26,9 @@ then
fi
# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties"
ags="generate -i modules/openapi-generator/src/test/resources/3_0/composed-schemas.yaml -g typescript-axios -o samples/client/petstore/typescript-axios/builds/composed-schemas $@"
export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DdebugOperations -DloggerPath=conf/log4j.properties"
java $JAVA_OPTS -jar $executable $ags
args="generate -t modules/openapi-generator/src/main/resources/ocaml -i modules/openapi-generator/src/test/resources/3_0/petstore-with-fake-endpoints-models-for-testing.yaml -g ocaml -o samples/openapi3/client/petstore/ocaml/ --additional-properties packageName=petstore_client $@"
echo "java ${JAVA_OPTS} -jar ${executable} ${args}"
java $JAVA_OPTS -jar $executable $args

View File

@@ -37,8 +37,8 @@ ags="generate -t $resources -i $input -g $generator -o $out_folder $@"
rm -rf $out_folder/.openapi*
rm -rf $out_folder/openapi_server
rm -rf $out_folder/tests*
rm -f $out_folder/README.md
rm -f $out_folder/requirements.txt
rm -f $out_folder/test-requirements.txt
rm $out_folder/README.md
rm $out_folder/requirements.txt
rm $out_folder/test-requirements.txt
java $JAVA_OPTS -jar $executable $ags

View File

@@ -6,5 +6,4 @@
./bin/typescript-axios-petstore-with-complex-headers.sh
./bin/typescript-axios-petstore-with-single-request-parameters.sh
./bin/typescript-axios-petstore-interfaces.sh
./bin/typescript-axios-petstore-composed-schemas.sh
./bin/typescript-axios-petstore.sh

View File

@@ -1,6 +1,7 @@
#!/bin/sh
SCRIPT="$0"
echo "# START SCRIPT: $SCRIPT"
while [ -h "$SCRIPT" ] ; do
ls=`ls -ld "$SCRIPT"`
@@ -21,11 +22,16 @@ executable="./modules/openapi-generator-cli/target/openapi-generator-cli.jar"
if [ ! -f "$executable" ]
then
mvn clean package
mvn -B clean package
fi
# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
ags="$@ generate -t modules/openapi-generator/src/main/resources/cpp-ue4 -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g cpp-ue4 -o samples/client/petstore/cpp-ue4"
echo "Creating default (fetch) client!"
ags="generate -i modules/openapi-generator/src/test/resources/3_0/petstore.yaml -g typescript -o samples/openapi3/client/petstore/typescript/builds/default --additional-properties=platform=node,npmName=ts-petstore-client $@"
java $JAVA_OPTS -jar $executable $ags
echo "Creating jquery client!"
ags="generate -i modules/openapi-generator/src/test/resources/3_0/petstore.yaml -g typescript -o samples/openapi3/client/petstore/typescript/builds/jquery --additional-properties=framework=jquery,npmName=ts-petstore-client $@"
java $JAVA_OPTS -jar $executable $ags

View File

@@ -50,6 +50,7 @@ declare -a samples=(
#"${root}/bin/php-slim4-server-petstore.sh"
"${root}/bin/php-ze-ph-petstore-server.sh"
"${root}/bin/openapi3/php-petstore.sh"
"${root}/bin/typescript.sh"
"${root}/bin/typescript-angularjs-petstore.sh"
"${root}/bin/typescript-angular-petstore-all.sh"
"${root}/bin/typescript-aurelia-petstore.sh"

View File

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

View File

@@ -1,24 +1,24 @@
call .\bin\windows\java-petstore-feign-10x.bat
call .\bin\windows\java-petstore-feign.bat
call .\bin\windows\java-petstore-google-api-client.bat
call .\bin\windows\java-petstore-jersey1.bat
call .\bin\windows\java-petstore-jersey2-java6.bat
call .\bin\windows\java-petstore-jersey2-java7.bat
call .\bin\windows\java-petstore-jersey2-java8.bat
call .\bin\windows\java-petstore-feign.bat
call .\bin\windows\java-petstore-feign-10x.bat
call .\bin\windows\java-petstore-native.bat
call .\bin\windows\java-petstore-okhttp-gson-parcelable.bat
call .\bin\windows\java-petstore-okhttp-gson.bat
call .\bin\windows\java-petstore-rest-assured.bat
call .\bin\windows\java-petstore-rest-assured-jackson.bat
call .\bin\windows\java-petstore-resteasy.bat
call .\bin\windows\java-petstore-resttemplate-withxml.bat
call .\bin\windows\java-petstore-resttemplate.bat
call .\bin\windows\java-petstore-okhttp-gson-parcelable.bat
call .\bin\windows\java-petstore-retrofit.bat
call .\bin\windows\java-petstore-retrofit2-play24.bat
call .\bin\windows\java-petstore-retrofit2-play25.bat
call .\bin\windows\java-petstore-retrofit2-play26.bat
call .\bin\windows\java-petstore-retrofit2.bat
call .\bin\windows\java-petstore-retrofit2rx.bat
call .\bin\windows\java-petstore-retrofit2rx2.bat
call .\bin\windows\java-petstore-vertx.bat
call .\bin\windows\java8-petstore-jersey2.bat
call .\bin\windows\java-petstore-retrofit2-play24.bat
call .\bin\windows\java-petstore-retrofit2-play25.bat
call .\bin\windows\java-petstore-retrofit2-play26.bat
call .\bin\windows\java-petstore-jersey2-java6.bat
call .\bin\windows\java-petstore-resttemplate.bat
call .\bin\windows\java-petstore-resttemplate-withxml.bat
call .\bin\windows\java-petstore-webclient.bat
call .\bin\windows\java-petstore-resteasy.bat
call .\bin\windows\java-petstore-google-api-client.bat
call .\bin\windows\java-petstore-rest-assured.bat
call .\bin\windows\java-petstore-rest-assured-jackson.bat
call .\bin\windows\java-petstore-vertx.bat

View File

@@ -5,6 +5,6 @@ If Not Exist %executable% (
)
REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M
set ags=generate --artifact-id petstore-java-client-jersey1 -t modules\openapi-generator\src\main\resources\Java -i modules\openapi-generator\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -g java -o samples\client\petstore\java\jersey1 --additional-properties hideGenerationTimestamp=true --library=jersey1
set ags=generate --artifact-id petstore-java-client-jersey1 -t modules\openapi-generator\src\main\resources\Java -i modules\openapi-generator\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -g java -o samples\client\petstore\java\jersey1 --additional-properties hideGenerationTimestamp=true --library=jersey1 --additional-properties useNullForUnknownEnumValue=true
java %JAVA_OPTS% -jar %executable% %ags%

View File

@@ -1,10 +0,0 @@
set executable=.\modules\openapi-generator-cli\target\openapi-generator-cli.jar
If Not Exist %executable% (
mvn clean package
)
REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M
set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -g java -c bin\java-petstore-jersey2-java7.json -o samples\client\petstore\java\jersey2-java7 --additional-properties hideGenerationTimestamp=true
java %JAVA_OPTS% -jar %executable% %ags%

View File

@@ -5,6 +5,6 @@ If Not Exist %executable% (
)
REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M
set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -g java -c bin\java-petstore-jersey2-java8.json -o samples\client\petstore\java\jersey2-java8 --additional-properties hideGenerationTimestamp=true
set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -g java -c bin\java-petstore-jersey2-java7.json -o samples\client\petstore\java\jersey2 --additional-properties hideGenerationTimestamp=true
java %JAVA_OPTS% -jar %executable% %ags%

View File

@@ -5,6 +5,6 @@ If Not Exist %executable% (
)
REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M
set ags=generate -t modules\openapi-generator\src\main\resources\Java\libraries\rest-assured -i modules\openapi-generator\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -g java -c bin\java-petstore-rest-assured.json -o samples\client\petstore\java\rest-assured --additional-properties hideGenerationTimestamp=true --additional-properties useBeanValidation=true --additional-properties performBeanValidation=true --additional-properties booleanGetterPrefix=is
set ags=generate -t modules\openapi-generator\src\main\resources\Java\libraries\rest-assured -i modules\openapi-generator\src\test\resources\2_0\petstore-with-fake-endpoints-models-for-testing.yaml -g java -c bin\java-petstore-rest-assured.json -o samples\client\petstore\java\rest-assured --additional-properties hideGenerationTimestamp=true,booleanGetterPrefix=is
java %JAVA_OPTS% -jar %executable% %ags%

View File

@@ -1,10 +0,0 @@
set executable=.\modules\openapi-generator-cli\target\openapi-generator-cli.jar
If Not Exist %executable% (
mvn clean package
)
REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M
set ags=generate -i modules/openapi-generator/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -g java -c bin/java-petstore-webclient.json -o samples/client/petstore/java/webclient --additional-properties hideGenerationTimestamp=true
java %JAVA_OPTS% -jar %executable% %ags%

View File

@@ -5,6 +5,6 @@ If Not Exist %executable% (
)
REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -DloggerPath=conf/log4j.properties
set ags=generate -i modules\openapi-generator\src\test\resources\3_0\petstore.yaml -g ocaml -o samples\client\petstore\ocaml
set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore.yaml -g ocaml -o samples\client\petstore\ocaml
java %JAVA_OPTS% -jar %executable% %ags%

View File

@@ -6,5 +6,4 @@ call bin\windows\typescript-axios-petstore-with-complex-headers.bat
call bin\windows\typescript-axios-petstore-with-single-request-parameters.bat
call bin\windows\typescript-axios-petstore-with-npm-version.bat
call bin\windows\typescript-axios-petstore-interfaces.bat
call bin\windows\typescript-axios-petstore-composed-schemas.bat
call bin\windows\typescript-axios-petstore-with-npm-version-and-separate-models-and-api.bat

View File

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

View File

@@ -15,7 +15,6 @@ The following generators are available:
* [cpp-qt5-client](generators/cpp-qt5-client.md)
* [cpp-restsdk](generators/cpp-restsdk.md)
* [cpp-tizen](generators/cpp-tizen.md)
* [cpp-ue4 (beta)](generators/cpp-ue4.md)
* [csharp](generators/csharp.md)
* [csharp-dotnet2 (deprecated)](generators/csharp-dotnet2.md)
* [csharp-netcore](generators/csharp-netcore.md)
@@ -60,6 +59,7 @@ The following generators are available:
* [scalaz](generators/scalaz.md)
* [swift4-deprecated (deprecated)](generators/swift4-deprecated.md)
* [swift5 (beta)](generators/swift5.md)
* [typescript (experimental)](generators/typescript.md)
* [typescript-angular](generators/typescript-angular.md)
* [typescript-angularjs](generators/typescript-angularjs.md)
* [typescript-aurelia](generators/typescript-aurelia.md)
@@ -104,6 +104,7 @@ The following generators are available:
* [kotlin-spring](generators/kotlin-spring.md)
* [kotlin-vertx (beta)](generators/kotlin-vertx.md)
* [nodejs-express-server (beta)](generators/nodejs-express-server.md)
* [nodejs-server-deprecated (deprecated)](generators/nodejs-server-deprecated.md)
* [php-laravel](generators/php-laravel.md)
* [php-lumen](generators/php-lumen.md)
* [php-silex-deprecated (deprecated)](generators/php-silex-deprecated.md)

View File

@@ -1,142 +1,132 @@
---
title: Config Options for cpp-ue4
sidebar_label: cpp-ue4
title: Config Options for typescript
sidebar_label: typescript
---
| Option | Description | Values | Default |
| ------ | ----------- | ------ | ------- |
|allowUnicodeIdentifiers|boolean, toggles whether unicode identifiers are allowed in names or not, default is false| |false|
|cppNamespace|C++ namespace (convention: name::space::for::api).| |OpenAPI|
|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true|
|fileContentDataType|Specifies the type to use for the content of a file - i.e. Blob (Browser) / Buffer (node)| |Buffer|
|framework|Specify the framework which should be used in the client code.|<dl><dt>**fetch-api**</dt><dd>fetch-api</dd><dt>**jquery**</dt><dd>jquery</dd></dl>|fetch-api|
|legacyDiscriminatorBehavior|This flag is used by OpenAPITools codegen to influence the processing of the discriminator attribute in OpenAPI documents. This flag has no impact if the OAS document does not use the discriminator attribute. The default value of this flag is set in each language-specific code generator (e.g. Python, Java, go...)using the method toModelName. Note to developers supporting a language generator in OpenAPITools; to fully support the discriminator attribute as defined in the OAS specification 3.x, language generators should set this flag to true by default; however this requires updating the mustache templates to generate a language-specific discriminator lookup function that iterates over {{#mappedModels}} and does not iterate over {{children}}, {{#anyOf}}, or {{#oneOf}}.|<dl><dt>**true**</dt><dd>The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.</dd><dt>**false**</dt><dd>The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.</dd></dl>|true|
|optionalProjectFile|Generate Build.cs| |true|
|modelPropertyNaming|Naming convention for the property: 'camelCase', 'PascalCase', 'snake_case' and 'original', which keeps the original name| |camelCase|
|npmName|The name under which you want to publish generated npm package. Required to generate a full package| |null|
|npmRepository|Use this property to set an url your private npmRepo in the package.json| |null|
|npmVersion|The version of your npm package. If not provided, using the version from the OpenAPI specification file.| |1.0.0|
|platform|Specifies the platform the code should run on. The default is 'node' for the 'request' framework and 'browser' otherwise.|<dl><dt>**browser**</dt><dd>browser</dd><dt>**node**</dt><dd>node</dd></dl>|browser|
|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|
|reservedWordPrefix|Prefix to prepend to reserved words in order to avoid conflicts| |r_|
|snapshot|When setting this property to true, the version will be suffixed with -SNAPSHOT.yyyyMMddHHmm| |false|
|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true|
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
|unrealModuleName|Name of the generated unreal module (optional)| |OpenAPI|
|variableNameFirstCharacterUppercase|Make first character of variable name uppercase (eg. value -&gt; Value)| |true|
|supportsES6|Generate code that conforms to ES6.| |false|
|useRxJS|Enable this to internally use rxjs observables. If disabled, a stub is used instead. This is required for the 'angular' framework.| |false|
## IMPORT MAPPING
| Type/Alias | Imports |
| ---------- | ------- |
|HttpFileInput|#include &quot;OpenAPIHelpers.h&quot;|
## INSTANTIATION TYPES
| Type/Alias | Instantiated By |
| ---------- | --------------- |
|array|Array|
## LANGUAGE PRIMITIVES
<ul class="column-ul">
<li>FDateTime</li>
<li>FGuid</li>
<li>FString</li>
<li>TArray</li>
<li>TArray&lt;uint8&gt;</li>
<li>TMap</li>
<li>TSharedPtr&lt;FJsonObject&gt;</li>
<li>bool</li>
<li>double</li>
<li>float</li>
<li>int32</li>
<li>int64</li>
<li>Array</li>
<li>Boolean</li>
<li>Date</li>
<li>Double</li>
<li>Error</li>
<li>File</li>
<li>Float</li>
<li>Integer</li>
<li>Long</li>
<li>Map</li>
<li>Object</li>
<li>String</li>
<li>any</li>
<li>boolean</li>
<li>number</li>
<li>string</li>
</ul>
## RESERVED WORDS
<ul class="column-ul">
<li>alignas</li>
<li>alignof</li>
<li>and</li>
<li>and_eq</li>
<li>asm</li>
<li>auto</li>
<li>bitand</li>
<li>bitor</li>
<li>bool</li>
<li>abstract</li>
<li>await</li>
<li>boolean</li>
<li>break</li>
<li>byte</li>
<li>case</li>
<li>catch</li>
<li>char</li>
<li>char16_t</li>
<li>char32_t</li>
<li>class</li>
<li>compl</li>
<li>concept</li>
<li>const</li>
<li>const_cast</li>
<li>constexpr</li>
<li>continue</li>
<li>decltype</li>
<li>debugger</li>
<li>default</li>
<li>delete</li>
<li>do</li>
<li>double</li>
<li>dynamic_cast</li>
<li>else</li>
<li>enum</li>
<li>explicit</li>
<li>export</li>
<li>extern</li>
<li>extends</li>
<li>false</li>
<li>final</li>
<li>finally</li>
<li>float</li>
<li>for</li>
<li>friend</li>
<li>formParams</li>
<li>function</li>
<li>goto</li>
<li>headerParams</li>
<li>if</li>
<li>inline</li>
<li>implements</li>
<li>import</li>
<li>in</li>
<li>instanceof</li>
<li>int</li>
<li>linux</li>
<li>interface</li>
<li>let</li>
<li>long</li>
<li>mutable</li>
<li>namespace</li>
<li>native</li>
<li>new</li>
<li>noexcept</li>
<li>not</li>
<li>not_eq</li>
<li>nullptr</li>
<li>operator</li>
<li>or</li>
<li>or_eq</li>
<li>null</li>
<li>package</li>
<li>private</li>
<li>protected</li>
<li>public</li>
<li>register</li>
<li>reinterpret_cast</li>
<li>requires</li>
<li>queryParameters</li>
<li>requestOptions</li>
<li>return</li>
<li>short</li>
<li>signed</li>
<li>sizeof</li>
<li>static</li>
<li>static_assert</li>
<li>static_cast</li>
<li>struct</li>
<li>super</li>
<li>switch</li>
<li>template</li>
<li>synchronized</li>
<li>this</li>
<li>thread_local</li>
<li>throw</li>
<li>transient</li>
<li>true</li>
<li>try</li>
<li>typedef</li>
<li>typeid</li>
<li>typename</li>
<li>union</li>
<li>unsigned</li>
<li>using</li>
<li>virtual</li>
<li>typeof</li>
<li>useFormData</li>
<li>var</li>
<li>varLocalDeferred</li>
<li>varLocalPath</li>
<li>void</li>
<li>volatile</li>
<li>wchar_t</li>
<li>while</li>
<li>xor</li>
<li>xor_eq</li>
<li>with</li>
<li>yield</li>
</ul>
## FEATURE SET

View File

@@ -30,8 +30,8 @@ public class CodegenOperation {
isResponseBinary = false, isResponseFile = false, hasReference = false,
isRestfulIndex, isRestfulShow, isRestfulCreate, isRestfulUpdate, isRestfulDestroy,
isRestful, isDeprecated, isCallbackRequest, uniqueItems;
public String path, operationId, returnType, httpMethod, returnBaseType,
returnContainer, summary, unescapedNotes, notes, baseName, defaultResponse;
public String path, operationId, returnType, returnFormat, httpMethod, returnBaseType,
returnContainer, summary, unescapedNotes, notes, baseName, defaultResponse;
public CodegenDiscriminator discriminator;
public List<Map<String, String>> consumes, produces, prioritizedContentTypes;
public List<CodegenServer> servers = new ArrayList<CodegenServer>();

View File

@@ -3430,8 +3430,9 @@ public class DefaultCodegen implements CodegenConfig {
op.examples = new ExampleGenerator(schemas, this.openAPI).generateFromResponseSchema(exampleStatusCode, responseSchema, getProducesInfo(this.openAPI, operation));
op.defaultResponse = toDefaultValue(responseSchema);
op.returnType = cm.dataType;
op.hasReference = schemas.containsKey(op.returnBaseType);
op.returnFormat = cm.dataFormat;
op.hasReference = schemas != null && schemas.containsKey(op.returnBaseType);
// lookup discriminator
Schema schema = schemas.get(op.returnBaseType);
if (schema != null) {
@@ -4243,7 +4244,7 @@ public class DefaultCodegen implements CodegenConfig {
return false;
}
}
// TODO revise below as it should be replaced by ModelUtils.isFileSchema(parameterSchema)
public boolean isDataTypeFile(String dataType) {
if (dataType != null) {

View File

@@ -30,7 +30,6 @@ import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.security.*;
import io.swagger.v3.oas.models.tags.Tag;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.comparator.PathFileComparator;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.config.GlobalSettings;
@@ -50,7 +49,6 @@ import org.slf4j.LoggerFactory;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.ZonedDateTime;
import java.util.*;
@@ -59,7 +57,6 @@ import static org.openapitools.codegen.utils.OnceLogger.once;
@SuppressWarnings("rawtypes")
public class DefaultGenerator extends AbstractGenerator implements Generator {
private static final String METADATA_DIR = ".openapi-generator";
protected final Logger LOGGER = LoggerFactory.getLogger(DefaultGenerator.class);
protected CodegenConfig config;
protected ClientOptInput opts;
@@ -863,7 +860,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
));
}
String versionMetadata = config.outputFolder() + File.separator + METADATA_DIR + File.separator + "VERSION";
String versionMetadata = config.outputFolder() + File.separator + ".openapi-generator" + File.separator + "VERSION";
if (generateMetadata) {
File versionMetadataFile = new File(versionMetadata);
try {
@@ -933,6 +930,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
bundle.put("hasOAuthMethods", true);
bundle.put("oauthMethods", ProcessUtils.getOAuthMethods(authMethods));
}
if (ProcessUtils.hasHttpBearerMethods(authMethods)) {
bundle.put("hasHttpBearerMethods", true);
}
@@ -1059,38 +1057,6 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
sb.append(System.lineSeparator());
System.err.println(sb.toString());
} else {
if (generateMetadata) {
try {
StringBuilder sb = new StringBuilder();
File outDir = new File(this.config.getOutputDir());
List<File> filesToSort = new ArrayList<>();
// Avoid side-effecting sort in this path when generateMetadata=true
files.forEach(f -> {
// We have seen NPE on CI for getPath() returning null, so guard against this (to be fixed in 5.0 template management refactor)
//noinspection ConstantConditions
if (f != null && f.getPath() != null) {
filesToSort.add(f);
}
});
filesToSort.sort(PathFileComparator.PATH_COMPARATOR);
filesToSort.forEach(f -> {
String relativePath = outDir.toPath().relativize(f.toPath()).toString();
if (!relativePath.equals(METADATA_DIR + File.separator + "VERSION")) {
sb.append(relativePath).append(System.lineSeparator());
}
});
String targetFile = config.outputFolder() + File.separator + METADATA_DIR + File.separator + "FILES";
File filesFile = writeToFile(targetFile, sb.toString().getBytes(StandardCharsets.UTF_8));
files.add(filesFile);
} catch (Exception e) {
LOGGER.warn("Failed to write FILES metadata to track generated files.");
}
}
}
// reset GlobalSettings, so that the running thread can be reused for another generator-run
@@ -1479,6 +1445,8 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
return result;
}
protected File writeInputStreamToFile(String filename, InputStream in, String templateFile) throws IOException {
if (in != null) {
byte[] bytes = IOUtils.toByteArray(in);

View File

@@ -164,7 +164,6 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp
typeMapping.put("UUID", "string");
typeMapping.put("URI", "string");
typeMapping.put("Error", "Error");
typeMapping.put("AnyType", "any");
cliOptions.add(new CliOption(CodegenConstants.ENUM_NAME_SUFFIX, CodegenConstants.ENUM_NAME_SUFFIX_DESC).defaultValue(this.enumSuffix));
cliOptions.add(new CliOption(CodegenConstants.ENUM_PROPERTY_NAMING, CodegenConstants.ENUM_PROPERTY_NAMING_DESC).defaultValue(this.enumPropertyNaming.name()));
@@ -809,38 +808,7 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp
@Override
public String toAnyOfName(List<String> names, ComposedSchema composedSchema) {
List<String> types = getTypesFromSchemas(composedSchema.getAnyOf());
return String.join(" | ", types);
}
@Override
public String toOneOfName(List<String> names, ComposedSchema composedSchema) {
List<String> types = getTypesFromSchemas(composedSchema.getOneOf());
return String.join(" | ", types);
}
@Override
public String toAllOfName(List<String> names, ComposedSchema composedSchema) {
List<String> types = getTypesFromSchemas(composedSchema.getAllOf());
return String.join(" & ", types);
}
/**
* Extracts the list of type names from a list of schemas.
* Excludes `AnyType` if there are other valid types extracted.
*
* @param schemas list of schemas
* @return list of types
*/
protected List<String> getTypesFromSchemas(List<Schema> schemas) {
List<Schema> filteredSchemas = schemas.size() > 1
? schemas.stream().filter(schema -> super.getSchemaType(schema) != "AnyType").collect(Collectors.toList())
: schemas;
return filteredSchemas.stream().map(schema -> {
List<String> types = composedSchema.getAnyOf().stream().map(schema -> {
String schemaType = getSchemaType(schema);
if (ModelUtils.isArraySchema(schema)) {
ArraySchema ap = (ArraySchema) schema;
@@ -849,5 +817,34 @@ public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen imp
}
return schemaType;
}).distinct().collect(Collectors.toList());
return String.join(" | ", types);
}
@Override
public String toOneOfName(List<String> names, ComposedSchema composedSchema) {
List<String> types = composedSchema.getOneOf().stream().map(schema -> {
String schemaType = getSchemaType(schema);
if (ModelUtils.isArraySchema(schema)) {
ArraySchema ap = (ArraySchema) schema;
Schema inner = ap.getItems();
schemaType = schemaType + "<" + getSchemaType(inner) + ">";
}
return schemaType;
}).distinct().collect(Collectors.toList());
return String.join(" | ", types);
}
@Override
public String toAllOfName(List<String> names, ComposedSchema composedSchema) {
List<String> types = composedSchema.getAllOf().stream().map(schema -> {
String schemaType = getSchemaType(schema);
if (ModelUtils.isArraySchema(schema)) {
ArraySchema ap = (ArraySchema) schema;
Schema inner = ap.getItems();
schemaType = schemaType + "<" + getSchemaType(inner) + ">";
}
return schemaType;
}).distinct().collect(Collectors.toList());
return String.join(" & ", types);
}
}

View File

@@ -1,544 +0,0 @@
/*
* Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openapitools.codegen.languages;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Schema;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.CodegenType;
import org.openapitools.codegen.SupportingFile;
import org.openapitools.codegen.meta.GeneratorMetadata;
import org.openapitools.codegen.meta.Stability;
import org.openapitools.codegen.utils.ModelUtils;
import java.io.File;
import java.util.*;
import static org.openapitools.codegen.utils.StringUtils.camelize;
public class CppUE4ClientCodegen extends AbstractCppCodegen {
public static final String CPP_NAMESPACE = "cppNamespace";
public static final String CPP_NAMESPACE_DESC = "C++ namespace (convention: name::space::for::api).";
public static final String UNREAL_MODULE_NAME = "unrealModuleName";
public static final String UNREAL_MODULE_NAME_DESC = "Name of the generated unreal module (optional)";
public static final String OPTIONAL_PROJECT_FILE_DESC = "Generate Build.cs";
protected String unrealModuleName = "OpenAPI";
// Will be treated as pointer
protected Set<String> pointerClasses = new HashSet<String>();
// source folder where to write the files
protected String privateFolder = "Private";
protected String publicFolder = "Public";
protected String apiVersion = "1.0.0";
protected Map<String, String> namespaces = new HashMap<String, String>();
// Will be included using the <> syntax, not used in Unreal's coding convention
protected Set<String> systemIncludes = new HashSet<String>();
protected String cppNamespace = unrealModuleName;
protected boolean optionalProjectFileFlag = true;
public CppUE4ClientCodegen() {
super();
generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata)
.stability(Stability.BETA)
.build();
// set the output folder here
outputFolder = "generated-code/cpp-ue4";
// set modelNamePrefix as default for cpp-ue4
if ("".equals(modelNamePrefix)) {
modelNamePrefix = unrealModuleName;
}
/*
* Models. You can write model files using the modelTemplateFiles map.
* if you want to create one template for file, you can do so here.
* for multiple files for model, just put another entry in the `modelTemplateFiles` with
* a different extension
*/
modelTemplateFiles.put(
"model-header.mustache",
".h");
modelTemplateFiles.put(
"model-source.mustache",
".cpp");
/*
* Api classes. You can write classes for each Api file with the apiTemplateFiles map.
* as with models, add multiple entries with different extensions for multiple files per
* class
*/
apiTemplateFiles.put(
"api-header.mustache", // the template to use
".h"); // the extension for each file to write
apiTemplateFiles.put(
"api-source.mustache", // the template to use
".cpp"); // the extension for each file to write
apiTemplateFiles.put(
"api-operations-header.mustache", // the template to use
".h"); // the extension for each file to write
apiTemplateFiles.put(
"api-operations-source.mustache", // the template to use
".cpp"); // the extension for each file to write
/*
* Template Location. This is the location which templates will be read from. The generator
* will use the resource stream to attempt to read the templates.
*/
embeddedTemplateDir = templateDir = "cpp-ue4";
// CLI options
addOption(CPP_NAMESPACE, CPP_NAMESPACE_DESC, this.cppNamespace);
addOption(UNREAL_MODULE_NAME, UNREAL_MODULE_NAME_DESC, this.unrealModuleName);
addSwitch(CodegenConstants.OPTIONAL_PROJECT_FILE, OPTIONAL_PROJECT_FILE_DESC, this.optionalProjectFileFlag);
/*
* Additional Properties. These values can be passed to the templates and
* are available in models, apis, and supporting files
*/
additionalProperties.put("apiVersion", apiVersion);
additionalProperties().put("modelNamePrefix", modelNamePrefix);
additionalProperties().put("modelPackage", modelPackage);
additionalProperties().put("apiPackage", apiPackage);
additionalProperties().put("dllapi", unrealModuleName.toUpperCase(Locale.ROOT) + "_API");
additionalProperties().put("unrealModuleName", unrealModuleName);
// Write defaults namespace in properties so that it can be accessible in templates.
// At this point command line has not been parsed so if value is given
// in command line it will superseed this content
additionalProperties.put("cppNamespace", cppNamespace);
additionalProperties.put("unrealModuleName", unrealModuleName);
/*
* Language Specific Primitives. These types will not trigger imports by
* the client generator
*/
languageSpecificPrimitives = new HashSet<String>(
Arrays.asList(
"bool",
"int32",
"int64",
"float",
"double",
"FString",
"FDateTime",
"FGuid",
"TArray",
"TArray<uint8>", // For byte arrays
"TMap",
"TSharedPtr<FJsonObject>")
);
supportingFiles.add(new SupportingFile("model-base-header.mustache", publicFolder, modelNamePrefix + "BaseModel.h"));
supportingFiles.add(new SupportingFile("model-base-source.mustache", privateFolder, modelNamePrefix + "BaseModel.cpp"));
supportingFiles.add(new SupportingFile("helpers-header.mustache", publicFolder, modelNamePrefix + "Helpers.h"));
supportingFiles.add(new SupportingFile("helpers-source.mustache", privateFolder, modelNamePrefix + "Helpers.cpp"));
if (optionalProjectFileFlag) {
supportingFiles.add(new SupportingFile("Build.cs.mustache", unrealModuleName + ".Build.cs"));
supportingFiles.add(new SupportingFile("module-header.mustache", privateFolder, unrealModuleName + "Module.h"));
supportingFiles.add(new SupportingFile("module-source.mustache", privateFolder, unrealModuleName + "Module.cpp"));
}
super.typeMapping = new HashMap<String, String>();
// Maps C++ types during call to getSchemaType, see DefaultCodegen.getSchemaType and not the types/formats
// defined in openapi specification "array" is also used explicitly in the generator for containers
typeMapping.clear();
typeMapping.put("integer", "int32");
typeMapping.put("long", "int64");
typeMapping.put("float", "float");
typeMapping.put("number", "double");
typeMapping.put("double", "double");
typeMapping.put("string", "FString");
typeMapping.put("byte", "uint8");
typeMapping.put("binary", "TArray<uint8>");
typeMapping.put("ByteArray", "TArray<uint8>");
typeMapping.put("password", "FString");
typeMapping.put("boolean", "bool");
typeMapping.put("date", "FDateTime");
typeMapping.put("Date", "FDateTime");
typeMapping.put("date-time", "FDateTime");
typeMapping.put("DateTime", "FDateTime");
typeMapping.put("array", "TArray");
typeMapping.put("list", "TArray");
typeMapping.put("map", "TMap");
typeMapping.put("object", "TSharedPtr<FJsonObject>");
typeMapping.put("Object", "TSharedPtr<FJsonObject>");
typeMapping.put("file", "HttpFileInput");
typeMapping.put("UUID", "FGuid");
importMapping = new HashMap<String, String>();
importMapping.put("HttpFileInput", "#include \"" + modelNamePrefix + "Helpers.h\"");
namespaces = new HashMap<String, String>();
}
@Override
public void processOpts() {
super.processOpts();
if (additionalProperties.containsKey("cppNamespace")) {
cppNamespace = (String) additionalProperties.get("cppNamespace");
}
additionalProperties.put("cppNamespaceDeclarations", cppNamespace.split("\\::"));
boolean updateSupportingFiles = false;
if (additionalProperties.containsKey("unrealModuleName")) {
unrealModuleName = (String) additionalProperties.get("unrealModuleName");
additionalProperties().put("dllapi", unrealModuleName.toUpperCase(Locale.ROOT) + "_API");
modelNamePrefix = unrealModuleName;
updateSupportingFiles = true;
}
if (additionalProperties.containsKey("modelNamePrefix")) {
modelNamePrefix = (String) additionalProperties.get("modelNamePrefix");
updateSupportingFiles = true;
}
if (additionalProperties.containsKey(CodegenConstants.OPTIONAL_PROJECT_FILE)) {
setOptionalProjectFileFlag(convertPropertyToBooleanAndWriteBack(CodegenConstants.OPTIONAL_PROJECT_FILE));
} else {
additionalProperties.put(CodegenConstants.OPTIONAL_PROJECT_FILE, optionalProjectFileFlag);
}
if (updateSupportingFiles) {
supportingFiles.clear();
supportingFiles.add(new SupportingFile("model-base-header.mustache", publicFolder, modelNamePrefix + "BaseModel.h"));
supportingFiles.add(new SupportingFile("model-base-source.mustache", privateFolder, modelNamePrefix + "BaseModel.cpp"));
supportingFiles.add(new SupportingFile("helpers-header.mustache", publicFolder, modelNamePrefix + "Helpers.h"));
supportingFiles.add(new SupportingFile("helpers-source.mustache", privateFolder, modelNamePrefix + "Helpers.cpp"));
if (optionalProjectFileFlag) {
supportingFiles.add(new SupportingFile("Build.cs.mustache", unrealModuleName + ".Build.cs"));
supportingFiles.add(new SupportingFile("module-header.mustache", privateFolder, unrealModuleName + "Module.h"));
supportingFiles.add(new SupportingFile("module-source.mustache", privateFolder, unrealModuleName + "Module.cpp"));
}
importMapping.put("HttpFileInput", "#include \"" + modelNamePrefix + "Helpers.h\"");
}
}
public void setOptionalProjectFileFlag(boolean flag) {
this.optionalProjectFileFlag = flag;
}
/**
* Configures the type of generator.
*
* @return the CodegenType for this generator
* @see org.openapitools.codegen.CodegenType
*/
@Override
public CodegenType getTag() {
return CodegenType.CLIENT;
}
/**
* Configures a friendly name for the generator. This will be used by the generator
* to select the library with the -l flag.
*
* @return the friendly name for the generator
*/
@Override
public String getName() {
return "cpp-ue4";
}
/**
* Returns human-friendly help for the generator. Provide the consumer with help
* tips, parameters here
*
* @return A string value for the help message
*/
@Override
public String getHelp() {
return "Generates a Unreal Engine 4 C++ Module (beta).";
}
@Override
public String toModelImport(String name) {
if (namespaces.containsKey(name)) {
return "using " + namespaces.get(name) + ";";
} else if (systemIncludes.contains(name)) {
return "#include <" + name + ">";
}
String folder = modelPackage().replace("::", File.separator);
if (!folder.isEmpty())
folder += File.separator;
return "#include \"" + folder + name + ".h\"";
}
@Override
protected boolean needToImport(String type) {
boolean shouldImport = super.needToImport(type);
if (shouldImport)
return !languageSpecificPrimitives.contains(type);
else
return false;
}
/**
* Escapes a reserved word as defined in the `reservedWords` array. Handle escaping
* those terms here. This logic is only called if a variable matches the reserved words
*
* @return the escaped term
*/
@Override
public String escapeReservedWord(String name) {
if (this.reservedWordsMappings().containsKey(name)) {
return this.reservedWordsMappings().get(name);
}
return "_" + name;
}
/**
* Location to write model files. You can use the modelPackage() as defined when the class is
* instantiated
*/
@Override
public String modelFileFolder() {
return outputFolder + File.separator + modelPackage().replace("::", File.separator);
}
/**
* Location to write api files. You can use the apiPackage() as defined when the class is
* instantiated
*/
@Override
public String apiFileFolder() {
return outputFolder + File.separator + apiPackage().replace("::", File.separator);
}
/*
@Override
public String modelFilename(String templateName, String tag) {
String suffix = modelTemplateFiles().get(templateName);
String folder = privateFolder;
if (suffix == ".h") {
folder = publicFolder;
}
return modelFileFolder() + File.separator + folder + File.separator + toModelFilename(tag) + suffix;
}
*/
@Override
public String toModelFilename(String name) {
name = sanitizeName(name);
return modelNamePrefix + camelize(name);
}
@Override
public String apiFilename(String templateName, String tag) {
String suffix = apiTemplateFiles().get(templateName);
String folder = privateFolder;
if (".h".equals(suffix)) {
folder = publicFolder;
}
if (templateName.startsWith("api-operations")) {
return apiFileFolder() + File.separator + folder + File.separator + toApiFilename(tag) + "Operations" + suffix;
} else {
return apiFileFolder() + File.separator + folder + File.separator + toApiFilename(tag) + suffix;
}
}
@Override
public String toApiFilename(String name) {
name = sanitizeName(name);
return modelNamePrefix + camelize(name) + "Api";
}
/**
* Optional - type declaration. This is a String which is used by the templates to instantiate your
* types. There is typically special handling for different property types
*
* @return a string value used as the `dataType` field for model templates, `returnType` for api templates
*/
@Override
public String getTypeDeclaration(Schema p) {
String openAPIType = getSchemaType(p);
if (ModelUtils.isArraySchema(p)) {
ArraySchema ap = (ArraySchema) p;
String inner = getSchemaType(ap.getItems());
return getSchemaType(p) + "<" + getTypeDeclaration(inner) + ">";
} else if (ModelUtils.isMapSchema(p)) {
String inner = getSchemaType(ModelUtils.getAdditionalProperties(p));
return getSchemaType(p) + "<FString, " + getTypeDeclaration(inner) + ">";
}
if (pointerClasses.contains(openAPIType)) {
return openAPIType + "*";
} else if (languageSpecificPrimitives.contains(openAPIType)) {
return toModelName(openAPIType);
} else {
return openAPIType;
}
}
@Override
public String toDefaultValue(Schema p) {
if (ModelUtils.isStringSchema(p)) {
if (p.getDefault() != null) {
return "TEXT(\"" + p.getDefault().toString() + "\")";
} else {
return null;
}
} else if (ModelUtils.isBooleanSchema(p)) {
if (p.getDefault() != null) {
return p.getDefault().toString();
} else {
return "false";
}
} else if (ModelUtils.isDateSchema(p)) {
return "FDateTime(0)";
} else if (ModelUtils.isDateTimeSchema(p)) {
return "FDateTime(0)";
} else if (ModelUtils.isDoubleSchema(p)) {
if (p.getDefault() != null) {
return p.getDefault().toString();
} else {
return "0.0";
}
} else if (ModelUtils.isFloatSchema(p)) {
if (p.getDefault() != null) {
return p.getDefault().toString();
} else {
return "0.0f";
}
} else if (ModelUtils.isIntegerSchema(p)) {
if (p.getDefault() != null) {
return p.getDefault().toString();
} else {
return "0";
}
} else if (ModelUtils.isLongSchema(p)) {
if (p.getDefault() != null) {
return p.getDefault().toString();
} else {
return "0";
}
}
return null;
}
/**
* Optional - OpenAPI type conversion. This is used to map OpenAPI types in a `Property` into
* either language specific types via `typeMapping` or into complex models if there is not a mapping.
*
* @return a string value of the type or complex model for this property
* @see io.swagger.v3.oas.models.media.Schema
*/
@Override
public String getSchemaType(Schema p) {
String openAPIType = super.getSchemaType(p);
String type = null;
if (typeMapping.containsKey(openAPIType)) {
type = typeMapping.get(openAPIType);
if (languageSpecificPrimitives.contains(type)) {
return toModelName(type);
}
if (pointerClasses.contains(type)) {
return type;
}
} else {
type = openAPIType;
}
return toModelName(type);
}
@Override
public String toModelName(String type) {
if (typeMapping.keySet().contains(type) ||
typeMapping.values().contains(type) ||
importMapping.values().contains(type) ||
defaultIncludes.contains(type) ||
languageSpecificPrimitives.contains(type)) {
return type;
} else {
return modelNamePrefix + camelize(sanitizeName(type), false);
}
}
@Override
public String toVarName(String name) {
// sanitize name
name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
// if it's all uppper case, convert to lower case
if (name.matches("^[A-Z_]*$")) {
name = name.toLowerCase(Locale.ROOT);
}
// for reserved word or word starting with number, append _
if (isReservedWord(name) || name.matches("^\\d.*")) {
name = escapeReservedWord(name);
}
//Unreal variable names are CamelCase
return camelize(name, false);
}
@Override
public String toEnumVarName(String name, String datatype) {
return toVarName(name);
}
@Override
public String toParamName(String name) {
return toVarName(name);
}
@Override
public String toApiName(String type) {
return modelNamePrefix + camelize(type, false) + "Api";
}
@Override
public String escapeQuotationMark(String input) {
// remove " to avoid code injection
return input.replace("\"", "");
}
@Override
public String escapeUnsafeCharacters(String input) {
return input.replace("*/", "*_/").replace("/*", "/_*");
}
public String toBooleanGetter(String name) {
return "Is" + getterAndSetterCapitalize(name);
}
public String toGetter(String name) {
return "Get" + getterAndSetterCapitalize(name);
}
public String toSetter(String name) {
return "Set" + getterAndSetterCapitalize(name);
}
}

View File

@@ -380,9 +380,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen
} else if (JERSEY2.equals(getLibrary())) {
supportingFiles.add(new SupportingFile("JSON.mustache", invokerFolder, "JSON.java"));
supportingFiles.add(new SupportingFile("ApiResponse.mustache", invokerFolder, "ApiResponse.java"));
if (ProcessUtils.hasHttpSignatureMethods(openAPI)) {
supportingFiles.add(new SupportingFile("auth/HttpSignatureAuth.mustache", authFolder, "HttpSignatureAuth.java"));
}
supportingFiles.add(new SupportingFile("auth/HttpSignatureAuth.mustache", authFolder, "HttpSignatureAuth.java"));
supportingFiles.add(new SupportingFile("AbstractOpenApiSchema.mustache", (sourceFolder + File.separator + modelPackage().replace('.', File.separatorChar)).replace('/', File.separatorChar), "AbstractOpenApiSchema.java"));
forceSerializationLibrary(SERIALIZATION_LIBRARY_JACKSON);
} else if (NATIVE.equals(getLibrary())) {

View File

@@ -0,0 +1,479 @@
/*
* Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
* Copyright 2018 SmartBear Software
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openapitools.codegen.languages;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.PathItem.HttpMethod;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.info.Info;
import org.openapitools.codegen.*;
import org.openapitools.codegen.config.GlobalSettings;
import org.openapitools.codegen.meta.GeneratorMetadata;
import org.openapitools.codegen.meta.Stability;
import org.openapitools.codegen.meta.features.*;
import org.openapitools.codegen.utils.URLPathUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.net.URL;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import static org.openapitools.codegen.utils.StringUtils.*;
public class 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";
public static final String SERVER_PORT = "serverPort";
protected String apiVersion = "1.0.0";
protected String projectName = "openapi-server";
protected String defaultServerPort = "8080";
protected boolean googleCloudFunctions;
protected String exportedName;
public NodeJSServerCodegen() {
super();
modifyFeatureSet(features -> features
.includeDocumentationFeatures(DocumentationFeature.Readme)
.wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON))
.securityFeatures(EnumSet.noneOf(SecurityFeature.class))
.excludeGlobalFeatures(
GlobalFeature.XMLStructureDefinitions,
GlobalFeature.Callbacks,
GlobalFeature.LinkObjects,
GlobalFeature.ParameterStyling
)
.excludeSchemaSupportFeatures(
SchemaSupportFeature.Polymorphism
)
.excludeParameterFeatures(
ParameterFeature.Cookie
)
);
// mark the generator as deprecated in the documentation
generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata)
.stability(Stability.DEPRECATED)
.build();
// set the output folder here
outputFolder = "generated-code/nodejs";
/*
* Models. You can write model files using the modelTemplateFiles map.
* if you want to create one template for file, you can do so here.
* for multiple files for model, just put another entry in the `modelTemplateFiles` with
* a different extension
*/
modelTemplateFiles.clear();
/*
* Api classes. You can write classes for each Api file with the apiTemplateFiles map.
* as with models, add multiple entries with different extensions for multiple files per
* class
*/
apiTemplateFiles.put(
"controller.mustache", // the template to use
".js"); // the extension for each file to write
/*
* Template Location. This is the location which templates will be read from. The generator
* will use the resource stream to attempt to read the templates.
*/
embeddedTemplateDir = templateDir = "nodejs";
/*
* Reserved words. Override this with reserved words specific to your language
*/
setReservedWordsLowerCase(
Arrays.asList(
"break", "case", "class", "catch", "const", "continue", "debugger",
"default", "delete", "do", "else", "enum", "export", "extends", "finally",
"for", "function", "if", "import", "in", "instanceof", "let", "new",
"return", "super", "switch", "this", "throw", "try", "typeof", "var",
"void", "while", "with", "yield")
);
/*
* Additional Properties. These values can be passed to the templates and
* are available in models, apis, and supporting files
*/
additionalProperties.put("apiVersion", apiVersion);
additionalProperties.put("implFolder", implFolder);
supportingFiles.add(new SupportingFile("writer.mustache", ("utils").replace(".", File.separator), "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."));
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."));
cliOptions.add(new CliOption(SERVER_PORT,
"TCP port to listen on."));
}
@Override
public String apiPackage() {
return "controllers";
}
/**
* Configures the type of generator.
*
* @return the CodegenType for this generator
* @see org.openapitools.codegen.CodegenType
*/
@Override
public CodegenType getTag() {
return CodegenType.SERVER;
}
/**
* Configures a friendly name for the generator. This will be used by the generator
* to select the library with the -g flag.
*
* @return the friendly name for the generator
*/
@Override
public String getName() {
return "nodejs-server-deprecated";
}
/**
* Returns human-friendly help for the generator. Provide the consumer with help
* tips, parameters here
*
* @return A string value for the help message
*/
@Override
public String getHelp() {
return "[DEPRECATED] Generates a nodejs server library using the swagger-tools project. By default, " +
"it will also generate service classes--which you can disable with the `-Dnoservice` environment variable.";
}
@Override
public String toApiName(String name) {
if (name.length() == 0) {
return "DefaultController";
}
return camelize(name);
}
@Override
public String toApiFilename(String name) {
return toApiName(name);
}
@Override
public String apiFilename(String templateName, String tag) {
String result = super.apiFilename(templateName, tag);
if (templateName.equals("service.mustache")) {
String stringToMatch = File.separator + "controllers" + File.separator;
String replacement = File.separator + implFolder + File.separator;
result = result.replaceAll(Pattern.quote(stringToMatch), replacement);
}
return result;
}
private String implFileFolder(String output) {
return outputFolder + File.separator + output + File.separator + apiPackage().replace('.', File.separatorChar);
}
/**
* Escapes a reserved word as defined in the `reservedWords` array. Handle escaping
* those terms here. This logic is only called if a variable matches the reserved words
*
* @return the escaped term
*/
@Override
public String escapeReservedWord(String name) {
if (this.reservedWordsMappings().containsKey(name)) {
return this.reservedWordsMappings().get(name);
}
return "_" + name;
}
/**
* Location to write api files. You can use the apiPackage() as defined when the class is
* instantiated
*/
@Override
public String apiFileFolder() {
return outputFolder + File.separator + apiPackage().replace('.', File.separatorChar);
}
public boolean getGoogleCloudFunctions() {
return googleCloudFunctions;
}
public void setGoogleCloudFunctions(boolean value) {
googleCloudFunctions = value;
}
public String getExportedName() {
return exportedName;
}
public void setExportedName(String name) {
exportedName = name;
}
@Override
public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> objs, List<Object> allModels) {
@SuppressWarnings("unchecked")
Map<String, Object> objectMap = (Map<String, Object>) objs.get("operations");
@SuppressWarnings("unchecked")
List<CodegenOperation> operations = (List<CodegenOperation>) objectMap.get("operation");
for (CodegenOperation operation : operations) {
operation.httpMethod = operation.httpMethod.toLowerCase(Locale.ROOT);
List<CodegenParameter> params = operation.allParams;
if (params != null && params.size() == 0) {
operation.allParams = null;
}
List<CodegenResponse> responses = operation.responses;
if (responses != null) {
for (CodegenResponse resp : responses) {
if ("0".equals(resp.code)) {
resp.code = "default";
}
}
}
if (operation.examples != null && !operation.examples.isEmpty()) {
// Leave application/json* items only
for (Iterator<Map<String, String>> it = operation.examples.iterator(); it.hasNext(); ) {
final Map<String, String> example = it.next();
final String contentType = example.get("contentType");
if (contentType == null || !contentType.startsWith("application/json")) {
it.remove();
}
}
}
}
return objs;
}
@SuppressWarnings("unchecked")
private static List<Map<String, Object>> getOperations(Map<String, Object> objs) {
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
Map<String, Object> apiInfo = (Map<String, Object>) objs.get("apiInfo");
List<Map<String, Object>> apis = (List<Map<String, Object>>) apiInfo.get("apis");
for (Map<String, Object> api : apis) {
result.add((Map<String, Object>) api.get("operations"));
}
return result;
}
private static List<Map<String, Object>> sortOperationsByPath(List<CodegenOperation> ops) {
Multimap<String, CodegenOperation> opsByPath = ArrayListMultimap.create();
for (CodegenOperation op : ops) {
opsByPath.put(op.path, op);
}
List<Map<String, Object>> opsByPathList = new ArrayList<Map<String, Object>>();
for (Entry<String, Collection<CodegenOperation>> entry : opsByPath.asMap().entrySet()) {
Map<String, Object> opsByPathEntry = new HashMap<String, Object>();
opsByPathList.add(opsByPathEntry);
opsByPathEntry.put("path", entry.getKey());
opsByPathEntry.put("operation", entry.getValue());
List<CodegenOperation> operationsForThisPath = Lists.newArrayList(entry.getValue());
operationsForThisPath.get(operationsForThisPath.size() - 1).hasMore = false;
if (opsByPathList.size() < opsByPath.asMap().size()) {
opsByPathEntry.put("hasMore", "true");
}
}
return opsByPathList;
}
@Override
public void processOpts() {
super.processOpts();
StringBuilder message = new StringBuilder();
message.append(System.lineSeparator()).append(System.lineSeparator())
.append("=======================================================================================")
.append(System.lineSeparator())
.append("IMPORTANT: The nodejs-server generator has been deprecated.")
.append(System.lineSeparator())
.append("Currently, Node.js server doesn't work as its dependency doesn't support OpenAPI Spec3.")
.append(System.lineSeparator())
.append("For further details, see https://github.com/OpenAPITools/openapi-generator/issues/34")
.append(System.lineSeparator())
.append("=======================================================================================")
.append(System.lineSeparator()).append(System.lineSeparator());
LOGGER.warn(message.toString());
if (additionalProperties.containsKey(GOOGLE_CLOUD_FUNCTIONS)) {
setGoogleCloudFunctions(
Boolean.valueOf(additionalProperties.get(GOOGLE_CLOUD_FUNCTIONS).toString()));
}
if (additionalProperties.containsKey(EXPORTED_NAME)) {
setExportedName((String) additionalProperties.get(EXPORTED_NAME));
}
/*
* Supporting Files. You can write single files for the generator with the
* entire object tree available. If the input file has a suffix of `.mustache
* it will be processed by the template engine. Otherwise, it will be copied
*/
// supportingFiles.add(new SupportingFile("controller.mustache",
// "controllers",
// "controller.js")
// );
supportingFiles.add(new SupportingFile("openapi.mustache",
"api",
"openapi.yaml")
);
if (getGoogleCloudFunctions()) {
supportingFiles.add(new SupportingFile("index-gcf.mustache", "", "index.js")
.doNotOverwrite());
} else {
supportingFiles.add(new SupportingFile("index.mustache", "", "index.js")
.doNotOverwrite());
}
supportingFiles.add(new SupportingFile("package.mustache", "", "package.json")
.doNotOverwrite());
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")
.doNotOverwrite());
if (GlobalSettings.getProperty("noservice") == null) {
apiTemplateFiles.put(
"service.mustache", // the template to use
"Service.js"); // the extension for each file to write
}
}
@Override
public void preprocessOpenAPI(OpenAPI openAPI) {
URL url = URLPathUtils.getServerURL(openAPI, serverVariableOverrides());
String host = URLPathUtils.getProtocolAndHost(url);
String port = URLPathUtils.getPort(url, defaultServerPort) ;
String basePath = url.getPath();
if (additionalProperties.containsKey(SERVER_PORT)) {
port = additionalProperties.get(SERVER_PORT).toString();
}
this.additionalProperties.put(SERVER_PORT, port);
if (openAPI.getInfo() != null) {
Info info = openAPI.getInfo();
if (info.getTitle() != null) {
// when info.title is defined, use it for projectName
// used in package.json
projectName = info.getTitle()
.replaceAll("[^a-zA-Z0-9]", "-")
.replaceAll("^[-]*", "")
.replaceAll("[-]*$", "")
.replaceAll("[-]{2,}", "-")
.toLowerCase(Locale.ROOT);
this.additionalProperties.put("projectName", projectName);
}
}
if (getGoogleCloudFunctions()) {
// Note that Cloud Functions don't allow customizing port name, simply checking host
// is good enough.
if (!host.endsWith(".cloudfunctions.net")) {
LOGGER.warn("Host " + host + " seems not matching with cloudfunctions.net URL.");
}
if (!additionalProperties.containsKey(EXPORTED_NAME)) {
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
Paths paths = openAPI.getPaths();
if (paths != null) {
for (String pathname : paths.keySet()) {
PathItem path = paths.get(pathname);
Map<HttpMethod, Operation> operationMap = path.readOperationsMap();
if (operationMap != null) {
for (HttpMethod method : operationMap.keySet()) {
Operation operation = operationMap.get(method);
String tag = "default";
if (operation.getTags() != null && operation.getTags().size() > 0) {
tag = toApiName(operation.getTags().get(0));
}
if (operation.getOperationId() == null) {
operation.setOperationId(getOrGenerateOperationId(operation, pathname, method.toString()));
}
if (operation.getExtensions() == null ||
operation.getExtensions().get("x-swagger-router-controller") == null) {
operation.addExtension("x-swagger-router-controller", sanitizeTag(tag));
}
}
}
}
}
}
@Override
public Map<String, Object> postProcessSupportingFileData(Map<String, Object> objs) {
generateYAMLSpecFile(objs);
for (Map<String, Object> operations : getOperations(objs)) {
@SuppressWarnings("unchecked")
List<CodegenOperation> ops = (List<CodegenOperation>) operations.get("operation");
List<Map<String, Object>> opsByPathList = sortOperationsByPath(ops);
operations.put("operationsByPath", opsByPathList);
}
return super.postProcessSupportingFileData(objs);
}
@Override
public String removeNonNameElementToCamelCase(String name) {
return removeNonNameElementToCamelCase(name, "[-:;#]");
}
@Override
public String escapeUnsafeCharacters(String input) {
return input.replace("*/", "*_/").replace("/*", "/_*");
}
@Override
public String escapeQuotationMark(String input) {
// remove " to avoid code injection
return input.replace("\"", "");
}
}

View File

@@ -0,0 +1,865 @@
/*
* Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
* Copyright 2018 SmartBear Software
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openapitools.codegen.languages;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.NumberSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.*;
import org.openapitools.codegen.meta.GeneratorMetadata;
import org.openapitools.codegen.meta.Stability;
import org.openapitools.codegen.utils.ModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import static org.openapitools.codegen.utils.StringUtils.camelize;
import static org.openapitools.codegen.utils.StringUtils.underscore;
public class TypeScriptClientCodegen extends DefaultCodegen implements CodegenConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(TypeScriptClientCodegen.class);
private static final String X_DISCRIMINATOR_TYPE = "x-discriminator-value";
private static final String UNDEFINED_VALUE = "undefined";
private static final String FRAMEWORK_SWITCH = "framework";
private static final String FRAMEWORK_SWITCH_DESC = "Specify the framework which should be used in the client code.";
private static final String[] FRAMEWORKS = { "fetch-api", "jquery" };
private static final String PLATFORM_SWITCH = "platform";
private static final String PLATFORM_SWITCH_DESC = "Specifies the platform the code should run on. The default is 'node' for the 'request' framework and 'browser' otherwise.";
private static final String[] PLATFORMS = { "browser", "node" };
private static final String FILE_CONTENT_DATA_TYPE= "fileContentDataType";
private static final String FILE_CONTENT_DATA_TYPE_DESC = "Specifies the type to use for the content of a file - i.e. Blob (Browser) / Buffer (node)";
private static final String USE_RXJS_SWITCH = "useRxJS";
private static final String USE_RXJS_SWITCH_DESC = "Enable this to internally use rxjs observables. If disabled, a stub is used instead. This is required for the 'angular' framework.";
private final Map<String, String> frameworkToHttpLibMap;
// NPM Options
private static final String SNAPSHOT = "snapshot";
@SuppressWarnings("squid:S5164")
protected static final ThreadLocal<SimpleDateFormat> SNAPSHOT_SUFFIX_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMddHHmm", Locale.ROOT));
private static final String NPM_REPOSITORY = "npmRepository";
private static final String NPM_NAME = "npmName";
private static final String NPM_VERSION = "npmVersion";
// NPM Option Values
protected String npmRepository = null;
protected String snapshot = null;
protected String npmName = null;
protected String npmVersion = "1.0.0";
protected String modelPropertyNaming = "camelCase";
protected HashSet<String> languageGenericTypes;
public TypeScriptClientCodegen() {
super();
this.frameworkToHttpLibMap = new HashMap<>();
this.frameworkToHttpLibMap.put("fetch-api", "isomorphic-fetch");
this.frameworkToHttpLibMap.put("jquery", "jquery");
this.generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata).stability(Stability.EXPERIMENTAL).build();
// clear import mapping (from default generator) as TS does not use it
// at the moment
importMapping.clear();
outputFolder = "generated-code" + File.separator + "typescript";
embeddedTemplateDir = templateDir = "typescript";
supportsInheritance = true;
// NOTE: TypeScript uses camel cased reserved words, while models are title cased. We don't want lowercase comparisons.
reservedWords.addAll(Arrays.asList(
// local variable names used in API methods (endpoints)
"varLocalPath", "queryParameters", "headerParams", "formParams", "useFormData", "varLocalDeferred",
"requestOptions",
// 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(
"string",
"String",
"boolean",
"Boolean",
"Double",
"Integer",
"Long",
"Float",
"Object",
"Array",
"Date",
"number",
"any",
"File",
"Error",
"Map"
));
languageGenericTypes = new HashSet<String>(Arrays.asList(
"Array"
));
instantiationTypes.put("array", "Array");
typeMapping = new HashMap<String, String>();
typeMapping.put("Array", "Array");
typeMapping.put("array", "Array");
typeMapping.put("List", "Array");
typeMapping.put("boolean", "boolean");
typeMapping.put("string", "string");
typeMapping.put("int", "number");
typeMapping.put("float", "number");
typeMapping.put("number", "number");
typeMapping.put("long", "number");
typeMapping.put("short", "number");
typeMapping.put("char", "string");
typeMapping.put("double", "number");
typeMapping.put("object", "any");
typeMapping.put("integer", "number");
typeMapping.put("Map", "any");
typeMapping.put("date", "string");
typeMapping.put("DateTime", "Date");
typeMapping.put("binary", "any");
// TODO: allow other types for file e.g. Blob
typeMapping.put("File", "any");
typeMapping.put("ByteArray", "string");
typeMapping.put("UUID", "string");
typeMapping.put("Error", "Error");
cliOptions.add(new CliOption(NPM_NAME, "The name under which you want to publish generated npm package." +
" Required to generate a full package"));
cliOptions.add(new CliOption(NPM_VERSION, "The version of your npm package. If not provided, using the version from the OpenAPI specification file.").defaultValue(this.getNpmVersion()));
cliOptions.add(new CliOption(NPM_REPOSITORY, "Use this property to set an url your private npmRepo in the package.json"));
cliOptions.add(CliOption.newBoolean(SNAPSHOT,
"When setting this property to true, the version will be suffixed with -SNAPSHOT." + this.SNAPSHOT_SUFFIX_FORMAT.get().toPattern(),
false));
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"));
cliOptions.add(new CliOption(TypeScriptClientCodegen.FILE_CONTENT_DATA_TYPE, TypeScriptClientCodegen.FILE_CONTENT_DATA_TYPE_DESC).defaultValue("Buffer"));
cliOptions.add(new CliOption(TypeScriptClientCodegen.USE_RXJS_SWITCH, TypeScriptClientCodegen.USE_RXJS_SWITCH_DESC).defaultValue("false"));
CliOption frameworkOption = new CliOption(TypeScriptClientCodegen.FRAMEWORK_SWITCH, TypeScriptClientCodegen.FRAMEWORK_SWITCH_DESC);
for (String option: TypeScriptClientCodegen.FRAMEWORKS) {
// TODO: improve description?
frameworkOption.addEnum(option, option);
}
frameworkOption.defaultValue(FRAMEWORKS[0]);
cliOptions.add(frameworkOption);
CliOption platformOption = new CliOption(TypeScriptClientCodegen.PLATFORM_SWITCH, TypeScriptClientCodegen.PLATFORM_SWITCH_DESC);
for (String option: TypeScriptClientCodegen.PLATFORMS) {
// TODO: improve description?
platformOption.addEnum(option, option);
}
platformOption.defaultValue(PLATFORMS[0]);
cliOptions.add(platformOption);
// TODO: gen package.json?
//Documentation
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
supportingFiles.add(new SupportingFile("package.mustache", "", "package.json"));
supportingFiles.add(new SupportingFile("tsconfig.mustache", "", "tsconfig.json"));
supportingFiles.add(new SupportingFile(".gitignore.mustache", "", ".gitignore"));
supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh"));
// Util
supportingFiles.add(new SupportingFile("util.mustache", "", "util.ts"));
supportingFiles.add(new SupportingFile("api" + File.separator + "exception.mustache", "apis", "exception.ts"));
// http
supportingFiles.add(new SupportingFile("http" + File.separator + "http.mustache", "http", "http.ts"));
supportingFiles.add(new SupportingFile("http" + File.separator + "servers.mustache", "servers.ts"));
supportingFiles.add(new SupportingFile("configuration.mustache", "", "configuration.ts"));
supportingFiles.add(new SupportingFile("auth" + File.separator + "auth.mustache", "auth", "auth.ts"));
supportingFiles.add(new SupportingFile("model" + File.separator + "models_all.mustache", "models", "all.ts"));
// TODO: add supporting files depending on cli parameter e.g. fetch vs angular
supportingFiles.add(new SupportingFile("types" + File.separator + "PromiseAPI.mustache", "types", "PromiseAPI.ts"));
supportingFiles.add(new SupportingFile("types" + File.separator + "ObservableAPI.mustache", "types", "ObservableAPI.ts"));
// models
// TODO: properly set model and api packages
this.setModelPackage("");
supportingFiles.add(new SupportingFile("model" + File.separator + "ObjectSerializer.mustache", "models", "ObjectSerializer.ts"));
modelTemplateFiles.put("model" + File.separator + "model.mustache", ".ts");
// api
this.setApiPackage("");
supportingFiles.add(new SupportingFile("api" + File.separator + "middleware.mustache", "", "middleware.ts"));
this.supportingFiles.add(new SupportingFile("api" + File.separator + "baseapi.mustache", "apis", "baseapi.ts"));
this.apiTemplateFiles.put("api" + File.separator + "api.mustache", ".ts");
}
public String getNpmName() {
return npmName;
}
public void setNpmName(String npmName) {
this.npmName = npmName;
}
public String getNpmRepository() {
return npmRepository;
}
public void setNpmRepository(String npmRepository) {
this.npmRepository = npmRepository;
}
public String getNpmVersion() {
return npmVersion;
}
public void setNpmVersion(String npmVersion) {
this.npmVersion = npmVersion;
}
@Override
public CodegenType getTag() {
return CodegenType.CLIENT;
}
@Override
public void preprocessOpenAPI(OpenAPI openAPI) {
if (additionalProperties.containsKey(NPM_NAME)) {
// If no npmVersion is provided in additional properties, version from API specification is used.
// If none of them is provided then fallbacks to default version
if (additionalProperties.containsKey(NPM_VERSION)) {
this.setNpmVersion(additionalProperties.get(NPM_VERSION).toString());
} else if (openAPI.getInfo() != null && openAPI.getInfo().getVersion() != null) {
this.setNpmVersion(openAPI.getInfo().getVersion());
}
if (additionalProperties.containsKey(SNAPSHOT) && Boolean.parseBoolean(additionalProperties.get(SNAPSHOT).toString())) {
if (npmVersion.toUpperCase(Locale.ROOT).matches("^.*-SNAPSHOT$")) {
this.setNpmVersion(npmVersion + "." + SNAPSHOT_SUFFIX_FORMAT.get().format(new Date()));
} else {
this.setNpmVersion(npmVersion + "-SNAPSHOT." + SNAPSHOT_SUFFIX_FORMAT.get().format(new Date()));
}
}
additionalProperties.put(NPM_VERSION, npmVersion);
}
}
@Override
public Map<String, Object> postProcessSupportingFileData(Map<String, Object> objs) {
final Object propFramework = additionalProperties.get(FRAMEWORK_SWITCH);
Map<String, Boolean> frameworks = new HashMap<>();
for (String framework: FRAMEWORKS) {
frameworks.put(framework, framework.equals(propFramework));
}
objs.put("framework", propFramework);
objs.put("frameworks", frameworks);
objs.put("fileContentDataType", additionalProperties.get(FILE_CONTENT_DATA_TYPE));
return objs;
}
@Override
public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> operations, List<Object> models) {
// Add additional filename information for model imports in the apis
List<Map<String, Object>> imports = (List<Map<String, Object>>) operations.get("imports");
for (Map<String, Object> im : imports) {
im.put("filename", ((String) im.get("import")).replace(".", File.separator));
im.put("classname", getModelnameFromModelFilename(im.get("import").toString()));
}
@SuppressWarnings("unchecked")
Map<String, Object> operationsMap = (Map<String, Object>) operations.get("operations");
List<CodegenOperation> operationList = (List<CodegenOperation>) operationsMap.get("operation");
for (CodegenOperation operation: operationList) {
List<CodegenResponse> responses = operation.responses;
operation.returnType = this.getReturnType(responses);
}
return operations;
}
private String getReturnType(List<CodegenResponse> responses) {
StringBuilder returnType = new StringBuilder();
boolean firstReturnType = true;
boolean atLeastOneSuccess = false;
boolean addVoid = false;
for (CodegenResponse response: responses) {
// TODO: we should probably catch an exception here
if (response.is2xx) {
if (response.dataType != null) {
if (!firstReturnType) {
returnType.append(" | ");
}
returnType.append(response.dataType);
firstReturnType = false;
atLeastOneSuccess = true;
} else {
addVoid = true;
}
}
}
if (!atLeastOneSuccess) {
return null;
} else if (addVoid) {
returnType.append(" | void");
}
return returnType.toString();
}
private String getModelnameFromModelFilename(String filename) {
String name = filename.substring((modelPackage() + File.separator).length());
return camelize(name);
}
@Override
public String escapeReservedWord(String name) {
if (this.reservedWordsMappings().containsKey(name)) {
return this.reservedWordsMappings().get(name);
}
return "_" + name;
}
@Override
public String toParamName(String name) {
// should be the same as variable name
return toVarName(name);
}
@Override
public String toVarName(String name) {
// sanitize name
name = sanitizeName(name);
if ("_".equals(name)) {
name = "_u";
}
// if it's all uppper case, do nothing
if (name.matches("^[A-Z_]*$")) {
return name;
}
name = getNameUsingModelPropertyNaming(name);
// for reserved word or word starting with number, append _
if (isReservedWord(name) || name.matches("^\\d.*")) {
name = escapeReservedWord(name);
}
return name;
}
@Override
public String toModelName(String name) {
name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
if (!StringUtils.isEmpty(modelNamePrefix)) {
name = modelNamePrefix + "_" + name;
}
if (!StringUtils.isEmpty(modelNameSuffix)) {
name = name + "_" + modelNameSuffix;
}
// model name cannot use reserved keyword, e.g. return
if (isReservedWord(name)) {
String modelName = camelize("model_" + name);
LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to " + modelName);
return modelName;
}
// model name starts with number
if (name.matches("^\\d.*")) {
String modelName = camelize("model_" + name); // e.g. 200Response => Model200Response (after camelize)
LOGGER.warn(name + " (model name starts with number) cannot be used as model name. Renamed to " + modelName);
return modelName;
}
if (languageSpecificPrimitives.contains(name)) {
String modelName = camelize("model_" + name);
LOGGER.warn(name + " (model name matches existing language type) cannot be used as a model name. Renamed to " + modelName);
return modelName;
}
// camelize the model name
// phone_number => PhoneNumber
return camelize(name);
}
@Override
public String toModelFilename(String name) {
// should be the same as the model name
return toModelName(name);
}
@Override
protected String getParameterDataType(Parameter parameter, Schema p) {
// handle enums of various data types
Schema inner;
if (ModelUtils.isArraySchema(p)) {
ArraySchema mp1 = (ArraySchema) p;
inner = mp1.getItems();
return this.getSchemaType(p) + "<" + this.getParameterDataType(parameter, inner) + ">";
} else if (ModelUtils.isMapSchema(p)) {
inner = (Schema) p.getAdditionalProperties();
return "{ [key: string]: " + this.getParameterDataType(parameter, inner) + "; }";
} else if (ModelUtils.isStringSchema(p)) {
// Handle string enums
if (p.getEnum() != null) {
return enumValuesToEnumTypeUnion(p.getEnum(), "string");
}
} else if (ModelUtils.isIntegerSchema(p)) {
// Handle integer enums
if (p.getEnum() != null) {
return numericEnumValuesToEnumTypeUnion(new ArrayList<Number>(p.getEnum()));
}
} else if (ModelUtils.isNumberSchema(p)) {
// Handle double enums
if (p.getEnum() != null) {
return numericEnumValuesToEnumTypeUnion(new ArrayList<Number>(p.getEnum()));
}
}
/* TODO revise the logic below
else if (ModelUtils.isDateSchema(p)) {
// Handle date enums
DateSchema sp = (DateSchema) p;
if (sp.getEnum() != null) {
return enumValuesToEnumTypeUnion(sp.getEnum(), "string");
}
} else if (ModelUtils.isDateTimeSchema(p)) {
// Handle datetime enums
DateTimeSchema sp = (DateTimeSchema) p;
if (sp.getEnum() != null) {
return enumValuesToEnumTypeUnion(sp.getEnum(), "string");
}
}*/
return this.getTypeDeclaration(p);
}
/**
* Converts a list of strings to a literal union for representing enum values as a type.
* Example output: 'available' | 'pending' | 'sold'
*
* @param values list of allowed enum values
* @param dataType either "string" or "number"
* @return a literal union for representing enum values as a type
*/
protected String enumValuesToEnumTypeUnion(List<String> values, String dataType) {
StringBuilder b = new StringBuilder();
boolean isFirst = true;
for (String value : values) {
if (!isFirst) {
b.append(" | ");
}
b.append(toEnumValue(value.toString(), dataType));
isFirst = false;
}
return b.toString();
}
/**
* Converts a list of numbers to a literal union for representing enum values as a type.
* Example output: 3 | 9 | 55
*
* @param values a list of numbers
* @return a literal union for representing enum values as a type
*/
protected String numericEnumValuesToEnumTypeUnion(List<Number> values) {
List<String> stringValues = new ArrayList<>();
for (Number value : values) {
stringValues.add(value.toString());
}
return enumValuesToEnumTypeUnion(stringValues, "number");
}
@Override
public String toDefaultValue(Schema p) {
if (ModelUtils.isBooleanSchema(p)) {
return UNDEFINED_VALUE;
} else if (ModelUtils.isDateSchema(p)) {
return UNDEFINED_VALUE;
} else if (ModelUtils.isDateTimeSchema(p)) {
return UNDEFINED_VALUE;
} else if (ModelUtils.isNumberSchema(p)) {
if (p.getDefault() != null) {
return p.getDefault().toString();
}
return UNDEFINED_VALUE;
} else if (ModelUtils.isIntegerSchema(p)) {
if (p.getDefault() != null) {
return p.getDefault().toString();
}
return UNDEFINED_VALUE;
} else if (ModelUtils.isStringSchema(p)) {
if (p.getDefault() != null) {
return "'" + (String) p.getDefault() + "'";
}
return UNDEFINED_VALUE;
} else {
return UNDEFINED_VALUE;
}
}
@Override
protected boolean isReservedWord(String word) {
// NOTE: This differs from super's implementation in that TypeScript does _not_ want case insensitive matching.
return reservedWords.contains(word);
}
@Override
public String getSchemaType(Schema p) {
String openAPIType = super.getSchemaType(p);
String type = null;
if (typeMapping.containsKey(openAPIType)) {
type = typeMapping.get(openAPIType);
if (languageSpecificPrimitives.contains(type))
return type;
} else
type = openAPIType;
return toModelName(type);
}
@Override
public String toOperationId(String operationId) {
// throw exception if method name is empty
if (StringUtils.isEmpty(operationId)) {
throw new RuntimeException("Empty method name (operationId) not allowed");
}
// method name cannot use reserved keyword, e.g. return
// append _ at the beginning, e.g. _return
if (isReservedWord(operationId)) {
return escapeReservedWord(camelize(sanitizeName(operationId), true));
}
return camelize(sanitizeName(operationId), true);
}
public void setModelPropertyNaming(String naming) {
if ("original".equals(naming) || "camelCase".equals(naming) ||
"PascalCase".equals(naming) || "snake_case".equals(naming)) {
this.modelPropertyNaming = naming;
} else {
throw new IllegalArgumentException("Invalid model property naming '" +
naming + "'. Must be 'original', 'camelCase', " +
"'PascalCase' or 'snake_case'");
}
}
public String getModelPropertyNaming() {
return this.modelPropertyNaming;
}
public String getNameUsingModelPropertyNaming(String name) {
switch (CodegenConstants.MODEL_PROPERTY_NAMING_TYPE.valueOf(getModelPropertyNaming())) {
case original:
return name;
case camelCase:
return camelize(name, true);
case PascalCase:
return camelize(name);
case snake_case:
return underscore(name);
default:
throw new IllegalArgumentException("Invalid model property naming '" +
name + "'. Must be 'original', 'camelCase', " +
"'PascalCase' or 'snake_case'");
}
}
@Override
public String toEnumValue(String value, String datatype) {
if ("number".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";
}
// for symbol, e.g. $, #
if (getSymbolName(name) != null) {
return camelize(getSymbolName(name));
}
// number
if ("number".equals(datatype)) {
String varName = "NUMBER_" + name;
varName = varName.replaceAll("-", "MINUS_");
varName = varName.replaceAll("\\+", "PLUS_");
varName = varName.replaceAll("\\.", "_DOT_");
return varName;
}
// string
String enumName = sanitizeName(name);
enumName = enumName.replaceFirst("^_", "");
enumName = enumName.replaceFirst("_$", "");
// camelize the enum variable name
// ref: https://basarat.gitbooks.io/typescript/content/docs/enums.html
enumName = camelize(enumName);
if (enumName.matches("\\d.*")) { // starts with number
return "_" + enumName;
} else {
return enumName;
}
}
@Override
public String toEnumName(CodegenProperty property) {
String enumName = toModelName(property.name) + "Enum";
if (enumName.matches("\\d.*")) { // starts with number
return "_" + enumName;
} else {
return enumName;
}
}
@Override
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
// process enum in models
List<Map<String, Object>> models = (List<Map<String, Object>>) postProcessModelsEnum(objs).get("models");
for (Object _mo : models) {
Map<String, Object> mo = (Map<String, Object>) _mo;
CodegenModel cm = (CodegenModel) mo.get("model");
cm.imports = new TreeSet(cm.imports);
// name enum with model name, e.g. StatusEnum => Pet.StatusEnum
for (CodegenProperty var : cm.vars) {
if (Boolean.TRUE.equals(var.isEnum)) {
var.datatypeWithEnum = var.datatypeWithEnum.replace(var.enumName, cm.classname + var.enumName);
}
}
if (cm.parent != null) {
for (CodegenProperty var : cm.allVars) {
if (Boolean.TRUE.equals(var.isEnum)) {
var.datatypeWithEnum = var.datatypeWithEnum
.replace(var.enumName, cm.classname + var.enumName);
}
}
}
}
for (Map<String, Object> mo : models) {
CodegenModel cm = (CodegenModel) mo.get("model");
// Add additional filename information for imports
mo.put("tsImports", toTsImports(cm, cm.imports));
}
return objs;
}
private List<Map<String, String>> toTsImports(CodegenModel cm, Set<String> imports) {
List<Map<String, String>> tsImports = new ArrayList<>();
for (String im : imports) {
if (!im.equals(cm.classname)) {
HashMap<String, String> tsImport = new HashMap<>();
// TVG: This is used as class name in the import statements of the model file
tsImport.put("classname", im);
tsImport.put("filename", toModelFilename(im));
tsImports.add(tsImport);
}
}
return tsImports;
}
@Override
public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
Map<String, Object> result = super.postProcessAllModels(objs);
for (Map.Entry<String, Object> entry : result.entrySet()) {
Map<String, Object> inner = (Map<String, Object>) entry.getValue();
List<Map<String, Object>> models = (List<Map<String, Object>>) inner.get("models");
for (Map<String, Object> mo : models) {
CodegenModel cm = (CodegenModel) mo.get("model");
if (cm.discriminator != null && cm.children != null) {
for (CodegenModel child : cm.children) {
this.setDiscriminatorValue(child, cm.discriminator.getPropertyName(), this.getDiscriminatorValue(child));
}
}
}
}
return result;
}
private void setDiscriminatorValue(CodegenModel model, String baseName, String value) {
for (CodegenProperty prop : model.allVars) {
if (prop.baseName.equals(baseName)) {
prop.discriminatorValue = value;
}
}
if (model.children != null) {
final boolean newDiscriminator = model.discriminator != null;
for (CodegenModel child : model.children) {
this.setDiscriminatorValue(child, baseName, newDiscriminator ? value : this.getDiscriminatorValue(child));
}
}
}
private String getDiscriminatorValue(CodegenModel model) {
return model.vendorExtensions.containsKey(X_DISCRIMINATOR_TYPE) ?
(String) model.vendorExtensions.get(X_DISCRIMINATOR_TYPE) : model.classname;
}
@Override
public String escapeQuotationMark(String input) {
// remove ', " to avoid code injection
return input.replace("\"", "").replace("'", "");
}
@Override
public String escapeUnsafeCharacters(String input) {
return input.replace("*/", "*_/").replace("/*", "/_*");
}
@Override
public String getName() {
return "typescript";
}
@Override
public String getHelp() {
return "Generates a TypeScript client library using Fetch API (beta).";
}
@Override
public void processOpts() {
super.processOpts();
if (additionalProperties.containsKey(CodegenConstants.MODEL_PROPERTY_NAMING)) {
setModelPropertyNaming((String) additionalProperties.get(CodegenConstants.MODEL_PROPERTY_NAMING));
}
convertPropertyToBooleanAndWriteBack(CodegenConstants.SUPPORTS_ES6);
// change package names
apiPackage = this.apiPackage + ".apis";
modelPackage = this.modelPackage + ".models";
testPackage = this.testPackage + ".tests";
additionalProperties.putIfAbsent(FRAMEWORK_SWITCH, FRAMEWORKS[0]);
supportingFiles.add(new SupportingFile("index.mustache", "index.ts"));
String httpLibName = this.getHttpLibForFramework(additionalProperties.get(FRAMEWORK_SWITCH).toString());
supportingFiles.add(new SupportingFile(
"http" + File.separator + httpLibName + ".mustache",
"http", httpLibName + ".ts"
));
Object propPlatform = additionalProperties.get(PLATFORM_SWITCH);
if (propPlatform == null) {
propPlatform = "browser";
additionalProperties.put("platform", propPlatform);
}
Map<String, Boolean> platforms = new HashMap<>();
for (String platform: PLATFORMS) {
platforms.put(platform, platform.equals(propPlatform));
}
additionalProperties.put("platforms", platforms);
additionalProperties.putIfAbsent(FILE_CONTENT_DATA_TYPE, propPlatform.equals("node") ? "Buffer" : "Blob");
final boolean useRxJS = convertPropertyToBooleanAndWriteBack(USE_RXJS_SWITCH);
if (!useRxJS) {
supportingFiles.add(new SupportingFile("rxjsStub.mustache", "rxjsStub.ts"));
}
// NPM Settings
if (additionalProperties.containsKey(NPM_NAME)) {
setNpmName(additionalProperties.get(NPM_NAME).toString());
}
if (additionalProperties.containsKey(NPM_VERSION)) {
setNpmVersion(additionalProperties.get(NPM_VERSION).toString());
}
if (additionalProperties.containsKey(NPM_REPOSITORY)) {
setNpmRepository(additionalProperties.get(NPM_REPOSITORY).toString());
}
}
private String getHttpLibForFramework(String object) {
return this.frameworkToHttpLibMap.get(object);
}
@Override
public String getTypeDeclaration(Schema p) {
Schema inner;
if (ModelUtils.isArraySchema(p)) {
inner = ((ArraySchema) p).getItems();
return this.getSchemaType(p) + "<" + this.getTypeDeclaration(inner) + ">";
} else if (ModelUtils.isMapSchema(p)) {
inner = (Schema) p.getAdditionalProperties();
return "{ [key: string]: " + this.getTypeDeclaration(inner) + "; }";
} else if (ModelUtils.isFileSchema(p)) {
// TODO: Change type declaration
return "HttpFile";
} else if (ModelUtils.isBinarySchema(p)) {
return "any";
} else {
return super.getTypeDeclaration(p);
}
}
@Override
protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, Schema schema) {
codegenModel.additionalPropertiesType = getTypeDeclaration((Schema) schema.getAdditionalProperties());
addImport(codegenModel, codegenModel.additionalPropertiesType);
}
}

View File

@@ -108,7 +108,7 @@ public class ProcessUtils {
public static boolean hasHttpBearerMethods(List<CodegenSecurity> authMethods) {
if (authMethods != null && !authMethods.isEmpty()) {
for (CodegenSecurity cs : authMethods) {
if (Boolean.TRUE.equals(cs.isBasicBearer)) {
if (Boolean.TRUE.equals(cs.isBasicBasic)) {
return true;
}
}

View File

@@ -53,15 +53,9 @@ import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import {{invokerPackage}}.auth.Authentication;
{{#hasHttpBasicMethods}}
import {{invokerPackage}}.auth.HttpBasicAuth;
{{/hasHttpBasicMethods}}
{{#hasHttpBearerMethods}}
import {{invokerPackage}}.auth.HttpBearerAuth;
{{/hasHttpBearerMethods}}
{{#hasApiKeyMethods}}
import {{invokerPackage}}.auth.ApiKeyAuth;
{{/hasApiKeyMethods}}
{{#hasOAuthMethods}}
import {{invokerPackage}}.auth.OAuth;
{{/hasOAuthMethods}}
@@ -273,24 +267,6 @@ public class ApiClient {
return authentications.get(authName);
}
{{#hasHttpBearerMethods}}
/**
* Helper method to set access token for the first Bearer authentication.
* @param bearerToken Bearer token
*/
public void setBearerToken(String bearerToken) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBearerAuth) {
((HttpBearerAuth) auth).setBearerToken(bearerToken);
return;
}
}
throw new RuntimeException("No Bearer authentication configured!");
}
{{/hasHttpBearerMethods}}
{{#hasHttpBasicMethods}}
/**
* Helper method to set username for the first HTTP basic authentication.
* @param username Username
@@ -319,9 +295,6 @@ public class ApiClient {
throw new RuntimeException("No HTTP basic authentication configured!");
}
{{/hasHttpBasicMethods}}
{{#hasApiKeyMethods}}
/**
* Helper method to set API key value for the first API key authentication.
* @param apiKey the API key
@@ -350,8 +323,6 @@ public class ApiClient {
throw new RuntimeException("No API key authentication configured!");
}
{{/hasApiKeyMethods}}
{{#hasOAuthMethods}}
/**
* Helper method to set access token for the first OAuth2 authentication.
@@ -369,6 +340,20 @@ public class ApiClient {
{{/hasOAuthMethods}}
/**
* Helper method to set access token for the first Bearer authentication.
* @param bearerToken Bearer token
*/
public void setBearerToken(String bearerToken) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBearerAuth) {
((HttpBearerAuth) auth).setBearerToken(bearerToken);
return;
}
}
throw new RuntimeException("No Bearer authentication configured!");
}
/**
* Set the User-Agent header's value (by adding to the default header map).
* @param userAgent User agent

View File

@@ -3,13 +3,10 @@
package {{invokerPackage}}.model;
import {{invokerPackage}}.ApiException;
import java.util.Objects;
import java.lang.reflect.Type;
import java.util.Map;
import javax.ws.rs.core.GenericType;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* Abstract class for oneOf,anyOf schemas defined in OpenAPI spec
*/
@@ -42,7 +39,6 @@ public abstract class AbstractOpenApiSchema {
*
* @return an instance of the actual schema/object
*/
@JsonValue
public Object getActualInstance() {return instance;}
/***
@@ -61,46 +57,6 @@ public abstract class AbstractOpenApiSchema {
return schemaType;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("class ").append(getClass()).append(" {\n");
sb.append(" instance: ").append(toIndentedString(instance)).append("\n");
sb.append(" isNullable: ").append(toIndentedString(isNullable)).append("\n");
sb.append(" schemaType: ").append(toIndentedString(schemaType)).append("\n");
sb.append("}");
return sb.toString();
}
/**
* Convert the given object to string with each line indented by 4 spaces
* (except the first line).
*/
private String toIndentedString(java.lang.Object o) {
if (o == null) {
return "null";
}
return o.toString().replace("\n", "\n ");
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AbstractOpenApiSchema a = (AbstractOpenApiSchema) o;
return Objects.equals(this.instance, a.instance) &&
Objects.equals(this.isNullable, a.isNullable) &&
Objects.equals(this.schemaType, a.schemaType);
}
@Override
public int hashCode() {
return Objects.hash(instance, isNullable, schemaType);
}
/***
* Is nullalble
*

View File

@@ -59,9 +59,7 @@ import java.util.regex.Pattern;
import {{invokerPackage}}.auth.Authentication;
import {{invokerPackage}}.auth.HttpBasicAuth;
import {{invokerPackage}}.auth.HttpBearerAuth;
{{#hasHttpSignatureMethods}}
import {{invokerPackage}}.auth.HttpSignatureAuth;
{{/hasHttpSignatureMethods}}
import {{invokerPackage}}.auth.ApiKeyAuth;
{{#hasOAuthMethods}}
import {{invokerPackage}}.auth.OAuth;

View File

@@ -1,51 +1,11 @@
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
{{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{>xmlAnnotation}}
@JsonDeserialize(using={{classname}}.{{classname}}Deserializer.class)
public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-implements}}, {{{.}}}{{/vendorExtensions.x-implements}} {
public static class {{classname}}Deserializer extends StdDeserializer<{{classname}}> {
public {{classname}}Deserializer() {
this({{classname}}.class);
}
public {{classname}}Deserializer(Class<?> vc) {
super(vc);
}
@Override
public {{classname}} deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode tree = jp.readValueAsTree();
int match = 0;
Object deserialized = null;
{{#oneOf}}
try {
deserialized = tree.traverse(jp.getCodec()).readValueAs({{{.}}}.class);
match++;
} catch (Exception e) {
// deserialization failed, continue
}
{{/oneOf}}
if (match == 1) {
{{classname}} ret = new {{classname}}();
ret.setActualInstance(deserialized);
return ret;
}
throw new IOException(String.format("Failed deserialization for {{classname}}: %d classes match result, expected 1", match));
}
}
// store a list of schema names defined in oneOf
public final static Map<String, GenericType> schemas = new HashMap<String, GenericType>();
@@ -54,13 +14,6 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
super("oneOf", {{#isNullable}}Boolean.TRUE{{/isNullable}}{{^isNullable}}Boolean.FALSE{{/isNullable}});
}
{{#oneOf}}
public {{classname}}({{{.}}} o) {
super("oneOf", {{#isNullable}}Boolean.TRUE{{/isNullable}}{{^isNullable}}Boolean.FALSE{{/isNullable}});
setActualInstance(o);
}
{{/oneOf}}
static {
{{#oneOf}}
schemas.put("{{{.}}}", new GenericType<{{{.}}}>() {

View File

@@ -338,13 +338,13 @@
<version>${commons-io-version}</version>
</dependency>
{{/supportJava6}}
{{#hasHttpSignatureMethods}}
{{#hasHttpBasicMethods}}
<dependency>
<groupId>org.tomitribe</groupId>
<artifactId>tomitribe-http-signatures</artifactId>
<version>${http-signature-version}</version>
</dependency>
{{/hasHttpSignatureMethods}}
{{/hasHttpBasicMethods}}
{{#hasOAuthMethods}}
<dependency>
<groupId>com.github.scribejava</groupId>
@@ -387,9 +387,9 @@
<threetenbp-version>2.9.10</threetenbp-version>
{{/threetenbp}}
<junit-version>4.13</junit-version>
{{#hasHttpSignatureMethods}}
{{#hasHttpBasicMethods}}
<http-signature-version>1.4</http-signature-version>
{{/hasHttpSignatureMethods}}
{{/hasHttpBasicMethods}}
{{#hasOAuthMethods}}
<scribejava-apis-version>6.9.0</scribejava-apis-version>
{{/hasOAuthMethods}}

View File

@@ -66,15 +66,9 @@ import java.util.Map.Entry;
import java.util.TimeZone;
import {{invokerPackage}}.auth.Authentication;
{{#hasHttpBasicMethods}}
import {{invokerPackage}}.auth.HttpBasicAuth;
{{/hasHttpBasicMethods}}
{{#hasHttpBearerMethods}}
import {{invokerPackage}}.auth.HttpBearerAuth;
{{/hasHttpBearerMethods}}
{{#hasApiKeyMethods}}
import {{invokerPackage}}.auth.ApiKeyAuth;
{{/hasApiKeyMethods}}
{{#hasOAuthMethods}}
import {{invokerPackage}}.auth.OAuth;
{{/hasOAuthMethods}}
@@ -176,103 +170,93 @@ public class ApiClient {
return authentications.get(authName);
}
{{#hasHttpBearerMethods}}
/**
* Helper method to set token for HTTP bearer authentication.
* @param bearerToken the token
*/
public void setBearerToken(String bearerToken) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBearerAuth) {
((HttpBearerAuth) auth).setBearerToken(bearerToken);
return;
/**
* Helper method to set token for HTTP bearer authentication.
* @param bearerToken the token
*/
public void setBearerToken(String bearerToken) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBearerAuth) {
((HttpBearerAuth) auth).setBearerToken(bearerToken);
return;
}
}
throw new RuntimeException("No Bearer authentication configured!");
}
throw new RuntimeException("No Bearer authentication configured!");
}
{{/hasHttpBearerMethods}}
{{#hasHttpBasicMethods}}
/**
* Helper method to set username for the first HTTP basic authentication.
* @param username Username
*/
public void setUsername(String username) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBasicAuth) {
((HttpBasicAuth) auth).setUsername(username);
return;
}
/**
* Helper method to set username for the first HTTP basic authentication.
* @param username the username
*/
public void setUsername(String username) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBasicAuth) {
((HttpBasicAuth) auth).setUsername(username);
return;
}
}
throw new RuntimeException("No HTTP basic authentication configured!");
}
throw new RuntimeException("No HTTP basic authentication configured!");
}
/**
* Helper method to set password for the first HTTP basic authentication.
* @param password Password
*/
public void setPassword(String password) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBasicAuth) {
((HttpBasicAuth) auth).setPassword(password);
return;
}
/**
* Helper method to set password for the first HTTP basic authentication.
* @param password the password
*/
public void setPassword(String password) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBasicAuth) {
((HttpBasicAuth) auth).setPassword(password);
return;
}
}
throw new RuntimeException("No HTTP basic authentication configured!");
}
throw new RuntimeException("No HTTP basic authentication configured!");
}
{{/hasHttpBasicMethods}}
{{#hasApiKeyMethods}}
/**
* Helper method to set API key value for the first API key authentication.
* @param apiKey the API key
*/
public void setApiKey(String apiKey) {
for (Authentication auth : authentications.values()) {
if (auth instanceof ApiKeyAuth) {
((ApiKeyAuth) auth).setApiKey(apiKey);
return;
}
/**
* Helper method to set API key value for the first API key authentication.
* @param apiKey the API key
*/
public void setApiKey(String apiKey) {
for (Authentication auth : authentications.values()) {
if (auth instanceof ApiKeyAuth) {
((ApiKeyAuth) auth).setApiKey(apiKey);
return;
}
}
throw new RuntimeException("No API key authentication configured!");
}
throw new RuntimeException("No API key authentication configured!");
}
/**
* Helper method to set API key prefix for the first API key authentication.
* @param apiKeyPrefix API key prefix
*/
public void setApiKeyPrefix(String apiKeyPrefix) {
for (Authentication auth : authentications.values()) {
if (auth instanceof ApiKeyAuth) {
((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix);
return;
}
/**
* Helper method to set API key prefix for the first API key authentication.
* @param apiKeyPrefix the API key prefix
*/
public void setApiKeyPrefix(String apiKeyPrefix) {
for (Authentication auth : authentications.values()) {
if (auth instanceof ApiKeyAuth) {
((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix);
return;
}
}
throw new RuntimeException("No API key authentication configured!");
}
throw new RuntimeException("No API key authentication configured!");
}
{{/hasApiKeyMethods}}
{{#hasOAuthMethods}}
/**
* Helper method to set access token for the first OAuth2 authentication.
* @param accessToken Access token
*/
public void setAccessToken(String accessToken) {
for (Authentication auth : authentications.values()) {
if (auth instanceof OAuth) {
((OAuth) auth).setAccessToken(accessToken);
return;
}
{{#hasOAuthMethods}}
/**
* Helper method to set access token for the first OAuth2 authentication.
* @param accessToken the access token
*/
public void setAccessToken(String accessToken) {
for (Authentication auth : authentications.values()) {
if (auth instanceof OAuth) {
((OAuth) auth).setAccessToken(accessToken);
return;
}
}
throw new RuntimeException("No OAuth2 authentication configured!");
}
throw new RuntimeException("No OAuth2 authentication configured!");
}
{{/hasOAuthMethods}}
/**
{{/hasOAuthMethods}}
/**
* Set the User-Agent header's value (by adding to the default header map).
* @param userAgent the user agent string
* @return ApiClient this client

View File

@@ -16,7 +16,6 @@ org.openapitools.codegen.languages.CppPistacheServerCodegen
org.openapitools.codegen.languages.CppRestbedServerCodegen
org.openapitools.codegen.languages.CppRestSdkClientCodegen
org.openapitools.codegen.languages.CppTizenClientCodegen
org.openapitools.codegen.languages.CppUE4ClientCodegen
org.openapitools.codegen.languages.CSharpClientCodegen
org.openapitools.codegen.languages.CSharpNetCoreClientCodegen
org.openapitools.codegen.languages.CSharpDotNet2ClientCodegen
@@ -72,6 +71,7 @@ org.openapitools.codegen.languages.LuaClientCodegen
org.openapitools.codegen.languages.MarkdownDocumentationCodegen
org.openapitools.codegen.languages.MysqlSchemaCodegen
org.openapitools.codegen.languages.NimClientCodegen
org.openapitools.codegen.languages.NodeJSServerCodegen
org.openapitools.codegen.languages.NodeJSExpressServerCodegen
org.openapitools.codegen.languages.ObjcClientCodegen
org.openapitools.codegen.languages.OCamlClientCodegen
@@ -116,6 +116,7 @@ org.openapitools.codegen.languages.StaticHtmlGenerator
org.openapitools.codegen.languages.StaticHtml2Generator
org.openapitools.codegen.languages.Swift4Codegen
org.openapitools.codegen.languages.Swift5ClientCodegen
org.openapitools.codegen.languages.TypeScriptClientCodegen
org.openapitools.codegen.languages.TypeScriptAngularClientCodegen
org.openapitools.codegen.languages.TypeScriptAngularJsClientCodegen
org.openapitools.codegen.languages.TypeScriptAureliaClientCodegen
@@ -125,4 +126,4 @@ org.openapitools.codegen.languages.TypeScriptInversifyClientCodegen
org.openapitools.codegen.languages.TypeScriptJqueryClientCodegen
org.openapitools.codegen.languages.TypeScriptNodeClientCodegen
org.openapitools.codegen.languages.TypeScriptReduxQueryClientCodegen
org.openapitools.codegen.languages.TypeScriptRxjsClientCodegen
org.openapitools.codegen.languages.TypeScriptRxjsClientCodegen

View File

@@ -1,19 +0,0 @@
{{>licenseInfo}}
using System;
using System.IO;
using UnrealBuildTool;
public class {{unrealModuleName}} : ModuleRules
{
public {{unrealModuleName}}(ReadOnlyTargetRules Target) : base(Target)
{
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"Http",
"Json",
}
);
}
}

View File

@@ -1,42 +0,0 @@
{{>licenseInfo}}
#pragma once
#include "CoreMinimal.h"
#include "{{modelNamePrefix}}BaseModel.h"
{{#cppNamespaceDeclarations}}
namespace {{this}}
{
{{/cppNamespaceDeclarations}}
class {{dllapi}} {{classname}}
{
public:
{{classname}}();
~{{classname}}();
void SetURL(const FString& Url);
void AddHeaderParam(const FString& Key, const FString& Value);
void ClearHeaderParams();
{{#operations}}{{#operation}}class {{operationIdCamelCase}}Request;
class {{operationIdCamelCase}}Response;
{{/operation}}{{/operations}}
{{#operations}}{{#operation}}DECLARE_DELEGATE_OneParam(F{{operationIdCamelCase}}Delegate, const {{operationIdCamelCase}}Response&);
{{/operation}}{{/operations}}
{{#operations}}{{#operation}}{{#description}}/* {{{description}}} */
{{/description}}bool {{operationIdCamelCase}}(const {{operationIdCamelCase}}Request& Request, const F{{operationIdCamelCase}}Delegate& Delegate = F{{operationIdCamelCase}}Delegate()) const;
{{/operation}}{{/operations}}
private:
{{#operations}}{{#operation}}void On{{operationIdCamelCase}}Response(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded, F{{operationIdCamelCase}}Delegate Delegate) const;
{{/operation}}{{/operations}}
bool IsValid() const;
void HandleResponse(FHttpResponsePtr HttpResponse, bool bSucceeded, Response& InOutResponse) const;
FString Url;
TMap<FString,FString> AdditionalHeaderParams;
};
{{#cppNamespaceDeclarations}}
}
{{/cppNamespaceDeclarations}}

View File

@@ -1,64 +0,0 @@
{{>licenseInfo}}
#pragma once
#include "{{modelNamePrefix}}BaseModel.h"
#include "{{classname}}.h"
{{#imports}}{{{import}}}
{{/imports}}
{{#cppNamespaceDeclarations}}
namespace {{this}}
{
{{/cppNamespaceDeclarations}}
{{#operations}}
{{#operation}}
/* {{summary}}
{{#notes}} *
* {{notes}}{{/notes}}
*/
class {{dllapi}} {{classname}}::{{operationIdCamelCase}}Request : public Request
{
public:
virtual ~{{operationIdCamelCase}}Request() {}
void SetupHttpRequest(const TSharedRef<IHttpRequest>& HttpRequest) const final;
FString ComputePath() const final;
{{#allParams}}
{{#isEnum}}
{{#allowableValues}}
enum class {{{enumName}}}
{
{{#enumVars}}
{{name}},
{{/enumVars}}
};
{{/allowableValues}}
{{#description}}/* {{{description}}} */
{{/description}}{{^required}}TOptional<{{/required}}{{{datatypeWithEnum}}}{{^required}}>{{/required}} {{paramName}}{{#required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/required}};
{{/isEnum}}
{{^isEnum}}
{{#description}}/* {{{description}}} */
{{/description}}{{^required}}TOptional<{{/required}}{{{dataType}}}{{^required}}>{{/required}} {{paramName}}{{#required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/required}};
{{/isEnum}}
{{/allParams}}
};
class {{dllapi}} {{classname}}::{{operationIdCamelCase}}Response : public Response
{
public:
virtual ~{{operationIdCamelCase}}Response() {}
{{#responses.0}}
void SetHttpResponseCode(EHttpResponseCodes::Type InHttpResponseCode) final;
{{/responses.0}}
bool FromJson(const TSharedPtr<FJsonValue>& JsonObject) final;
{{#returnType}}{{{returnType}}} Content;{{/returnType}}
};
{{/operation}}
{{/operations}}
{{#cppNamespaceDeclarations}}
}
{{/cppNamespaceDeclarations}}

View File

@@ -1,286 +0,0 @@
{{>licenseInfo}}
#include "{{classname}}Operations.h"
#include "{{unrealModuleName}}Module.h"
#include "{{modelNamePrefix}}Helpers.h"
#include "Dom/JsonObject.h"
#include "Templates/SharedPointer.h"
#include "HttpModule.h"
#include "PlatformHttp.h"
{{#cppNamespaceDeclarations}}
namespace {{this}}
{
{{/cppNamespaceDeclarations}}
{{#operations}}{{#operation}}
{{#allParams}}
{{#isEnum}}
inline FString ToString(const {{classname}}::{{operationIdCamelCase}}Request::{{{enumName}}}& Value)
{
{{#allowableValues}}
switch (Value)
{
{{#enumVars}}
case {{classname}}::{{operationIdCamelCase}}Request::{{{enumName}}}::{{name}}:
return TEXT({{{value}}});
{{/enumVars}}
}
{{/allowableValues}}
UE_LOG(Log{{unrealModuleName}}, Error, TEXT("Invalid {{classname}}::{{operationIdCamelCase}}Request::{{{enumName}}} Value (%d)"), (int)Value);
return TEXT("");
}
inline FStringFormatArg ToStringFormatArg(const {{classname}}::{{operationIdCamelCase}}Request::{{{enumName}}}& Value)
{
return FStringFormatArg(ToString(Value));
}
inline void WriteJsonValue(JsonWriter& Writer, const {{classname}}::{{operationIdCamelCase}}Request::{{{enumName}}}& Value)
{
WriteJsonValue(Writer, ToString(Value));
}
inline bool TryGetJsonValue(const TSharedPtr<FJsonValue>& JsonValue, {{classname}}::{{operationIdCamelCase}}Request::{{{enumName}}}& Value)
{
{{#allowableValues}}
FString TmpValue;
if (JsonValue->TryGetString(TmpValue))
{
static TMap<FString, {{classname}}::{{operationIdCamelCase}}Request::{{{enumName}}}> StringToEnum = { {{#enumVars}}
{ TEXT({{{value}}}), {{classname}}::{{operationIdCamelCase}}Request::{{{enumName}}}::{{name}} },{{/enumVars}} };
const auto Found = StringToEnum.Find(TmpValue);
if(Found)
{
Value = *Found;
return true;
}
}
{{/allowableValues}}
return false;
}
{{/isEnum}}
{{/allParams}}
FString {{classname}}::{{operationIdCamelCase}}Request::ComputePath() const
{
{{^pathParams.0}}
FString Path(TEXT("{{{path}}}"));
{{/pathParams.0}}
{{#pathParams.0}}
TMap<FString, FStringFormatArg> PathParams = { {{#pathParams}}
{ TEXT("{{baseName}}"), ToStringFormatArg({{paramName}}) }{{#hasMore}},{{/hasMore}}{{/pathParams}} };
FString Path = FString::Format(TEXT("{{{path}}}"), PathParams);
{{/pathParams.0}}
{{#queryParams.0}}
TArray<FString> QueryParams;
{{#queryParams}}
{{#required}}
{{^collectionFormat}}
QueryParams.Add(FString(TEXT("{{baseName}}=")) + ToUrlString({{paramName}}));
{{/collectionFormat}}
{{#collectionFormat}}
QueryParams.Add(FString(TEXT("{{baseName}}=")) + CollectionToUrlString_{{collectionFormat}}({{paramName}}, TEXT("{{baseName}}")));
{{/collectionFormat}}
{{/required}}
{{^required}}
{{^collectionFormat}}
if({{paramName}}.IsSet())
{
QueryParams.Add(FString(TEXT("{{baseName}}=")) + ToUrlString({{paramName}}.GetValue()));
}
{{/collectionFormat}}
{{#collectionFormat}}
if({{paramName}}.IsSet())
{
QueryParams.Add(FString(TEXT("{{baseName}}=")) + CollectionToUrlString_{{collectionFormat}}({{paramName}}.GetValue(), TEXT("{{baseName}}")));
}
{{/collectionFormat}}
{{/required}}
{{/queryParams}}
Path += TCHAR('?');
Path += FString::Join(QueryParams, TEXT("&"));
{{/queryParams.0}}
return Path;
}
void {{classname}}::{{operationIdCamelCase}}Request::SetupHttpRequest(const TSharedRef<IHttpRequest>& HttpRequest) const
{
static const TArray<FString> Consumes = { {{#consumes}}TEXT("{{{mediaType}}}"){{#hasMore}}, {{/hasMore}}{{/consumes}} };
//static const TArray<FString> Produces = { {{#produces}}TEXT("{{{mediaType}}}"){{#hasMore}}, {{/hasMore}}{{/produces}} };
HttpRequest->SetVerb(TEXT("{{httpMethod}}"));
{{#headerParams.0}}
// Header parameters
{{#headerParams}}
{{#required}}
HttpRequest->SetHeader(TEXT("{{baseName}}"), {{paramName}});
{{/required}}
{{^required}}
if ({{paramName}}.IsSet())
{
HttpRequest->SetHeader(TEXT("{{baseName}}"), {{paramName}}.GetValue());
}
{{/required}}
{{/headerParams}}
{{/headerParams.0}}
// Default to Json Body request
if (Consumes.Num() == 0 || Consumes.Contains(TEXT("application/json")))
{
{{#bodyParams.0}}
// Body parameters
FString JsonBody;
JsonWriter Writer = TJsonWriterFactory<>::Create(&JsonBody);
Writer->WriteObjectStart();
{{#bodyParams}}
{{#required}}
Writer->WriteIdentifierPrefix(TEXT("{{baseName}}")); WriteJsonValue(Writer, {{paramName}});
{{/required}}
{{^required}}
if ({{paramName}}.IsSet())
{
Writer->WriteIdentifierPrefix(TEXT("{{baseName}}")); WriteJsonValue(Writer, {{paramName}}.GetValue());
}
{{/required}}
{{/bodyParams}}
Writer->WriteObjectEnd();
Writer->Close();
HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json; charset=utf-8"));
HttpRequest->SetContentAsString(JsonBody);
{{/bodyParams.0}}
{{#formParams.0}}
{{#formParams}}
UE_LOG(Log{{unrealModuleName}}, Error, TEXT("Form parameter ({{baseName}}) was ignored, cannot be used in JsonBody"));
{{/formParams}}
{{/formParams.0}}
}
else if (Consumes.Contains(TEXT("multipart/form-data")))
{
{{#formParams.0}}
HttpMultipartFormData FormData;
{{#formParams}}
{{#isContainer}}
UE_LOG(Log{{unrealModuleName}}, Error, TEXT("Form parameter ({{baseName}}) was ignored, Collections are not supported in multipart form"));
{{/isContainer}}
{{^isContainer}}
{{#required}}
{{#isFile}}
FormData.AddFilePart(TEXT("{{baseName}}"), {{paramName}});
{{/isFile}}
{{#isBinary}}
FormData.AddBinaryPart(TEXT("{{baseName}}"), {{paramName}});
{{/isBinary}}
{{#isBinary}}
{{^isFile}}
FormData.AddStringPart(TEXT("{{baseName}}"), *ToUrlString({{paramName}}));
{{/isFile}}
{{/isBinary}}
{{/required}}
{{^required}}
if({{paramName}}.IsSet())
{
{{#isFile}}
FormData.AddFilePart(TEXT("{{baseName}}"), {{paramName}}.GetValue());
{{/isFile}}
{{#isBinary}}
FormData.AddBinaryPart(TEXT("{{baseName}}"), {{paramName}}.GetValue());
{{/isBinary}}
{{^isBinary}}
{{^isFile}}
FormData.AddStringPart(TEXT("{{baseName}}"), *ToUrlString({{paramName}}.GetValue()));
{{/isFile}}
{{/isBinary}}
}
{{/required}}
{{/isContainer}}
{{/formParams}}
FormData.SetupHttpRequest(HttpRequest);
{{/formParams.0}}
{{#bodyParams.0}}
{{#bodyParams}}
UE_LOG(Log{{unrealModuleName}}, Error, TEXT("Body parameter ({{baseName}}) was ignored, not supported in multipart form"));
{{/bodyParams}}
{{/bodyParams.0}}
}
else if (Consumes.Contains(TEXT("application/x-www-form-urlencoded")))
{
{{#formParams.0}}
TArray<FString> FormParams;
{{#formParams}}
{{#isContainer}}
UE_LOG(Log{{unrealModuleName}}, Error, TEXT("Form parameter ({{baseName}}) was ignored, Collections are not supported in urlencoded requests"));
{{/isContainer}}
{{#isFile}}
UE_LOG(Log{{unrealModuleName}}, Error, TEXT("Form parameter ({{baseName}}) was ignored, Files are not supported in urlencoded requests"));
{{/isFile}}
{{^isFile}}
{{^isContainer}}
{{#required}}
FormParams.Add(FString(TEXT("{{baseName}}=")) + ToUrlString({{paramName}}));
{{/required}}
{{^required}}
if({{paramName}}.IsSet())
{
FormParams.Add(FString(TEXT("{{baseName}}=")) + ToUrlString({{paramName}}.GetValue()));
}
{{/required}}
{{/isContainer}}
{{/isFile}}
{{/formParams}}
HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/x-www-form-urlencoded; charset=utf-8"));
HttpRequest->SetContentAsString(FString::Join(FormParams, TEXT("&")));
{{/formParams.0}}
{{#bodyParams.0}}
{{#bodyParams}}
UE_LOG(Log{{unrealModuleName}}, Error, TEXT("Body parameter ({{baseName}}) was ignored, not supported in urlencoded requests"));
{{/bodyParams}}
{{/bodyParams.0}}
}
else
{
UE_LOG(Log{{unrealModuleName}}, Error, TEXT("Request ContentType not supported (%s)"), *FString::Join(Consumes, TEXT(",")));
}
}
{{#responses.0}}
void {{classname}}::{{operationIdCamelCase}}Response::SetHttpResponseCode(EHttpResponseCodes::Type InHttpResponseCode)
{
Response::SetHttpResponseCode(InHttpResponseCode);
switch ((int)InHttpResponseCode)
{
{{#responses}}
case {{code}}:
{{#isDefault}}
default:
{{/isDefault}}
SetResponseString(TEXT("{{message}}"));
break;
{{/responses}}
}
}
{{/responses.0}}
bool {{classname}}::{{operationIdCamelCase}}Response::FromJson(const TSharedPtr<FJsonValue>& JsonValue)
{
{{#returnType}}
return TryGetJsonValue(JsonValue, Content);
{{/returnType}}
{{^returnType}}
return true;
{{/returnType}}
}
{{/operation}}{{/operations}}
{{#cppNamespaceDeclarations}}
}
{{/cppNamespaceDeclarations}}

View File

@@ -1,120 +0,0 @@
{{>licenseInfo}}
#include "{{classname}}.h"
#include "{{classname}}Operations.h"
#include "{{unrealModuleName}}Module.h"
#include "HttpModule.h"
#include "Serialization/JsonSerializer.h"
{{#cppNamespaceDeclarations}}
namespace {{this}}
{
{{/cppNamespaceDeclarations}}
{{classname}}::{{classname}}()
: Url(TEXT("{{basePath}}"))
{
}
{{classname}}::~{{classname}}() {}
void {{classname}}::SetURL(const FString& InUrl)
{
Url = InUrl;
}
void {{classname}}::AddHeaderParam(const FString& Key, const FString& Value)
{
AdditionalHeaderParams.Add(Key, Value);
}
void {{classname}}::ClearHeaderParams()
{
AdditionalHeaderParams.Reset();
}
bool {{classname}}::IsValid() const
{
if (Url.IsEmpty())
{
UE_LOG(Log{{unrealModuleName}}, Error, TEXT("{{classname}}: Endpoint Url is not set, request cannot be performed"));
return false;
}
return true;
}
void {{classname}}::HandleResponse(FHttpResponsePtr HttpResponse, bool bSucceeded, Response& InOutResponse) const
{
InOutResponse.SetHttpResponse(HttpResponse);
InOutResponse.SetSuccessful(bSucceeded);
if (bSucceeded && HttpResponse.IsValid())
{
InOutResponse.SetHttpResponseCode((EHttpResponseCodes::Type)HttpResponse->GetResponseCode());
FString ContentType = HttpResponse->GetContentType();
FString Content;
if (ContentType == TEXT("application/json"))
{
Content = HttpResponse->GetContentAsString();
TSharedPtr<FJsonValue> JsonValue;
auto Reader = TJsonReaderFactory<>::Create(Content);
if (FJsonSerializer::Deserialize(Reader, JsonValue) && JsonValue.IsValid())
{
if (InOutResponse.FromJson(JsonValue))
return; // Successfully parsed
}
}
else if(ContentType == TEXT("text/plain"))
{
Content = HttpResponse->GetContentAsString();
InOutResponse.SetResponseString(Content);
return; // Successfully parsed
}
// Report the parse error but do not mark the request as unsuccessful. Data could be partial or malformed, but the request succeeded.
UE_LOG(Log{{unrealModuleName}}, Error, TEXT("Failed to deserialize Http response content (type:%s):\n%s"), *ContentType , *Content);
return;
}
// By default, assume we failed to establish connection
InOutResponse.SetHttpResponseCode(EHttpResponseCodes::RequestTimeout);
}
{{#operations}}
{{#operation}}
bool {{classname}}::{{operationIdCamelCase}}(const {{operationIdCamelCase}}Request& Request, const F{{operationIdCamelCase}}Delegate& Delegate /*= F{{operationIdCamelCase}}Delegate()*/) const
{
if (!IsValid())
return false;
TSharedRef<IHttpRequest> HttpRequest = FHttpModule::Get().CreateRequest();
HttpRequest->SetURL(*(Url + Request.ComputePath()));
for(const auto& It : AdditionalHeaderParams)
{
HttpRequest->SetHeader(It.Key, It.Value);
}
Request.SetupHttpRequest(HttpRequest);
HttpRequest->OnProcessRequestComplete().BindRaw(this, &{{classname}}::On{{operationIdCamelCase}}Response, Delegate);
return HttpRequest->ProcessRequest();
}
void {{classname}}::On{{operationIdCamelCase}}Response(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded, F{{operationIdCamelCase}}Delegate Delegate) const
{
{{operationIdCamelCase}}Response Response;
HandleResponse(HttpResponse, bSucceeded, Response);
Delegate.ExecuteIfBound(Response);
}
{{/operation}}
{{/operations}}
{{#cppNamespaceDeclarations}}
}
{{/cppNamespaceDeclarations}}

View File

@@ -1,405 +0,0 @@
{{>licenseInfo}}
#pragma once
#include "{{modelNamePrefix}}BaseModel.h"
#include "Serialization/JsonSerializer.h"
#include "Dom/JsonObject.h"
#include "Misc/Base64.h"
class IHttpRequest;
{{#cppNamespaceDeclarations}}
namespace {{this}}
{
{{/cppNamespaceDeclarations}}
typedef TSharedRef<TJsonWriter<>> JsonWriter;
//////////////////////////////////////////////////////////////////////////
class {{dllapi}} HttpFileInput
{
public:
HttpFileInput(const TCHAR* InFilePath);
HttpFileInput(const FString& InFilePath);
// This will automatically set the content type if not already set
void SetFilePath(const TCHAR* InFilePath);
void SetFilePath(const FString& InFilePath);
// Optional if it can be deduced from the FilePath
void SetContentType(const TCHAR* ContentType);
HttpFileInput& operator=(const HttpFileInput& Other) = default;
HttpFileInput& operator=(const FString& InFilePath) { SetFilePath(*InFilePath); return*this; }
HttpFileInput& operator=(const TCHAR* InFilePath) { SetFilePath(InFilePath); return*this; }
const FString& GetFilePath() const { return FilePath; }
const FString& GetContentType() const { return ContentType; }
// Returns the filename with extension
FString GetFilename() const;
private:
FString FilePath;
FString ContentType;
};
//////////////////////////////////////////////////////////////////////////
class HttpMultipartFormData
{
public:
void SetBoundary(const TCHAR* InBoundary);
void SetupHttpRequest(const TSharedRef<IHttpRequest>& HttpRequest);
void AddStringPart(const TCHAR* Name, const TCHAR* Data);
void AddJsonPart(const TCHAR* Name, const FString& JsonString);
void AddBinaryPart(const TCHAR* Name, const TArray<uint8>& ByteArray);
void AddFilePart(const TCHAR* Name, const HttpFileInput& File);
private:
void AppendString(const TCHAR* Str);
const FString& GetBoundary() const;
mutable FString Boundary;
TArray<uint8> FormData;
static const TCHAR* Delimiter;
static const TCHAR* Newline;
};
//////////////////////////////////////////////////////////////////////////
// Decodes Base64Url encoded strings, see https://en.wikipedia.org/wiki/Base64#Variants_summary_table
template<typename T>
bool Base64UrlDecode(const FString& Base64String, T& Value)
{
FString TmpCopy(Base64String);
TmpCopy.ReplaceInline(TEXT("-"), TEXT("+"));
TmpCopy.ReplaceInline(TEXT("_"), TEXT("/"));
return FBase64::Decode(TmpCopy, Value);
}
// Encodes strings in Base64Url, see https://en.wikipedia.org/wiki/Base64#Variants_summary_table
template<typename T>
FString Base64UrlEncode(const T& Value)
{
FString Base64String = FBase64::Encode(Value);
Base64String.ReplaceInline(TEXT("+"), TEXT("-"));
Base64String.ReplaceInline(TEXT("/"), TEXT("_"));
return Base64String;
}
template<typename T>
inline FStringFormatArg ToStringFormatArg(const T& Value)
{
return FStringFormatArg(Value);
}
inline FStringFormatArg ToStringFormatArg(const FDateTime& Value)
{
return FStringFormatArg(Value.ToIso8601());
}
inline FStringFormatArg ToStringFormatArg(const TArray<uint8>& Value)
{
return FStringFormatArg(Base64UrlEncode(Value));
}
template<typename T, typename std::enable_if<!std::is_base_of<Model, T>::value, int>::type = 0>
inline FString ToString(const T& Value)
{
return FString::Format(TEXT("{0}"), { ToStringFormatArg(Value) });
}
inline FString ToString(const FString& Value)
{
return Value;
}
inline FString ToString(const TArray<uint8>& Value)
{
return Base64UrlEncode(Value);
}
inline FString ToString(const Model& Value)
{
FString String;
JsonWriter Writer = TJsonWriterFactory<>::Create(&String);
Value.WriteJson(Writer);
Writer->Close();
return String;
}
template<typename T>
inline FString ToUrlString(const T& Value)
{
return FPlatformHttp::UrlEncode(ToString(Value));
}
template<typename T>
inline FString CollectionToUrlString(const TArray<T>& Collection, const TCHAR* Separator)
{
FString Output;
if(Collection.Num() == 0)
return Output;
Output += ToUrlString(Collection[0]);
for(int i = 1; i < Collection.Num(); i++)
{
Output += FString::Format(TEXT("{0}{1}"), { Separator, *ToUrlString(Collection[i]) });
}
return Output;
}
template<typename T>
inline FString CollectionToUrlString_csv(const TArray<T>& Collection, const TCHAR* BaseName)
{
return CollectionToUrlString(Collection, TEXT(","));
}
template<typename T>
inline FString CollectionToUrlString_ssv(const TArray<T>& Collection, const TCHAR* BaseName)
{
return CollectionToUrlString(Collection, TEXT(" "));
}
template<typename T>
inline FString CollectionToUrlString_tsv(const TArray<T>& Collection, const TCHAR* BaseName)
{
return CollectionToUrlString(Collection, TEXT("\t"));
}
template<typename T>
inline FString CollectionToUrlString_pipes(const TArray<T>& Collection, const TCHAR* BaseName)
{
return CollectionToUrlString(Collection, TEXT("|"));
}
template<typename T>
inline FString CollectionToUrlString_multi(const TArray<T>& Collection, const TCHAR* BaseName)
{
FString Output;
if(Collection.Num() == 0)
return Output;
Output += FString::Format(TEXT("{0}={1}"), { FStringFormatArg(BaseName), ToUrlString(Collection[0]) });
for(int i = 1; i < Collection.Num(); i++)
{
Output += FString::Format(TEXT("&{0}={1}"), { FStringFormatArg(BaseName), ToUrlString(Collection[i]) });
}
return Output;
}
//////////////////////////////////////////////////////////////////////////
template<typename T, typename std::enable_if<!std::is_base_of<Model, T>::value, int>::type = 0>
inline void WriteJsonValue(JsonWriter& Writer, const T& Value)
{
Writer->WriteValue(Value);
}
inline void WriteJsonValue(JsonWriter& Writer, const FDateTime& Value)
{
Writer->WriteValue(Value.ToIso8601());
}
inline void WriteJsonValue(JsonWriter& Writer, const Model& Value)
{
Value.WriteJson(Writer);
}
template<typename T>
inline void WriteJsonValue(JsonWriter& Writer, const TArray<T>& Value)
{
Writer->WriteArrayStart();
for (const auto& Element : Value)
{
WriteJsonValue(Writer, Element);
}
Writer->WriteArrayEnd();
}
template<typename T>
inline void WriteJsonValue(JsonWriter& Writer, const TMap<FString, T>& Value)
{
Writer->WriteObjectStart();
for (const auto& It : Value)
{
Writer->WriteIdentifierPrefix(It.Key);
WriteJsonValue(Writer, It.Value);
}
Writer->WriteObjectEnd();
}
inline void WriteJsonValue(JsonWriter& Writer, const TSharedPtr<FJsonObject>& Value)
{
if (Value.IsValid())
{
FJsonSerializer::Serialize(Value.ToSharedRef(), Writer, false);
}
else
{
Writer->WriteObjectStart();
Writer->WriteObjectEnd();
}
}
inline void WriteJsonValue(JsonWriter& Writer, const TArray<uint8>& Value)
{
Writer->WriteValue(ToString(Value));
}
//////////////////////////////////////////////////////////////////////////
template<typename T>
inline bool TryGetJsonValue(const TSharedPtr<FJsonObject>& JsonObject, const FString& Key, T& Value)
{
const TSharedPtr<FJsonValue> JsonValue = JsonObject->TryGetField(Key);
if (JsonValue.IsValid() && !JsonValue->IsNull())
{
return TryGetJsonValue(JsonValue, Value);
}
return false;
}
template<typename T>
inline bool TryGetJsonValue(const TSharedPtr<FJsonObject>& JsonObject, const FString& Key, TOptional<T>& OptionalValue)
{
if(JsonObject->HasField(Key))
{
T Value;
if (TryGetJsonValue(JsonObject, Key, Value))
{
OptionalValue = Value;
return true;
}
else
return false;
}
return true; // Absence of optional value is not a parsing error
}
inline bool TryGetJsonValue(const TSharedPtr<FJsonValue>& JsonValue, FString& Value)
{
FString TmpValue;
if (JsonValue->TryGetString(TmpValue))
{
Value = TmpValue;
return true;
}
else
return false;
}
inline bool TryGetJsonValue(const TSharedPtr<FJsonValue>& JsonValue, FDateTime& Value)
{
FString TmpValue;
if (JsonValue->TryGetString(TmpValue))
return FDateTime::Parse(TmpValue, Value);
else
return false;
}
inline bool TryGetJsonValue(const TSharedPtr<FJsonValue>& JsonValue, bool& Value)
{
bool TmpValue;
if (JsonValue->TryGetBool(TmpValue))
{
Value = TmpValue;
return true;
}
else
return false;
}
template<typename T, typename std::enable_if<!std::is_base_of<Model, T>::value, int>::type = 0>
inline bool TryGetJsonValue(const TSharedPtr<FJsonValue>& JsonValue, T& Value)
{
T TmpValue;
if (JsonValue->TryGetNumber(TmpValue))
{
Value = TmpValue;
return true;
}
else
return false;
}
inline bool TryGetJsonValue(const TSharedPtr<FJsonValue>& JsonValue, Model& Value)
{
const TSharedPtr<FJsonObject>* Object;
if (JsonValue->TryGetObject(Object))
return Value.FromJson(*Object);
else
return false;
}
template<typename T>
inline bool TryGetJsonValue(const TSharedPtr<FJsonValue>& JsonValue, TArray<T>& ArrayValue)
{
const TArray<TSharedPtr<FJsonValue>>* JsonArray;
if (JsonValue->TryGetArray(JsonArray))
{
bool ParseSuccess = true;
const int32 Count = JsonArray->Num();
ArrayValue.Reset(Count);
for (int i = 0; i < Count; i++)
{
T TmpValue;
ParseSuccess &= TryGetJsonValue((*JsonArray)[i], TmpValue);
ArrayValue.Emplace(MoveTemp(TmpValue));
}
return ParseSuccess;
}
return false;
}
template<typename T>
inline bool TryGetJsonValue(const TSharedPtr<FJsonValue>& JsonValue, TMap<FString, T>& MapValue)
{
const TSharedPtr<FJsonObject>* Object;
if (JsonValue->TryGetObject(Object))
{
MapValue.Reset();
bool ParseSuccess = true;
for (const auto& It : (*Object)->Values)
{
T TmpValue;
ParseSuccess &= TryGetJsonValue(It.Value, TmpValue);
MapValue.Emplace(It.Key, MoveTemp(TmpValue));
}
return ParseSuccess;
}
return false;
}
inline bool TryGetJsonValue(const TSharedPtr<FJsonValue>& JsonValue, TSharedPtr<FJsonObject>& JsonObjectValue)
{
const TSharedPtr<FJsonObject>* Object;
if (JsonValue->TryGetObject(Object))
{
JsonObjectValue = *Object;
return true;
}
return false;
}
inline bool TryGetJsonValue(const TSharedPtr<FJsonValue>& JsonValue, TArray<uint8>& Value)
{
FString TmpValue;
if (JsonValue->TryGetString(TmpValue))
{
Base64UrlDecode(TmpValue, Value);
return true;
}
else
return false;
}
{{#cppNamespaceDeclarations}}
}
{{/cppNamespaceDeclarations}}

View File

@@ -1,187 +0,0 @@
{{>licenseInfo}}
#include "{{modelNamePrefix}}Helpers.h"
#include "{{unrealModuleName}}Module.h"
#include "Interfaces/IHttpRequest.h"
#include "PlatformHttp.h"
#include "Misc/FileHelper.h"
{{#cppNamespaceDeclarations}}
namespace {{this}}
{
{{/cppNamespaceDeclarations}}
HttpFileInput::HttpFileInput(const TCHAR* InFilePath)
{
SetFilePath(InFilePath);
}
HttpFileInput::HttpFileInput(const FString& InFilePath)
{
SetFilePath(InFilePath);
}
void HttpFileInput::SetFilePath(const TCHAR* InFilePath)
{
FilePath = InFilePath;
if(ContentType.IsEmpty())
{
ContentType = FPlatformHttp::GetMimeType(InFilePath);
}
}
void HttpFileInput::SetFilePath(const FString& InFilePath)
{
SetFilePath(*InFilePath);
}
void HttpFileInput::SetContentType(const TCHAR* InContentType)
{
ContentType = InContentType;
}
FString HttpFileInput::GetFilename() const
{
return FPaths::GetCleanFilename(FilePath);
}
//////////////////////////////////////////////////////////////////////////
const TCHAR* HttpMultipartFormData::Delimiter = TEXT("--");
const TCHAR* HttpMultipartFormData::Newline = TEXT("\r\n");
void HttpMultipartFormData::SetBoundary(const TCHAR* InBoundary)
{
checkf(Boundary.IsEmpty(), TEXT("Boundary must be set before usage"));
Boundary = InBoundary;
}
const FString& HttpMultipartFormData::GetBoundary() const
{
if (Boundary.IsEmpty())
{
// Generate a random boundary with enough entropy, should avoid occurences of the boundary in the data.
// Since the boundary is generated at every request, in case of failure, retries should succeed.
Boundary = FGuid::NewGuid().ToString(EGuidFormats::Short);
}
return Boundary;
}
void HttpMultipartFormData::SetupHttpRequest(const TSharedRef<IHttpRequest>& HttpRequest)
{
if(HttpRequest->GetVerb() != TEXT("POST"))
{
UE_LOG(Log{{unrealModuleName}}, Error, TEXT("Expected POST verb when using multipart form data"));
}
// Append final boundary
AppendString(Delimiter);
AppendString(*GetBoundary());
AppendString(Delimiter);
HttpRequest->SetHeader("Content-Type", FString::Printf(TEXT("multipart/form-data; boundary=%s"), *GetBoundary()));
HttpRequest->SetContent(FormData);
}
void HttpMultipartFormData::AddStringPart(const TCHAR* Name, const TCHAR* Data)
{
// Add boundary
AppendString(Delimiter);
AppendString(*GetBoundary());
AppendString(Newline);
// Add header
AppendString(*FString::Printf(TEXT("Content-Disposition: form-data; name = \"%s\""), Name));
AppendString(Newline);
AppendString(*FString::Printf(TEXT("Content-Type: text/plain; charset=utf-8")));
AppendString(Newline);
// Add header to body splitter
AppendString(Newline);
// Add Data
AppendString(Data);
AppendString(Newline);
}
void HttpMultipartFormData::AddJsonPart(const TCHAR* Name, const FString& JsonString)
{
// Add boundary
AppendString(Delimiter);
AppendString(*GetBoundary());
AppendString(Newline);
// Add header
AppendString(*FString::Printf(TEXT("Content-Disposition: form-data; name=\"%s\""), Name));
AppendString(Newline);
AppendString(*FString::Printf(TEXT("Content-Type: application/json; charset=utf-8")));
AppendString(Newline);
// Add header to body splitter
AppendString(Newline);
// Add Data
AppendString(*JsonString);
AppendString(Newline);
}
void HttpMultipartFormData::AddBinaryPart(const TCHAR* Name, const TArray<uint8>& ByteArray)
{
// Add boundary
AppendString(Delimiter);
AppendString(*GetBoundary());
AppendString(Newline);
// Add header
AppendString(*FString::Printf(TEXT("Content-Disposition: form-data; name=\"%s\""), Name));
AppendString(Newline);
AppendString(*FString::Printf(TEXT("Content-Type: application/octet-stream")));
AppendString(Newline);
// Add header to body splitter
AppendString(Newline);
// Add Data
FormData.Append(ByteArray);
AppendString(Newline);
}
void HttpMultipartFormData::AddFilePart(const TCHAR* Name, const HttpFileInput& File)
{
TArray<uint8> FileContents;
if (!FFileHelper::LoadFileToArray(FileContents, *File.GetFilePath()))
{
UE_LOG(Log{{unrealModuleName}}, Error, TEXT("Failed to load file (%s)"), *File.GetFilePath());
return;
}
// Add boundary
AppendString(Delimiter);
AppendString(*GetBoundary());
AppendString(Newline);
// Add header
AppendString(*FString::Printf(TEXT("Content-Disposition: form-data; name=\"%s\"; filename=\"%s\""), Name, *File.GetFilename()));
AppendString(Newline);
AppendString(*FString::Printf(TEXT("Content-Type: %s"), *File.GetContentType()));
AppendString(Newline);
// Add header to body splitter
AppendString(Newline);
// Add Data
FormData.Append(FileContents);
AppendString(Newline);
}
void HttpMultipartFormData::AppendString(const TCHAR* Str)
{
FTCHARToUTF8 utf8Str(Str);
FormData.Append((uint8*)utf8Str.Get(), utf8Str.Length());
}
{{#cppNamespaceDeclarations}}
}
{{/cppNamespaceDeclarations}}

View File

@@ -1,59 +0,0 @@
{{>licenseInfo}}
#pragma once
#include "Interfaces/IHttpRequest.h"
#include "Interfaces/IHttpResponse.h"
#include "Serialization/JsonWriter.h"
#include "Dom/JsonObject.h"
{{#cppNamespaceDeclarations}}
namespace {{this}}
{
{{/cppNamespaceDeclarations}}
typedef TSharedRef<TJsonWriter<>> JsonWriter;
class {{dllapi}} Model
{
public:
virtual ~Model() {}
virtual void WriteJson(JsonWriter& Writer) const = 0;
virtual bool FromJson(const TSharedPtr<FJsonObject>& JsonObject) = 0;
};
class {{dllapi}} Request
{
public:
virtual ~Request() {}
virtual void SetupHttpRequest(const TSharedRef<IHttpRequest>& HttpRequest) const = 0;
virtual FString ComputePath() const = 0;
};
class {{dllapi}} Response
{
public:
virtual ~Response() {}
virtual bool FromJson(const TSharedPtr<FJsonValue>& JsonObject) = 0;
void SetSuccessful(bool InSuccessful) { Successful = InSuccessful; }
bool IsSuccessful() const { return Successful; }
virtual void SetHttpResponseCode(EHttpResponseCodes::Type InHttpResponseCode);
EHttpResponseCodes::Type GetHttpResponseCode() const { return ResponseCode; }
void SetResponseString(const FString& InResponseString) { ResponseString = InResponseString; }
const FString& GetResponseString() const { return ResponseString; }
void SetHttpResponse(const FHttpResponsePtr& InHttpResponse) { HttpResponse = InHttpResponse; }
const FHttpResponsePtr& GetHttpResponse() const { return HttpResponse; }
private:
bool Successful;
EHttpResponseCodes::Type ResponseCode;
FString ResponseString;
FHttpResponsePtr HttpResponse;
};
{{#cppNamespaceDeclarations}}
}
{{/cppNamespaceDeclarations}}

View File

@@ -1,21 +0,0 @@
{{>licenseInfo}}
#include "{{modelNamePrefix}}BaseModel.h"
{{#cppNamespaceDeclarations}}
namespace {{this}}
{
{{/cppNamespaceDeclarations}}
void Response::SetHttpResponseCode(EHttpResponseCodes::Type InHttpResponseCode)
{
ResponseCode = InHttpResponseCode;
SetSuccessful(EHttpResponseCodes::IsOk(InHttpResponseCode));
if(InHttpResponseCode == EHttpResponseCodes::RequestTimeout)
{
SetResponseString(TEXT("Request Timeout"));
}
}
{{#cppNamespaceDeclarations}}
}
{{/cppNamespaceDeclarations}}

View File

@@ -1,51 +0,0 @@
{{>licenseInfo}}
#pragma once
#include "{{modelNamePrefix}}BaseModel.h"
{{#imports}}{{{import}}}
{{/imports}}
{{#cppNamespaceDeclarations}}
namespace {{this}}
{
{{/cppNamespaceDeclarations}}
{{#models}}
{{#model}}
/*
* {{classname}}
*
* {{description}}
*/
class {{dllapi}} {{classname}} : public Model
{
public:
virtual ~{{classname}}() {}
bool FromJson(const TSharedPtr<FJsonObject>& JsonObject) final;
void WriteJson(JsonWriter& Writer) const final;
{{#vars}}
{{#isEnum}}
{{#allowableValues}}
enum class {{{enumName}}}
{
{{#enumVars}}
{{name}},
{{/enumVars}}
};
{{/allowableValues}}
{{#description}}/* {{{description}}} */
{{/description}}{{^required}}TOptional<{{/required}}{{{datatypeWithEnum}}}{{^required}}>{{/required}} {{name}}{{#required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/required}};
{{/isEnum}}
{{^isEnum}}
{{#description}}/* {{{description}}} */
{{/description}}{{^required}}TOptional<{{/required}}{{{datatype}}}{{^required}}>{{/required}} {{name}}{{#required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/required}};
{{/isEnum}}
{{/vars}}
};
{{/model}}
{{/models}}
{{#cppNamespaceDeclarations}}
}
{{/cppNamespaceDeclarations}}

View File

@@ -1,98 +0,0 @@
{{>licenseInfo}}
#include "{{classname}}.h"
#include "{{unrealModuleName}}Module.h"
#include "{{modelNamePrefix}}Helpers.h"
#include "Templates/SharedPointer.h"
{{#cppNamespaceDeclarations}}
namespace {{this}}
{
{{/cppNamespaceDeclarations}}
{{#models}}{{#model}}
{{#hasEnums}}
{{#vars}}
{{#isEnum}}
inline FString ToString(const {{classname}}::{{{enumName}}}& Value)
{
{{#allowableValues}}
switch (Value)
{
{{#enumVars}}
case {{classname}}::{{{enumName}}}::{{name}}:
return TEXT({{{value}}});
{{/enumVars}}
}
{{/allowableValues}}
UE_LOG(Log{{unrealModuleName}}, Error, TEXT("Invalid {{classname}}::{{{enumName}}} Value (%d)"), (int)Value);
return TEXT("");
}
inline FStringFormatArg ToStringFormatArg(const {{classname}}::{{{enumName}}}& Value)
{
return FStringFormatArg(ToString(Value));
}
inline void WriteJsonValue(JsonWriter& Writer, const {{classname}}::{{{enumName}}}& Value)
{
WriteJsonValue(Writer, ToString(Value));
}
inline bool TryGetJsonValue(const TSharedPtr<FJsonValue>& JsonValue, {{classname}}::{{{enumName}}}& Value)
{
FString TmpValue;
if (JsonValue->TryGetString(TmpValue))
{
static TMap<FString, {{classname}}::{{{enumName}}}> StringToEnum = { {{#enumVars}}
{ TEXT({{{value}}}), {{classname}}::{{{enumName}}}::{{name}} },{{/enumVars}} };
const auto Found = StringToEnum.Find(TmpValue);
if(Found)
{
Value = *Found;
return true;
}
}
return false;
}
{{/isEnum}}
{{/vars}}
{{/hasEnums}}
void {{classname}}::WriteJson(JsonWriter& Writer) const
{
{{#parent}}
#error inheritance not handled right now
{{/parent}}
Writer->WriteObjectStart();
{{#vars}}
{{#required}}
Writer->WriteIdentifierPrefix(TEXT("{{baseName}}")); WriteJsonValue(Writer, {{name}});
{{/required}}
{{^required}}
if ({{name}}.IsSet())
{
Writer->WriteIdentifierPrefix(TEXT("{{baseName}}")); WriteJsonValue(Writer, {{name}}.GetValue());
}
{{/required}}
{{/vars}}
Writer->WriteObjectEnd();
}
bool {{classname}}::FromJson(const TSharedPtr<FJsonObject>& JsonObject)
{
bool ParseSuccess = true;
{{#vars}}
ParseSuccess &= TryGetJsonValue(JsonObject, TEXT("{{baseName}}"), {{name}});
{{/vars}}
return ParseSuccess;
}
{{/model}}
{{/models}}
{{#cppNamespaceDeclarations}}
}
{{/cppNamespaceDeclarations}}

View File

@@ -1,15 +0,0 @@
{{>licenseInfo}}
#pragma once
#include "Modules/ModuleInterface.h"
#include "Modules/ModuleManager.h"
#include "Logging/LogMacros.h"
DECLARE_LOG_CATEGORY_EXTERN(Log{{unrealModuleName}}, Log, All);
class {{dllapi}} {{unrealModuleName}}Module : public IModuleInterface
{
public:
void StartupModule() final;
void ShutdownModule() final;
};

View File

@@ -1,14 +0,0 @@
{{>licenseInfo}}
#include "{{unrealModuleName}}Module.h"
IMPLEMENT_MODULE({{unrealModuleName}}Module, {{unrealModuleName}});
DEFINE_LOG_CATEGORY(Log{{unrealModuleName}});
void {{unrealModuleName}}Module::StartupModule()
{
}
void {{unrealModuleName}}Module::ShutdownModule()
{
}

View File

@@ -0,0 +1,27 @@
# OpenAPI generated server
## Overview
This server was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate a server stub.
{{^googleCloudFunctions}}
### Running the server
To run the server, run:
```
npm start
```
To view the Swagger UI interface:
```
open http://localhost:{{serverPort}}/docs
```
{{/googleCloudFunctions}}
{{#googleCloudFunctions}}
### Deploying the function
To deploy this module into Google Cloud Functions, you will have to use Google Cloud SDK commandline tool.
See [Google Cloud Functions quick start guide](https://cloud.google.com/functions/docs/quickstart) and [Deploying Cloud Functions](https://cloud.google.com/functions/docs/deploying/) for the details.
{{/googleCloudFunctions}}
This project leverages the mega-awesome [swagger-tools](https://github.com/apigee-127/swagger-tools) middleware which does most all the work.

View File

@@ -0,0 +1,21 @@
'use strict';
var utils = require('../utils/writer.js');
{{#operations}}
var {{classname}} = require('../{{implFolder}}/{{classname}}Service');
{{#operation}}
module.exports.{{nickname}} = function {{nickname}} (req, 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}}

View File

@@ -0,0 +1,44 @@
'use strict';
var swaggerTools = require('swagger-tools');
var jsyaml = require('js-yaml');
var fs = require('fs');
// swaggerRouter configuration
var options = {
controllers: './controllers',
useStubs: false
};
// The Swagger document (require it, build it programmatically, fetch it from a URL, ...)
var spec = fs.readFileSync('./api/openapi.yaml', 'utf8');
var swaggerDoc = jsyaml.safeLoad(spec);
function toPromise(f, req, res) {
return new Promise(function(resolve, reject) {
f(req, res, function(err) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
exports.{{exportedName}} = function(req, res) {
swaggerTools.initializeMiddleware(swaggerDoc, function(middleware) {
var metadata = middleware.swaggerMetadata();
var validator = middleware.swaggerValidator();
var router = middleware.swaggerRouter(options);
req.url = swaggerDoc.basePath + req.url;
toPromise(metadata, req, res).then(function() {
return toPromise(validator, req, res);
}).then(function() {
return toPromise(router, req, res);
}).catch(function(err) {
console.error(err);
res.status(res.statusCode || 400).send(err);
});
});
};

View File

@@ -0,0 +1,44 @@
'use strict';
var fs = require('fs'),
path = require('path'),
http = require('http');
var app = require('connect')();
var swaggerTools = require('swagger-tools');
var jsyaml = require('js-yaml');
var serverPort = {{serverPort}};
// swaggerRouter configuration
var options = {
swaggerUi: path.join(__dirname, '/openapi.json'),
controllers: path.join(__dirname, './controllers'),
useStubs: process.env.NODE_ENV === 'development' // Conditionally turn on stubs (mock mode)
};
// The Swagger document (require it, build it programmatically, fetch it from a URL, ...)
var spec = fs.readFileSync(path.join(__dirname,'api/openapi.yaml'), 'utf8');
var swaggerDoc = jsyaml.safeLoad(spec);
// Initialize the Swagger middleware
swaggerTools.initializeMiddleware(swaggerDoc, function (middleware) {
// Interpret Swagger resources and attach metadata to request - must be first in swagger-tools middleware chain
app.use(middleware.swaggerMetadata());
// Validate Swagger requests
app.use(middleware.swaggerValidator());
// Route validated requests to appropriate controller
app.use(middleware.swaggerRouter(options));
// Serve the Swagger documents and Swagger UI
app.use(middleware.swaggerUi());
// Start the server
http.createServer(app).listen(serverPort, function () {
console.log('Your server is listening on port %d (http://localhost:%d)', serverPort, serverPort);
console.log('Swagger-ui is available on http://localhost:%d/docs', serverPort);
});
});

View File

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

View File

@@ -0,0 +1,24 @@
{
"name": "{{projectName}}",
"version": "{{appVersion}}",
"description": "{{{appDescription}}}",
"main": "index.js",
{{^googleCloudFunctions}}
"scripts": {
"prestart": "npm install",
"start": "node index.js"
},
{{/googleCloudFunctions}}
"keywords": [
"openapi-tools"
],
"license": "Unlicense",
"private": true,
"dependencies": {
{{^googleCloudFunctions}}
"connect": "^3.2.0",
{{/googleCloudFunctions}}
"js-yaml": "^3.3.0",
"swagger-tools": "0.10.1"
}
}

View File

@@ -0,0 +1,44 @@
'use strict';
{{#operations}}
{{#operation}}
/**
{{#summary}}
* {{{summary}}}
{{/summary}}
{{#notes}}
* {{{notes}}}
{{/notes}}
*
{{#allParams}}
* {{paramName}} {{{dataType}}} {{{description}}}{{^required}} (optional){{/required}}
{{/allParams}}
{{^returnType}}
* no response value expected for this operation
{{/returnType}}
{{#returnType}}
* returns {{{returnType}}}
{{/returnType}}
**/
exports.{{{operationId}}} = function({{#allParams}}{{paramName}}{{#hasMore}},{{/hasMore}}{{/allParams}}) {
return new Promise(function(resolve, reject) {
{{#returnType}}
var examples = {};
{{#examples}}
examples['{{contentType}}'] = {{{example}}};
{{/examples}}
if (Object.keys(examples).length > 0) {
resolve(examples[Object.keys(examples)[0]]);
} else {
resolve();
}
{{/returnType}}
{{^returnType}}
resolve();
{{/returnType}}
});
}
{{/operation}}
{{/operations}}

View File

@@ -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);
}

View File

@@ -1,7 +1,7 @@
required_properties = set([
'_data_store',
'_check_type',
'_spec_property_naming',
'_from_server',
'_path_to_item',
'_configuration',
'_visited_composed_classes',
@@ -15,7 +15,7 @@
constant_args = {
'_check_type': _check_type,
'_path_to_item': _path_to_item,
'_spec_property_naming': _spec_property_naming,
'_from_server': _from_server,
'_configuration': _configuration,
'_visited_composed_classes': self._visited_composed_classes,
}

View File

@@ -1,7 +1,7 @@
required_properties = set([
'_data_store',
'_check_type',
'_spec_property_naming',
'_from_server',
'_path_to_item',
'_configuration',
'_visited_composed_classes',

View File

@@ -1,5 +1,5 @@
@convert_js_args_to_python_args
def __init__(self{{#requiredVars}}{{^defaultValue}}, {{name}}{{/defaultValue}}{{/requiredVars}}{{#requiredVars}}{{#defaultValue}}, {{name}}={{{defaultValue}}}{{/defaultValue}}{{/requiredVars}}, _check_type=True, _spec_property_naming=False, _path_to_item=(), _configuration=None, _visited_composed_classes=(), **kwargs): # noqa: E501
def __init__(self{{#requiredVars}}{{^defaultValue}}, {{name}}{{/defaultValue}}{{/requiredVars}}{{#requiredVars}}{{#defaultValue}}, {{name}}={{{defaultValue}}}{{/defaultValue}}{{/requiredVars}}, _check_type=True, _from_server=False, _path_to_item=(), _configuration=None, _visited_composed_classes=(), **kwargs): # noqa: E501
"""{{classname}} - a model defined in OpenAPI
{{#requiredVars}}
@@ -26,10 +26,8 @@
_path_to_item (tuple/list): This is a list of keys or values to
drill down to the model in received_data
when deserializing a response
_spec_property_naming (bool): True if the variable names in the input data
are serialized names, as specified in the OpenAPI document.
False if the variable names in the input data
are pythonic names, e.g. snake case (default)
_from_server (bool): True if the data is from the server
False if the data is from the client (default)
_configuration (Configuration): the instance to use when
deserializing a file_type parameter.
If passed, type conversion is attempted
@@ -56,7 +54,7 @@
self._data_store = {}
self._check_type = _check_type
self._spec_property_naming = _spec_property_naming
self._from_server = _from_server
self._path_to_item = _path_to_item
self._configuration = _configuration
self._visited_composed_classes = _visited_composed_classes + (self.__class__,)

View File

@@ -33,7 +33,7 @@
if self._check_type:
value = validate_and_convert_types(
value, required_types_mixed, path_to_item, self._spec_property_naming,
value, required_types_mixed, path_to_item, self._from_server,
self._check_type, configuration=self._configuration)
if (name,) in self.allowed_values:
check_allowed_values(

View File

@@ -564,17 +564,15 @@ def order_response_types(required_types):
return sorted_types
def remove_uncoercible(required_types_classes, current_item, spec_property_naming,
def remove_uncoercible(required_types_classes, current_item, from_server,
must_convert=True):
"""Only keeps the type conversions that are possible
Args:
required_types_classes (tuple): tuple of classes that are required
these should be ordered by COERCION_INDEX_BY_TYPE
spec_property_naming (bool): True if the variable names in the input
data are serialized names as specified in the OpenAPI document.
False if the variables names in the input data are python
variable names in PEP-8 snake case.
from_server (bool): a boolean of whether the data is from the server
if false, the data is from the client
current_item (any): the current item (input data) to be converted
Keyword Args:
@@ -604,7 +602,7 @@ def remove_uncoercible(required_types_classes, current_item, spec_property_namin
continue
class_pair = (current_type_simple, required_type_class_simplified)
if must_convert and class_pair in COERCIBLE_TYPE_PAIRS[spec_property_naming]:
if must_convert and class_pair in COERCIBLE_TYPE_PAIRS[from_server]:
results_classes.append(required_type_class)
elif class_pair in UPCONVERSION_TYPE_PAIRS:
results_classes.append(required_type_class)
@@ -779,7 +777,7 @@ def get_discriminator_class(model_class,
model_class._composed_schemas.get('allOf', ())
for cls in composed_children:
# Check if the schema has inherited discriminators.
if hasattr(cls, 'discriminator') and cls.discriminator is not None:
if cls.discriminator is not None:
used_model_class = get_discriminator_class(
cls, discr_name, discr_value, cls_visited)
if used_model_class is not None:
@@ -788,7 +786,7 @@ def get_discriminator_class(model_class,
def deserialize_model(model_data, model_class, path_to_item, check_type,
configuration, spec_property_naming):
configuration, from_server):
"""Deserializes model_data to model instance.
Args:
@@ -798,10 +796,8 @@ def deserialize_model(model_data, model_class, path_to_item, check_type,
check_type (bool): whether to check the data tupe for the values in
the model
configuration (Configuration): the instance to use to convert files
spec_property_naming (bool): True if the variable names in the input
data are serialized names as specified in the OpenAPI document.
False if the variables names in the input data are python
variable names in PEP-8 snake case.
from_server (bool): True if the data is from the server
False if the data is from the client
Returns:
model instance
@@ -815,7 +811,7 @@ def deserialize_model(model_data, model_class, path_to_item, check_type,
kw_args = dict(_check_type=check_type,
_path_to_item=path_to_item,
_configuration=configuration,
_spec_property_naming=spec_property_naming)
_from_server=from_server)
if issubclass(model_class, ModelSimple):
instance = model_class(value=model_data, **kw_args)
@@ -866,7 +862,7 @@ def deserialize_file(response_data, configuration, content_disposition=None):
def attempt_convert_item(input_value, valid_classes, path_to_item,
configuration, spec_property_naming, key_type=False,
configuration, from_server, key_type=False,
must_convert=False, check_type=True):
"""
Args:
@@ -874,10 +870,8 @@ def attempt_convert_item(input_value, valid_classes, path_to_item,
valid_classes (any): the classes that are valid
path_to_item (list): the path to the item to convert
configuration (Configuration): the instance to use to convert files
spec_property_naming (bool): True if the variable names in the input
data are serialized names as specified in the OpenAPI document.
False if the variables names in the input data are python
variable names in PEP-8 snake case.
from_server (bool): True if data is from the server, False is data is
from the client
key_type (bool): if True we need to convert a key type (not supported)
must_convert (bool): if True we must convert
check_type (bool): if True we check the type or the returned data in
@@ -893,7 +887,7 @@ def attempt_convert_item(input_value, valid_classes, path_to_item,
"""
valid_classes_ordered = order_response_types(valid_classes)
valid_classes_coercible = remove_uncoercible(
valid_classes_ordered, input_value, spec_property_naming)
valid_classes_ordered, input_value, from_server)
if not valid_classes_coercible or key_type:
# we do not handle keytype errors, json will take care
# of this for us
@@ -905,7 +899,7 @@ def attempt_convert_item(input_value, valid_classes, path_to_item,
if issubclass(valid_class, OpenApiModel):
return deserialize_model(input_value, valid_class,
path_to_item, check_type,
configuration, spec_property_naming)
configuration, from_server)
elif valid_class == file_type:
return deserialize_file(input_value, configuration)
return deserialize_primitive(input_value, valid_class,
@@ -977,7 +971,7 @@ def is_valid_type(input_class_simple, valid_classes):
def validate_and_convert_types(input_value, required_types_mixed, path_to_item,
spec_property_naming, _check_type, configuration=None):
from_server, _check_type, configuration=None):
"""Raises a TypeError is there is a problem, otherwise returns value
Args:
@@ -988,10 +982,8 @@ def validate_and_convert_types(input_value, required_types_mixed, path_to_item,
path_to_item: (list) the path to the data being validated
this stores a list of keys or indices to get to the data being
validated
spec_property_naming (bool): True if the variable names in the input
data are serialized names as specified in the OpenAPI document.
False if the variables names in the input data are python
variable names in PEP-8 snake case.
from_server (bool): True if data is from the server
False if data is from the client
_check_type: (boolean) if true, type will be checked and conversion
will be attempted.
configuration: (Configuration): the configuration class to use
@@ -1019,7 +1011,7 @@ def validate_and_convert_types(input_value, required_types_mixed, path_to_item,
valid_classes,
path_to_item,
configuration,
spec_property_naming,
from_server,
key_type=False,
must_convert=True
)
@@ -1032,14 +1024,14 @@ def validate_and_convert_types(input_value, required_types_mixed, path_to_item,
if len(valid_classes) > 1 and configuration:
# there are valid classes which are not the current class
valid_classes_coercible = remove_uncoercible(
valid_classes, input_value, spec_property_naming, must_convert=False)
valid_classes, input_value, from_server, must_convert=False)
if valid_classes_coercible:
converted_instance = attempt_convert_item(
input_value,
valid_classes_coercible,
path_to_item,
configuration,
spec_property_naming,
from_server,
key_type=False,
must_convert=False
)
@@ -1066,7 +1058,7 @@ def validate_and_convert_types(input_value, required_types_mixed, path_to_item,
inner_value,
inner_required_types,
inner_path,
spec_property_naming,
from_server,
_check_type,
configuration=configuration
)
@@ -1084,7 +1076,7 @@ def validate_and_convert_types(input_value, required_types_mixed, path_to_item,
inner_val,
inner_required_types,
inner_path,
spec_property_naming,
from_server,
_check_type,
configuration=configuration
)
@@ -1190,8 +1182,8 @@ def convert_js_args_to_python_args(fn):
from functools import wraps
@wraps(fn)
def wrapped_init(self, *args, **kwargs):
spec_property_naming = kwargs.get('_spec_property_naming', False)
if spec_property_naming:
from_server = kwargs.get('_from_server', False)
if from_server:
kwargs = change_keys_js_to_python(kwargs, self.__class__)
return fn(self, *args, **kwargs)
return wrapped_init

View File

@@ -0,0 +1 @@
dist

View File

@@ -0,0 +1,30 @@
## {{npmName}}@{{npmVersion}}
This generator creates TypeScript/JavaScript client that utilizes {{framework}}.
### Building
To build and compile the typescript sources to javascript use:
```
npm install
npm run build
```
### Publishing
First build the package then run ```npm publish```
### Consuming
navigate to the folder of your consuming project and run one of the following commands.
_published:_
```
npm install {{npmName}}@{{npmVersion}} --save
```
_unPublished (not recommended):_
```
npm install PATH_TO_GENERATED_PACKAGE --save

View File

@@ -0,0 +1,212 @@
// TODO: better import syntax?
import { BaseAPIRequestFactory, RequiredError } from './baseapi';
import {Configuration} from '../configuration';
import { RequestContext, HttpMethod, ResponseContext, HttpFile} from '../http/http';
{{#platforms}}
{{#node}}
import * as FormData from "form-data";
{{/node}}
{{/platforms}}
import {ObjectSerializer} from '../models/ObjectSerializer';
import {ApiException} from './exception';
import {isCodeInRange} from '../util';
{{#imports}}
import { {{classname}} } from '..{{filename}}';
{{/imports}}
{{#operations}}
/**
* {{#description}}{{{description}}}{{/description}}{{^description}}no description{{/description}}
*/
export class {{classname}}RequestFactory extends BaseAPIRequestFactory {
{{#operation}}
/**
{{#notes}}
* {{&notes}}
{{/notes}}
{{#summary}}
* {{&summary}}
{{/summary}}
{{#allParams}}
* @param {{paramName}} {{description}}
{{/allParams}}
*/
public {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: Configuration): RequestContext {
let config = options || this.configuration;
{{#allParams}}
{{#required}}
// verify required parameter '{{paramName}}' is not null or undefined
if ({{paramName}} === null || {{paramName}} === undefined) {
throw new RequiredError('Required parameter {{paramName}} was null or undefined when calling {{nickname}}.');
}
{{/required}}
{{/allParams}}
// Path Params
const localVarPath = '{{{path}}}'{{#pathParams}}
.replace('{' + '{{baseName}}' + '}', encodeURIComponent(String({{paramName}}))){{/pathParams}};
// Make Request Context
const requestContext = config.baseServer.makeRequestContext(localVarPath, HttpMethod.{{httpMethod}});
requestContext.setHeaderParam("Accept", "application/json, */*;q=0.8")
// Query Params
{{#queryParams}}
if ({{paramName}} !== undefined) {
requestContext.setQueryParam("{{baseName}}", ObjectSerializer.serialize({{paramName}}, "{{{dataType}}}", "{{dataFormat}}"));
}
{{/queryParams}}
// Header Params
{{#headerParams}}
requestContext.setHeaderParam("{{baseName}}", ObjectSerializer.serialize({{paramName}}, "{{{dataType}}}", "{{dataFormat}}"));
{{/headerParams}}
// Form Params
{{#hasFormParams}}
let localVarFormParams = new FormData();
{{/hasFormParams}}
{{#formParams}}
{{#isListContainer}}
if ({{paramName}}) {
{{#isCollectionFormatMulti}}
{{paramName}}.forEach((element) => {
localVarFormParams.append('{{baseName}}', element as any);
})
{{/isCollectionFormatMulti}}
{{^isCollectionFormatMulti}}
// TODO: replace .append with .set
localVarFormParams.append('{{baseName}}', {{paramName}}.join(COLLECTION_FORMATS["{{collectionFormat}}"]));
{{/isCollectionFormatMulti}}
}
{{/isListContainer}}
{{^isListContainer}}
if ({{paramName}} !== undefined) {
// TODO: replace .append with .set
{{^isFile}}
localVarFormParams.append('{{baseName}}', {{paramName}} as any);
{{/isFile}}
{{#isFile}}
{{#platforms}}
{{#node}}
localVarFormParams.append('{{baseName}}', {{paramName}}.data, {{paramName}}.name);
{{/node}}
{{#browser}}
localVarFormParams.append('{{baseName}}', {{paramName}}, {{paramName}}.name);
{{/browser}}
{{/platforms}}
{{/isFile}}
}
{{/isListContainer}}
{{/formParams}}
{{#hasFormParams}}
requestContext.setBody(localVarFormParams);
{{/hasFormParams}}
// Body Params
{{#bodyParam}}
const contentType = ObjectSerializer.getPreferredMediaType([{{#consumes}}
"{{{mediaType}}}"{{#hasMore}},{{/hasMore}}
{{/consumes}}]);
requestContext.setHeaderParam("Content-Type", contentType);
const serializedBody = ObjectSerializer.stringify(
ObjectSerializer.serialize({{paramName}}, "{{{dataType}}}", "{{dataFormat}}"),
contentType
);
requestContext.setBody(serializedBody);
{{/bodyParam}}
{{#hasAuthMethods}}
let authMethod = null;
{{/hasAuthMethods}}
// Apply auth methods
{{#authMethods}}
authMethod = config.authMethods["{{name}}"]
if (authMethod) {
authMethod.applySecurityAuthentication(requestContext);
}
{{/authMethods}}
return requestContext;
}
{{/operation}}
}
{{/operations}}
{{#operations}}
export class {{classname}}ResponseProcessor {
{{#operation}}
/**
* Unwraps the actual response sent by the server from the response context and deserializes the response content
* to the expected objects
*
* @params response Response returned by the server for a request to {{nickname}}
* @throws ApiException if the response code was not in [200, 299]
*/
public async {{nickname}}(response: ResponseContext): Promise<{{#returnType}}{{{returnType}}}{{/returnType}} {{^returnType}}void{{/returnType}}> {
const contentType = ObjectSerializer.normalizeMediaType(response.headers["content-type"]);
{{#responses}}
if (isCodeInRange("{{code}}", response.httpStatusCode)) {
{{#dataType}}
{{#isBinary}}
const body: {{{dataType}}} = await response.getBodyAsFile() as any as {{{returnType}}};
{{/isBinary}}
{{^isBinary}}
const body: {{{dataType}}} = ObjectSerializer.deserialize(
ObjectSerializer.parse(await response.body.text(), contentType),
"{{{dataType}}}", "{{returnFormat}}"
) as {{{dataType}}};
{{/isBinary}}
{{#is2xx}}
return body;
{{/is2xx}}
{{^is2xx}}
throw new ApiException<{{{dataType}}}>({{code}}, body);
{{/is2xx}}
{{/dataType}}
{{^dataType}}
{{#is2xx}}
return;
{{/is2xx}}
{{^is2xx}}
throw new ApiException<string>(response.httpStatusCode, "{{message}}");
{{/is2xx}}
{{/dataType}}
}
{{/responses}}
// Work around for missing responses in specification, e.g. for petstore.yaml
if (response.httpStatusCode >= 200 && response.httpStatusCode <= 299) {
{{#returnType}}
{{#isBinary}}
const body: {{{returnType}}} = await response.getBodyAsFile() as any as {{{returnType}}};
{{/isBinary}}
{{^isBinary}}
const body: {{{returnType}}} = ObjectSerializer.deserialize(
ObjectSerializer.parse(await response.body.text(), contentType),
"{{{returnType}}}", "{{returnFormat}}"
) as {{{returnType}}};
{{/isBinary}}
return body;
{{/returnType}}
{{^returnType}}
return;
{{/returnType}}
}
let body = response.body || "";
throw new ApiException<string>(response.httpStatusCode, "Unknown API Status Code!\nBody: \"" + body + "\"");
}
{{/operation}}
}
{{/operations}}

View File

@@ -0,0 +1,37 @@
import { Configuration } from '../configuration'
/**
*
* @export
*/
export const COLLECTION_FORMATS = {
csv: ",",
ssv: " ",
tsv: "\t",
pipes: "|",
};
/**
*
* @export
* @class BaseAPI
*/
export class BaseAPIRequestFactory {
constructor(protected configuration: Configuration) {
}
};
/**
*
* @export
* @class RequiredError
* @extends {Error}
*/
export class RequiredError extends Error {
name: "RequiredError" = "RequiredError";
constructor(public field: string, msg?: string) {
super(msg);
}
}

View File

@@ -0,0 +1,14 @@
/**
* Represents an error caused by an api call i.e. it has attributes for a HTTP status code
* and the returned body object.
*
* Example
* API returns a ErrorMessageObject whenever HTTP status code is not in [200, 299]
* => ApiException(404, someErrorMessageObject)
*
*/
export class ApiException<T> extends Error {
public constructor(public code: number, public body: T) {
super("HTTP-Code: " + code + "\nMessage: " + JSON.stringify(body))
}
}

View File

@@ -0,0 +1,66 @@
import {RequestContext, ResponseContext} from './http/http';
import { Observable, from } from {{#useRxJS}}'rxjs'{{/useRxJS}}{{^useRxJS}}'./rxjsStub'{{/useRxJS}};
/**
* Defines the contract for a middleware intercepting requests before
* they are sent (but after the RequestContext was created)
* and before the ResponseContext is unwrapped.
*
*/
export interface Middleware {
/**
* Modifies the request before the request is sent.
*
* @param context RequestContext of a request which is about to be sent to the server
* @returns an observable of the updated request context
*
*/
pre(context: RequestContext): Observable<RequestContext>;
/**
* Modifies the returned response before it is deserialized.
*
* @param context ResponseContext of a sent request
* @returns an observable of the modified response context
*/
post(context: ResponseContext): Observable<ResponseContext>;
}
export class PromiseMiddlewareWrapper implements Middleware {
public constructor(private middleware: PromiseMiddleware) {
}
pre(context: RequestContext): Observable<RequestContext> {
return from(this.middleware.pre(context));
}
post(context: ResponseContext): Observable<ResponseContext> {
return from(this.middleware.post(context));
}
}
/**
* Defines the contract for a middleware intercepting requests before
* they are sent (but after the RequestContext was created)
* and before the ResponseContext is unwrapped.
*
*/
export interface PromiseMiddleware {
/**
* Modifies the request before the request is sent.
*
* @param context RequestContext of a request which is about to be sent to the server
* @returns an observable of the updated request context
*
*/
pre(context: RequestContext): Promise<RequestContext>;
/**
* Modifies the returned response before it is deserialized.
*
* @param context ResponseContext of a sent request
* @returns an observable of the modified response context
*/
post(context: ResponseContext): Promise<ResponseContext>;
}

View File

@@ -0,0 +1,152 @@
import {RequestContext} from '../http/http';
// typings for btoa are incorrect
//@ts-ignore
import * as btoa from "btoa";
/**
* Base class for all authentication schemes.
*
*/
export abstract class SecurityAuthentication {
public constructor(private name: string) {
}
/*
*
* @return returns the name of the security authentication as specified in OAI
*/
public getName(): string {
return this.name;
}
/**
* Applies the authentication scheme to the request context
*
* @params context the request context which should use this authentication scheme
*/
public abstract applySecurityAuthentication(context: RequestContext): void;
}
/**
* Applies no authentication.
*
*/
export class NoAuthentication extends SecurityAuthentication {
public constructor() {
super("_no_auth");
}
public applySecurityAuthentication(_context: RequestContext) {
}
}
/**
* Applies an api key to the request context.
*
*/
export class APIKeyAuthentication extends SecurityAuthentication {
/**
* Configures this api key authentication with the necessary properties
*
* @param authName: name of this authentication scheme as specified in the swagger.json
* @param paramName: Parameter name used for the api key
* @param keyLocation: Parameter location, either query, header or cookie.
* @param apiKey: The api key to be used for every request
*/
public constructor(authName: string, private paramName: string, private keyLocation: "query" | "header" | "cookie", private apiKey: string) {
super(authName);
}
public applySecurityAuthentication(context: RequestContext) {
if (this.keyLocation === "header") {
context.setHeaderParam(this.paramName, this.apiKey);
} else if (this.keyLocation === "cookie") {
context.addCookie(this.paramName, this.apiKey);
} else if (this.keyLocation === "query") {
context.setQueryParam(this.paramName, this.apiKey);
}
}
}
/**
* Applies basic http authentication to a request.
*
*/
export class HttpBasicAuthentication extends SecurityAuthentication {
/**
* Configures the http authentication with the required details.
*
*
* @param authName name of the authentication scheme as defined in swagger json
* @param username username for http basic authentication
* @param password password for http basic authentication
*/
public constructor(authName: string, private username: string, private password: string) {
super(authName);
}
public applySecurityAuthentication(context: RequestContext) {
let comb = this.username + ":" + this.password;
context.setHeaderParam("Authentication", "Basic " + btoa(comb));
}
}
// TODO: How to handle oauth2 authentication!
export class OAuth2Authentication extends SecurityAuthentication {
public constructor(authName: string) {
super(authName);
}
public applySecurityAuthentication(context: RequestContext) {
// TODO
}
}
export type AuthMethods = {
{{#authMethods}}
"{{name}}"?: {{#isApiKey}}APIKeyAuthentication{{/isApiKey}}{{#isHttp}}HttpBasicAuthentication{{/isHttp}}{{#isOAuth}}OAuth2Authentication{{/isOAuth}},
{{/authMethods}}
}
export type ApiKeyConfiguration = string;
export type HttpBasicConfiguration = { "username": string, "password": string };
export type OAuth2Configuration = string;
export type AuthMethodsConfiguration = { {{#authMethods}}"{{name}}"?:{{#isApiKey}}ApiKeyConfiguration{{/isApiKey}}{{#isHttp}}HttpBasicConfiguration{{/isHttp}}{{#isOAuth}}OAuth2Configuration{{/isOAuth}}, {{/authMethods}} }
/**
* Creates the authentication methods from a swagger description.
*
*/
export function configureAuthMethods(conf: AuthMethodsConfiguration | undefined): AuthMethods {
let authMethods: AuthMethods = {
}
if (!conf) {
return authMethods;
}
{{#authMethods}}
if (conf["{{name}}"]) {
{{#isApiKey}}
authMethods["{{name}}"] = new APIKeyAuthentication("{{name}}", "{{keyParamName}}", {{#isKeyInQuery}}"query"{{/isKeyInQuery}}{{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{#isKeyInCookie}}"cookie"{{/isKeyInCookie}}, <string> conf["{{name}}"]);
{{/isApiKey}}
{{#isBasic}}
authMethods["{{name}}"] = new HttpBasicAuthentication("{{name}}", config["{{name}}"]["username"], config["{{name}}"]["password"]);
{{/isBasic}}
{{#isOAuth}}
authMethods["{{name}}"] = new OAuth2Authentication("{{name}}");
{{/isOAuth}}
}
{{/authMethods}}
return authMethods;
}

View File

@@ -0,0 +1,67 @@
import {HttpLibrary} from './http/http';
import {Middleware, PromiseMiddleware, PromiseMiddlewareWrapper} from './middleware';
{{#frameworks}}
{{#fetch-api}}
import {IsomorphicFetchHttpLibrary} from "./http/isomorphic-fetch";
{{/fetch-api}}
{{#jquery}}
import {JQueryHttpLibrary} from "./http/jquery";
{{/jquery}}
{{/frameworks}}
import {ServerConfiguration, server1} from './servers';
import {configureAuthMethods, AuthMethods, AuthMethodsConfiguration} from './auth/auth';
/**
* Inetrface with which a configuration object can be configured.
*
*/
export interface ConfigurationParameters {
/**
* Default server to use
*/
baseServer?: ServerConfiguration<any>;
/**
* HTTP library to use e.g. IsomorphicFetch
*/
httpApi?: HttpLibrary;
/**
* The middlewares which will be applied to requests and responses
*/
middleware?: Middleware[]; // middleware to apply before/after fetch requests
/**
* configures all middlewares using the promise api instead of observables (which Middleware uses)
*/
promiseMiddleware?: PromiseMiddleware[];
/**
* Configuration for the available authentication methods
*/
authMethods?: AuthMethodsConfiguration
}
export class Configuration {
baseServer: ServerConfiguration<any>;
httpApi: HttpLibrary;
middleware: Middleware[];
authMethods: AuthMethods;
/**
* Creates a new configuration object based on the given configuration.
* If a property is not included in conf, a default is used:
* - baseServer: server1
* - httpApi: IsomorphicFetchHttpLibrary
* - middleware: []
* - promiseMiddleware: []
* - authMethods: {}
* @param conf particial configuration
*/
constructor(conf: ConfigurationParameters = {}) {
this.baseServer = conf.baseServer !== undefined ? conf.baseServer : server1;
this.httpApi = conf.httpApi || {{#frameworks}}{{#fetch-api}}new IsomorphicFetchHttpLibrary(){{/fetch-api}}{{#jquery}}new JQueryHttpLibrary{{/jquery}}{{/frameworks}}; // TODO: replace with window.fetch if available?
this.middleware = conf.middleware || [];
this.authMethods = configureAuthMethods(conf.authMethods);
if (conf.promiseMiddleware) {
conf.promiseMiddleware.forEach(m => this.middleware.push(new PromiseMiddlewareWrapper(m)));
}
}
}

View File

@@ -0,0 +1,52 @@
#!/bin/sh
# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/
#
# Usage example: /bin/sh ./git_push.sh wing328 openapi-pestore-perl "minor update"
git_user_id=$1
git_repo_id=$2
release_note=$3
if [ "$git_user_id" = "" ]; then
git_user_id="{{{gitUserId}}}"
echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id"
fi
if [ "$git_repo_id" = "" ]; then
git_repo_id="{{{gitRepoId}}}"
echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id"
fi
if [ "$release_note" = "" ]; then
release_note="{{{releaseNote}}}"
echo "[INFO] No command line input provided. Set \$release_note to $release_note"
fi
# Initialize the local directory as a Git repository
git init
# Adds the files in the local repository and stages them for commit.
git add .
# Commits the tracked changes and prepares them to be pushed to a remote repository.
git commit -m "$release_note"
# Sets the new remote
git_remote=`git remote`
if [ "$git_remote" = "" ]; then # git remote not defined
if [ "$GIT_TOKEN" = "" ]; then
echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment."
git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git
else
git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git
fi
fi
git pull origin master
# Pushes (Forces) the changes in the local repository up to the remote repository
echo "Git pushing to https://github.com/${git_user_id}/${git_repo_id}.git"
git push origin master 2>&1 | grep -v 'To https'

View File

@@ -0,0 +1,246 @@
{{#platforms}}
{{#node}}
// TODO: evaluate if we can easily get rid of this library
import * as FormData from "form-data";
{{/node}}
{{/platforms}}
// typings of url-parse are incorrect...
// @ts-ignore
import * as URLParse from "url-parse";
import { Observable } from {{#useRxJS}}'rxjs'{{/useRxJS}}{{^useRxJS}}'../rxjsStub'{{/useRxJS}};
{{#frameworks}}
{{#fetch-api}}
export * from './isomorphic-fetch';
{{/fetch-api}}
{{#jquery}}
export * from './jquery';
{{/jquery}}
{{/frameworks}}
/**
* Represents an HTTP method.
*/
export enum HttpMethod {
GET = "GET",
HEAD = "HEAD",
POST = "POST",
PUT = "PUT",
DELETE = "DELETE",
CONNECT = "CONNECT",
OPTIONS = "OPTIONS",
TRACE = "TRACE",
PATCH = "PATCH"
}
/**
* Represents an HTTP file which will be transferred from or to a server.
*/
{{#platforms}}
{{#node}}
export type HttpFile = {
data: {{{fileContentDataType}}},
name: string
};
{{/node}}
{{#browser}}
export type HttpFile = {{{fileContentDataType}}} & { readonly name: string };
{{/browser}}
{{/platforms}}
export class HttpException extends Error {
public constructor(msg: string) {
super(msg);
}
}
/**
* Represents the body of an outgoing HTTP request.
*/
export type RequestBody = undefined | string | FormData;
/**
* Represents an HTTP request context
*/
export class RequestContext {
private headers: { [key: string]: string } = {};
private body: RequestBody = undefined;
private url: URLParse;
/**
* Creates the request context using a http method and request resource url
*
* @param url url of the requested resource
* @param httpMethod http method
*/
public constructor(url: string, private httpMethod: HttpMethod) {
this.url = URLParse(url, true);
}
/*
* Returns the url set in the constructor including the query string
*
*/
public getUrl(): string {
return this.url.toString();
}
/**
* Replaces the url set in the constructor with this url.
*
*/
public setUrl(url: string) {
this.url = URLParse(url, true);
}
/**
* Sets the body of the http request either as a string or FormData
*
* Note that setting a body on a HTTP GET, HEAD, DELETE, CONNECT or TRACE
* request is discouraged.
* https://httpwg.org/http-core/draft-ietf-httpbis-semantics-latest.html#rfc.section.7.3.1
*
* @param body the body of the request
*/
public setBody(body: RequestBody) {
this.body = body;
}
public getHttpMethod(): HttpMethod {
return this.httpMethod;
}
public getHeaders(): { [key: string]: string } {
return this.headers;
}
public getBody(): RequestBody {
return this.body;
}
public setQueryParam(name: string, value: string) {
let queryObj = this.url.query;
queryObj[name] = value;
this.url.set("query", queryObj);
}
/**
* Sets a cookie with the name and value. NO check for duplicate cookies is performed
*
*/
public addCookie(name: string, value: string): void {
if (!this.headers["Cookie"]) {
this.headers["Cookie"] = "";
}
this.headers["Cookie"] += name + "=" + value + "; ";
}
public setHeaderParam(key: string, value: string): void {
this.headers[key] = value;
}
}
export interface ResponseBody {
text(): Promise<string>;
binary(): Promise<{{{fileContentDataType}}}>;
}
/**
* Helper class to generate a `ResponseBody` from binary data
*/
export class SelfDecodingBody implements ResponseBody {
constructor(private dataSource: Promise<{{{fileContentDataType}}}>) {}
binary(): Promise<{{{fileContentDataType}}}> {
return this.dataSource;
}
async text(): Promise<string> {
const data: {{{fileContentDataType}}} = await this.dataSource;
{{#platforms}}
{{#node}}
return data.toString();
{{/node}}
{{#browser}}
// @ts-ignore
if (data.text) {
// @ts-ignore
return data.text();
}
return new Promise<string>((resolve, reject) => {
const reader = new FileReader();
reader.addEventListener("load", () => resolve(reader.result));
reader.addEventListener("error", () => reject(reader.error));
reader.readAsText(data);
});
{{/browser}}
{{/platforms}}
}
}
export class ResponseContext {
public constructor(
public httpStatusCode: number,
public headers: { [key: string]: string },
public body: ResponseBody
) {}
/**
* Parse header value in the form `value; param1="value1"`
*
* E.g. for Content-Type or Content-Disposition
* Parameter names are converted to lower case
* The first parameter is returned with the key `""`
*/
public getParsedHeader(headerName: string): { [parameter: string]: string } {
const result: { [parameter: string]: string } = {};
if (!this.headers[headerName]) {
return result;
}
const parameters = this.headers[headerName].split(";");
for (const parameter of parameters) {
let [key, value] = parameter.split("=", 2);
key = key.toLowerCase().trim();
if (value === undefined) {
result[""] = key;
} else {
value = value.trim();
if (value.startsWith('"') && value.endsWith('"')) {
value = value.substring(1, value.length - 1);
}
result[key] = value;
}
}
return result;
}
public async getBodyAsFile(): Promise<HttpFile> {
const data = await this.body.binary();
const fileName = this.getParsedHeader("content-disposition")["filename"] || "";
{{#platforms}}
{{#node}}
return { data, name: fileName };
{{/node}}
{{#browser}}
const contentType = this.headers["content-type"] || "";
try {
return new File([data], fileName, { type: contentType });
} catch (error) {
/** Fallback for when the File constructor is not available */
return Object.assign(data, {
name: fileName,
type: contentType
});
}
{{/browser}}
{{/platforms}}
}
}
export interface HttpLibrary {
send(request: RequestContext): Observable<ResponseContext>;
}

View File

@@ -0,0 +1,53 @@
import {HttpLibrary, RequestContext, ResponseContext} from './http';
import { from, Observable } from {{#useRxJS}}'rxjs'{{/useRxJS}}{{^useRxJS}}'../rxjsStub'{{/useRxJS}};
{{#platforms}}
{{#node}}
import fetch from "node-fetch";
{{/node}}
{{#browser}}
import "whatwg-fetch";
{{/browser}}
{{/platforms}}
export class IsomorphicFetchHttpLibrary implements HttpLibrary {
public send(request: RequestContext): Observable<ResponseContext> {
let method = request.getHttpMethod().toString();
let body = request.getBody();
const resultPromise = fetch(request.getUrl(), {
method: method,
body: body as any,
headers: request.getHeaders(),
{{#platforms}}
{{#browser}}
credentials: "same-origin"
{{/browser}}
{{/platforms}}
}).then((resp: any) => {
const headers: { [name: string]: string } = {};
resp.headers.forEach((value: string, name: string) => {
headers[name] = value;
});
{{#platforms}}
{{#node}}
const body = {
text: () => resp.text(),
binary: () => resp.buffer()
};
{{/node}}
{{^node}}
const body = {
text: () => resp.text(),
binary: () => resp.blob()
};
{{/node}}
{{/platforms}}
return new ResponseContext(resp.status, headers, body);
});
return from<Promise<ResponseContext>>(resultPromise);
}
}

View File

@@ -0,0 +1,87 @@
import { HttpLibrary, RequestContext, ResponseContext, HttpException, SelfDecodingBody } from './http';
import * as e6p from 'es6-promise'
import { from, Observable } from {{#useRxJS}}'rxjs'{{/useRxJS}}{{^useRxJS}}'../rxjsStub'{{/useRxJS}};
e6p.polyfill();
import * as $ from 'jquery';
export class JQueryHttpLibrary implements HttpLibrary {
public send(request: RequestContext): Observable<ResponseContext> {
let method = request.getHttpMethod().toString();
let body = request.getBody();
let headerParams = request.getHeaders()
let requestOptions: any = {
url: request.getUrl(),
type: method,
headers: request.getHeaders(),
processData: false,
xhrFields: { withCredentials: true },
data: body
};
// If we want a blob, we have to set the xhrFields' responseType AND add a
// custom converter to overwrite the default deserialization of JQuery...
requestOptions["xhrFields"] = { responseType: 'blob' };
requestOptions["converters"] = {}
requestOptions["converters"]["* blob"] = (result:any) => result;
requestOptions["dataType"] = "blob";
if (request.getHeaders()['Content-Type']) {
requestOptions.contentType = headerParams['Content-Type'];
}
requestOptions.dataFilter = ((headerParams: { [key:string]: string}) => {
return (data: string, type: string) => {
if (headerParams["Accept"] == "application/json" && data == "") {
return "{}"
} else {
return data
}
}
})(headerParams);
if (request.getHeaders()["Cookie"]) {
throw new HttpException("Setting the \"Cookie\"-Header field is blocked by every major browser when using jquery.ajax requests. Please switch to another library like fetch to enable this option");
}
if (body && body.constructor.name == "FormData") {
requestOptions.contentType = false;
}
const sentRequest = $.ajax(requestOptions);
const resultPromise = new Promise<ResponseContext>((resolve, reject) => {
sentRequest.done((data, _, jqXHR) => {
const result = new ResponseContext(
jqXHR.status,
this.getResponseHeaders(jqXHR),
new SelfDecodingBody(Promise.resolve(data))
);
resolve(result);
})
sentRequest.fail((jqXHR: any) => {
const headers = this.getResponseHeaders(jqXHR)
const result = new ResponseContext(jqXHR.status, headers, jqXHR.responseText);
resolve(result);
})
})
return from(resultPromise);
}
private getResponseHeaders(jqXHR: any): { [key: string]: string } {
const responseHeaders: { [key: string]: string } = {};
var headers = jqXHR.getAllResponseHeaders();
headers = headers.split("\n");
headers.forEach(function (header: any) {
header = header.split(": ");
var key = header.shift();
if (key.length == 0) return
// chrome60+ force lowercase, other browsers can be different
key = key.toLowerCase();
responseHeaders[key] = header.join(": ");
});
return responseHeaders
}
}

View File

@@ -0,0 +1,57 @@
import {RequestContext, HttpMethod} from './http/http';
/**
*
* Represents the configuration of a server including its
* url template and variable configuration based on the url.
*
*/
export class ServerConfiguration<T> {
public constructor(private url: string, private variableConfiguration: T) {
}
/**
* Sets the value of the variables of this server.
*
* @param variableConfiguration a partial variable configuration for the variables contained in the url
*/
public setVariables(variableConfiguration: Partial<T>) {
for (const key in variableConfiguration) {
const val = variableConfiguration[key]
// We know that val isn't undefined here - hopefully
if (val !== undefined) {
this.variableConfiguration[key] = val as T[Extract<keyof T, string>];
}
}
}
public getConfiguration(): T {
return this.variableConfiguration
}
private getUrl() {
let replacedUrl = this.url;
for (const key in this.variableConfiguration) {
var re = new RegExp("{" + key + "}","g");
replacedUrl = replacedUrl.replace(re, this.variableConfiguration[key].toString());
}
return replacedUrl
}
/**
* Creates a new request context for this server using the url with variables
* replaced with their respective values and the endpoint of the request appended.
*
* @param endpoint the endpoint to be queried on the server
* @param httpMethod httpMethod to be used
*
*/
public makeRequestContext(endpoint: string, httpMethod: HttpMethod): RequestContext {
return new RequestContext(this.getUrl() + endpoint, httpMethod);
}
}
{{#servers}}
export const server{{-index}} = new ServerConfiguration<{ {{#variables}} "{{name}}": {{#enumValues}}"{{.}}"{{^-last}} | {{/-last}}{{/enumValues}}{{^enumValues}}string{{/enumValues}}{{^-last}},{{/-last}} {{/variables}} }>("{{url}}", { {{#variables}} "{{name}}": "{{defaultValue}}" {{^-last}},{{/-last}}{{/variables}} })
{{/servers}}

View File

@@ -0,0 +1,17 @@
import 'es6-promise/auto';
export * from './http/http';
export * from './auth/auth';
export * from './models/all';
export { Configuration} from './configuration'
export * from './apis/exception';
export * from './servers';
{{#useRxJS}}
export * from './types/ObservableAPI';
export { Middleware } from './middleware';
{{/useRxJS}}
{{^useRxJS}}
export * from './types/PromiseAPI';
export { PromiseMiddleware as Middleware } from './middleware';
{{/useRxJS}}

View File

@@ -5,7 +5,7 @@
* {{#version}}OpenAPI spec version: {{{version}}}{{/version}}
* {{#infoEmail}}Contact: {{{infoEmail}}}{{/infoEmail}}
*
* NOTE: This class is auto generated by OpenAPI Generator
* https://github.com/OpenAPITools/openapi-generator
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@@ -0,0 +1,240 @@
{{#models}}
{{#model}}
export * from './{{{ classFilename }}}';
{{/model}}
{{/models}}
{{#models}}
{{#model}}
import { {{classname}}{{#hasEnums}}{{#vars}}{{#isEnum}}, {{classname}}{{enumName}} {{/isEnum}} {{/vars}}{{/hasEnums}} } from './{{{ classFilename }}}';
{{/model}}
{{/models}}
/* tslint:disable:no-unused-variable */
let primitives = [
"string",
"boolean",
"double",
"integer",
"long",
"float",
"number",
"any"
];
const supportedMediaTypes: { [mediaType: string]: number } = {
"application/json": Infinity,
"application/octet-stream": 0
}
let enumsMap: Set<string> = new Set<string>([
{{#models}}
{{#model}}
{{#hasEnums}}
{{#vars}}
{{#isEnum}}
"{{classname}}{{enumName}}",
{{/isEnum}}
{{/vars}}
{{/hasEnums}}
{{/model}}
{{/models}}
]);
let typeMap: {[index: string]: any} = {
{{#models}}
{{#model}}
"{{classname}}": {{classname}},
{{/model}}
{{/models}}
}
export class ObjectSerializer {
public static findCorrectType(data: any, expectedType: string) {
if (data == undefined) {
return expectedType;
} else if (primitives.indexOf(expectedType.toLowerCase()) !== -1) {
return expectedType;
} else if (expectedType === "Date") {
return expectedType;
} else {
if (enumsMap.has(expectedType)) {
return expectedType;
}
if (!typeMap[expectedType]) {
return expectedType; // w/e we don't know the type
}
// Check the discriminator
let discriminatorProperty = typeMap[expectedType].discriminator;
if (discriminatorProperty == null) {
return expectedType; // the type does not have a discriminator. use it.
} else {
if (data[discriminatorProperty]) {
var discriminatorType = data[discriminatorProperty];
if(typeMap[discriminatorType]){
return discriminatorType; // use the type given in the discriminator
} else {
return expectedType; // discriminator did not map to a type
}
} else {
return expectedType; // discriminator was not present (or an empty string)
}
}
}
}
public static serialize(data: any, type: string, format: string) {
if (data == undefined) {
return data;
} else if (primitives.indexOf(type.toLowerCase()) !== -1) {
return data;
} else if (type.lastIndexOf("Array<", 0) === 0) { // string.startsWith pre es6
let subType: string = type.replace("Array<", ""); // Array<Type> => Type>
subType = subType.substring(0, subType.length - 1); // Type> => Type
let transformedData: any[] = [];
for (let index in data) {
let date = data[index];
transformedData.push(ObjectSerializer.serialize(date, subType, format));
}
return transformedData;
} else if (type === "Date") {
if (format == "date") {
let month = data.getMonth()+1
month = month < 10 ? "0" + month.toString() : month.toString()
let day = data.getDate();
day = day < 10 ? "0" + day.toString() : day.toString();
return data.getFullYear() + "-" + month + "-" + day;
} else {
return data.toISOString();
}
} else {
if (enumsMap.has(type)) {
return data;
}
if (!typeMap[type]) { // in case we dont know the type
return data;
}
// Get the actual type of this object
type = this.findCorrectType(data, type);
// get the map for the correct type.
let attributeTypes = typeMap[type].getAttributeTypeMap();
let instance: {[index: string]: any} = {};
for (let index in attributeTypes) {
let attributeType = attributeTypes[index];
instance[attributeType.baseName] = ObjectSerializer.serialize(data[attributeType.name], attributeType.type, attributeType.format);
}
return instance;
}
}
public static deserialize(data: any, type: string, format: string) {
// polymorphism may change the actual type.
type = ObjectSerializer.findCorrectType(data, type);
if (data == undefined) {
return data;
} else if (primitives.indexOf(type.toLowerCase()) !== -1) {
return data;
} else if (type.lastIndexOf("Array<", 0) === 0) { // string.startsWith pre es6
let subType: string = type.replace("Array<", ""); // Array<Type> => Type>
subType = subType.substring(0, subType.length - 1); // Type> => Type
let transformedData: any[] = [];
for (let index in data) {
let date = data[index];
transformedData.push(ObjectSerializer.deserialize(date, subType, format));
}
return transformedData;
} else if (type === "Date") {
return new Date(data);
} else {
if (enumsMap.has(type)) {// is Enum
return data;
}
if (!typeMap[type]) { // dont know the type
return data;
}
let instance = new typeMap[type]();
let attributeTypes = typeMap[type].getAttributeTypeMap();
for (let index in attributeTypes) {
let attributeType = attributeTypes[index];
instance[attributeType.name] = ObjectSerializer.deserialize(data[attributeType.baseName], attributeType.type, attributeType.format);
}
return instance;
}
}
/**
* Normalize media type
*
* We currently do not handle any media types attributes, i.e. anything
* after a semicolon. All content is assumed to be UTF-8 compatible.
*/
public static normalizeMediaType(mediaType: string | undefined): string | undefined {
if (mediaType === undefined) {
return undefined;
}
return mediaType.split(";")[0].trim().toLowerCase();
}
/**
* From a list of possible media types, choose the one we can handle best.
*
* The order of the given media types does not have any impact on the choice
* made.
*/
public static getPreferredMediaType(mediaTypes: Array<string>): string {
/** According to OAS 3 we should default to json */
if (!mediaTypes) {
return "application/json";
}
const normalMediaTypes = mediaTypes.map(this.normalizeMediaType);
let selectedMediaType: string | undefined = undefined;
let selectedRank: number = -Infinity;
for (const mediaType of normalMediaTypes) {
if (supportedMediaTypes[mediaType!] > selectedRank) {
selectedMediaType = mediaType;
selectedRank = supportedMediaTypes[mediaType!];
}
}
if (selectedMediaType === undefined) {
throw new Error("None of the given media types are supported: " + mediaTypes.join(", "));
}
return selectedMediaType!;
}
/**
* Convert data to a string according the given media type
*/
public static stringify(data: any, mediaType: string): string {
if (mediaType === "application/json") {
return JSON.stringify(data);
}
throw new Error("The mediaType " + mediaType + " is not supported by ObjectSerializer.stringify.");
}
/**
* Parse data from a string according to the given media type
*/
public static parse(rawData: string, mediaType: string | undefined) {
if (mediaType === undefined) {
throw new Error("Cannot parse content. No Content-Type defined.");
}
if (mediaType === "application/json") {
return JSON.parse(rawData);
}
throw new Error("The mediaType " + mediaType + " is not supported by ObjectSerializer.parse.");
}
}

View File

@@ -0,0 +1,79 @@
{{>licenseInfo}}
{{#models}}
{{#model}}
{{#tsImports}}
import { {{classname}} } from './{{filename}}';
{{/tsImports}}
import { HttpFile } from '../http/http';
{{#description}}
/**
* {{{description}}}
*/
{{/description}}
export class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{
{{#vars}}
{{#description}}
/**
* {{{description}}}
*/
{{/description}}
'{{name}}'{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}};
{{/vars}}
{{#discriminator}}
static readonly discriminator: string | undefined = "{{discriminatorName}}";
{{/discriminator}}
{{^discriminator}}
static readonly discriminator: string | undefined = undefined;
{{/discriminator}}
{{^isArrayModel}}
static readonly attributeTypeMap: Array<{name: string, baseName: string, type: string, format: string}> = [
{{#vars}}
{
"name": "{{name}}",
"baseName": "{{baseName}}",
"type": "{{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}",
"format": "{{dataFormat}}"
}{{#hasMore}},
{{/hasMore}}
{{/vars}}
];
static getAttributeTypeMap() {
{{#parent}}
return super.getAttributeTypeMap().concat({{classname}}.attributeTypeMap);
{{/parent}}
{{^parent}}
return {{classname}}.attributeTypeMap;
{{/parent}}
}
{{/isArrayModel}}
public constructor() {
{{#parent}}
super();
{{/parent}}
{{#allVars}}
{{#discriminatorValue}}
this.{{name}} = "{{discriminatorValue}}";
{{/discriminatorValue}}
{{/allVars}}
{{#discriminatorName}}
this.{{discriminatorName}} = "{{classname}}";
{{/discriminatorName}}
}
}
{{#hasEnums}}
{{#vars}}
{{#isEnum}}
export type {{classname}}{{enumName}} ={{#allowableValues}}{{#values}} "{{.}}" {{^-last}}|{{/-last}}{{/values}}{{/allowableValues}};
{{/isEnum}}
{{/vars}}
{{/hasEnums}}
{{/model}}
{{/models}}

View File

@@ -0,0 +1,5 @@
{{#models}}
{{#model}}
export * from './{{{ classFilename }}}'
{{/model}}
{{/models}}

View File

@@ -0,0 +1,58 @@
{
"name": "{{npmName}}",
"version": "{{npmVersion}}",
"description": "OpenAPI client for {{npmName}}",
"author": "OpenAPI-Generator Contributors",
"keywords": [
"fetch",
"typescript",
"openapi-client",
"openapi-generator"
],
"license": "Unlicense",
"main": "./dist/index.js",
"typings": "./dist/index.d.ts",
"scripts": {
"build": "tsc",
"prepublishOnly": "npm run build"
},
"dependencies": {
{{#frameworks}}
{{#fetch-api}}
{{#platforms}}
{{#node}}
"node-fetch": "^2.6.0",
"@types/node-fetch": "^2.5.7",
{{/node}}
{{#browser}}
"whatwg-fetch": "^3.0.0",
{{/browser}}
{{/platforms}}
{{/fetch-api}}
{{#jquery}}
"@types/jquery": "^3.3.29",
"jquery": "^3.4.1",
{{/jquery}}
{{/frameworks}}
{{#platforms}}
{{#node}}
"@types/node": "*",
"form-data": "^2.5.0",
{{/node}}
{{/platforms}}
{{#useRxJS}}
"rxjs": "^6.4.0",
{{/useRxJS}}
"btoa": "^1.2.1",
"es6-promise": "^4.2.4",
"url-parse": "^1.4.3"
},
"devDependencies": {
"typescript": "^2.9.2"
}{{#npmRepository}},{{/npmRepository}}
{{#npmRepository}}
"publishConfig":{
"registry":"{{npmRepository}}"
}
{{/npmRepository}}
}

View File

@@ -0,0 +1,27 @@
export class Observable<T> {
constructor(private promise: Promise<T>) {}
toPromise() {
return this.promise;
}
pipe<S>(callback: (value: T) => S | Promise<S>): Observable<S> {
return new Observable(this.promise.then(callback));
}
}
export function from<T>(promise: Promise<any>) {
return new Observable(promise);
}
export function of<T>(value: T) {
return new Observable<T>(Promise.resolve(value));
}
export function mergeMap<T, S>(callback: (value: T) => Observable<S>) {
return (value: T) => callback(value).toPromise();
}
export function map(callback: any) {
return callback;
}

View File

@@ -0,0 +1,36 @@
{
"compilerOptions": {
"strict": true,
/* Basic Options */
"target": "{{#supportsES6}}es6{{/supportsES6}}{{^supportsES6}}es5{{/supportsES6}}",
"module": "{{#supportsES6}}es6{{/supportsES6}}{{^supportsES6}}commonjs{{/supportsES6}}",
"declaration": true,
/* Additional Checks */
"noUnusedLocals": false, /* Report errors on unused locals. */ // TODO: reenable (unused imports!)
"noUnusedParameters": false, /* Report errors on unused parameters. */ // TODO: set to true again
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
"noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
"removeComments": true,
"sourceMap": true,
"outDir": "./dist",
"noLib": false,
{{#platforms}}
{{#node}}
"lib": [ "es6" ]
{{/node}}
{{#browser}}
"lib": [ "es6", "dom" ]
{{/browser}}
{{/platforms}}
},
"exclude": [
"dist",
"node_modules"
],
"filesGlob": [
"./**/*.ts",
]
}

View File

@@ -0,0 +1,67 @@
import { ResponseContext, RequestContext, HttpFile } from '../http/http';
import * as models from '../models/all';
import { Configuration} from '../configuration'
import { Observable, of } from {{#useRxJS}}'rxjs'{{/useRxJS}}{{^useRxJS}}'../rxjsStub'{{/useRxJS}};
import {mergeMap, map} from {{#useRxJS}}'rxjs/operators'{{/useRxJS}}{{^useRxJS}}'../rxjsStub'{{/useRxJS}};
{{#models}}
{{#model}}
import { {{{ classname }}} } from '../models/{{{ classFilename }}}';
{{/model}}
{{/models}}
{{#apiInfo}}
{{#apis}}
{{#operations}}
import { {{classname}}RequestFactory, {{classname}}ResponseProcessor} from "../apis/{{classname}}";
export class Observable{{classname}} {
private requestFactory: {{classname}}RequestFactory;
private responseProcessor: {{classname}}ResponseProcessor;
private configuration: Configuration;
public constructor(configuration: Configuration, requestFactory?: {{classname}}RequestFactory, responseProcessor?: {{classname}}ResponseProcessor) {
this.configuration = configuration;
this.requestFactory = requestFactory || new {{classname}}RequestFactory(configuration);
this.responseProcessor = responseProcessor || new {{classname}}ResponseProcessor();
}
{{#operation}}
/**
{{#notes}}
* {{&notes}}
{{/notes}}
{{#summary}}
* {{&summary}}
{{/summary}}
{{#allParams}}
* @param {{paramName}} {{description}}
{{/allParams}}
*/
public {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: Configuration): Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> {
const requestContext = this.requestFactory.{{nickname}}({{#allParams}}{{paramName}}, {{/allParams}}options);
// build promise chain
let middlewarePreObservable = of(requestContext);
for (let middleware of this.configuration.middleware) {
middlewarePreObservable = middlewarePreObservable.pipe(mergeMap((ctx: RequestContext) => middleware.pre(ctx)));
}
return middlewarePreObservable.pipe(mergeMap((ctx: RequestContext) => this.configuration.httpApi.send(ctx))).
pipe(mergeMap((response: ResponseContext) => {
let middlewarePostObservable = of(response);
for (let middleware of this.configuration.middleware) {
middlewarePostObservable = middlewarePostObservable.pipe(mergeMap((rsp: ResponseContext) => middleware.post(rsp)));
}
return middlewarePostObservable.pipe(map((rsp: ResponseContext) => this.responseProcessor.{{nickname}}(rsp)));
}));
}
{{/operation}}
}
{{/operations}}
{{/apis}}
{{/apiInfo}}

View File

@@ -0,0 +1,49 @@
import { ResponseContext, RequestContext, HttpFile } from '../http/http';
import * as models from '../models/all';
import { Configuration} from '../configuration'
{{#models}}
{{#model}}
import { {{{ classname }}} } from '../models/{{{ classFilename }}}';
{{/model}}
{{/models}}
{{#apiInfo}}
{{#apis}}
import { Observable{{classname}} } from './ObservableAPI';
{{#operations}}
import { {{classname}}RequestFactory, {{classname}}ResponseProcessor} from "../apis/{{classname}}";
export class Promise{{classname}} {
private api: Observable{{classname}}
public constructor(configuration: Configuration, requestFactory?: {{classname}}RequestFactory, responseProcessor?: {{classname}}ResponseProcessor) {
this.api = new Observable{{classname}}(configuration, requestFactory, responseProcessor);
}
{{#operation}}
/**
{{#notes}}
* {{&notes}}
{{/notes}}
{{#summary}}
* {{&summary}}
{{/summary}}
{{#allParams}}
* @param {{paramName}} {{description}}
{{/allParams}}
*/
public {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: Configuration): Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> {
const result = this.api.{{nickname}}({{#allParams}}{{paramName}}, {{/allParams}}options);
return result.toPromise();
}
{{/operation}}
}
{{/operations}}
{{/apis}}
{{/apiInfo}}

View File

@@ -0,0 +1,28 @@
/**
* Returns if a specific http code is in a given code range
* where the code range is defined as a combination of digits
* and "X" (the letter X) with a length of 3
*
* @param codeRange string with length 3 consisting of digits and "X" (the letter X)
* @param code the http status code to be checked against the code range
*/
export function isCodeInRange(codeRange: string, code: number): boolean {
// This is how the default value is encoded in OAG
if (codeRange === "0") {
return true;
}
if (codeRange == code.toString()) {
return true;
} else {
const codeString = code.toString();
if (codeString.length != codeRange.length) {
return false;
}
for (let i = 0; i < codeString.length; i++) {
if (codeRange.charAt(i) != "X" && codeRange.charAt(i) != codeString.charAt(i)) {
return false;
}
}
return true;
}
}

View File

@@ -298,6 +298,7 @@ public class SpringCodegenTest {
final String multipartMixedApi = files.get("/src/main/java/org/openapitools/api/MultipartMixedApi.java");
Assert.assertTrue(multipartMixedApi.contains("MultipartFile file"));
Assert.assertTrue(multipartMixedApi.contains("@RequestPart(value = \"file\", required = true)"));
System.out.println(multipartMixedApi);
Assert.assertTrue(multipartMixedApi.contains("@Valid @RequestPart(value = \"marker\", required = false)"));
}

View File

@@ -1,107 +0,0 @@
openapi: 3.0.1
info:
version: 1.0.0
title: Example
license:
name: MIT
servers:
- url: http://api.example.xyz/v1
paths:
/pets:
patch:
requestBody:
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Dog'
# This field will not match to any type.
- description: Any kind of pet
discriminator:
propertyName: pet_type
responses:
'200':
description: Updated
/pets-filtered:
patch:
requestBody:
content:
application/json:
schema:
anyOf:
- $ref: '#/components/schemas/PetByAge'
- $ref: '#/components/schemas/PetByType'
# This field will not match to any type.
- description: Any kind of filter
responses:
'200':
description: Updated
/file:
post:
requestBody:
content:
application/json:
schema:
type: object
properties:
file:
allOf:
- type: file
# This field will not match to any type.
- description: The file to upload
responses:
'200':
description: File uploaded
components:
schemas:
Pet:
type: object
required:
- pet_type
Dog:
allOf:
# This field will not match to any type.
- description: Dog information
- $ref: '#/components/schemas/Pet'
- type: object
properties:
bark:
type: boolean
breed:
type: string
enum: [Dingo, Husky, Retriever, Shepherd]
Cat:
allOf:
- $ref: '#/components/schemas/Pet'
- type: object
properties:
hunts:
type: boolean
age:
type: integer
PetByAge:
type: object
properties:
age:
type: integer
nickname:
type: string
required:
- age
PetByType:
type: object
properties:
pet_type:
type: string
enum: [Cat, Dog]
hunts:
type: boolean
required:
- pet_type

28
pom.xml
View File

@@ -991,6 +991,30 @@
<module>samples/server/petstore/jaxrs-spec-interface-response</module>
</modules>
</profile>
<profile>
<id>typescript-client-tests-default</id>
<activation>
<property>
<name>env</name>
<value>java</value>
</property>
</activation>
<modules>
<module>samples/openapi3/client/petstore/typescript/tests/default</module>
</modules>
</profile>
<profile>
<id>typescript-client-tests-jquery</id>
<activation>
<property>
<name>env</name>
<value>java</value>
</property>
</activation>
<modules>
<module>samples/openapi3/client/petstore/typescript/tests/jquery</module>
</modules>
</profile>
<profile>
<id>typescript-fetch-client-tests-default</id>
<activation>
@@ -1259,6 +1283,10 @@
<module>samples/client/petstore/python-tornado</module>
<module>samples/openapi3/client/petstore/python</module>
<module>samples/openapi3/client/petstore/python-experimental</module>
<module>samples/openapi3/client/petstore/typescript/builds/default</module>
<module>samples/openapi3/client/petstore/typescript/tests/default</module>
<module>samples/openapi3/client/petstore/typescript/builds/jquery</module>
<module>samples/openapi3/client/petstore/typescript/tests/jquery</module>
<module>samples/client/petstore/typescript-fetch/builds/default</module>
<module>samples/client/petstore/typescript-fetch/builds/es6-target</module>
<module>samples/client/petstore/typescript-fetch/builds/with-npm-version</module>

View File

@@ -1,28 +0,0 @@
.Rbuildignore
.gitignore
.travis.yml
DESCRIPTION
NAMESPACE
R/api_client.R
R/api_response.R
R/category.R
R/model_api_response.R
R/order.R
R/pet.R
R/pet_api.R
R/store_api.R
R/tag.R
R/user.R
R/user_api.R
README.md
docs/Category.md
docs/ModelApiResponse.md
docs/Order.md
docs/Pet.md
docs/PetApi.md
docs/StoreApi.md
docs/Tag.md
docs/User.md
docs/UserApi.md
git_push.sh
tests/testthat.R

View File

@@ -1,30 +0,0 @@
README.md
config/project-scratch-def.json
force-app/main/default/classes/OAS.cls
force-app/main/default/classes/OAS.cls-meta.xml
force-app/main/default/classes/OASApiResponse.cls
force-app/main/default/classes/OASApiResponse.cls-meta.xml
force-app/main/default/classes/OASCategory.cls
force-app/main/default/classes/OASCategory.cls-meta.xml
force-app/main/default/classes/OASClient.cls
force-app/main/default/classes/OASClient.cls-meta.xml
force-app/main/default/classes/OASOrder.cls
force-app/main/default/classes/OASOrder.cls-meta.xml
force-app/main/default/classes/OASPet.cls
force-app/main/default/classes/OASPet.cls-meta.xml
force-app/main/default/classes/OASPetApi.cls
force-app/main/default/classes/OASPetApi.cls-meta.xml
force-app/main/default/classes/OASResponseMock.cls
force-app/main/default/classes/OASResponseMock.cls-meta.xml
force-app/main/default/classes/OASStoreApi.cls
force-app/main/default/classes/OASStoreApi.cls-meta.xml
force-app/main/default/classes/OASTag.cls
force-app/main/default/classes/OASTag.cls-meta.xml
force-app/main/default/classes/OASTest.cls
force-app/main/default/classes/OASTest.cls-meta.xml
force-app/main/default/classes/OASUser.cls
force-app/main/default/classes/OASUser.cls-meta.xml
force-app/main/default/classes/OASUserApi.cls
force-app/main/default/classes/OASUserApi.cls-meta.xml
force-app/main/default/namedCredentials/OpenAPI_Petstore.namedCredential-meta.xml
sfdx-project.json

View File

@@ -1 +1 @@
5.0.0-SNAPSHOT
4.3.1-SNAPSHOT

View File

@@ -1,39 +0,0 @@
.gitignore
ApiClient.cpp
ApiClient.h
ApiConfiguration.cpp
ApiConfiguration.h
ApiException.cpp
ApiException.h
CMakeLists.txt
HttpContent.cpp
HttpContent.h
IHttpBody.h
JsonBody.cpp
JsonBody.h
ModelBase.cpp
ModelBase.h
MultipartFormData.cpp
MultipartFormData.h
Object.cpp
Object.h
README.md
api/PetApi.cpp
api/PetApi.h
api/StoreApi.cpp
api/StoreApi.h
api/UserApi.cpp
api/UserApi.h
git_push.sh
model/ApiResponse.cpp
model/ApiResponse.h
model/Category.cpp
model/Category.h
model/Order.cpp
model/Order.h
model/Pet.cpp
model/Pet.h
model/Tag.cpp
model/Tag.h
model/User.cpp
model/User.h

View File

@@ -1,30 +0,0 @@
/**
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* OpenAPI spec version: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator
* https://github.com/OpenAPITools/openapi-generator
* Do not edit the class manually.
*/
using System;
using System.IO;
using UnrealBuildTool;
public class OpenAPI : ModuleRules
{
public OpenAPI(ReadOnlyTargetRules Target) : base(Target)
{
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"Http",
"Json",
}
);
}
}

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