Compare commits

..

61 Commits

Author SHA1 Message Date
William Cheng
165549d099 update tests 2026-01-18 12:15:23 +08:00
William Cheng
2a31b78b1f update tests 2026-01-18 12:04:08 +08:00
William Cheng
7e620032a1 update tests 2026-01-18 11:57:05 +08:00
William Cheng
d3377590ee add more R samples for testing 2026-01-18 11:46:47 +08:00
William Cheng
49e3626e26 [R] update R github workflow (#22716)
* fix working dir

* fix dir

* build locally

* install devtools

* set dir

* set dir

* set current dir

* set current dir

* set current dir

* set version

* show error only

* trigger build failure

* Revert "trigger build failure"

This reverts commit 7d20114b84.

* trigger build failure testing petstore

* install local package

* install local package

* trigger build failure

* test petstore localhost

* Revert "test petstore localhost"

This reverts commit 008d23bcc2.

* undo test_petstore.R
2026-01-18 02:37:59 +08:00
William Cheng
701d1f5443 add r github workflow 2026-01-18 00:32:30 +08:00
Rens Groothuijsen
ae5848b215 fix(typescript-axios): Add missing import in case of separate models and API (#22712) 2026-01-17 15:54:08 +08:00
dependabot[bot]
87d1df3eeb build(deps): bump tar and @angular/cli (#22714)
Bumps [tar](https://github.com/isaacs/node-tar) to 7.5.3 and updates ancestor dependency [@angular/cli](https://github.com/angular/angular-cli). These dependencies need to be updated together.


Updates `tar` from 6.2.0 to 7.5.3
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.2.0...v7.5.3)

Updates `@angular/cli` from 17.1.0 to 21.1.0
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/17.1.0...v21.1.0)

---
updated-dependencies:
- dependency-name: tar
  dependency-version: 7.5.3
  dependency-type: indirect
- dependency-name: "@angular/cli"
  dependency-version: 21.1.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-17 15:53:22 +08:00
dependabot[bot]
92e1593bc0 build(deps): bump tar and @angular/cli (#22713)
Bumps [tar](https://github.com/isaacs/node-tar) to 7.5.3 and updates ancestor dependency [@angular/cli](https://github.com/angular/angular-cli). These dependencies need to be updated together.


Updates `tar` from 7.4.3 to 7.5.3
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v7.4.3...v7.5.3)

Updates `@angular/cli` from 19.0.2 to 21.1.0
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/19.0.2...v21.1.0)

---
updated-dependencies:
- dependency-name: tar
  dependency-version: 7.5.3
  dependency-type: indirect
- dependency-name: "@angular/cli"
  dependency-version: 21.1.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-17 15:53:11 +08:00
dsteeley
de464cea30 Refresh some dependencies in the Rust server generator (#22710) 2026-01-16 22:29:25 +08:00
William Cheng
e7287d1cdb Merge branch 'master' of https://github.com/openapitools/openapi-generator 2026-01-15 10:55:00 +08:00
William Cheng
06a511df31 update samples 2026-01-15 10:53:50 +08:00
dependabot[bot]
4d6d8a1edc build(deps): bump actions/setup-dotnet from 5.0.1 to 5.1.0 (#22699)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 5.0.1 to 5.1.0.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v5.0.1...v5.1.0)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-version: 5.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-15 10:49:12 +08:00
Marc Schlegel
8e0e429231 Mill Module (#22652)
* Initial Mill support

* Updated Docs after changing git and pom settings to case classes

* Update Docs

* include Mill plugin in Docker build

* fix scaladoc

* fix cubic-dev-ai findings
2026-01-14 17:29:05 +08:00
Rens Groothuijsen
e0b27485db [typescript-axios] Handle sets as arrays in input parameters (#22642)
* [typescript-axios] Handle sets as arrays in input parameters

* Include docstring to explain purpose of replaceWithSerializableTypeIfNeeded function
2026-01-14 15:43:04 +08:00
Piotr Kubowicz
643138321b [kotlin] Add integration test for query params (#22588)
This is a test for a regression from #22512 where param values
were written as a list.
2026-01-14 15:36:06 +08:00
William Cheng
b1b556ad63 [kotlin] Add integration test for query params (#22695)
* [kotlin] Add integration test for query params

This is a test for a regression from #22512 where param values
were written as a list.

* add test implementation for kotlin spring client

* add sha tests

---------

Co-authored-by: Piotr Kubowicz <piotr.kubowicz@gmail.com>
2026-01-14 15:34:53 +08:00
dependabot[bot]
7671288210 build(deps-dev): bump virtualenv in /samples/client/echo_api/python (#22691)
Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.27.1 to 20.36.1.
- [Release notes](https://github.com/pypa/virtualenv/releases)
- [Changelog](https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst)
- [Commits](https://github.com/pypa/virtualenv/compare/20.27.1...20.36.1)

---
updated-dependencies:
- dependency-name: virtualenv
  dependency-version: 20.36.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-14 14:33:31 +08:00
dependabot[bot]
287eadf4f6 build(deps-dev): bump hono (#22693)
Bumps [hono](https://github.com/honojs/hono) from 4.11.3 to 4.11.4.
- [Release notes](https://github.com/honojs/hono/releases)
- [Commits](https://github.com/honojs/hono/compare/v4.11.3...v4.11.4)

---
updated-dependencies:
- dependency-name: hono
  dependency-version: 4.11.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-14 14:31:02 +08:00
Roland Fredenhagen
4534e8d1a0 Resolve vendor extensions on schemas referenced in parameters. (#22690)
fixes #9138
2026-01-14 14:27:16 +08:00
Nicolas Rodriguez
a40dc56d38 [CRYSTAL] object_id method should be a reserved words (#22577)
* style(crystal): fix coding style

* fix(crystal): object_id is so central in Crystal that it should not be overridden by user code

See: https://crystal-lang.org/api/1.18.2/Reference.html#object_id%3AUInt64-instance-method

* style(crystal): fix coding style

* fix(crystal): fix partial_oneof_module by using a class instead of a module

Fix https://github.com/OpenAPITools/openapi-generator/issues/22563

* fix(crystal): remove non-working code

It's not working because it uses #send method which doesn't exist in Crystal

See: https://crystal-lang.org/reference/1.18/crystal_for_rubyists/metaprogramming_help.html#differences-between-ruby-and-crystal

* fix(crystal): update reserved words, separate reserved keywords from methods

* fix(crystal): update samples

* fix(crystal): set default values when options are not passed

* fix(crystal): follow up https://github.com/OpenAPITools/openapi-generator/pull/22545

* fix(crystal): remove travis.yml file

* style(crystal): fix coding style

* style(crystal): use kwargs when calling @api_client

* style(crystal): use kwargs when calling Crest::Request.new

* fix(crystal): remove useless return_type argument

* fix(crystal): map object type to JSON::Any type

* fix(crystal): reduce use of require

* fix(crystal): return early if value is nil

* fix(crystal): update doc

* fix(crystal): remove dead code

* fix(crystal): kemal is not used in specs

* fix(crystal): class name should be in PascalCase

Fix:
-  class EnumAttributeValidatorFor_type < EnumAttributeValidator
+  class EnumAttributeValidatorForType < EnumAttributeValidator

* fix(crystal): fix ameba warnings
2026-01-13 23:40:20 +08:00
Serge
7af1d025f9 fix(typesctipt-angular-21-provided-on-root): fix typo in pom (#22689) 2026-01-13 23:28:52 +08:00
William Cheng
848aecccda fix: [JAVA][SPRING] Nullaways warn with JSpecify => add missing annotation to parameter of method toIndentedString (#22685)
* fix: add annotation to method parameter

* fix: all impacted samples

---------

Co-authored-by: Philippe Kernevez <philippe@kernevez.net>
2026-01-13 12:56:31 +08:00
William Cheng
e8faf7c46b update swift samples 2026-01-13 11:27:52 +08:00
dependabot[bot]
4ec77dd283 build(deps-dev): bump js-yaml (#22680)
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.0 to 4.1.1.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.1.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 11:19:50 +08:00
dependabot[bot]
c51253c40f build(deps): bump qs and body-parser (#22681)
Bumps [qs](https://github.com/ljharb/qs) and [body-parser](https://github.com/expressjs/body-parser). These dependencies needed to be updated together.

Updates `qs` from 6.13.0 to 6.14.1
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.13.0...v6.14.1)

Updates `body-parser` from 1.20.3 to 1.20.4
- [Release notes](https://github.com/expressjs/body-parser/releases)
- [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/body-parser/compare/1.20.3...1.20.4)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.1
  dependency-type: indirect
- dependency-name: body-parser
  dependency-version: 1.20.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 11:19:39 +08:00
Devon
ae42568b26 [Rust] Enum Query Parameter Serialization Fixes (#22683)
* [Rust] Enum Query Parameter Serialization Fixes

Adds tests to ensure this won't regress again. Also fixes some other compile errors with Box<> and file uploads.

* Remove duplicate query param integration tests from petstore samples

* re-gen samples

* fix enum boxing tests

* stream files

* samples

* doc generator fix & snapshot

* doc generation fixes, update samples

* another attempt to fix the doc generator

* improve doc generation - don't try link to internal models, and fixing links missing in some scenarios

the rust doc generator will be the death of me

* also fix hyper

* applying same fix to hyper

* snapshot fixes
2026-01-13 11:18:56 +08:00
Lennard Sprong
58b12baed2 [Swift6] Remove QueryStringEncodable for models (#21150) 2026-01-12 21:42:20 +00:00
Jason Ak
eb65e93e1d Fix Swift oneOf discriminator decoding with enumUnknownDefaultCase (#22635) 2026-01-12 19:32:48 +00:00
dependabot[bot]
d90bfe093c build(deps): bump qs, body-parser and express (#22678)
Bumps [qs](https://github.com/ljharb/qs), [body-parser](https://github.com/expressjs/body-parser) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `qs` from 6.14.0 to 6.14.1
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.14.0...v6.14.1)

Updates `body-parser` from 1.20.3 to 1.20.4
- [Release notes](https://github.com/expressjs/body-parser/releases)
- [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/body-parser/compare/1.20.3...1.20.4)

Updates `express` from 4.21.2 to 4.22.1
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/v4.22.1/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.21.2...v4.22.1)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.1
  dependency-type: indirect
- dependency-name: body-parser
  dependency-version: 1.20.4
  dependency-type: indirect
- dependency-name: express
  dependency-version: 4.22.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 01:11:19 +08:00
Serge
08367def47 feat(typescript-angular): add angular 21 support (#22636) 2026-01-13 01:10:57 +08:00
William Cheng
450215c4b8 [BUG][JAVA][SPRING] api util must test variable nullity (#22679)
* fix: test if return type is null before using it

* chore: change sample example

---------

Co-authored-by: Philippe Kernevez <philippe@kernevez.net>
2026-01-12 16:41:08 +08:00
Devon
99baae676b [cpp] Fix Nested Map & Additional Properties Support (#22639)
* progress fixing nested map support in cpp generators

* [cpp] Fix Nested Map Support

* additional properties support

* fix potential np3

* cleanup javadoc

* inline

* explicit memory header
2026-01-12 15:54:21 +08:00
dependabot[bot]
f2a6b6d8b4 build(deps): bump qs (#22676)
Bumps [qs](https://github.com/ljharb/qs) from 6.12.1 to 6.14.1.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.12.1...v6.14.1)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-12 15:48:20 +08:00
Julian Vennen
2b1f61874e [php][php-nextgen] Mark nullable things as nullable in phpdoc (#22650)
* [php][php-nextgen] Mark nullable things as nullable in phpdoc

* [php][php-nextgen] Use exts instead of vendorExtensions
2026-01-12 13:47:11 +08:00
Alec Petersen
17f2e4634c [REQ][CSHARP] Make TokenProvider abstract method GetAsync protected for override (#22615)
* Make GetAsync protected internal so external inheritors can override it

* Remove non-zero token length requirement to allow more flexibility for token providers

* Update samples
2026-01-12 13:46:03 +08:00
Mattias Sehlstedt
b3c4c49430 [Fix] [Regression] Resolve the discriminator type from a 3.1 sibling (#22634)
* Add support for resolving the discriminator type from a 3.1 sibling

* Set gradlew to be executable

* Generate sample FILES again
2026-01-12 13:44:55 +08:00
Devon
dff00c86d6 Fix nested map support (#22643)
Also includes a fix for enums :)
2026-01-12 13:42:16 +08:00
Eren
0da98b06f8 [kotlin-client] Fix enum @JsonCreator to throw for unknown values (#22663) 2026-01-12 13:34:50 +08:00
dependabot[bot]
a2883f3d20 build(deps): bump urllib3 in /samples/client/echo_api/python (#22667)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.0 to 2.6.3.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.6.0...2.6.3)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-version: 2.6.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-12 13:33:55 +08:00
dependabot[bot]
d2556d453b build(deps): bump qs (#22669)
Bumps [qs](https://github.com/ljharb/qs) from 6.14.0 to 6.14.1.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.14.0...v6.14.1)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-12 12:39:12 +08:00
William Cheng
b2254b9b94 Normalize headers in components (#22672)
* normalize headers in components

* update comment
2026-01-12 12:26:17 +08:00
William Cheng
f1322a0c5a Better null check in normalizeSchema (#22671)
* uncomment exit

* better null check when normalizing schema
2026-01-11 22:01:44 +08:00
William Cheng
23dae2bcd8 Improve sample update with better exception handling (#22670)
* refactor java tests

* better exception handling when updating samples

* update samples to avoid using exception

* fix tests

* update

* update
2026-01-11 18:52:29 +08:00
dependabot[bot]
46c2c30f47 build(deps): bump qs from 6.14.0 to 6.14.1 in /website (#22668)
Bumps [qs](https://github.com/ljharb/qs) from 6.14.0 to 6.14.1.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.14.0...v6.14.1)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-11 15:54:11 +08:00
William Cheng
73a24f02d9 clean up ts angular v12-v15 samples (#22647) 2026-01-11 15:47:15 +08:00
Bruno Coelho
ae8352d9f9 [swift][client] Update CI to use Xcode 26 (#22648)
* [swift][client] Remove old objc samples

* [swift][client] Update CI to use Xcode 26

* Revert "[swift][client] Remove old objc samples"

This reverts commit b50189968b.
2026-01-09 10:30:18 +00:00
dependabot[bot]
32aa36d821 build(deps-dev): bump qs (#22645)
Bumps [qs](https://github.com/ljharb/qs) from 6.13.0 to 6.14.1.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.13.0...v6.14.1)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-09 13:06:49 +08:00
William Cheng
944533826b update python fastapi urllib3 to newer version (#22644) 2026-01-09 12:49:38 +08:00
Neeme Praks
753330dd99 [kotlin-spring] Revert nested property placeholder in @RequestMapping that Spring cannot resolve (#22625) 2026-01-06 22:19:42 +08:00
Jay
8a82a3eeb2 Add Sarvika Technologies to the list of links (#22628) 2026-01-06 22:16:01 +08:00
dependabot[bot]
570915e028 build(deps): bump qs (#22606)
Bumps [qs](https://github.com/ljharb/qs) from 6.10.3 to 6.14.1.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.10.3...v6.14.1)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-31 16:47:43 +08:00
dependabot[bot]
83f712467a build(deps): bump uri in /samples/client/petstore/ruby-faraday (#22607)
Bumps [uri](https://github.com/ruby/uri) from 0.13.0 to 0.13.3.
- [Release notes](https://github.com/ruby/uri/releases)
- [Commits](https://github.com/ruby/uri/compare/v0.13.0...v0.13.3)

---
updated-dependencies:
- dependency-name: uri
  dependency-version: 0.13.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-31 16:47:28 +08:00
dependabot[bot]
b2ef09b7fd build(deps): bump qs and express (#22608)
Bumps [qs](https://github.com/ljharb/qs) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `qs` from 6.11.0 to 6.14.1
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.11.0...v6.14.1)

Updates `express` from 4.18.2 to 4.22.1
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/v4.22.1/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.2...v4.22.1)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.1
  dependency-type: indirect
- dependency-name: express
  dependency-version: 4.22.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-31 15:26:17 +08:00
William Cheng
d39e015487 Use defer file close in go client (#22596)
* use defer file close in go client

* update samples
2025-12-30 18:27:10 +08:00
William Cheng
cd01ba9bc1 Update go samples with more tests, improve code format (#22593)
* update go samples with more tests

* fix indention
2025-12-30 14:40:46 +08:00
Eric Huang
97af5d1f52 [go] fix default value for array type (#22584)
* Better handling array schema for `toDefaultValue`

Signed-off-by: titaneric <chenyihuang001@gmail.com>

* add string array and enum array to existing test case

Signed-off-by: titaneric <chenyihuang001@gmail.com>

---------

Signed-off-by: titaneric <chenyihuang001@gmail.com>
2025-12-30 14:08:24 +08:00
Xi Lu
873e27233b [Normalizer] Copy the title field when creating the ArraySchema object in processNormalize31Spec (#22592)
* Copy the title field when creating the ArraySchema object in processNormalize31Spec()

* Copy the title field when creating the ArraySchema object in processNormalize31Spec()
2025-12-30 14:00:12 +08:00
Linh Tran Tuan
80be730dcd [Rust-Axum] Adapt changes upon Host extractor sunset (#22585)
* [Rust-Axum] Adapt changes upon Host extractor sunset

* Update
2025-12-30 13:49:25 +08:00
dependabot[bot]
19945d909f build(deps): bump actions/setup-dotnet from 5.0.0 to 5.0.1 (#22580)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 5.0.0 to 5.0.1.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v5...v5.0.1)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-version: 5.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 13:25:02 +08:00
William Cheng
23eff6672f Prepare v7.19.0 snapshot (#22569)
* Revert "7.18.0 release (#22567)"

This reverts commit 51228436e0.

* prepare 7.19.0 snapshot

* update samples

* update doc

* fix date
2025-12-22 19:25:23 +08:00
3022 changed files with 52172 additions and 136350 deletions

View File

@@ -26,7 +26,7 @@ jobs:
- samples/server/petstore/aspnet/fastendpoints-useValidators
steps:
- uses: actions/checkout@v5
- uses: actions/setup-dotnet@v5.0.1
- uses: actions/setup-dotnet@v5.1.0
with:
dotnet-version: '8.0.x'
- name: Build

View File

@@ -26,7 +26,7 @@ jobs:
- samples/client/petstore/csharp/restsharp/standard2.0/Petstore/
steps:
- uses: actions/checkout@v5
- uses: actions/setup-dotnet@v5.0.1
- uses: actions/setup-dotnet@v5.1.0
with:
dotnet-version: '7.0.x'
- name: Build

View File

@@ -28,7 +28,7 @@ jobs:
# - samples/client/petstore/csharp/unityWebRequest/standard2.0/Petstore/
steps:
- uses: actions/checkout@v5
- uses: actions/setup-dotnet@v5.0.1
- uses: actions/setup-dotnet@v5.1.0
with:
dotnet-version: 3.1.*
- name: Build

View File

@@ -48,7 +48,7 @@ jobs:
# - samples/client/petstore/csharp/unityWebRequest/net10/Petstore
steps:
- uses: actions/checkout@v5
- uses: actions/setup-dotnet@v5.0.0
- uses: actions/setup-dotnet@v5.1.0
with:
dotnet-version: '10.x'
- name: Build

View File

@@ -25,7 +25,7 @@ jobs:
- samples/server/petstore/aspnetcore-6.0-useSwashBuckle
steps:
- uses: actions/checkout@v5
- uses: actions/setup-dotnet@v5.0.1
- uses: actions/setup-dotnet@v5.1.0
with:
dotnet-version: '6.0.x'
- name: Build

View File

@@ -43,7 +43,7 @@ jobs:
- samples/client/petstore/csharp/restsharp/standard2.0/ConditionalSerialization/
steps:
- uses: actions/checkout@v5
- uses: actions/setup-dotnet@v5.0.1
- uses: actions/setup-dotnet@v5.1.0
with:
dotnet-version: '7.0.x'
- name: Build

View File

@@ -19,7 +19,7 @@ jobs:
- samples/client/echo_api/csharp/restsharp/net8/EchoApi
steps:
- uses: actions/checkout@v5
- uses: actions/setup-dotnet@v5.0.1
- uses: actions/setup-dotnet@v5.1.0
with:
dotnet-version: '8.0.x'
- name: Run echo server

View File

@@ -19,7 +19,7 @@ jobs:
- samples/client/petstore/csharp/restsharp/net8/useVirtualForHooks/
steps:
- uses: actions/checkout@v5
- uses: actions/setup-dotnet@v5.0.1
- uses: actions/setup-dotnet@v5.1.0
with:
dotnet-version: '8.0.x'
- name: Build

View File

@@ -26,7 +26,7 @@ jobs:
- samples/server/petstore/aspnetcore-8.0-use-centralized-package-version-management
steps:
- uses: actions/checkout@v5
- uses: actions/setup-dotnet@v5.0.1
- uses: actions/setup-dotnet@v5.1.0
with:
dotnet-version: '8.0.x'
- name: Build

View File

@@ -34,7 +34,7 @@ jobs:
- samples/client/petstore/csharp/generichost/net8/UseDateTimeForDate
steps:
- uses: actions/checkout@v5
- uses: actions/setup-dotnet@v5.0.1
- uses: actions/setup-dotnet@v5.1.0
with:
dotnet-version: '8.0.x'
- name: Build

View File

@@ -41,7 +41,7 @@ jobs:
#- samples/client/petstore/csharp/unityWebRequest/net9/Petstore
steps:
- uses: actions/checkout@v5
- uses: actions/setup-dotnet@v5.0.1
- uses: actions/setup-dotnet@v5.1.0
with:
dotnet-version: '9.0.x'
- name: Build

View File

@@ -7,6 +7,7 @@ on:
- samples/client/petstore/java/webclient-jakarta/**
- samples/client/petstore/java/restclient-*/**
- samples/client/others/java/webclient-sealedInterface/**
- samples/client/others/java/webclient-sealedInterface_3_1/**
- samples/client/petstore/java/webclient-useSingleRequestParameter/**
- samples/client/others/java/restclient-enum-in-multipart/**
pull_request:
@@ -15,6 +16,7 @@ on:
- samples/client/petstore/java/webclient-jakarta/**
- samples/client/petstore/java/restclient-*/**
- samples/client/others/java/webclient-sealedInterface/**
- samples/client/others/java/webclient-sealedInterface_3_1/**
- samples/client/petstore/java/webclient-useSingleRequestParameter/**
- samples/client/others/java/restclient-enum-in-multipart/**
jobs:
@@ -34,6 +36,7 @@ jobs:
- samples/client/petstore/java/restclient-useSingleRequestParameter
- samples/client/petstore/java/restclient-useSingleRequestParameter-static
- samples/client/others/java/webclient-sealedInterface
- samples/client/others/java/webclient-sealedInterface_3_1
- samples/client/petstore/java/webclient-useSingleRequestParameter
- samples/client/others/java/restclient-enum-in-multipart
steps:

View File

@@ -23,7 +23,7 @@ jobs:
- samples/server/petstore/java-play-framework-fake-endpoints
- samples/server/petstore/java-play-framework-fake-endpoints-with-security
- samples/server/petstore/java-play-framework-no-bean-validation
- samples/server/petstore/java-play-framework-no-exception-handling
- samples/server/petstore/java-play-framework-no-excp-handling
- samples/server/petstore/java-play-framework-no-interface
- samples/server/petstore/java-play-framework-no-nullable
- samples/server/petstore/java-play-framework-no-swagger-ui

69
.github/workflows/samples-r.yaml vendored Normal file
View File

@@ -0,0 +1,69 @@
name: Samples R clients
on:
push:
paths:
- samples/client/petstore/R/**
- samples/client/petstore/R-httr2/**
- samples/client/petstore/R-httr2-wrapper/**
pull_request:
paths:
- samples/client/petstore/R/**
- samples/client/petstore/R-httr2/**
- samples/client/petstore/R-httr2-wrapper/**
jobs:
build:
name: Build R projects
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
sample:
# clients
- samples/client/petstore/R/
- samples/client/petstore/R-httr2/
- samples/client/petstore/R-httr2-wrapper/
services:
petstore-api:
image: swaggerapi/petstore
ports:
- 80:8080
env:
SWAGGER_HOST: http://petstore.swagger.io
SWAGGER_BASE_PATH: /v2
steps:
- uses: actions/checkout@v5
- name: Add hosts to /etc/hosts
run: |
sudo echo "127.0.0.1 petstore.swagger.io" | sudo tee -a /etc/hosts
- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true
- name: Install devtools
run: install.packages("devtools")
shell: Rscript {0}
working-directory: ${{ matrix.sample }}
- name: Build
run: devtools::build()
shell: Rscript {0}
working-directory: ${{ matrix.sample }}
- uses: r-lib/actions/setup-r-dependencies@v2
with:
working-directory: ${{ matrix.sample }}
extra-packages: any::rcmdcheck
needs: check
- uses: r-lib/actions/check-r-package@v2
with:
working-directory: ${{ matrix.sample }}
upload-snapshots: true
build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")'
error-on: '"error"'
- name: Install local package
run: R CMD INSTALL .
shell: bash # Ensure correct shell for command execution
working-directory: ${{ matrix.sample }}

View File

@@ -42,7 +42,7 @@ jobs:
- samples/openapi3/server/petstore/springboot-delegate
- samples/openapi3/server/petstore/spring-boot-oneof
- samples/server/petstore/spring-boot-nullable-set
- samples/server/petstore/spring-boot-defaultInterface-unhandledException
- samples/server/petstore/spring-boot-defaultInterface-unhandledExcp
- samples/server/petstore/springboot
- samples/server/petstore/springboot-beanvalidation
- samples/server/petstore/springboot-builtin-validation

View File

@@ -15,6 +15,7 @@ on:
- samples/client/petstore/typescript-angular-v18-provided-in-root/**
- samples/client/petstore/typescript-angular-v19-provided-in-root/**
- samples/client/petstore/typescript-angular-v20-provided-in-root/**
- samples/client/petstore/typescript-angular-v21-provided-in-root/**
- samples/openapi3/client/petstore/typescript/builds/default/**
# comment out due to build failure
#- samples/openapi3/client/petstore/typescript/tests/default/**
@@ -54,6 +55,7 @@ on:
- samples/client/petstore/typescript-angular-v18-provided-in-root/**
- samples/client/petstore/typescript-angular-v19-provided-in-root/**
- samples/client/petstore/typescript-angular-v20-provided-in-root/**
- samples/client/petstore/typescript-angular-v21-provided-in-root/**
- samples/openapi3/client/petstore/typescript/builds/default/**
#- samples/openapi3/client/petstore/typescript/tests/default/**
- samples/openapi3/client/petstore/typescript/builds/jquery/**
@@ -104,6 +106,7 @@ jobs:
- samples/client/petstore/typescript-angular-v18-provided-in-root/
- samples/client/petstore/typescript-angular-v19-provided-in-root/
- samples/client/petstore/typescript-angular-v20-provided-in-root/
- samples/client/petstore/typescript-angular-v21-provided-in-root/
- samples/openapi3/client/petstore/typescript/builds/default/
#- samples/openapi3/client/petstore/typescript/tests/default/
- samples/openapi3/client/petstore/typescript/builds/jquery/

View File

@@ -13,6 +13,7 @@ COPY ./google_checkstyle.xml ${GEN_DIR}
# All poms are copied, then we go offline, to allow for better caching of code changes without fetching all dependencies each time
COPY ./modules/openapi-generator-gradle-plugin/pom.xml ${GEN_DIR}/modules/openapi-generator-gradle-plugin/
COPY ./modules/openapi-generator-maven-plugin/pom.xml ${GEN_DIR}/modules/openapi-generator-maven-plugin/
COPY ./modules/openapi-generator-mill-plugin/pom.xml ${GEN_DIR}/modules/openapi-generator-mill-plugin/
COPY ./modules/openapi-generator-online/pom.xml ${GEN_DIR}/modules/openapi-generator-online/
COPY ./modules/openapi-generator-cli/pom.xml ${GEN_DIR}/modules/openapi-generator-cli/
COPY ./modules/openapi-generator-core/pom.xml ${GEN_DIR}/modules/openapi-generator-core/
@@ -23,6 +24,7 @@ RUN mvn dependency:go-offline
# Modules are copied individually here to allow for caching of docker layers between major.minor versions
COPY ./modules/openapi-generator-gradle-plugin ${GEN_DIR}/modules/openapi-generator-gradle-plugin
COPY ./modules/openapi-generator-maven-plugin ${GEN_DIR}/modules/openapi-generator-maven-plugin
COPY ./modules/openapi-generator-mill-plugin ${GEN_DIR}/modules/openapi-generator-mill-plugin
COPY ./modules/openapi-generator-online ${GEN_DIR}/modules/openapi-generator-online
COPY ./modules/openapi-generator-cli ${GEN_DIR}/modules/openapi-generator-cli
COPY ./modules/openapi-generator-core ${GEN_DIR}/modules/openapi-generator-core

View File

@@ -757,6 +757,7 @@ Here are some companies/projects (alphabetical order) using OpenAPI Generator in
- [REST United](https://restunited.com)
- [Robocorp](https://www.robocorp.com)
- [Robotinfra](https://www.robotinfra.com)
- [Sarvika Technologies Pvt. Ltd.](https://www.sarvika.com)
- [SearchApi](https://www.searchapi.io/)
- [SmartHR](https://smarthr.co.jp/)
- [Sony Interactive Entertainment](https://www.sie.com/en/index.html)

View File

@@ -1,5 +1,6 @@
generatorName: go
outputDir: samples/client/others/go/allof_multiple_ref_and_discriminator
inputSpec: modules/openapi-generator/src/test/resources/3_0/go/allof_multiple_ref_and_discriminator.yaml
templateDir: modules/openapi-generator/src/main/resources/go
additionalProperties:
hideGenerationTimestamp: "true"

View File

@@ -1,5 +1,6 @@
generatorName: go
outputDir: samples/client/others/go/oneof-anyof-required
inputSpec: modules/openapi-generator/src/test/resources/3_0/go/spec-with-oneof-anyof-required.yaml
templateDir: modules/openapi-generator/src/main/resources/go
additionalProperties:
hideGenerationTimestamp: "true"

View File

@@ -1,6 +1,7 @@
generatorName: go
outputDir: samples/client/others/go/oneof-discriminator-lookup
inputSpec: modules/openapi-generator/src/test/resources/3_0/go/spec-with-oneof-discriminator.yaml
templateDir: modules/openapi-generator/src/main/resources/go
additionalProperties:
useOneOfDiscriminatorLookup: "true"
hideGenerationTimestamp: "true"

View File

@@ -1,5 +1,5 @@
generatorName: java-play-framework
outputDir: samples/server/petstore/java-play-framework-no-exception-handling
outputDir: samples/server/petstore/java-play-framework-no-excp-handling
inputSpec: modules/openapi-generator/src/test/resources/2_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/JavaPlayFramework
additionalProperties:

View File

@@ -0,0 +1,10 @@
generatorName: java
outputDir: samples/client/others/java/webclient-sealedInterface_3_1
library: webclient
inputSpec: modules/openapi-generator/src/test/resources/3_1/oneof_polymorphism_and_inheritance.yaml
templateDir: modules/openapi-generator/src/main/resources/Java
additionalProperties:
artifactId: sealed-interface-webclient
hideGenerationTimestamp: "true"
useOneOfInterfaces: true
useSealedOneOfInterfaces: true

View File

@@ -0,0 +1,9 @@
generatorName: rust
outputDir: samples/client/others/rust/reqwest/multipart-async
library: reqwest
inputSpec: modules/openapi-generator/src/test/resources/3_0/rust/multipart-file-upload.yaml
templateDir: modules/openapi-generator/src/main/resources/rust
additionalProperties:
supportAsync: true
useSingleRequestParameter: true
packageName: multipart-upload-reqwest-async

View File

@@ -1,5 +1,5 @@
generatorName: spring
outputDir: samples/server/petstore/spring-boot-defaultInterface-unhandledException
outputDir: samples/server/petstore/spring-boot-defaultInterface-unhandledExcp
inputSpec: modules/openapi-generator/src/test/resources/3_0/spring/petstore-with-fake-endpoints-models-for-testing.yaml
templateDir: modules/openapi-generator/src/main/resources/JavaSpring
additionalProperties:

View File

@@ -1,6 +0,0 @@
generatorName: typescript-angular
outputDir: samples/client/petstore/typescript-angular-v12-oneOf/builds/default
inputSpec: modules/openapi-generator/src/test/resources/3_0/oneOfArrayMapImport.yaml
templateDir: modules/openapi-generator/src/main/resources/typescript-angular
additionalProperties:
ngVersion: 12.2.0

View File

@@ -1,7 +0,0 @@
generatorName: typescript-angular
outputDir: samples/client/petstore/typescript-angular-v12-provided-in-any/builds/default
inputSpec: modules/openapi-generator/src/test/resources/2_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/typescript-angular
additionalProperties:
ngVersion: 12.2.0
providedIn: any

View File

@@ -1,10 +0,0 @@
generatorName: typescript-angular
outputDir: samples/client/petstore/typescript-angular-v12-provided-in-root/builds/with-npm
inputSpec: modules/openapi-generator/src/test/resources/2_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/typescript-angular
additionalProperties:
ngVersion: 12.2.0
npmVersion: 1.0.0
npmName: '@openapitools/typescript-angular-petstore'
npmRepository: https://skimdb.npmjs.com/registry
snapshot: false

View File

@@ -1,6 +0,0 @@
generatorName: typescript-angular
outputDir: samples/client/petstore/typescript-angular-v12-provided-in-root/builds/default
inputSpec: modules/openapi-generator/src/test/resources/2_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/typescript-angular
additionalProperties:
ngVersion: 12.2.0

View File

@@ -1,6 +0,0 @@
generatorName: typescript-angular
outputDir: samples/client/petstore/typescript-angular-v13-oneOf/builds/default
inputSpec: modules/openapi-generator/src/test/resources/3_0/oneOfArrayMapImport.yaml
templateDir: modules/openapi-generator/src/main/resources/typescript-angular
additionalProperties:
ngVersion: 13.0.1

View File

@@ -1,7 +0,0 @@
generatorName: typescript-angular
outputDir: samples/client/petstore/typescript-angular-v13-provided-in-any/builds/default
inputSpec: modules/openapi-generator/src/test/resources/2_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/typescript-angular
additionalProperties:
ngVersion: 13.0.1
providedIn: any

View File

@@ -1,11 +0,0 @@
generatorName: typescript-angular
outputDir: samples/client/petstore/typescript-angular-v13-provided-in-root/builds/with-npm
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/typescript-angular
additionalProperties:
ngVersion: 13.0.1
npmVersion: 1.0.0
npmName: '@openapitools/typescript-angular-petstore'
npmRepository: https://skimdb.npmjs.com/registry
snapshot: false
supportsES6: true

View File

@@ -1,8 +0,0 @@
generatorName: typescript-angular
outputDir: samples/client/petstore/typescript-angular-v14-query-param-object-format
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/typescript-angular
additionalProperties:
ngVersion: 14.0.5
supportsES6: true
queryParamObjectFormat: json

View File

@@ -1,7 +0,0 @@
generatorName: typescript-angular
outputDir: samples/client/petstore/typescript-angular-v15-provided-in-root/builds/default
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/typescript-angular
additionalProperties:
ngVersion: 15.0.3
supportsES6: true

View File

@@ -1,7 +1,10 @@
generatorName: typescript-angular
outputDir: samples/client/petstore/typescript-angular-v13-provided-in-root/builds/default
outputDir: samples/client/petstore/typescript-angular-v21-provided-in-root/builds/default
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/typescript-angular
additionalProperties:
ngVersion: 13.0.1
ngVersion: 21.0.0
supportsES6: true
ngVersion21: true
enumNameMappings:
delivered: SHIPPED

View File

@@ -1,7 +1,9 @@
generatorName: typescript-angular
outputDir: samples/client/petstore/typescript-angular-v14-provided-in-root/builds/default
outputDir: samples/client/petstore/typescript-angular-v21/builds/default
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/typescript-angular
additionalProperties:
ngVersion: 14.0.5
ngVersion: 21.0.0
npmName: sample-angular-21-0-0
supportsES6: true
ngVersion21: true

View File

@@ -60,6 +60,13 @@ else
# shellcheck disable=SC2086
# shellcheck disable=SC2068
java ${JAVA_OPTS} -jar "$executable" batch ${BATCH_OPTS} --includes-base-dir "${root}" --fail-fast -- ${files[@]}
if java ${JAVA_OPTS} -jar "$executable" batch ${BATCH_OPTS} --includes-base-dir "${root}" --fail-fast -- ${files[@]} 2>&1 | tee /dev/pts/0 | grep -q -i "exception"; then
echo "Found exception(s) when running the generator(s) to update the samples."
export GENERATE_ERROR=1
fi
fi
if [[ -n "$GENERATE_ERROR" ]]; then
echo "Found exception(s) when running the generator(s) to update the samples."
exit 1
fi

View File

@@ -62,3 +62,7 @@
sha256: b2093528aac971193f2863a70f46eea45cf8bda79120b133a614599e80d8b46d
- filename: "samples/server/petstore/rust-axum/output/openapi-v3/tests/oneof_untagged.rs"
sha256: 1d3fb01f65e98290b1d3eece28014c7d3e3f2fdf18e7110249d3c591cc4642ab
- filename: "samples/client/petstore/kotlin-jvm-spring-3-restclient/src/test/kotlin/org/openapitools/integration/PetApiTest.kt"
sha256: 82a6be39c1ed3dada96dfa1833a6709834cb3f9f9d50a19cbd9d49699e46df4f
- filename: "samples/client/petstore/kotlin-jvm-spring-3-restclient/src/test/kotlin/org/openapitools/integration/UserApiTest.kt"
sha256: bc64fb94857a3598e1332f1278307c3078ea9ec4b4aa75690e6eda86e9729a8d

View File

@@ -48,4 +48,4 @@ workflows:
meta:
bitrise.io:
stack: osx-xcode-16.3.x
stack: osx-xcode-26.2.x

View File

@@ -94,7 +94,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
<li>if</li>
<li>in</li>
<li>include</li>
<li>instance</li>
<li>instance_sizeof</li>
<li>is_a?</li>
<li>lib</li>
<li>macro</li>
@@ -102,9 +102,11 @@ These options may be applied as additional-properties (cli) or configOptions (pl
<li>next</li>
<li>nil</li>
<li>nil?</li>
<li>object_id</li>
<li>of</li>
<li>out</li>
<li>pointerof</li>
<li>previous_def</li>
<li>private</li>
<li>protected</li>
<li>require</li>

View File

@@ -11,7 +11,7 @@ title: Documentation for the typescript-angular Generator
| generator type | CLIENT | |
| generator language | Typescript | |
| generator default templating engine | mustache | |
| helpTxt | Generates a TypeScript Angular (9.x - 20.x) client library. | |
| helpTxt | Generates a TypeScript Angular (9.x - 21.x) client library. | |
## CONFIG OPTIONS
These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details.
@@ -34,7 +34,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|modelPropertyNaming|Naming convention for the property: 'camelCase', 'PascalCase', 'snake_case' and 'original', which keeps the original name. Only change it if you provide your own run-time code for (de-)serialization of models| |original|
|modelSuffix|The suffix of the generated model.| |null|
|ngPackagrVersion|The version of ng-packagr compatible with Angular (see ngVersion option).| |null|
|ngVersion|The version of Angular. (At least 9.0.0)| |20.0.0|
|ngVersion|The version of Angular. (At least 9.0.0)| |21.0.0|
|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|

View File

@@ -120,3 +120,56 @@ openApiGenerate {
```
*If you want to create separate tasks (for example when you have more than one api spec and require different parameters for each), this is how to do so in Gradle 7+: `tasks.register('taskName', org.openapitools.generator.gradle.plugin.tasks.GenerateTask) { ... }`.*
## Mill
This Mill library provides a Mill module that can be used to generate code from OpenAPI specifications.
### Example
```scala
//| mill-version: 1.0.6
//| mvnDeps:
//| - org.openapitools:openapi-generator-mill-plugin:7.19.0 # 1.
import mill.*
import org.openapitools.generator.mill.OpenApiModule // 2.
object `package` extends JavaModule with MavenModule with OpenApiModule { // 3.
// other Mill config...
object openapi extends OpenApiConfig { // 4.
def inputSpec: T[PathRef] = Task.Source(BuildCtx.workspaceRoot / "api" / "petstore.yaml")
// other config options...
}
override def generatedSources: T[Seq[PathRef]] = Seq(
PathRef(Task.dest),
openapi.generate(), // 5.
)
}
```
1. Add the plugin to your `build.mill` as `mvnDeps` in the header section
2. import `org.openapitools.generator.mill.OpenApiModule`
3. add `OpenApiModule` to the module definition
4. configure 1-n `OpenApiConfig` as sub-modules
5. run the generation as part of the `compile` task
This gives access to the following tasks:
| Task | Description |
|---------------------------|---------------------------------------------------------------------------------------------|
| <configName>.generate | Generate code via Open API Tools Generator for Open API 2.0 or 3.x specification documents. |
| <configName>.validateSpec | Validates the configured spec |
and a command
| Command | Description |
|---------------------|------------------------------------------------|
| validateOpenapiSpec | Takes the path to a spec file and validates it |
For full details of all options, see the [plugin README](https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator-mill-plugin).

View File

@@ -0,0 +1,173 @@
openapi-generator-mill-plugin
============================
A [Mill](https://mill-build.org) library to support the OpenAPI generator project.
Usage
============================
1. Add the plugin to your `build.mill` as `mvnDeps` in the header section
2. import `org.openapitools.generator.mill.OpenApiModule`
3. add `OpenApiModule` to the module definition
4. configure 1-n `OpenApiConfig` as sub-modules
```scala
//| mill-version: 1.0.6
//| mvnDeps:
//| - org.openapitools:openapi-generator-mill-plugin:7.19.0 # 1.
import mill.*
import org.openapitools.generator.mill.OpenApiModule // 2.
object `package` extends JavaModule with MavenModule with OpenApiModule { // 3.
override def mvnDeps = Seq(
mvn"jakarta.platform:jakarta.jakartaee-api:11.0.0",
mvn"com.fasterxml.jackson.core:jackson-databind:2.20.0",
)
object openapi extends OpenApiConfig { // 4.
def inputSpec: T[PathRef] = Task.Source(BuildCtx.workspaceRoot / "api" / "petstore.yaml")
def apiPackage: T[String] = "com.acme.foo.boundary.web.api"
def modelPackage: T[String] = "com.acme.foo.boundary.web.model"
def generatorName: T[String] = "jaxrs-spec"
def sourceFolder: T[String] = "src/main/java"
def additionalProperties: T[Map[String, String]] = Map(
"dateLibrary" -> "java8",
"useJakartaEe" -> "true",
"useSwaggerAnnotations" -> "false",
"interfaceOnly" -> "true",
"useTags" -> "true",
)
}
}
```
Followed by:
```bash
mill openapi.generate
```
Usually you want to include the generation to the `compile` phase and have the sources in your source-tree which can
be achieved by adding the generation task to the `generatedSources`.
```scala
override def generatedSources: T[Seq[PathRef]] = Seq(
PathRef(Task.dest),
openapi.generate(),
)
```
Followed by:
```bash
mill __.compile
```
This works because `generatedSources` expects a list of `PathRef`s which constitute all folders that contain additional
(generated) sources and the `generate` task from each `OpenApiConfig` returns a `PathRef` to the folder where it put the sources.
### General Configuration parameters for OpenApiConfig
| Option | Description |
|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `verbose` | verbose mode (`false` by default) |
| `inputSpec` | OpenAPI Spec file path |
| `inputSpecRootDirectory` | Local root folder with spec file(s) |
| `mergedFileName` | Name of the file that will contain all merged specs |
| `generatorName` | target generator name |
| `cleanupOutput` | Defines whether the output directory should be cleaned up before generating the output (`false` by default). |
| `cleanup` | Defines a task which contains an `Option[Path => Unit]` which is called after the generation completed. Useful for instance to delete generated Types which are already replaced by import/type-mappings. |
| `gitSettings` | sets Git information of the project (with `host`, `userId` and `repoId`) |
| `templateDirectory` | directory with mustache templates |
| `engine` | The name of templating engine to use, "mustache" (default) or "handlebars" (beta) |
| `auth` | adds authorization headers when fetching the OpenAPI definitions remotely. Pass in a URL-encoded string of `name:header` with a comma separating multiple values |
| `skipOverwrite` | Specifies if the existing files should be overwritten during the generation. (`false` by default) |
| `apiPackage` | the package to use for generated api objects/classes |
| `modelPackage` | the package to use for generated model objects/classes |
| `invokerPackage` | the package to use for the generated invoker objects |
| `packageName` | the default package name to use for the generated objects |
| `artifactSettings` | sets project information in generated pom.xml/build.gradle or other build script. Language-specific conversions occur in non-jvm generators |
| `library` | library template (sub-template) |
| `modelNamePrefix` | Sets the prefix for model classes and enums |
| `modelNameSuffix` | Sets the suffix for model classes and enums |
| `apiNameSuffix` | Sets the suffix for api classes |
| `ignoreFileOverride` | specifies the full path to a `.openapi-generator-ignore` used for pattern based overrides of generated outputs |
| `httpUserAgent` | Sets custom User-Agent header value |
| `removeOperationIdPrefix` | remove operationId prefix (e.g. user_getName => getName) |
| `skipOperationExample` | skip examples defined in the operation |
| `logToStderr` | write all log messages (not just errors) to STDERR |
| `enablePostProcessFile` | post-processing hook |
| `skipValidateSpec` | Whether or not to skip validating the input spec prior to generation. By default, invalid specifications will result in an error. |
| `strictSpec` | Whether or not to treat an input document strictly against the spec. 'MUST' and 'SHALL' wording in OpenAPI spec is strictly adhered to. e.g. when false, no fixes will be applied to documents which pass validation but don't follow the spec. |
| `openapiNormalizer` | specifies the rules to be enabled in OpenAPI normalizer in the form of RULE_1=true,RULE_2=original. |
| `generateAliasAsModel` | generate alias (array, map) as model |
| `configOptions` | N/A | a **map** of generator-specific parameters. To show a full list of generator-specified parameters (options), please use `configHelp` (explained below)
| `importMappings` | specifies mappings between a given class and the import that should be used for that class in the format of type=import,type=import. You can also have multiple occurrences of this option |
| `typeMappings` | sets mappings between OpenAPI spec types and generated code types in the format of OpenAPIType=generatedType,OpenAPIType=generatedType. For example: `array=List,map=Map,string=String`. You can also have multiple occurrences of this option. To map a specified format, use type+format, e.g. string+password=EncryptedString will map `type: string, format: password` to `EncryptedString`. |
| `schemaMappings` | specifies mappings between the schema and the new name in the format of schema_a=Cat,schema_b=Bird. https://openapi-generator.tech/docs/customization/#schema-mapping |
| `nameMappings` | specifies mappings between the property name and the new name in the format of property_a=firstProperty,property_b=secondProperty. https://openapi-generator.tech/docs/customization/#name-mapping |
| `modelNameMappings` | specifies mappings between the model name and the new name in the format of model_a=FirstModel,model_b=SecondModel. https://openapi-generator.tech/docs/customization/#name-mapping |
| `parameterNameMappings` | specifies mappings between the parameter name and the new name in the format of param_a=first_parameter,param_b=second_parameter. https://openapi-generator.tech/docs/customization/#name-mapping |
| `inlineSchemaNameMappings` | specifies mappings between the inline schema name and the new name in the format of inline_object_2=Cat,inline_object_5=Bird. |
| `inlineSchemaOptions` | specifies the options used when naming inline schema in inline model resolver |
| `languageSpecificPrimitives` | specifies additional language specific primitive types in the format of type1,type2,type3,type3. For example: `String,boolean,Boolean,Double`. You can also have multiple occurrences of this option |
| `additionalProperties` | sets additional properties that can be referenced by the mustache templates in the format of name=value,name=value. You can also have multiple occurrences of this option |
| `reservedWordsMappings` | specifies how a reserved name should be escaped to. Otherwise, the default `_<name>` is used. For example `id=identifier`. You can also have multiple occurrences of this option |
| `generateApis` | generate the apis (`true` by default). To generate only a subset, define via `apiFilesConstrainedTo`. |
| `apiFilesConstrainedTo` | A comma separated list of apis to generate. All apis is the default. |
| `generateModels` | generate the models (`true` by default). To generate only a subset, define via `modelFilesConstrainedTo`. |
| `modelFilesConstrainedTo` | A comma separated list of models to generate. All models is the default. |
| `generateRecursiveDependentModels` | Enables dependent Models to be generated when `modelFilesConstrainedTo` is used. Default depends on `modelFilesConstrainedTo` (true when nonEmpty) |
| `generateSupportingFiles` | generate the supporting files (`true` by default). To generate only a subset, define via `supportingFilesConstrainedTo`. |
| `supportingFilesConstrainedTo` | A list of supporting files to generate. When not defined, all files will be generated. |
| `generateModelTests` | generate the model tests (currently disabled) |
| `generateModelDocumentation` | generate the model documentation (`true` by default) |
| `generateApiTests` | generate the api tests (currently disabled) |
| `generateApiDocumentation` | generate the api documentation (`true` by default) |
| `dryRun` | Defines whether the generator should run in dry-run mode. In dry-run mode no files are written and a summary about file states is output ( `false` by default). |
### Type and import mappings
To override the mappings between OpenAPI spec types and the types used in the generated code, set `typeMappings`.
```scala
def typeMappings: T[Map[String, String]] = Map(
"time" -> "LocalTime"
)
```
For types that are not already included in the generator configuration, you may need to add a corresponding `importMapping` too.
```scala
def typeMappings: T[Map[String, String]] = Map(
"binary" -> "StreamingOutput",
"file" -> "StreamingOutput"
)
def importMappings: T[Map[String, String]] = Map(
"StreamingOutput" -> "javax.ws.rs.core.StreamingOutput",
)
```
### Validate Command
You can validate any OpenAPI spec file by calling `validateOpenapiSpec` on the `OpenApiModule`.
```bash
mill validateOpenapiSpec $(pwd)/api/petstore-v3.0-invalid.yaml
```
This command has two additional parameters:
* `--failOnWarnings true` enable failing the check already on warnings
* `--recommend false`
You can also validate your `OpenApiConfig` object by calling `validate` on it.
```bash
mill yourConfigObject.validate
```

View File

@@ -0,0 +1,9 @@
To test the Mill plugin, this project can be used.
> ![NOTE]
> The `mill-build` folder is only needed to look up the plugin from the local Maven repository for development purposes.
It requires that the current SNAPSHOT version of the plugin is installed in the local Maven repository.
Replace the version in `build.mill` or set the environment variable `$MILL_OPENAPITOOLS_PLUGIN_VERSION` to the desired version.
Run `./mill __.compile` to test if the plugin works or some of the modules tasks like `./mill openapi.validate`.

View File

@@ -0,0 +1,103 @@
openapi: "3.0.0"
servers:
- url: http://petstore.swagger.io/v1
paths:
/pets:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
responses:
'200':
description: A paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create a pet
tags:
- pets
responses:
'201':
description: Null response
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/pets/{petId}:
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
'200':
description: Expected response to a valid request
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components:
schemas:
Pet:
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"
Error:
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string

View File

@@ -0,0 +1,736 @@
openapi: 3.0.0
servers:
- url: 'http://petstore.swagger.io/v2'
info:
description: >-
This is a sample server Petstore server. For this sample, you can use the api key
`special-key` to test the authorization filters.
version: 1.0.0
title: OpenAPI Petstore
license:
name: Apache-2.0
url: 'https://www.apache.org/licenses/LICENSE-2.0.html'
tags:
- name: pet
description: Everything about your Pets
- name: store
description: Access to Petstore orders
- name: user
description: Operations about user
paths:
/pet:
post:
tags:
- pet
summary: Add a new pet to the store
description: ''
operationId: addPet
responses:
'200':
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/Pet'
application/json:
schema:
$ref: '#/components/schemas/Pet'
'405':
description: Invalid input
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
requestBody:
$ref: '#/components/requestBodies/Pet'
put:
tags:
- pet
summary: Update an existing pet
description: ''
operationId: updatePet
responses:
'200':
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/Pet'
application/json:
schema:
$ref: '#/components/schemas/Pet'
'400':
description: Invalid ID supplied
'404':
description: Pet not found
'405':
description: Validation exception
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
requestBody:
$ref: '#/components/requestBodies/Pet'
/pet/findByStatus:
get:
tags:
- pet
summary: Finds Pets by status
description: Multiple status values can be provided with comma separated strings
operationId: findPetsByStatus
parameters:
- name: status
in: query
description: Status values that need to be considered for filter
required: true
style: form
explode: false
schema:
type: array
items:
type: string
enum:
- available
- pending
- sold
default: available
responses:
'200':
description: successful operation
content:
application/xml:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
'400':
description: Invalid status value
security:
- petstore_auth:
- 'read:pets'
/pet/findByTags:
get:
tags:
- pet
summary: Finds Pets by tags
description: >-
Multiple tags can be provided with comma separated strings. Use tag1,
tag2, tag3 for testing.
operationId: findPetsByTags
parameters:
- name: tags
in: query
description: Tags to filter by
required: true
style: form
explode: false
schema:
type: array
items:
type: string
responses:
'200':
description: successful operation
content:
application/xml:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
'400':
description: Invalid tag value
security:
- petstore_auth:
- 'read:pets'
deprecated: true
'/pet/{petId}':
get:
tags:
- pet
summary: Find pet by ID
description: Returns a single pet
operationId: getPetById
parameters:
- name: petId
in: path
description: ID of pet to return
required: true
schema:
type: integer
format: int64
responses:
'200':
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/Pet'
application/json:
schema:
$ref: '#/components/schemas/Pet'
'400':
description: Invalid ID supplied
'404':
description: Pet not found
security:
- api_key: []
post:
tags:
- pet
summary: Updates a pet in the store with form data
description: ''
operationId: updatePetWithForm
parameters:
- name: petId
in: path
description: ID of pet that needs to be updated
required: true
schema:
type: integer
format: int64
responses:
'405':
description: Invalid input
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
requestBody:
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
name:
description: Updated name of the pet
type: string
status:
description: Updated status of the pet
type: string
delete:
tags:
- pet
summary: Deletes a pet
description: ''
operationId: deletePet
parameters:
- name: api_key
in: header
required: false
schema:
type: string
- name: petId
in: path
description: Pet id to delete
required: true
schema:
type: integer
format: int64
responses:
'400':
description: Invalid pet value
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
'/pet/{petId}/uploadImage':
post:
tags:
- pet
summary: uploads an image
description: ''
operationId: uploadFile
parameters:
- name: petId
in: path
description: ID of pet to update
required: true
schema:
type: integer
format: int64
responses:
'200':
description: successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
additionalMetadata:
description: Additional data to pass to server
type: string
file:
description: file to upload
type: string
format: binary
/store/inventory:
get:
tags:
- store
summary: Returns pet inventories by status
description: Returns a map of status codes to quantities
operationId: getInventory
responses:
'200':
description: successful operation
content:
application/json:
schema:
type: object
additionalProperties:
type: integer
format: int32
security:
- api_key: []
/store/order:
post:
tags:
- store
summary: Place an order for a pet
description: ''
operationId: placeOrder
responses:
'200':
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/Order'
application/json:
schema:
$ref: '#/components/schemas/Order'
'400':
description: Invalid Order
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
description: order placed for purchasing the pet
required: true
'/store/order/{orderId}':
get:
tags:
- store
summary: Find purchase order by ID
description: >-
For valid response try integer IDs with value <= 5 or > 10. Other values
will generate exceptions
operationId: getOrderById
parameters:
- name: orderId
in: path
description: ID of pet that needs to be fetched
required: true
schema:
type: integer
format: int64
minimum: 1
maximum: 5
responses:
'200':
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/Order'
application/json:
schema:
$ref: '#/components/schemas/Order'
'400':
description: Invalid ID supplied
'404':
description: Order not found
delete:
tags:
- store
summary: Delete purchase order by ID
description: >-
For valid response try integer IDs with value < 1000. Anything above
1000 or nonintegers will generate API errors
operationId: deleteOrder
parameters:
- name: orderId
in: path
description: ID of the order that needs to be deleted
required: true
schema:
type: string
responses:
'400':
description: Invalid ID supplied
'404':
description: Order not found
/user:
post:
tags:
- user
summary: Create user
description: This can only be done by the logged in user.
operationId: createUser
responses:
default:
description: successful operation
security:
- api_key: []
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/User'
description: Created user object
required: true
/user/createWithArray:
post:
tags:
- user
summary: Creates list of users with given input array
description: ''
operationId: createUsersWithArrayInput
responses:
default:
description: successful operation
security:
- api_key: []
requestBody:
$ref: '#/components/requestBodies/UserArray'
/user/createWithList:
post:
tags:
- user
summary: Creates list of users with given input array
description: ''
operationId: createUsersWithListInput
responses:
default:
description: successful operation
security:
- api_key: []
requestBody:
$ref: '#/components/requestBodies/UserArray'
/user/login:
get:
tags:
- user
summary: Logs user into the system
description: ''
operationId: loginUser
parameters:
- name: username
in: query
description: The user name for login
required: true
schema:
type: string
pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$'
- name: password
in: query
description: The password for login in clear text
required: true
schema:
type: string
responses:
'200':
description: successful operation
headers:
Set-Cookie:
description: >-
Cookie authentication key for use with the `api_key`
apiKey authentication.
schema:
type: string
example: AUTH_KEY=abcde12345; Path=/; HttpOnly
X-Rate-Limit:
description: calls per hour allowed by the user
schema:
type: integer
format: int32
X-Expires-After:
description: date in UTC when token expires
schema:
type: string
format: date-time
content:
application/xml:
schema:
type: string
application/json:
schema:
type: string
'400':
description: Invalid username/password supplied
/user/logout:
get:
tags:
- user
summary: Logs out current logged in user session
description: ''
operationId: logoutUser
responses:
default:
description: successful operation
security:
- api_key: []
'/user/{username}':
get:
tags:
- user
summary: Get user by user name
description: ''
operationId: getUserByName
parameters:
- name: username
in: path
description: The name that needs to be fetched. Use user1 for testing.
required: true
schema:
type: string
responses:
'200':
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/User'
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
description: Invalid username supplied
'404':
description: User not found
put:
tags:
- user
summary: Updated user
description: This can only be done by the logged in user.
operationId: updateUser
parameters:
- name: username
in: path
description: name that need to be deleted
required: true
schema:
type: string
responses:
'400':
description: Invalid user supplied
'404':
description: User not found
security:
- api_key: []
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/User'
description: Updated user object
required: true
delete:
tags:
- user
summary: Delete user
description: This can only be done by the logged in user.
operationId: deleteUser
parameters:
- name: username
in: path
description: The name that needs to be deleted
required: true
schema:
type: string
responses:
'400':
description: Invalid username supplied
'404':
description: User not found
security:
- api_key: []
externalDocs:
description: Find out more about Swagger
url: 'http://swagger.io'
components:
requestBodies:
UserArray:
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
description: List of user object
required: true
Pet:
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
application/xml:
schema:
$ref: '#/components/schemas/Pet'
description: Pet object that needs to be added to the store
required: true
securitySchemes:
petstore_auth:
type: oauth2
flows:
implicit:
authorizationUrl: 'http://petstore.swagger.io/api/oauth/dialog'
scopes:
'write:pets': modify pets in your account
'read:pets': read your pets
api_key:
type: apiKey
name: api_key
in: header
schemas:
Order:
title: Pet Order
description: An order for a pets from the pet store
type: object
properties:
id:
type: integer
format: int64
petId:
type: integer
format: int64
quantity:
type: integer
format: int32
shipDate:
type: string
format: date-time
status:
type: string
description: Order Status
enum:
- placed
- approved
- delivered
complete:
type: boolean
default: false
xml:
name: Order
Category:
title: Pet category
description: A category for a pet
type: object
properties:
id:
type: integer
format: int64
name:
type: string
pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$'
xml:
name: Category
User:
title: a User
description: A User who is purchasing from the pet store
type: object
properties:
id:
type: integer
format: int64
username:
type: string
firstName:
type: string
lastName:
type: string
email:
type: string
password:
type: string
phone:
type: string
userStatus:
type: integer
format: int32
description: User Status
xml:
name: User
Tag:
title: Pet Tag
description: A tag for a pet
type: object
properties:
id:
type: integer
format: int64
name:
type: string
xml:
name: Tag
Pet:
title: a Pet
description: A pet for sale in the pet store
type: object
required:
- name
- photoUrls
properties:
id:
type: integer
format: int64
category:
$ref: '#/components/schemas/Category'
name:
type: string
example: doggie
photoUrls:
type: array
xml:
name: photoUrl
wrapped: true
items:
type: string
tags:
type: array
xml:
name: tag
wrapped: true
items:
$ref: '#/components/schemas/Tag'
status:
type: string
description: pet status in the store
enum:
- available
- pending
- sold
xml:
name: Pet
ApiResponse:
title: An uploaded response
description: Describes the result of uploading an image resource
type: object
properties:
code:
type: integer
format: int32
type:
type: string
message:
type: string

View File

@@ -0,0 +1,53 @@
//| mill-version: 1.0.6
//| mvnDeps:
//| - com.lihaoyi::mill-contrib-versionfile:$MILL_VERSION
//| - org.openapitools:openapi-generator-mill-plugin:$MILL_OPENAPITOOLS_PLUGIN_VERSION
//|
package build
import mill.*
import mill.api.BuildCtx
import mill.javalib.publish.{Developer, License, VersionControl}
import mill.scalalib.publish.PomSettings
import mill.util.BuildInfo.{millBinPlatform, millVersion}
import javalib.*
import mill.contrib.versionfile.VersionFileModule
import org.openapitools.generator.mill.OpenApiModule
object `package` extends JavaModule with MavenModule with OpenApiModule {
// dependency on openapi-generator is set in Mills meta-build
// this is needed to point to a local maven repo
override def mvnDeps = Seq(
mvn"jakarta.platform:jakarta.jakartaee-api:11.0.0",
mvn"com.fasterxml.jackson.core:jackson-databind:2.20.0",
mvn"org.openapitools:jackson-databind-nullable:0.2.8"
)
object openapi extends OpenApiConfig {
override def inputSpec: T[PathRef] = Task.Source(BuildCtx.workspaceRoot / "api" / "petstore.yaml")
override def apiPackage: T[String] = "com.acme.foo.boundary.web.api"
override def modelPackage: T[String] = "com.acme.foo.boundary.web.model"
override def generatorName: T[String] = "jaxrs-spec"
override def sourceFolder: T[String] = "src/gen/java"
override def gitSettings: T[Option[GitSettings]] = Some(GitSettings("host", "userid", "repoid"))
override def artifactSettings: T[Option[ArtifactSettings]] = Some(ArtifactSettings("groupid", "artifactid", None))
override def cleanupOutput: T[Boolean] = true
def additionalProperties: T[Map[String, String]] = Map(
"dateLibrary" -> "java8",
"useJakartaEe" -> "true",
"useSwaggerAnnotations" -> "false",
"interfaceOnly" -> "true",
"useTags" -> "true",
)
}
override def generatedSources: T[Seq[PathRef]] = Seq(
PathRef(Task.dest),
openapi.generate(),
)
}

View File

@@ -0,0 +1,333 @@
#!/usr/bin/env sh
# This is a wrapper script, that automatically selects or downloads Mill from Maven Central or GitHub release pages.
#
# This script determines the Mill version to use by trying these sources
# - env-variable `MILL_VERSION`
# - local file `.mill-version`
# - local file `.config/mill-version`
# - `mill-version` from YAML fronmatter of current buildfile
# - if accessible, find the latest stable version available on Maven Central (https://repo1.maven.org/maven2)
# - env-variable `DEFAULT_MILL_VERSION`
#
# If a version has the suffix '-native' a native binary will be used.
# If a version has the suffix '-jvm' an executable jar file will be used, requiring an already installed Java runtime.
# If no such suffix is found, the script will pick a default based on version and platform.
#
# Once a version was determined, it tries to use either
# - a system-installed mill, if found and it's version matches
# - an already downloaded version under ~/.cache/mill/download
#
# If no working mill version was found on the system,
# this script downloads a binary file from Maven Central or Github Pages (this is version dependent)
# into a cache location (~/.cache/mill/download).
#
# Mill Project URL: https://github.com/com-lihaoyi/mill
# Script Version: 1.0.0-M1-21-7b6fae-DIRTY892b63e8
#
# If you want to improve this script, please also contribute your changes back!
# This script was generated from: dist/scripts/src/mill.sh
#
# Licensed under the Apache License, Version 2.0
set -e
if [ "$1" = "--setup-completions" ] ; then
# Need to preserve the first position of those listed options
MILL_FIRST_ARG=$1
shift
fi
if [ -z "${DEFAULT_MILL_VERSION}" ] ; then
DEFAULT_MILL_VERSION="0.12.10"
fi
if [ -z "${GITHUB_RELEASE_CDN}" ] ; then
GITHUB_RELEASE_CDN=""
fi
MILL_REPO_URL="https://github.com/com-lihaoyi/mill"
if [ -z "${CURL_CMD}" ] ; then
CURL_CMD=curl
fi
# Explicit commandline argument takes precedence over all other methods
if [ "$1" = "--mill-version" ] ; then
echo "The --mill-version option is no longer supported." 1>&2
fi
MILL_BUILD_SCRIPT=""
if [ -f "build.mill" ] ; then
MILL_BUILD_SCRIPT="build.mill"
elif [ -f "build.mill.scala" ] ; then
MILL_BUILD_SCRIPT="build.mill.scala"
elif [ -f "build.sc" ] ; then
MILL_BUILD_SCRIPT="build.sc"
fi
# Please note, that if a MILL_VERSION is already set in the environment,
# We reuse it's value and skip searching for a value.
# If not already set, read .mill-version file
if [ -z "${MILL_VERSION}" ] ; then
if [ -f ".mill-version" ] ; then
MILL_VERSION="$(tr '\r' '\n' < .mill-version | head -n 1 2> /dev/null)"
elif [ -f ".config/mill-version" ] ; then
MILL_VERSION="$(tr '\r' '\n' < .config/mill-version | head -n 1 2> /dev/null)"
elif [ -n "${MILL_BUILD_SCRIPT}" ] ; then
MILL_VERSION="$(cat ${MILL_BUILD_SCRIPT} | grep '//[|] *mill-version: *' | sed 's;//| *mill-version: *;;')"
fi
fi
MILL_USER_CACHE_DIR="${XDG_CACHE_HOME:-${HOME}/.cache}/mill"
if [ -z "${MILL_DOWNLOAD_PATH}" ] ; then
MILL_DOWNLOAD_PATH="${MILL_USER_CACHE_DIR}/download"
fi
# If not already set, try to fetch newest from Github
if [ -z "${MILL_VERSION}" ] ; then
# TODO: try to load latest version from release page
echo "No mill version specified." 1>&2
echo "You should provide a version via a '//| mill-version: ' comment or a '.mill-version' file." 1>&2
mkdir -p "${MILL_DOWNLOAD_PATH}"
LANG=C touch -d '1 hour ago' "${MILL_DOWNLOAD_PATH}/.expire_latest" 2>/dev/null || (
# we might be on OSX or BSD which don't have -d option for touch
# but probably a -A [-][[hh]mm]SS
touch "${MILL_DOWNLOAD_PATH}/.expire_latest"; touch -A -010000 "${MILL_DOWNLOAD_PATH}/.expire_latest"
) || (
# in case we still failed, we retry the first touch command with the intention
# to show the (previously suppressed) error message
LANG=C touch -d '1 hour ago' "${MILL_DOWNLOAD_PATH}/.expire_latest"
)
# POSIX shell variant of bash's -nt operator, see https://unix.stackexchange.com/a/449744/6993
# if [ "${MILL_DOWNLOAD_PATH}/.latest" -nt "${MILL_DOWNLOAD_PATH}/.expire_latest" ] ; then
if [ -n "$(find -L "${MILL_DOWNLOAD_PATH}/.latest" -prune -newer "${MILL_DOWNLOAD_PATH}/.expire_latest")" ]; then
# we know a current latest version
MILL_VERSION=$(head -n 1 "${MILL_DOWNLOAD_PATH}"/.latest 2> /dev/null)
fi
if [ -z "${MILL_VERSION}" ] ; then
# we don't know a current latest version
echo "Retrieving latest mill version ..." 1>&2
LANG=C ${CURL_CMD} -s -i -f -I ${MILL_REPO_URL}/releases/latest 2> /dev/null | grep --ignore-case Location: | sed s'/^.*tag\///' | tr -d '\r\n' > "${MILL_DOWNLOAD_PATH}/.latest"
MILL_VERSION=$(head -n 1 "${MILL_DOWNLOAD_PATH}"/.latest 2> /dev/null)
fi
if [ -z "${MILL_VERSION}" ] ; then
# Last resort
MILL_VERSION="${DEFAULT_MILL_VERSION}"
echo "Falling back to hardcoded mill version ${MILL_VERSION}" 1>&2
else
echo "Using mill version ${MILL_VERSION}" 1>&2
fi
fi
MILL_NATIVE_SUFFIX="-native"
MILL_JVM_SUFFIX="-jvm"
FULL_MILL_VERSION=$MILL_VERSION
ARTIFACT_SUFFIX=""
set_artifact_suffix(){
if [ "$(expr substr $(uname -s) 1 5 2>/dev/null)" = "Linux" ]; then
if [ "$(uname -m)" = "aarch64" ]; then
ARTIFACT_SUFFIX="-native-linux-aarch64"
else
ARTIFACT_SUFFIX="-native-linux-amd64"
fi
elif [ "$(uname)" = "Darwin" ]; then
if [ "$(uname -m)" = "arm64" ]; then
ARTIFACT_SUFFIX="-native-mac-aarch64"
else
ARTIFACT_SUFFIX="-native-mac-amd64"
fi
else
echo "This native mill launcher supports only Linux and macOS." 1>&2
exit 1
fi
}
case "$MILL_VERSION" in
*"$MILL_NATIVE_SUFFIX")
MILL_VERSION=${MILL_VERSION%"$MILL_NATIVE_SUFFIX"}
set_artifact_suffix
;;
*"$MILL_JVM_SUFFIX")
MILL_VERSION=${MILL_VERSION%"$MILL_JVM_SUFFIX"}
;;
*)
case "$MILL_VERSION" in
0.1.*) ;;
0.2.*) ;;
0.3.*) ;;
0.4.*) ;;
0.5.*) ;;
0.6.*) ;;
0.7.*) ;;
0.8.*) ;;
0.9.*) ;;
0.10.*) ;;
0.11.*) ;;
0.12.*) ;;
*)
set_artifact_suffix
esac
;;
esac
MILL="${MILL_DOWNLOAD_PATH}/$MILL_VERSION$ARTIFACT_SUFFIX"
try_to_use_system_mill() {
if [ "$(uname)" != "Linux" ]; then
return 0
fi
MILL_IN_PATH="$(command -v mill || true)"
if [ -z "${MILL_IN_PATH}" ]; then
return 0
fi
SYSTEM_MILL_FIRST_TWO_BYTES=$(head --bytes=2 "${MILL_IN_PATH}")
if [ "${SYSTEM_MILL_FIRST_TWO_BYTES}" = "#!" ]; then
# MILL_IN_PATH is (very likely) a shell script and not the mill
# executable, ignore it.
return 0
fi
SYSTEM_MILL_PATH=$(readlink -e "${MILL_IN_PATH}")
SYSTEM_MILL_SIZE=$(stat --format=%s "${SYSTEM_MILL_PATH}")
SYSTEM_MILL_MTIME=$(stat --format=%y "${SYSTEM_MILL_PATH}")
if [ ! -d "${MILL_USER_CACHE_DIR}" ]; then
mkdir -p "${MILL_USER_CACHE_DIR}"
fi
SYSTEM_MILL_INFO_FILE="${MILL_USER_CACHE_DIR}/system-mill-info"
if [ -f "${SYSTEM_MILL_INFO_FILE}" ]; then
parseSystemMillInfo() {
LINE_NUMBER="${1}"
# Select the line number of the SYSTEM_MILL_INFO_FILE, cut the
# variable definition in that line in two halves and return
# the value, and finally remove the quotes.
sed -n "${LINE_NUMBER}p" "${SYSTEM_MILL_INFO_FILE}" |\
cut -d= -f2 |\
sed 's/"\(.*\)"/\1/'
}
CACHED_SYSTEM_MILL_PATH=$(parseSystemMillInfo 1)
CACHED_SYSTEM_MILL_VERSION=$(parseSystemMillInfo 2)
CACHED_SYSTEM_MILL_SIZE=$(parseSystemMillInfo 3)
CACHED_SYSTEM_MILL_MTIME=$(parseSystemMillInfo 4)
if [ "${SYSTEM_MILL_PATH}" = "${CACHED_SYSTEM_MILL_PATH}" ] \
&& [ "${SYSTEM_MILL_SIZE}" = "${CACHED_SYSTEM_MILL_SIZE}" ] \
&& [ "${SYSTEM_MILL_MTIME}" = "${CACHED_SYSTEM_MILL_MTIME}" ]; then
if [ "${CACHED_SYSTEM_MILL_VERSION}" = "${MILL_VERSION}" ]; then
MILL="${SYSTEM_MILL_PATH}"
return 0
else
return 0
fi
fi
fi
SYSTEM_MILL_VERSION=$(${SYSTEM_MILL_PATH} --version | head -n1 | sed -n 's/^Mill.*version \(.*\)/\1/p')
cat <<EOF > "${SYSTEM_MILL_INFO_FILE}"
CACHED_SYSTEM_MILL_PATH="${SYSTEM_MILL_PATH}"
CACHED_SYSTEM_MILL_VERSION="${SYSTEM_MILL_VERSION}"
CACHED_SYSTEM_MILL_SIZE="${SYSTEM_MILL_SIZE}"
CACHED_SYSTEM_MILL_MTIME="${SYSTEM_MILL_MTIME}"
EOF
if [ "${SYSTEM_MILL_VERSION}" = "${MILL_VERSION}" ]; then
MILL="${SYSTEM_MILL_PATH}"
fi
}
try_to_use_system_mill
# If not already downloaded, download it
if [ ! -s "${MILL}" ] || [ "$MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT" = "1" ] ; then
case $MILL_VERSION in
0.0.* | 0.1.* | 0.2.* | 0.3.* | 0.4.* )
DOWNLOAD_SUFFIX=""
DOWNLOAD_FROM_MAVEN=0
;;
0.5.* | 0.6.* | 0.7.* | 0.8.* | 0.9.* | 0.10.* | 0.11.0-M* )
DOWNLOAD_SUFFIX="-assembly"
DOWNLOAD_FROM_MAVEN=0
;;
*)
DOWNLOAD_SUFFIX="-assembly"
DOWNLOAD_FROM_MAVEN=1
;;
esac
case $MILL_VERSION in
0.12.0 | 0.12.1 | 0.12.2 | 0.12.3 | 0.12.4 | 0.12.5 | 0.12.6 | 0.12.7 | 0.12.8 | 0.12.9 | 0.12.10 | 0.12.11 )
DOWNLOAD_EXT="jar"
;;
0.12.* )
DOWNLOAD_EXT="exe"
;;
0.* )
DOWNLOAD_EXT="jar"
;;
*)
DOWNLOAD_EXT="exe"
;;
esac
DOWNLOAD_FILE=$(mktemp mill.XXXXXX)
if [ "$DOWNLOAD_FROM_MAVEN" = "1" ] ; then
DOWNLOAD_URL="https://repo1.maven.org/maven2/com/lihaoyi/mill-dist${ARTIFACT_SUFFIX}/${MILL_VERSION}/mill-dist${ARTIFACT_SUFFIX}-${MILL_VERSION}.${DOWNLOAD_EXT}"
else
MILL_VERSION_TAG=$(echo "$MILL_VERSION" | sed -E 's/([^-]+)(-M[0-9]+)?(-.*)?/\1\2/')
DOWNLOAD_URL="${GITHUB_RELEASE_CDN}${MILL_REPO_URL}/releases/download/${MILL_VERSION_TAG}/${MILL_VERSION}${DOWNLOAD_SUFFIX}"
unset MILL_VERSION_TAG
fi
if [ "$MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT" = "1" ] ; then
echo $DOWNLOAD_URL
echo $MILL
exit 0
fi
# TODO: handle command not found
echo "Downloading mill ${MILL_VERSION} from ${DOWNLOAD_URL} ..." 1>&2
${CURL_CMD} -f -L -o "${DOWNLOAD_FILE}" "${DOWNLOAD_URL}"
chmod +x "${DOWNLOAD_FILE}"
mkdir -p "${MILL_DOWNLOAD_PATH}"
mv "${DOWNLOAD_FILE}" "${MILL}"
unset DOWNLOAD_FILE
unset DOWNLOAD_SUFFIX
fi
if [ -z "$MILL_MAIN_CLI" ] ; then
MILL_MAIN_CLI="${0}"
fi
MILL_FIRST_ARG=""
if [ "$1" = "--bsp" ] || [ "${1#"-i"}" != "$1" ] || [ "$1" = "--interactive" ] || [ "$1" = "--no-server" ] || [ "$1" = "--no-daemon" ] || [ "$1" = "--repl" ] || [ "$1" = "--help" ] ; then
# Need to preserve the first position of those listed options
MILL_FIRST_ARG=$1
shift
fi
unset MILL_DOWNLOAD_PATH
unset MILL_OLD_DOWNLOAD_PATH
unset OLD_MILL
unset MILL_VERSION
unset MILL_REPO_URL
# -D mill.main.cli is for compatibility with Mill 0.10.9 - 0.13.0-M2
# We don't quote MILL_FIRST_ARG on purpose, so we can expand the empty value without quotes
# shellcheck disable=SC2086
exec "${MILL}" $MILL_FIRST_ARG -D "mill.main.cli=${MILL_MAIN_CLI}" "$@"

View File

@@ -0,0 +1,17 @@
import coursier.LocalRepositories.Dangerous
import coursier.Repositories
import mill.api.Task
import mill.meta.MillBuildRootModule
object `package` extends MillBuildRootModule {
override def repositories = Task {
Seq(
// central needed for default deps
Repositories.central.root,
// the previously installed snapshot is in local maven
// see docs on Coursier, why m2 is considered dangerous
Dangerous.maven2Local.root)
}
}

View File

@@ -0,0 +1,299 @@
@echo off
rem This is a wrapper script, that automatically selects or downloads Mill from Maven Central or GitHub release pages.
rem
rem This script determines the Mill version to use by trying these sources
rem - env-variable `MILL_VERSION`
rem - local file `.mill-version`
rem - local file `.config/mill-version`
rem - `mill-version` from YAML fronmatter of current buildfile
rem - if accessible, find the latest stable version available on Maven Central (https://repo1.maven.org/maven2)
rem - env-variable `DEFAULT_MILL_VERSION`
rem
rem If a version has the suffix '-native' a native binary will be used.
rem If a version has the suffix '-jvm' an executable jar file will be used, requiring an already installed Java runtime.
rem If no such suffix is found, the script will pick a default based on version and platform.
rem
rem Once a version was determined, it tries to use either
rem - a system-installed mill, if found and it's version matches
rem - an already downloaded version under %USERPROFILE%\.mill\download
rem
rem If no working mill version was found on the system,
rem this script downloads a binary file from Maven Central or Github Pages (this is version dependent)
rem into a cache location (%USERPROFILE%\.mill\download).
rem
rem Mill Project URL: https://github.com/com-lihaoyi/mill
rem Script Version: 1.0.0-M1-21-7b6fae-DIRTY892b63e8
rem
rem If you want to improve this script, please also contribute your changes back!
rem This script was generated from: dist/scripts/src/mill.bat
rem
rem Licensed under the Apache License, Version 2.0
rem setlocal seems to be unavailable on Windows 95/98/ME
rem but I don't think we need to support them in 2019
setlocal enabledelayedexpansion
if [!DEFAULT_MILL_VERSION!]==[] ( set "DEFAULT_MILL_VERSION=0.12.10" )
if [!MILL_GITHUB_RELEASE_CDN!]==[] ( set "MILL_GITHUB_RELEASE_CDN=" )
if [!MILL_MAIN_CLI!]==[] ( set "MILL_MAIN_CLI=%~f0" )
set "MILL_REPO_URL=https://github.com/com-lihaoyi/mill"
SET MILL_BUILD_SCRIPT=
if exist "build.mill" (
set MILL_BUILD_SCRIPT=build.mill
) else (
if exist "build.mill.scala" (
set MILL_BUILD_SCRIPT=build.mill.scala
) else (
if exist "build.sc" (
set MILL_BUILD_SCRIPT=build.sc
) else (
rem no-op
)
)
)
if [!MILL_VERSION!]==[] (
if exist .mill-version (
set /p MILL_VERSION=<.mill-version
) else (
if exist .config\mill-version (
set /p MILL_VERSION=<.config\mill-version
) else (
if not "%MILL_BUILD_SCRIPT%"=="" (
for /f "tokens=1-2*" %%a in ('findstr /C:"//| mill-version:" %MILL_BUILD_SCRIPT%') do (
set "MILL_VERSION=%%c"
)
) else (
rem no-op
)
)
)
)
if [!MILL_VERSION!]==[] set MILL_VERSION=%DEFAULT_MILL_VERSION%
if [!MILL_DOWNLOAD_PATH!]==[] set MILL_DOWNLOAD_PATH=%USERPROFILE%\.mill\download
rem without bat file extension, cmd doesn't seem to be able to run it
set "MILL_NATIVE_SUFFIX=-native"
set "MILL_JVM_SUFFIX=-jvm"
set "FULL_MILL_VERSION=%MILL_VERSION%"
set "MILL_EXT=.bat"
set "ARTIFACT_SUFFIX="
REM Check if MILL_VERSION contains MILL_NATIVE_SUFFIX
echo !MILL_VERSION! | findstr /C:"%MILL_NATIVE_SUFFIX%" >nul
if !errorlevel! equ 0 (
set "MILL_VERSION=%MILL_VERSION:-native=%"
REM -native images compiled with graal do not support windows-arm
REM https://github.com/oracle/graal/issues/9215
IF /I NOT "%PROCESSOR_ARCHITECTURE%"=="ARM64" (
set "ARTIFACT_SUFFIX=-native-windows-amd64"
set "MILL_EXT=.exe"
) else (
rem no-op
)
) else (
echo !MILL_VERSION! | findstr /C:"%MILL_JVM_SUFFIX%" >nul
if !errorlevel! equ 0 (
set "MILL_VERSION=%MILL_VERSION:-jvm=%"
) else (
set "SKIP_VERSION=false"
set "MILL_PREFIX=%MILL_VERSION:~0,4%"
if "!MILL_PREFIX!"=="0.1." set "SKIP_VERSION=true"
if "!MILL_PREFIX!"=="0.2." set "SKIP_VERSION=true"
if "!MILL_PREFIX!"=="0.3." set "SKIP_VERSION=true"
if "!MILL_PREFIX!"=="0.4." set "SKIP_VERSION=true"
if "!MILL_PREFIX!"=="0.5." set "SKIP_VERSION=true"
if "!MILL_PREFIX!"=="0.6." set "SKIP_VERSION=true"
if "!MILL_PREFIX!"=="0.7." set "SKIP_VERSION=true"
if "!MILL_PREFIX!"=="0.8." set "SKIP_VERSION=true"
if "!MILL_PREFIX!"=="0.9." set "SKIP_VERSION=true"
set "MILL_PREFIX=%MILL_VERSION:~0,5%"
if "!MILL_PREFIX!"=="0.10." set "SKIP_VERSION=true"
if "!MILL_PREFIX!"=="0.11." set "SKIP_VERSION=true"
if "!MILL_PREFIX!"=="0.12." set "SKIP_VERSION=true"
if "!SKIP_VERSION!"=="false" (
IF /I NOT "%PROCESSOR_ARCHITECTURE%"=="ARM64" (
set "ARTIFACT_SUFFIX=-native-windows-amd64"
set "MILL_EXT=.exe"
)
) else (
rem no-op
)
)
)
set MILL=%MILL_DOWNLOAD_PATH%\!FULL_MILL_VERSION!!MILL_EXT!
set MILL_RESOLVE_DOWNLOAD=
if not exist "%MILL%" (
set MILL_RESOLVE_DOWNLOAD=true
) else (
if defined MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT (
set MILL_RESOLVE_DOWNLOAD=true
) else (
rem no-op
)
)
if [!MILL_RESOLVE_DOWNLOAD!]==[true] (
set MILL_VERSION_PREFIX=%MILL_VERSION:~0,4%
set MILL_SHORT_VERSION_PREFIX=%MILL_VERSION:~0,2%
rem Since 0.5.0
set MILL_DOWNLOAD_SUFFIX=-assembly
rem Since 0.11.0
set MILL_DOWNLOAD_FROM_MAVEN=1
if [!MILL_VERSION_PREFIX!]==[0.0.] (
set MILL_DOWNLOAD_SUFFIX=
set MILL_DOWNLOAD_FROM_MAVEN=0
)
if [!MILL_VERSION_PREFIX!]==[0.1.] (
set MILL_DOWNLOAD_SUFFIX=
set MILL_DOWNLOAD_FROM_MAVEN=0
)
if [!MILL_VERSION_PREFIX!]==[0.2.] (
set MILL_DOWNLOAD_SUFFIX=
set MILL_DOWNLOAD_FROM_MAVEN=0
)
if [!MILL_VERSION_PREFIX!]==[0.3.] (
set MILL_DOWNLOAD_SUFFIX=
set MILL_DOWNLOAD_FROM_MAVEN=0
)
if [!MILL_VERSION_PREFIX!]==[0.4.] (
set MILL_DOWNLOAD_SUFFIX=
set MILL_DOWNLOAD_FROM_MAVEN=0
)
if [!MILL_VERSION_PREFIX!]==[0.5.] set MILL_DOWNLOAD_FROM_MAVEN=0
if [!MILL_VERSION_PREFIX!]==[0.6.] set MILL_DOWNLOAD_FROM_MAVEN=0
if [!MILL_VERSION_PREFIX!]==[0.7.] set MILL_DOWNLOAD_FROM_MAVEN=0
if [!MILL_VERSION_PREFIX!]==[0.8.] set MILL_DOWNLOAD_FROM_MAVEN=0
if [!MILL_VERSION_PREFIX!]==[0.9.] set MILL_DOWNLOAD_FROM_MAVEN=0
set MILL_VERSION_PREFIX=%MILL_VERSION:~0,5%
if [!MILL_VERSION_PREFIX!]==[0.10.] set MILL_DOWNLOAD_FROM_MAVEN=0
set MILL_VERSION_PREFIX=%MILL_VERSION:~0,8%
if [!MILL_VERSION_PREFIX!]==[0.11.0-M] set MILL_DOWNLOAD_FROM_MAVEN=0
set MILL_VERSION_PREFIX=%MILL_VERSION:~0,5%
set DOWNLOAD_EXT=exe
if [!MILL_SHORT_VERSION_PREFIX!]==[0.] set DOWNLOAD_EXT=jar
if [!MILL_VERSION_PREFIX!]==[0.12.] set DOWNLOAD_EXT=exe
if [!MILL_VERSION!]==[0.12.0] set DOWNLOAD_EXT=jar
if [!MILL_VERSION!]==[0.12.1] set DOWNLOAD_EXT=jar
if [!MILL_VERSION!]==[0.12.2] set DOWNLOAD_EXT=jar
if [!MILL_VERSION!]==[0.12.3] set DOWNLOAD_EXT=jar
if [!MILL_VERSION!]==[0.12.4] set DOWNLOAD_EXT=jar
if [!MILL_VERSION!]==[0.12.5] set DOWNLOAD_EXT=jar
if [!MILL_VERSION!]==[0.12.6] set DOWNLOAD_EXT=jar
if [!MILL_VERSION!]==[0.12.7] set DOWNLOAD_EXT=jar
if [!MILL_VERSION!]==[0.12.8] set DOWNLOAD_EXT=jar
if [!MILL_VERSION!]==[0.12.9] set DOWNLOAD_EXT=jar
if [!MILL_VERSION!]==[0.12.10] set DOWNLOAD_EXT=jar
if [!MILL_VERSION!]==[0.12.11] set DOWNLOAD_EXT=jar
set MILL_VERSION_PREFIX=
set MILL_SHORT_VERSION_PREFIX=
for /F "delims=- tokens=1" %%A in ("!MILL_VERSION!") do set MILL_VERSION_BASE=%%A
set MILL_VERSION_MILESTONE=
for /F "delims=- tokens=2" %%A in ("!MILL_VERSION!") do set MILL_VERSION_MILESTONE=%%A
set MILL_VERSION_MILESTONE_START=!MILL_VERSION_MILESTONE:~0,1!
if [!MILL_VERSION_MILESTONE_START!]==[M] (
set MILL_VERSION_TAG=!MILL_VERSION_BASE!-!MILL_VERSION_MILESTONE!
) else (
set MILL_VERSION_TAG=!MILL_VERSION_BASE!
)
if [!MILL_DOWNLOAD_FROM_MAVEN!]==[1] (
set MILL_DOWNLOAD_URL=https://repo1.maven.org/maven2/com/lihaoyi/mill-dist!ARTIFACT_SUFFIX!/!MILL_VERSION!/mill-dist!ARTIFACT_SUFFIX!-!MILL_VERSION!.!DOWNLOAD_EXT!
) else (
set MILL_DOWNLOAD_URL=!MILL_GITHUB_RELEASE_CDN!%MILL_REPO_URL%/releases/download/!MILL_VERSION_TAG!/!MILL_VERSION!!MILL_DOWNLOAD_SUFFIX!
)
if defined MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT (
echo !MILL_DOWNLOAD_URL!
echo !MILL!
exit /b 0
)
rem there seems to be no way to generate a unique temporary file path (on native Windows)
set MILL_DOWNLOAD_FILE=%MILL%.tmp
echo Downloading mill !MILL_VERSION! from !MILL_DOWNLOAD_URL! ... 1>&2
if not exist "%MILL_DOWNLOAD_PATH%" mkdir "%MILL_DOWNLOAD_PATH%"
rem curl is bundled with recent Windows 10
rem but I don't think we can expect all the users to have it in 2019
where /Q curl
if !ERRORLEVEL! EQU 0 (
curl -f -L "!MILL_DOWNLOAD_URL!" -o "!MILL_DOWNLOAD_FILE!"
) else (
rem bitsadmin seems to be available on Windows 7
rem without /dynamic, github returns 403
rem bitsadmin is sometimes needlessly slow but it looks better with /priority foreground
bitsadmin /transfer millDownloadJob /dynamic /priority foreground "!MILL_DOWNLOAD_URL!" "!MILL_DOWNLOAD_FILE!"
)
if not exist "!MILL_DOWNLOAD_FILE!" (
echo Could not download mill !MILL_VERSION! 1>&2
exit /b 1
)
move /y "!MILL_DOWNLOAD_FILE!" "%MILL%"
set MILL_DOWNLOAD_FILE=
set MILL_DOWNLOAD_SUFFIX=
)
set MILL_DOWNLOAD_PATH=
set MILL_VERSION=
set MILL_REPO_URL=
rem Need to preserve the first position of those listed options
set MILL_FIRST_ARG=
if [%~1%]==[--bsp] (
set MILL_FIRST_ARG=%1%
) else (
if [%~1%]==[-i] (
set MILL_FIRST_ARG=%1%
) else (
if [%~1%]==[--interactive] (
set MILL_FIRST_ARG=%1%
) else (
if [%~1%]==[--no-server] (
set MILL_FIRST_ARG=%1%
) else (
if [%~1%]==[--no-daemon] (
set MILL_FIRST_ARG=%1%
) else (
if [%~1%]==[--repl] (
set MILL_FIRST_ARG=%1%
) else (
if [%~1%]==[--help] (
set MILL_FIRST_ARG=%1%
)
)
)
)
)
)
)
set "MILL_PARAMS=%*%"
if not [!MILL_FIRST_ARG!]==[] (
for /f "tokens=1*" %%a in ("%*") do (
set "MILL_PARAMS=%%b"
)
)
rem -D mill.main.cli is for compatibility with Mill 0.10.9 - 0.13.0-M2
"%MILL%" %MILL_FIRST_ARG% -D "mill.main.cli=%MILL_MAIN_CLI%" %MILL_PARAMS%

View File

@@ -0,0 +1,52 @@
package com.acme.foo.boundary.web;
import com.acme.foo.boundary.web.api.PetApi;
import com.acme.foo.boundary.web.model.ModelApiResponse;
import com.acme.foo.boundary.web.model.Pet;
import jakarta.enterprise.context.RequestScoped;
import java.io.InputStream;
import java.util.List;
@RequestScoped
public class TestController implements PetApi {
@Override
public Pet addPet(Pet pet) {
return null;
}
@Override
public void deletePet(Long petId, String apiKey) {
}
@Override
public List<Pet> findPetsByStatus(List<String> status) {
return List.of();
}
@Override
public List<Pet> findPetsByTags(List<String> tags) {
return List.of();
}
@Override
public Pet getPetById(Long petId) {
return null;
}
@Override
public Pet updatePet(Pet pet) {
return null;
}
@Override
public void updatePetWithForm(Long petId, String name, String status) {
}
@Override
public ModelApiResponse uploadFile(Long petId, String additionalMetadata, InputStream _fileInputStream) {
return null;
}
}

View File

@@ -0,0 +1 @@
7.16.0-SNAPSHOT

View File

@@ -0,0 +1,180 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-project</artifactId>
<!-- RELEASE_VERSION -->
<version>7.19.0-SNAPSHOT</version>
<!-- /RELEASE_VERSION -->
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>openapi-generator-mill-plugin</artifactId>
<packaging>jar</packaging>
<name>openapi-generator-mill-plugin</name>
<description>Mill module to build modules from OpenAPI Generator</description>
<repositories>
<!-- enable central, to find Scala and Mill deps -->
<repository>
<id>central</id>
<name>Central Repository OSSRH</name>
<url>https://repo1.maven.org/maven2/</url>
</repository>
</repositories>
<properties>
<scala-plugin.version>4.9.5</scala-plugin.version>
<scala.version>3.7.4</scala.version>
<mill.version>1.0.6</mill.version>
</properties>
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala3-library_3</artifactId>
<version>${scala.version}</version>
</dependency>
<dependency>
<groupId>com.lihaoyi</groupId>
<artifactId>mill-libs_3</artifactId>
<version>${mill.version}</version>
</dependency>
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.lihaoyi</groupId>
<artifactId>mill-testkit_3</artifactId>
<version>${mill.version}</version>
<scope>test</scope>
</dependency>
<!-- using ScalaTest as it integrates best with TestNG -->
<dependency>
<groupId>org.scalatest</groupId>
<artifactId>scalatest_3</artifactId>
<version>3.2.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.scalatestplus</groupId>
<artifactId>testng-7-10_3</artifactId>
<version>3.2.19.0</version>
<scope>test</scope>
</dependency>
<!-- The following dependencies are needed to run Mill in a Unit-Test -->
<dependency>
<groupId>org.virtuslab.scala-cli</groupId>
<artifactId>config_3</artifactId>
<version>1.11.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.github.alexarchambault</groupId>
<artifactId>concurrent-reference-hash-map</artifactId>
<version>1.1.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<configuration>
<configLocation>${project.parent.basedir}${file.separator}google_checkstyle.xml</configLocation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<id>add-scala-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/main/scala</source>
</sources>
</configuration>
</execution>
<execution>
<id>add-scala-test-sources</id>
<phase>generate-test-sources</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>src/test/java</source>
<source>src/test/scala</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>${scala-plugin.version}</version>
<configuration>
<scalaVersion>${scala.version}</scalaVersion>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<environmentVariables>
<MILL_TEST_RESOURCE_DIR>${project.basedir}/src/test/resources</MILL_TEST_RESOURCE_DIR>
</environmentVariables>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>static-analysis</id>
<build>
<plugins>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<configuration>
<excludeFilterFile>${project.parent.basedir}${file.separator}spotbugs-exclude.xml</excludeFilterFile>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
</plugin>
<plugin>
<groupId>se.bjurr.violations</groupId>
<artifactId>violations-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -0,0 +1,624 @@
/*
* Original code copied from https://github.com/mikybars/openapi-generator-mill-plugin
* Original code published under the MIT License
* Original Copyright Miguel Ibars
*/
package org.openapitools.generator.mill
import io.swagger.parser.OpenAPIParser
import io.swagger.v3.parser.core.models.ParseOptions
import mainargs.arg
import mill.T
import mill.api.{PathRef, Result, Task}
import org.openapitools.codegen.CodegenConstants
import org.openapitools.codegen.DefaultGenerator
import org.openapitools.codegen.config.CodegenConfigurator
import org.openapitools.codegen.config.GlobalSettings
import org.openapitools.codegen.validations.oas.{OpenApiEvaluator, RuleConfiguration}
import os.{Path, RelPath}
import upickle.ReadWriter as RW
import scala.jdk.CollectionConverters.*
import scala.jdk.javaapi.CollectionConverters
/**
* Usage:
* {{{
* object myModule extends JavaModule with OpenApiModule {
*
* object openApiServer extends OpenApiConfig {
* def inputSpec: T[PathRef] = Task.Source(BuildCtx.workspaceRoot / "api" / "server-api.yaml")
* def apiPackage: T[String] = "com.acme.foo.boundary.web.api"
* def modelPackage: T[String] = "com.acme.foo.boundary.web.model"
* def generatorName: T[String] = "spring"
* def sourceFolder: T[String] = "src/main/java"
* def additionalProperties: T[Map[String, String]] = Map(
* "useSpringBoot3" -> "true",
* "dateLibrary" -> "java8",
* "interfaceOnly" -> "true",
* "performBeanValidation" -> "true",
* "useBeanValidation" -> "false",
* "skipDefaultInterface" -> "true",
* "useTags" -> "true",
* )
* }
*
* object openApiClient extends OpenApiConfig {
* def inputSpec = T[PathRef] = Task.Source(BuildCtx.workspaceRoot / "api" / "some-client-api.yaml")
* def apiPackage: T[String] = "com.acme.foo.boundary.client.some.api"
* def modelPackage: T[String] = "com.acme.foo.boundary.client.some.model"
* def generatorName: T[String] = "java"
* def modelNameSuffix: T[String] = "Dto"
* def sourceFolder: T[String] = "src/main/java"
* def additionalProperties: T[Map[String, String]] = Map(
* "useTags" -> "true",
* "dateLibrary" -> "java8",
* "library" -> "webclient",
* "useJakartaEe" -> "true",
* "useOneOfInterfaces" -> "true",
* "useAbstractionForFiles" -> "true",
* )
* }
*
* override def generatedSources: T[Seq[PathRef]] = Seq(
* PathRef(Task.dest),
* openApiServer.generate(),
* openApiClient.generate(),
* )
* }
* }}}
*/
trait OpenApiModule extends mill.api.Module {
trait OpenApiConfig extends mill.api.Module {
case class ArtifactSettings(
groupId: String,
artifactId: String,
artifactVersion: Option[String] = None
) derives RW
case class GitSettings(
host: String,
userId: String,
repoId: String
) derives RW
/** The Open API 2.0/3.x specification location. */
def inputSpec: T[PathRef]
/** The name of the generator which will handle codegen. */
def generatorName: T[String]
/** Package for generated api classes. */
def apiPackage: T[String]
/**
* The additional folder passed to the generator. Usually this is done via the [[additionalProperties]] but since
* some generators also use different defaults while others use none, this property needs to be set and will
* override anything set in [[additionalProperties]].
*
* This is necessary so the plugin can construct the correct source folder structure.
*/
def sourceFolder: T[String]
/**
* Suffix that will be appended to all api names. Default is the empty string.
*/
def apiNameSuffix: T[String] = ""
/**
* Adds authorization headers when fetching the OpenAPI definitions remotely.
* Pass in a URL-encoded string of name:header with a comma separating multiple values
*/
def auth: T[Option[String]] = None
/**
* Sets custom User-Agent header value
*/
def httpUserAgent: T[Option[String]] = None
/** Package for generated model classes. */
def modelPackage: T[String]
/**
* Prefix that will be prepended to all model names. Default is the empty string.
*/
def modelNamePrefix: T[String] = ""
/**
* Suffix that will be appended to all model names. Default is the empty string.
*/
def modelNameSuffix: T[String] = ""
/** Sets additional properties that can be referenced by the mustache templates. */
def additionalProperties: T[Map[String, String]] = Map.empty[String, String]
/**
* Defines the user's target type.
* {{{ "OffsetDateTime" -> "java.time.Instant" }}}
*
* @see [[https://openapi-generator.tech/docs/usage/#type-mappings-and-import-mappings]]
* */
def typeMappings: T[Map[String, String]] = Map.empty[String, String]
/**
* Informs the template of the type to be imported. Needed when type mappings are used.
* {{{ "OffsetDateTime" -> "java.time.Instant" }}}
* Since the [[typeMappings]] are used to change the default types, the import mappings are used to map the imports.
*
* @see [[https://openapi-generator.tech/docs/usage/#type-mappings-and-import-mappings]]
* */
def importMappings: T[Map[String, String]] = Map.empty[String, String]
/**
* Specifies mappings between a given schema and the new one.
*/
def schemaMappings: T[Map[String, String]] = Map.empty[String, String]
/** Specify if the spec should be validated. Default is true. */
def validateSpec: T[Boolean] = true
/**
* Specifies an override location for the .openapi-generator-ignore file.
*/
def ignoreFileOverride: T[Option[Path]] = None
/**
* Specifies how a reserved name should be escaped to.
*/
def reservedWordsMappings: T[Map[String, String]] = Map.empty[String, String]
/**
* Remove examples defined in the operation
*/
def skipOperationExample: T[Boolean] = false
/**
* Defines which API-related files should be generated. This allows you to create a subset of generated files (or none at all).
*
* This option enables/disables generation of ALL api-related files.
*
* NOTE: Configuring any one of [[apiFilesConstrainedTo]], [[modelFilesConstrainedTo]], or [[supportingFilesConstrainedTo]] results
* in others being disabled. That is, OpenAPI Generator considers any one of these to define a subset of generation.
* For more control over the generation of individual files, configure an ignored file and refer to it via [[ignoreFileOverride]].
*/
def apiFilesConstrainedTo: T[Seq[String]] = Seq.empty[String]
/**
* Defines which model-related files should be generated. This allows you to create a subset of generated files (or none at all).
*
* NOTE: Configuring any one of [[apiFilesConstrainedTo]], [[modelFilesConstrainedTo]], or [[supportingFilesConstrainedTo]] results
* in others being disabled. That is, OpenAPI Generator considers any one of these to define a subset of generation.
* For more control over the generation of individual files, configure an ignored file and refer to it via [[ignoreFileOverride]].
*/
def modelFilesConstrainedTo: T[Seq[String]] = Seq.empty[String]
/**
* Defines which supporting files should be generated. This allows you to create a subset of generated files (or none at all).
*
* Supporting files are those related to `projects/frameworks` which may be modified
* by consumers.
*
* NOTE: Configuring any one of [[apiFilesConstrainedTo]], [[modelFilesConstrainedTo]], or [[supportingFilesConstrainedTo]] results
* in others being disabled. That is, OpenAPI Generator considers any one of these to define a subset of generation.
* For more control over the generation of individual files, configure an ignored file and refer to it via [[ignoreFileOverride]].
*/
def supportingFilesConstrainedTo: T[Seq[String]] = Seq.empty[String]
/**
* Generate the APIs. Default is true.
*/
def generateApis: T[Boolean] = true
/**
* Defines whether api-related _documentation_ files should be generated.
*
* This option enables/disables generation of ALL api-related _documentation_ files.
*
* For more control over generation of individual files, configure an ignored file and
* refer to it via [[ignoreFileOverride]].
*/
def generateApiDocs: T[Boolean] = true
/**
* Defines whether api-related _test_ files should be generated.
*
* This option is currently disabled because Mill does not distinguish between normal- and test-sources.
*/
// TODO figure out a clean way to support this
final def generateApiTests: T[Boolean] = false
/**
* Generate the Models. Default is true.
*/
def generateModels: T[Boolean] = true
/**
* Defines whether model-related _documentation_ files should be generated.
*
* This option enables/disables generation of ALL model-related _documentation_ files.
*
* For more control over generation of individual files, configure an ignored file and
* refer to it via [[ignoreFileOverride]].
*/
def generateModelDocs: T[Boolean] = true
/**
* Defines whether model-related _test_ files should be generated.
*
* This option is currently disabled because Mill does not distinguish between normal- and test-sources.
*/
// TODO figure out a clean way to support this
def generateModelTests: T[Boolean] = false
/**
* Generate the supporting files. Default is true.
*/
def generateSupportingFiles: T[Boolean] = true
/**
* Generate the models recursively if models should generate selectively (see [[modelFilesConstrainedTo]]) and all
* dependent models are to generate.
* Is enabled by default when [[modelFilesConstrainedTo]] is not empty for convenience.
*/
def generateRecursiveDependentModels: T[Boolean] = modelFilesConstrainedTo().nonEmpty
/**
* Templating engine: "mustache" (default) or "handlebars" (beta)
*/
def engine: T[Option[String]] = None
/**
* Specifies mappings between the inline scheme name and the new name
*/
def inlineSchemaNameMappings: T[Map[String, String]] = Map.empty[String, String]
/**
* Specifies options for inline schemas
*/
def inlineSchemaOptions: T[Map[String, String]] = Map.empty[String, String]
/**
* Specifies mappings between the property name and the new name
*/
def nameMappings: T[Map[String, String]] = Map.empty[String, String]
/**
* Specifies mappings between the parameter name and the new name
*/
def parameterNameMappings: T[Map[String, String]] = Map.empty[String, String]
/**
* Specifies mappings between the model name and the new name
*/
def modelNameMappings: T[Map[String, String]] = Map.empty[String, String]
/**
* Specifies mappings between the enum name and the new name
*/
def enumNameMappings: T[Map[String, String]] = Map.empty[String, String]
/**
* Specifies mappings between the operation id name and the new name
*/
def operationIdNameMappings: T[Map[String, String]] = Map.empty[String, String]
/**
* Specifies mappings (rules) in OpenAPI normalizer
*/
def openapiNormalizer: T[Map[String, String]] = Map.empty[String, String]
/**
* Root package for generated code.
*/
def invokerPackage: T[Option[String]] = None
/**
* Artifact coordinates/packages used in generated build files.
*/
def artifactSettings: T[Option[ArtifactSettings]] = None
/**
* Reference the library template (sub-template) of a generator.
*/
def library: T[Option[String]] = None
/**
* To write all log messages (not just errors) to STDOUT
*/
def logToStderr: T[Boolean] = false
/**
* To enable the file post-processing hook. This enables executing an external post-processor (usually a linter program).
* This only enables the post-processor. To define the post-processing command, define an environment variable such as
* LANG_POST_PROCESS_FILE (e.g. GO_POST_PROCESS_FILE, SCALA_POST_PROCESS_FILE). Please open an issue if your target
* generator does not support this functionality.
*/
def enablePostProcessFile: T[Boolean] = false
/**
* Folder containing the template files.
*/
def templateDirectory: T[Option[Path]] = None
/**
* To remove operationId prefix (e.g. user_getName => getName)
*/
def removeOperationIdPrefix: T[Boolean] = false
/**
* To treat a document strictly against the spec. Default is true.
*/
def strictSpec: T[Boolean] = true
/**
* Specifies additional language-specific primitive types in the format of type1,type2,type3,type3. For example, `String,boolean,Boolean,Double`.
*/
def languageSpecificPrimitives: T[Set[String]] = Set.empty[String]
/**
* Git repository used in generated documentation.
*/
def gitSettings: T[Option[GitSettings]] = None
/**
* Sets specified global properties.
*/
def globalProperties: T[Map[String, String]] = Map.empty[String, String]
/**
* To skip spec validation. When true, we will skip the default behavior of validating a spec before generation.
*/
def skipValidateSpec: T[Boolean] = false
/**
* To generate alias (array, list, map) as model. When false, top-level objects defined as array, list, or map will result in those
* definitions generated as top-level Array-of-items, List-of-items, Map-of-items definitions.
* When true, A model representation either containing or extending the array,list,map (depending on specific generator implementation) will be generated.
*/
def generateAliasAsModel: T[Boolean] = false
/**
* Defines whether the output dir should be cleaned up before generating the output.
*/
def cleanupOutput: T[Boolean] = false
/**
* Specifies if the existing files should be overwritten during the generation.
*/
def skipOverwrite: T[Boolean] = false
/**
* Defines whether the generator should run in dry-run mode.
*/
def dryRun: T[Boolean] = false
/**
* An additional Task, which can be run after the generation phase.
*
* For instance, in case you have a shared type which is defined in your importMappings,
* the generator will still generate the type even though it is not used. The following
* cleanup will remove all files which are are not used.
* {{{
* override def cleanup: Task[Option[Path => Unit]] = Task.Anon {
* Some(
* (path: Path) => {
* val filenames = (for {
* (_, fqn) <- importMappings()
* if fqn.startsWith("my.relevant.package.")
* } yield fqn.split('.').last).toSeq
*
* os.walk(path)
* .filter { p => os.isFile(p) && filenames.contains(p.baseName) }
* .foreach { p => os.remove(p) }
* })
* }
* }}}
* This will match all full-qualified-names against the provided package and delete the matches.
* Note: this sample requires that custom type packages are completely separate.
*/
def cleanup: Task[Option[Path => Unit]] = Task.Anon {
None
}
/**
* Runs the OpenAPI generator with the given configuration.
*/
def generate: T[PathRef] = Task {
val configurator = CodegenConfigurator()
// don't call setAdditionalProperties with an immutable Scala Map, because the Setters after this one
// might add to the attributes as well (which will then cause an Exception)
additionalProperties().foreach((k, v) => configurator.addAdditionalProperty(k, v))
configurator.setApiNameSuffix(apiNameSuffix())
.setApiPackage(apiPackage())
.setEnumNameMappings(enumNameMappings().asJava)
.setGeneratorName(generatorName())
.setGlobalProperties(globalProperties().asJava)
.setInputSpec(inputSpec().path.toString())
.setInlineSchemaNameMappings(inlineSchemaNameMappings().asJava)
.setInlineSchemaOptions(inlineSchemaOptions().asJava)
.setImportMappings(importMappings().asJava)
.setLanguageSpecificPrimitives(languageSpecificPrimitives().asJava)
.setModelNameMappings(modelNameMappings().asJava)
.setModelNamePrefix(modelNamePrefix())
.setModelNameSuffix(modelNameSuffix())
.setModelPackage(modelPackage())
.setNameMappings(nameMappings().asJava)
.setOperationIdNameMappings(operationIdNameMappings().asJava)
.setOpenapiNormalizer(openapiNormalizer().asJava)
// should output-dir be configurable like in Gradle (don't think so)
.setOutputDir(Task.dest.toString())
.setParameterNameMappings(parameterNameMappings().asJava)
.setReservedWordsMappings(reservedWordsMappings().asJava)
.setSchemaMappings(schemaMappings().asJava)
.setTypeMappings(typeMappings().asJava)
.setValidateSpec(validateSpec())
if(generateApis()){
GlobalSettings.setProperty(CodegenConstants.APIS, apiFilesConstrainedTo().mkString(","))
}
if(generateModels()){
GlobalSettings.setProperty(CodegenConstants.MODELS, modelFilesConstrainedTo().mkString(","))
}
GlobalSettings.setProperty(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS, generateRecursiveDependentModels().toString)
if(generateSupportingFiles()){
GlobalSettings.setProperty(CodegenConstants.SUPPORTING_FILES, supportingFilesConstrainedTo().mkString(","))
}
GlobalSettings.setProperty(CodegenConstants.API_DOCS, generateApiDocs().toString)
GlobalSettings.setProperty(CodegenConstants.API_TESTS, generateApiTests().toString)
GlobalSettings.setProperty(CodegenConstants.MODEL_DOCS, generateModelDocs().toString)
GlobalSettings.setProperty(CodegenConstants.MODEL_TESTS, generateModelTests().toString)
engine() match {
case Some(s) if s.equalsIgnoreCase("handlebars") => configurator.setTemplatingEngineName("handlebars")
case Some(s) => configurator.setTemplatingEngineName(s) // in case other engines are supported
case None => () // use default
}
auth().filter(_.nonEmpty).foreach(authUrl => configurator.setAuth(authUrl))
httpUserAgent().foreach(userAgent => configurator.setHttpUserAgent(userAgent))
ignoreFileOverride().foreach(file => configurator.setIgnoreFileOverride(file.toNIO.toAbsolutePath.toString))
invokerPackage().foreach(value => configurator.setInvokerPackage(value))
artifactSettings().foreach(settings =>
configurator.setGroupId(settings.groupId)
configurator.setArtifactId(settings.artifactId)
settings.artifactVersion.foreach(value => configurator.setArtifactVersion(value))
)
library().foreach(value => configurator.setLibrary(value))
templateDirectory().foreach(file => configurator.setTemplateDir(file.toNIO.toAbsolutePath.toString))
gitSettings().foreach(git =>
configurator.setGitHost(git.host)
configurator.setGitUserId(git.userId)
configurator.setGitRepoId(git.repoId)
)
if (logToStderr()) {
configurator.setLogToStderr(true)
}
if (enablePostProcessFile()) {
configurator.setEnablePostProcessFile(true)
}
if (skipValidateSpec()) {
configurator.setValidateSpec(false)
}
if (generateAliasAsModel()) {
configurator.setGenerateAliasAsModel(true)
}
if (removeOperationIdPrefix()) {
configurator.setRemoveOperationIdPrefix(true)
}
if (skipOperationExample()) {
configurator.setSkipOperationExample(true)
}
if (strictSpec()) {
configurator.setStrictSpecBehavior(true)
}
if (skipOverwrite()) {
configurator.setSkipOverwrite(true)
}
if (cleanupOutput()) {
os.remove.all(Task.dest)
os.makeDir.all(Task.dest)
Task.log.info(s"Cleaned up output directory ${Task.dest} before code generation (cleanupOutput set to true).")
}
val dryRunSetting = dryRun()
// set source-folder as last to override potential duplicate
configurator.addAdditionalProperty("sourceFolder", sourceFolder())
DefaultGenerator(dryRunSetting).opts(configurator.toClientOptInput).generate()
Task.log.info(s"Successfully generated code to ${Task.dest}")
cleanup() match {
case Some(f) => f(Task.dest)
case None => // no-op
}
PathRef(Task.dest / RelPath(sourceFolder()))
}
/**
* Validates currently configured [[inputSpec]]. This task outputs a list of validation issues and errors.
*
* @param recommend prints warnings for recommended fixes. Default is true.
* @param failOnWarnings fails the call when there are warnings. Default is false.
* @return
*/
def validate(
recommend: Boolean = true,
failOnWarnings: Boolean = false
): Task.Command[Unit] = Task.Command {
given log: mill.api.Logger = Task.log
runValidation(inputSpec().path, recommend, failOnWarnings)
}
}
/**
* Command which checks a passed OpenAPI definition.
* This task outputs a list of validation issues and errors.
*
* @param spec the path to the file to be validated.
* @param recommend prints warnings for recommended fixes. Default is true.
* @param failOnWarnings fails the call when there are warnings. Default is false.
*/
def validateOpenapiSpec(
@arg(positional = true)
spec: String,
recommend: Boolean = true,
failOnWarnings: Boolean = false
): Task.Command[Unit] = Task.Command {
given log: mill.api.Logger = Task.log
runValidation(os.Path(spec), recommend, failOnWarnings)
}
private def runValidation(spec: Path, recommend: Boolean, failOnWarnings: Boolean)(using log: mill.api.Logger) = {
log.info(s"Validating spec $spec")
val options = ParseOptions()
options.setResolve(true)
val result = OpenAPIParser().readLocation(spec.toNIO.toAbsolutePath.toString, null, options)
val messages = CollectionConverters.asScala(result.getMessages).toSet
val ruleConfiguration = RuleConfiguration()
ruleConfiguration.setEnableRecommendations(recommend)
val evaluator = OpenApiEvaluator(ruleConfiguration)
val validationResult = evaluator.validate(result.getOpenAPI)
val warnings = CollectionConverters.asScala(validationResult.getWarnings)
val errors = CollectionConverters.asScala(validationResult.getErrors)
if (warnings.nonEmpty) {
val sb = StringBuilder("Spec has issues or recommendations.\nIssues:\n")
warnings.foreach(w => {
sb.append(s"\t${w.getMessage}\n")
log.debug(s"WARNING: ${w.getMessage}|${w.getDetails}")
})
log.info(sb.toString())
}
if (messages.nonEmpty || errors.nonEmpty) {
val sb = new StringBuilder("Spec is invalid.\nIssues:\n")
messages.foreach(m => {
sb.append(s"\t$m\n")
log.debug(s"ERROR: $m")
})
errors.foreach(e => {
sb.append(s"\t$e\n")
log.debug(s"ERROR: ${e.getMessage}|${e.getDetails}")
})
log.error(sb.toString())
Result.Failure("Validation failed.")
} else if (failOnWarnings && warnings.nonEmpty) {
log.error("Warnings found in the spec and 'treatWarningsAsErrors' is enabled.\nFailing validation.\n")
Result.Failure("Validation failed due to warnings (treatWarningsAsErrors = true).")
} else {
log.info("No error validations from swagger-parser or internal validations.")
Result.Success("Spec is valid.")
}
}
}

View File

@@ -0,0 +1,103 @@
openapi: "3.0.0"
servers:
- url: http://petstore.swagger.io/v1
paths:
/pets:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
responses:
'200':
description: A paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create a pet
tags:
- pets
responses:
'201':
description: Null response
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/pets/{petId}:
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
'200':
description: Expected response to a valid request
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components:
schemas:
Pet:
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"
Error:
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string

View File

@@ -0,0 +1,109 @@
openapi: "3.0.0"
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
servers:
- url: http://petstore.swagger.io/v1
paths:
/v3/pets:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
responses:
'200':
description: A paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create a pet
operationId: createPets
tags:
- pets
responses:
'201':
description: Null response
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/v3/pets/{petId}:
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
'200':
description: Expected response to a valid request
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components:
schemas:
Pet:
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"
Error:
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string

View File

@@ -0,0 +1,109 @@
package org.openapitools.generator.mill
import mill.*
import mill.api.{BuildCtx, Discover}
import mill.javalib.{Dep, JavaModule}
import mill.testkit.{TestRootModule, UnitTester}
import org.scalatest.matchers.should.Matchers
import org.testng.annotations.Test
import scala.language.postfixOps
object MillOpenapiModuleTestRoot extends TestRootModule {
lazy val millDiscover = Discover[this.type]
/** a project definition for Mill which should generate and compile */
object petstoreMicroprofile extends JavaModule with OpenApiModule {
override def mvnDeps: T[Seq[Dep]] = Seq(
Dep.parse("jakarta.ws.rs:jakarta.ws.rs-api:3.1.0"),
Dep.parse("jakarta.json.bind:jakarta.json.bind-api:3.0.0"),
Dep.parse("jakarta.json:jakarta.json-api:2.1.0"),
Dep.parse("org.eclipse.microprofile.rest.client:microprofile-rest-client-api:3.0.1"),
)
object openapi extends OpenApiConfig {
override def inputSpec: T[PathRef] = Task.Source(BuildCtx.workspaceRoot / "petstore-v3.1.yaml")
override def generatorName: T[String] = "java-microprofile"
override def apiPackage: T[String] = "com.acme.foo.boundary.web.api"
override def modelPackage: T[String] = "com.acme.foo.boundary.web.model"
override def sourceFolder: T[String] = "src/gen/java"
override def additionalProperties: T[Map[String, String]] = Map(
"microprofileRestClientVersion" -> "3.0",
"library" -> "microprofile",
"dateLibrary" -> "java8",
"interfaceOnly" -> "true",
"performBeanValidation" -> "true",
"useBeanValidation" -> "false",
"skipDefaultInterface" -> "true",
"useTags" -> "true",
)
}
override def generatedSources: T[Seq[PathRef]] = Seq(
PathRef(Task.dest),
openapi.generate(),
)
}
/** a project definition for Mill with an invalid openapi-spec */
object petstoreInvalid extends JavaModule with OpenApiModule {
override def mvnDeps: T[Seq[Dep]] = Seq(
Dep.parse("jakarta.ws.rs:jakarta.ws.rs-api:3.1.0"),
Dep.parse("jakarta.json.bind:jakarta.json.bind-api:3.0.0"),
Dep.parse("jakarta.json:jakarta.json-api:2.1.0"),
Dep.parse("org.eclipse.microprofile.rest.client:microprofile-rest-client-api:3.0.1"),
)
object openapi extends OpenApiConfig {
override def inputSpec: T[PathRef] = Task.Source(BuildCtx.workspaceRoot / "petstore-v3.0-invalid-due-to-missing-info-attribute.yaml")
override def generatorName: T[String] = "java-microprofile"
override def apiPackage: T[String] = "com.acme.foo.boundary.web.api"
override def modelPackage: T[String] = "com.acme.foo.boundary.web.model"
override def sourceFolder: T[String] = "src/gen/java"
override def additionalProperties: T[Map[String, String]] = Map(
"microprofileRestClientVersion" -> "3.0",
"library" -> "microprofile",
"dateLibrary" -> "java8",
"interfaceOnly" -> "true",
"performBeanValidation" -> "true",
"useBeanValidation" -> "false",
"skipDefaultInterface" -> "true",
"useTags" -> "true",
)
}
override def generatedSources: T[Seq[PathRef]] = Seq(
PathRef(Task.dest),
openapi.generate(),
)
}
}
class MillOpenapiModuleTest extends Matchers {
private val resourcePath = os.Path(sys.env("MILL_TEST_RESOURCE_DIR")) / "specs"
private def testEval() = UnitTester(MillOpenapiModuleTestRoot, resourcePath)
@Test
def petstoreMicroprofileGeneratesAndCompiles(): Unit = {
val result = testEval().scoped{ eval =>
// execute 'compile` task
eval.apply(MillOpenapiModuleTestRoot.petstoreMicroprofile.compile)
}
result shouldBe a[Right[_, _]]
}
@Test
def petstoreMicroprofileInvalidSpec(): Unit = {
val result = testEval().scoped { eval =>
// execute 'compile` task
eval.apply(MillOpenapiModuleTestRoot.petstoreInvalid.compile)
}
result shouldBe a[Left[_, _]]
}
}

View File

@@ -3301,7 +3301,7 @@ public class DefaultCodegen implements CodegenConfig {
}
if (refSchema.getProperties() != null && refSchema.getProperties().get(discPropName) != null) {
Schema discSchema = ModelUtils.getReferencedSchema(openAPI, (Schema) refSchema.getProperties().get(discPropName));
Schema discSchema = ModelUtils.getReferencedSchema(openAPI, getDiscriminatorSchema(refSchema, discPropName));
CodegenProperty cp = new CodegenProperty();
if (ModelUtils.isStringSchema(discSchema)) {
cp.isString = true;
@@ -3632,14 +3632,7 @@ public class DefaultCodegen implements CodegenConfig {
// FIXME: there are other ways to define the type of the discriminator property (inline
// for example). Handling those scenarios is too complicated for me, I'm leaving it for
// the future..
String propertyType =
Optional.ofNullable(schema.getProperties())
.map(p -> (Schema<?>) p.get(discriminatorPropertyName))
.map(Schema::get$ref)
.map(ModelUtils::getSimpleRef)
.map(this::toModelName)
.orElseGet(() -> typeMapping.get("string"));
discriminator.setPropertyType(propertyType);
discriminator.setPropertyType(getDiscriminatorPropertyType(schema, discriminatorPropertyName));
// check to see if the discriminator property is an enum string
boolean isEnum = Optional
@@ -3703,6 +3696,39 @@ public class DefaultCodegen implements CodegenConfig {
return discriminator;
}
/**
* Get the Schema for the discriminator type. Requires special handling due to siblings from OAS 3.1.
* An example of a sibling is an enum-ref that has its own description. This will lead to the enum being
* referenced as an allOf that in turn has a ref, rather than a regular ref directly to the enum.
*
* @param schema The input OAS schema.
* @param discriminatorName The name of the discriminator property.
*/
protected Schema getDiscriminatorSchema(Schema schema, String discriminatorName) {
if (schema.getProperties() == null) {
return null;
}
Schema discSchema = (Schema) schema.getProperties().get(discriminatorName);
if (ModelUtils.isAllOf(discSchema)) {
discSchema = (Schema) discSchema.getAllOf().get(0);
}
return discSchema;
}
/**
* Get the property type for the discriminator
*
* @param schema The input OAS schema.
* @param discriminatorPropertyName The name of the discriminator property.
*/
protected String getDiscriminatorPropertyType(Schema schema, String discriminatorPropertyName) {
return Optional.ofNullable(getDiscriminatorSchema(schema, discriminatorPropertyName))
.map(Schema::get$ref)
.map(ModelUtils::getSimpleRef)
.map(this::toModelName)
.orElseGet(() -> typeMapping.get("string"));
}
/**
* Handle the model for the 'additionalProperties' keyword in the OAS schema.
*
@@ -5323,9 +5349,6 @@ public class DefaultCodegen implements CodegenConfig {
if (parameter.getExtensions() != null && !parameter.getExtensions().isEmpty()) {
codegenParameter.vendorExtensions.putAll(parameter.getExtensions());
}
if (parameter.getSchema() != null && parameter.getSchema().getExtensions() != null && !parameter.getSchema().getExtensions().isEmpty()) {
codegenParameter.vendorExtensions.putAll(parameter.getSchema().getExtensions());
}
Schema parameterSchema;
@@ -5360,6 +5383,10 @@ public class DefaultCodegen implements CodegenConfig {
parameterSchema = null;
}
if (parameterSchema != null && parameterSchema.getExtensions() != null && !parameterSchema.getExtensions().isEmpty()) {
codegenParameter.vendorExtensions.putAll(parameterSchema.getExtensions());
}
if (parameter instanceof QueryParameter || "query".equalsIgnoreCase(parameter.getIn())) {
codegenParameter.isQueryParam = true;
codegenParameter.isAllowEmptyValue = parameter.getAllowEmptyValue() != null && parameter.getAllowEmptyValue();

View File

@@ -356,6 +356,7 @@ public class OpenAPINormalizer {
normalizeComponentsSecuritySchemes();
normalizeComponentsSchemas();
normalizeComponentsResponses();
normalizeComponentsHeaders();
}
/**
@@ -558,8 +559,21 @@ public class OpenAPINormalizer {
for (String headerKey : headers.keySet()) {
Header h = headers.get(headerKey);
Schema updatedHeader = normalizeSchema(h.getSchema(), new HashSet<>());
h.setSchema(updatedHeader);
if (h.getSchema() != null) { // not a $ref header
// example of header class
// description: null
// required: null
// deprecated: null
// style: null
// explode: null
// schema: null
// examples: null
// example: null
// content: null
// $ref: #/components/headers/Location
Schema updatedHeader = normalizeSchema(h.getSchema(), new HashSet<>());
h.setSchema(updatedHeader);
}
}
}
@@ -638,6 +652,18 @@ public class OpenAPINormalizer {
}
}
/**
* Normalizes schemas in component's headers.
*/
protected void normalizeComponentsHeaders() {
Map<String, Header> headers = openAPI.getComponents().getHeaders();
if (headers == null) {
return;
}
normalizeHeaders(headers);
}
/**
* Auto fix a self referencing schema using any type to replace the self-referencing sub-item.
*
@@ -696,7 +722,7 @@ public class OpenAPINormalizer {
*/
public Schema normalizeSchema(Schema schema, Set<Schema> visitedSchemas) {
// normalize reference schema
if (StringUtils.isNotEmpty(schema.get$ref())) {
if (schema != null && StringUtils.isNotEmpty(schema.get$ref())) {
normalizeReferenceSchema(schema);
}
@@ -860,7 +886,7 @@ public class OpenAPINormalizer {
}
for (Map.Entry<String, Schema> propertiesEntry : properties.entrySet()) {
Schema property = propertiesEntry.getValue();
// remove x-internal if needed (same logic as normalizeComponentsSchemas)
if (property.getExtensions() != null && getRule(REMOVE_X_INTERNAL)) {
Object xInternalValue = property.getExtensions().get(X_INTERNAL);
@@ -1777,6 +1803,7 @@ public class OpenAPINormalizer {
ArraySchema as = new ArraySchema();
as.setDescription(schema.getDescription());
as.setDefault(schema.getDefault());
as.setTitle(schema.getTitle());
if (schema.getExample() != null) {
as.setExample(schema.getExample());
}

View File

@@ -176,6 +176,22 @@ public abstract class CppQtAbstractCodegen extends AbstractCppCodegen implements
return "#include \"" + folder + name + ".h\"";
}
/**
* Resolve a schema reference. If the schema has a $ref, return the referenced schema.
* This is for nested maps.
*/
private Schema resolveSchema(Schema schema) {
if (schema == null) {
return null;
}
if (StringUtils.isNotEmpty(schema.get$ref())) {
String ref = ModelUtils.getSimpleRef(schema.get$ref());
Schema resolved = ModelUtils.getSchema(openAPI, ref);
return resolved != null ? resolved : schema;
}
return schema;
}
/**
* 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
@@ -185,15 +201,22 @@ public abstract class CppQtAbstractCodegen extends AbstractCppCodegen implements
@Override
@SuppressWarnings("rawtypes")
public String getTypeDeclaration(Schema p) {
String openAPIType = getSchemaType(p);
// Resolve the schema to check for nested maps/arrays - refs that point to map schemas
Schema resolved = resolveSchema(p);
if (ModelUtils.isArraySchema(p)) {
Schema inner = ModelUtils.getSchemaItems(p);
if (ModelUtils.isArraySchema(resolved)) {
Schema inner = ModelUtils.getSchemaItems(resolved);
return getSchemaType(p) + "<" + getTypeDeclaration(inner) + ">";
} else if (ModelUtils.isMapSchema(p)) {
Schema inner = ModelUtils.getAdditionalProperties(p);
return getSchemaType(p) + "<QString, " + getTypeDeclaration(inner) + ">";
} else if (ModelUtils.isBinarySchema(p)) {
} else if (ModelUtils.isMapSchema(resolved)) {
Schema inner = ModelUtils.getAdditionalProperties(resolved);
// inner can be null if additionalProperties is a boolean or not present
String innerType = inner != null ? getTypeDeclaration(inner) : PREFIX + "Object";
return getSchemaType(p) + "<QString, " + innerType + ">";
}
// For non-containers, use the original schema to preserve model names
String openAPIType = getSchemaType(p);
if (ModelUtils.isBinarySchema(p)) {
return getSchemaType(p);
} else if (ModelUtils.isFileSchema(p)) {
return getSchemaType(p);
@@ -210,29 +233,32 @@ public abstract class CppQtAbstractCodegen extends AbstractCppCodegen implements
@Override
@SuppressWarnings("rawtypes")
public String toDefaultValue(Schema p) {
if (ModelUtils.isBooleanSchema(p)) {
Schema schema = resolveSchema(p);
if (ModelUtils.isBooleanSchema(schema)) {
return "false";
} else if (ModelUtils.isDateSchema(p)) {
} else if (ModelUtils.isDateSchema(schema)) {
return "NULL";
} else if (ModelUtils.isDateTimeSchema(p)) {
} else if (ModelUtils.isDateTimeSchema(schema)) {
return "NULL";
} else if (ModelUtils.isNumberSchema(p)) {
if (SchemaTypeUtil.FLOAT_FORMAT.equals(p.getFormat())) {
} else if (ModelUtils.isNumberSchema(schema)) {
if (SchemaTypeUtil.FLOAT_FORMAT.equals(schema.getFormat())) {
return "0.0f";
}
return "0.0";
} else if (ModelUtils.isIntegerSchema(p)) {
if (SchemaTypeUtil.INTEGER64_FORMAT.equals(p.getFormat())) {
} else if (ModelUtils.isIntegerSchema(schema)) {
if (SchemaTypeUtil.INTEGER64_FORMAT.equals(schema.getFormat())) {
return "0L";
}
return "0";
} else if (ModelUtils.isMapSchema(p)) {
Schema inner = ModelUtils.getAdditionalProperties(p);
return "QMap<QString, " + getTypeDeclaration(inner) + ">()";
} else if (ModelUtils.isArraySchema(p)) {
Schema inner = ModelUtils.getSchemaItems(p);
} else if (ModelUtils.isMapSchema(schema)) {
Schema inner = ModelUtils.getAdditionalProperties(schema);
// inner can be null if additionalProperties is a boolean or not present
String innerType = inner != null ? getTypeDeclaration(inner) : PREFIX + "Object";
return "QMap<QString, " + innerType + ">()";
} else if (ModelUtils.isArraySchema(schema)) {
Schema inner = ModelUtils.getSchemaItems(schema);
return "QList<" + getTypeDeclaration(inner) + ">()";
} else if (ModelUtils.isStringSchema(p)) {
} else if (ModelUtils.isStringSchema(schema)) {
return "QString(\"\")";
} else if (!StringUtils.isEmpty(p.get$ref())) {
return toModelName(ModelUtils.getSimpleRef(p.get$ref())) + "()";

View File

@@ -291,6 +291,15 @@ public class CppRestSdkClientCodegen extends AbstractCppCodegen {
}
}
// Handle additionalProperties for models that have both properties and additionalProperties
Schema addlProps = ModelUtils.getAdditionalProperties(model);
if (addlProps != null && model.getProperties() != null && !model.getProperties().isEmpty()) {
// This model has both defined properties AND additionalProperties
codegenModel.additionalPropertiesType = getTypeDeclaration(addlProps);
// Add import for web::json::value which is used to store additional properties
codegenModel.imports.add("#include <cpprest/json.h>");
}
return codegenModel;
}
@@ -362,6 +371,34 @@ public class CppRestSdkClientCodegen extends AbstractCppCodegen {
return toApiName(name);
}
/**
* Resolve a schema reference. If the schema has a $ref, return the referenced schema.
* This is for nested maps.
*/
private Schema resolveSchema(Schema schema) {
if (schema == null) {
return null;
}
if (StringUtils.isNotEmpty(schema.get$ref())) {
String ref = ModelUtils.getSimpleRef(schema.get$ref());
Schema resolved = ModelUtils.getSchema(openAPI, ref);
return resolved != null ? resolved : schema;
}
return schema;
}
/**
* Check if a schema is a pure map (has additionalProperties but no defined properties).
* Schemas with both properties and additionalProperties should be treated as models, not maps.
*/
private boolean isPureMapSchema(Schema schema) {
if (!ModelUtils.isMapSchema(schema)) {
return false;
}
// If the schema has defined properties, it's not a pure map
return schema.getProperties() == null || schema.getProperties().isEmpty();
}
/**
* Optional - type declaration. This is a String which is used by the
* templates to instantiate your types. There is typically special handling
@@ -372,15 +409,23 @@ public class CppRestSdkClientCodegen extends AbstractCppCodegen {
*/
@Override
public String getTypeDeclaration(Schema p) {
String openAPIType = getSchemaType(p);
// Resolve the schema to check for nested maps/arrays - refs that point to map schemas
Schema resolved = resolveSchema(p);
if (ModelUtils.isArraySchema(p)) {
Schema inner = ModelUtils.getSchemaItems(p);
if (ModelUtils.isArraySchema(resolved)) {
Schema inner = ModelUtils.getSchemaItems(resolved);
return getSchemaType(p) + "<" + getTypeDeclaration(inner) + ">";
} else if (ModelUtils.isMapSchema(p)) {
Schema inner = ModelUtils.getAdditionalProperties(p);
return getSchemaType(p) + "<utility::string_t, " + getTypeDeclaration(inner) + ">";
} else if (ModelUtils.isFileSchema(p) || ModelUtils.isBinarySchema(p)) {
} else if (isPureMapSchema(resolved)) {
// Only treat as map if it has additionalProperties but NO defined properties
Schema inner = ModelUtils.getAdditionalProperties(resolved);
// inner can be null if additionalProperties is a boolean or not present
String innerType = inner != null ? getTypeDeclaration(inner) : "std::shared_ptr<Object>";
return getSchemaType(p) + "<utility::string_t, " + innerType + ">";
}
// For non-containers, use the original schema to preserve model names
String openAPIType = getSchemaType(p);
if (ModelUtils.isFileSchema(p) || ModelUtils.isBinarySchema(p)) {
return "std::shared_ptr<" + openAPIType + ">";
} else if (ModelUtils.isStringSchema(p)
|| ModelUtils.isDateSchema(p) || ModelUtils.isDateTimeSchema(p)
@@ -414,8 +459,10 @@ public class CppRestSdkClientCodegen extends AbstractCppCodegen {
return "0L";
}
return "0";
} else if (ModelUtils.isMapSchema(p)) {
String inner = getSchemaType(ModelUtils.getAdditionalProperties(p));
} else if (isPureMapSchema(p)) {
Schema innerSchema = ModelUtils.getAdditionalProperties(p);
// innerSchema can be null if additionalProperties is a boolean or not present
String inner = innerSchema != null ? getSchemaType(innerSchema) : "std::shared_ptr<Object>";
return "std::map<utility::string_t, " + inner + ">()";
} else if (ModelUtils.isArraySchema(p)) {
String inner = getSchemaType(ModelUtils.getSchemaItems(p));

View File

@@ -208,9 +208,23 @@ public class CppTinyClientCodegen extends AbstractCppCodegen implements CodegenC
return str;
}
/**
* Resolve a schema reference. If the schema has a $ref, return the referenced schema.
* This is for nested maps.
*/
private Schema resolveSchema(Schema schema) {
if (schema == null) {
return null;
}
if (StringUtils.isNotEmpty(schema.get$ref())) {
String ref = ModelUtils.getSimpleRef(schema.get$ref());
Schema resolved = ModelUtils.getSchema(openAPI, ref);
return resolved != null ? resolved : schema;
}
return schema;
}
private void makeTypeMappings() {
// Types
String cpp_array_type = "std::list";
typeMapping = new HashMap<>();
typeMapping.put("string", "std::string");
@@ -219,7 +233,8 @@ public class CppTinyClientCodegen extends AbstractCppCodegen implements CodegenC
typeMapping.put("long", "long");
typeMapping.put("boolean", "bool");
typeMapping.put("double", "double");
typeMapping.put("array", cpp_array_type);
typeMapping.put("array", "std::list");
typeMapping.put("map", "std::map");
typeMapping.put("number", "long");
typeMapping.put("binary", "std::string");
typeMapping.put("password", "std::string");
@@ -255,6 +270,19 @@ public class CppTinyClientCodegen extends AbstractCppCodegen implements CodegenC
@Override
public String getTypeDeclaration(Schema p) {
// Only resolve for nested maps - check if a $ref points to a map schema
Schema resolved = resolveSchema(p);
// Handle nested maps: if a $ref resolves to a map schema, build the nested type
if (ModelUtils.isMapSchema(resolved)) {
Schema inner = ModelUtils.getAdditionalProperties(resolved);
// inner can be null if additionalProperties is a boolean or not present
String innerType = inner != null ? getTypeDeclaration(inner) : "std::string";
return getSchemaType(p) + "<std::string, " + innerType + ">";
}
// For everything else (including arrays), use the original behavior
// The templates handle adding array item types themselves
String openAPIType = getSchemaType(p);
if (languageSpecificPrimitives.contains(openAPIType)) {
return toModelName(openAPIType);
@@ -296,7 +324,7 @@ public class CppTinyClientCodegen extends AbstractCppCodegen implements CodegenC
return "#include <string>";
} else if (name.equals("std::list")) {
return "#include <list>";
} else if (name.equals("Map")) {
} else if (name.equals("Map") || name.equals("std::map")) {
return "#include <map>";
}
return "#include \"" + name + ".h\"";

View File

@@ -29,8 +29,7 @@ import org.openapitools.codegen.model.ModelsMap;
import org.openapitools.codegen.model.OperationMap;
import org.openapitools.codegen.model.OperationsMap;
import org.openapitools.codegen.templating.mustache.PrefixWithHashLambda;
import org.openapitools.codegen.templating.mustache.UppercaseLambda;
import org.openapitools.codegen.templating.mustache.TitlecaseLambda;
import org.openapitools.codegen.templating.mustache.PascalCaseLambda;
import org.openapitools.codegen.utils.ModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -85,28 +84,32 @@ public class CrystalClientCodegen extends DefaultCodegen {
SecurityFeature.BasicAuth,
SecurityFeature.BearerToken,
SecurityFeature.ApiKey,
SecurityFeature.OAuth2_Implicit))
SecurityFeature.OAuth2_Implicit
))
.excludeGlobalFeatures(
GlobalFeature.XMLStructureDefinitions,
GlobalFeature.Callbacks,
GlobalFeature.LinkObjects,
GlobalFeature.ParameterStyling,
GlobalFeature.ParameterizedServer,
GlobalFeature.MultiServer)
GlobalFeature.MultiServer
)
.includeSchemaSupportFeatures(
SchemaSupportFeature.Polymorphism)
SchemaSupportFeature.Polymorphism
)
.excludeParameterFeatures(
ParameterFeature.Cookie)
ParameterFeature.Cookie
)
.includeClientModificationFeatures(
ClientModificationFeature.BasePath,
ClientModificationFeature.UserAgent));
generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata)
.stability(Stability.BETA)
.build();
ClientModificationFeature.UserAgent
)
);
supportsInheritance = true;
generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata).stability(Stability.BETA).build();
// clear import mapping (from default generator) as crystal does not use it
// at the moment
importMapping.clear();
@@ -130,19 +133,24 @@ public class CrystalClientCodegen extends DefaultCodegen {
hideGenerationTimestamp = Boolean.TRUE;
// reserved word. Ref:
// https://github.com/crystal-lang/crystal/wiki/Crystal-for-Rubyists#available-keywords
// https://crystal-lang.org/reference/1.18/crystal_for_rubyists/index.html#available-keywords
// https://crystal-lang.org/api/1.18.2/Reference.html
reservedWords = new HashSet<>(
Arrays.asList(
"abstract", "annotation", "do", "if", "nil?", "select", "union",
"alias", "else", "in", "of", "self", "unless",
"as", "elsif", "include", "out", "sizeof", "until",
"as?", "end", "instance", "sizeof", "pointerof", "struct", "verbatim",
"asm", "ensure", "is_a?", "private", "super", "when",
"begin", "enum", "lib", "protected", "then", "while",
"break", "extend", "macro", "require", "true", "with",
"case", "false", "module", "rescue", "type", "yield",
"class", "for", "next", "responds_to?", "typeof",
"def", "fun", "nil", "return", "uninitialized"));
// language reserved words (keywords)
"abstract", "do", "if", "nil?", "return", "uninitialized",
"alias", "else", "in", "of", "select", "union",
"as", "elsif", "include", "out", "self", "unless",
"as?", "end", "instance_sizeof", "pointerof", "sizeof", "until",
"asm", "ensure", "is_a?", "previous_def", "struct", "verbatim",
"begin", "enum", "lib", "private", "super", "when",
"break", "extend", "macro", "protected", "then", "while",
"case", "false", "module", "require", "true", "with",
"class", "for", "next", "rescue", "type", "yield",
"def", "fun", "nil", "responds_to?", "typeof",
// additional reserved words (methods)
"annotation", "object_id"
));
languageSpecificPrimitives.clear();
languageSpecificPrimitives.add("String");
@@ -174,8 +182,8 @@ public class CrystalClientCodegen extends DefaultCodegen {
typeMapping.put("List", "Array");
typeMapping.put("set", "Set");
typeMapping.put("map", "Hash");
typeMapping.put("object", "Object");
typeMapping.put("AnyType", "Object");
typeMapping.put("object", "JSON::Any");
typeMapping.put("AnyType", "JSON::Any");
typeMapping.put("file", "::File");
typeMapping.put("binary", "String");
typeMapping.put("ByteArray", "String");
@@ -188,31 +196,18 @@ public class CrystalClientCodegen extends DefaultCodegen {
primitiveTypes = new ArrayList<String>(typeMapping.values());
// remove modelPackage and apiPackage added by default
cliOptions.removeIf(opt -> CodegenConstants.MODEL_PACKAGE.equals(opt.getOpt()) ||
CodegenConstants.API_PACKAGE.equals(opt.getOpt()));
cliOptions.removeIf(opt -> CodegenConstants.MODEL_PACKAGE.equals(opt.getOpt()) || CodegenConstants.API_PACKAGE.equals(opt.getOpt()));
cliOptions.add(new CliOption(SHARD_NAME, "shard name (e.g. twitter_client").defaultValue("openapi_client"));
cliOptions.add(new CliOption(MODULE_NAME, "module name (e.g. TwitterClient").defaultValue("OpenAPIClient"));
cliOptions.add(new CliOption(SHARD_VERSION, "shard version.").defaultValue("1.0.0"));
cliOptions.add(new CliOption(SHARD_LICENSE, "shard license.").defaultValue("unlicense"));
cliOptions.add(new CliOption(SHARD_HOMEPAGE, "shard homepage.").defaultValue("http://org.openapitools"));
cliOptions.add(new CliOption(SHARD_DESCRIPTION, "shard description.").defaultValue("This shard maps to a REST API"));
cliOptions.add(new CliOption(SHARD_AUTHOR, "shard author (only one is supported)."));
cliOptions.add(new CliOption(SHARD_AUTHOR_EMAIL, "shard author email (only one is supported)."));
cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP,
CodegenConstants.HIDE_GENERATION_TIMESTAMP_DESC).defaultValue(Boolean.TRUE.toString()));
cliOptions.add(new CliOption(PARAMS_ENCODER,
"params_encoder setting (e.g. Crest::NestedParamsEncoder, Crest::EnumeratedFlatParamsEncoder, Crest::ZeroEnumeratedFlatParamsEncoder").
defaultValue("Crest::NestedParamsEncoder"));
cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, CodegenConstants.HIDE_GENERATION_TIMESTAMP_DESC).defaultValue(Boolean.TRUE.toString()));
cliOptions.add(new CliOption(PARAMS_ENCODER, "params_encoder setting (e.g. Crest::NestedParamsEncoder, Crest::EnumeratedFlatParamsEncoder, Crest::ZeroEnumeratedFlatParamsEncoder").defaultValue("Crest::NestedParamsEncoder"));
}
@Override
@@ -220,51 +215,63 @@ public class CrystalClientCodegen extends DefaultCodegen {
super.processOpts();
if (StringUtils.isEmpty(System.getenv("CRYSTAL_POST_PROCESS_FILE"))) {
LOGGER.info(
"Hint: Environment variable 'CRYSTAL_POST_PROCESS_FILE' (optional) not defined. E.g. to format the source code, please try 'export CRYSTAL_POST_PROCESS_FILE=\"/usr/local/bin/crystal tool format\"' (Linux/Mac)");
LOGGER.info("Hint: Environment variable 'CRYSTAL_POST_PROCESS_FILE' (optional) not defined. E.g. to format the source code, please try 'export CRYSTAL_POST_PROCESS_FILE=\"/usr/local/bin/crystal tool format\"' (Linux/Mac)");
} else if (!this.isEnablePostProcessFile()) {
LOGGER.info("Warning: Environment variable 'CRYSTAL_POST_PROCESS_FILE' is set but file post-processing is not enabled. To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI).");
}
if (additionalProperties.containsKey(SHARD_NAME)) {
setShardName((String) additionalProperties.get(SHARD_NAME));
} else {
additionalProperties.put(SHARD_NAME, shardName);
}
additionalProperties.put(SHARD_NAME, shardName);
if (additionalProperties.containsKey(MODULE_NAME)) {
setModuleName((String) additionalProperties.get(MODULE_NAME));
} else {
additionalProperties.put(MODULE_NAME, moduleName);
}
additionalProperties.put(MODULE_NAME, moduleName);
if (additionalProperties.containsKey(SHARD_VERSION)) {
setShardVersion((String) additionalProperties.get(SHARD_VERSION));
} else {
// not set, pass the default value to template
additionalProperties.put(SHARD_VERSION, shardVersion);
}
if (additionalProperties.containsKey(SHARD_LICENSE)) {
setShardLicense((String) additionalProperties.get(SHARD_LICENSE));
} else {
additionalProperties.put(SHARD_LICENSE, shardLicense);
}
if (additionalProperties.containsKey(SHARD_HOMEPAGE)) {
setShardHomepage((String) additionalProperties.get(SHARD_HOMEPAGE));
} else {
additionalProperties.put(SHARD_HOMEPAGE, shardHomepage);
}
if (additionalProperties.containsKey(SHARD_SUMMARY)) {
setShardSummary((String) additionalProperties.get(SHARD_SUMMARY));
} else {
additionalProperties.put(SHARD_SUMMARY, shardSummary);
}
if (additionalProperties.containsKey(SHARD_DESCRIPTION)) {
setShardDescription((String) additionalProperties.get(SHARD_DESCRIPTION));
} else {
additionalProperties.put(SHARD_DESCRIPTION, shardDescription);
}
if (additionalProperties.containsKey(SHARD_AUTHOR)) {
setShardAuthor((String) additionalProperties.get(SHARD_AUTHOR));
} else {
additionalProperties.put(SHARD_AUTHOR, shardAuthor);
}
if (additionalProperties.containsKey(SHARD_AUTHOR_EMAIL)) {
setShardAuthorEmail((String) additionalProperties.get(SHARD_AUTHOR_EMAIL));
} else {
additionalProperties.put(SHARD_AUTHOR_EMAIL, shardAuthorEmail);
}
if (additionalProperties.containsKey(PARAMS_ENCODER)) {
@@ -290,18 +297,14 @@ public class CrystalClientCodegen extends DefaultCodegen {
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh"));
supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore"));
supportingFiles.add(new SupportingFile("travis.mustache", "", ".travis.yml"));
supportingFiles.add(new SupportingFile("shard.mustache", "", "shard.yml"));
// crystal spec files
supportingFiles.add(new SupportingFile("spec_helper.mustache", specFolder, "spec_helper.cr")
.doNotOverwrite());
supportingFiles.add(new SupportingFile("spec_helper.mustache", specFolder, "spec_helper.cr").doNotOverwrite());
// add lambda for mustache templates
additionalProperties.put("lambdaPrefixWithHash", new PrefixWithHashLambda());
additionalProperties.put("lambdaUppercase", new UppercaseLambda());
additionalProperties.put("lambdaTitlecase", new TitlecaseLambda());
additionalProperties.put("lambdaPascalcase", new PascalCaseLambda());
}
@Override
@@ -321,14 +324,12 @@ public class CrystalClientCodegen extends DefaultCodegen {
@Override
public String apiFileFolder() {
return outputFolder + File.separator + srcFolder + File.separator + shardName + File.separator
+ apiPackage.replace("/", File.separator);
return outputFolder + File.separator + srcFolder + File.separator + shardName + File.separator + apiPackage.replace("/", File.separator);
}
@Override
public String modelFileFolder() {
return outputFolder + File.separator + srcFolder + File.separator + shardName + File.separator
+ modelPackage.replace("/", File.separator);
return outputFolder + File.separator + srcFolder + File.separator + shardName + File.separator + modelPackage.replace("/", File.separator);
}
@Override
@@ -407,9 +408,9 @@ public class CrystalClientCodegen extends DefaultCodegen {
// model name starts with number
if (modelName.matches("^\\d.*")) {
LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", modelName,
camelize("model_" + modelName));
modelName = "model_" + modelName; // e.g. 200Response => Model200Response (after camelize)
LOGGER.warn("{} (model name starts with number) cannot be used as model name. Renamed to {}", modelName, camelize("model_" + modelName));
// e.g. 200Response => Model200Response (after camelize)
modelName = "model_" + modelName;
}
// camelize the model name
@@ -548,8 +549,7 @@ public class CrystalClientCodegen extends DefaultCodegen {
// operationId starts with a number
if (operationId.matches("^\\d.*")) {
LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId,
underscore(sanitizeName("call_" + operationId)));
LOGGER.warn("{} (starting with a number) cannot be used as method name. Renamed to {}", operationId, underscore(sanitizeName("call_" + operationId)));
operationId = "call_" + operationId;
}
@@ -576,6 +576,7 @@ public class CrystalClientCodegen extends DefaultCodegen {
if (isSkipOperationExample()) {
return objs;
}
OperationMap operations = objs.getOperations();
HashMap<String, CodegenModel> modelMaps = ModelMap.toCodegenModelMap(allModels);
HashMap<String, Integer> processedModelMaps = new HashMap<>();
@@ -607,8 +608,7 @@ public class CrystalClientCodegen extends DefaultCodegen {
return objs;
}
private String constructExampleCode(CodegenParameter codegenParameter, HashMap<String, CodegenModel> modelMaps,
HashMap<String, Integer> processedModelMap) {
private String constructExampleCode(CodegenParameter codegenParameter, HashMap<String, CodegenModel> modelMaps, HashMap<String, Integer> processedModelMap) {
if (codegenParameter.isArray) { // array
if (codegenParameter.items == null) {
return "[]";
@@ -675,8 +675,7 @@ public class CrystalClientCodegen extends DefaultCodegen {
}
}
private String constructExampleCode(CodegenProperty codegenProperty, HashMap<String, CodegenModel> modelMaps,
HashMap<String, Integer> processedModelMap) {
private String constructExampleCode(CodegenProperty codegenProperty, HashMap<String, CodegenModel> modelMaps, HashMap<String, Integer> processedModelMap) {
if (codegenProperty.isArray) { // array
return "[" + constructExampleCode(codegenProperty.items, modelMaps, processedModelMap) + "]";
} else if (codegenProperty.isMap) {
@@ -743,8 +742,7 @@ public class CrystalClientCodegen extends DefaultCodegen {
}
}
private String constructExampleCode(CodegenModel codegenModel, HashMap<String, CodegenModel> modelMaps,
HashMap<String, Integer> processedModelMap) {
private String constructExampleCode(CodegenModel codegenModel, HashMap<String, CodegenModel> modelMaps, HashMap<String, Integer> processedModelMap) {
// break infinite recursion. Return, in case a model is already processed in the
// current context.
String model = codegenModel.name;
@@ -758,15 +756,24 @@ public class CrystalClientCodegen extends DefaultCodegen {
throw new RuntimeException("Invalid count when constructing example: " + count);
}
} else if (codegenModel.isEnum) {
List<Map<String, String>> enumVars = (List<Map<String, String>>) codegenModel.allowableValues
.get("enumVars");
List<Map<String, String>> enumVars = (List<Map<String, String>>) codegenModel.allowableValues.get("enumVars");
return moduleName + "::" + codegenModel.classname + "::" + enumVars.get(0).get("name");
} else if (codegenModel.oneOf != null && !codegenModel.oneOf.isEmpty()) {
String subModel = (String) codegenModel.oneOf.toArray()[0];
if (modelMaps.get(subModel) == null) {
LOGGER.warn("Cannot find codegen for SubModel: {} (model: {})", subModel, model);
return "";
if (subModel.startsWith("Array(")) {
subModel = StringUtils.removeEnd(subModel.substring(6), ")");
if (modelMaps.get(subModel) == null) {
LOGGER.warn("Cannot find codegen for SubModel: {} (model: {})", subModel, model);
return "";
} else {
LOGGER.info("Found Array codegen for SubModel: {} (model: {})", subModel, model);
String oneOf = "[" + constructExampleCode(modelMaps.get(subModel), modelMaps, processedModelMap) + "]";
return oneOf;
}
}
} else {
LOGGER.info("Found codegen for SubModel: {} (model: {})", subModel, model);
String oneOf = constructExampleCode(modelMaps.get(subModel), modelMaps, processedModelMap);
return oneOf;
}
@@ -781,7 +788,7 @@ public class CrystalClientCodegen extends DefaultCodegen {
}
String example = moduleName + "::" + toModelName(model) + ".new";
if (!propertyExamples.isEmpty()) {
example += "({" + StringUtils.join(propertyExamples, ", ") + "})";
example += "(" + StringUtils.join(propertyExamples, ", ") + ")";
}
return example;
}

View File

@@ -446,10 +446,28 @@ public class GoClientCodegen extends AbstractGoCodegen {
Object defaultValues = p.getDefault();
if (defaultValues instanceof ArrayNode) {
for (var value : (ArrayNode) defaultValues) {
joinedDefaultValues.add(value.toString());
if (value.isNull()) {
joinedDefaultValues.add("nil");
} else if (value.isTextual()) {
joinedDefaultValues.add("\"" + escapeText(value.asText()) + "\"");
} else {
joinedDefaultValues.add(value.toString());
}
}
return "{" + joinedDefaultValues + "}";
} else if (defaultValues instanceof List<?>) {
for (var value : (List<?>) defaultValues) {
if (value == null) {
joinedDefaultValues.add("nil");
} else if (value instanceof String) {
joinedDefaultValues.add("\"" + escapeText((String) value) + "\"");
} else {
joinedDefaultValues.add(value.toString());
}
}
return "{" + joinedDefaultValues + "}";
}
return null;
}
return super.toDefaultValue(p);

View File

@@ -458,10 +458,13 @@ public class NimClientCodegen extends DefaultCodegen implements CodegenConfig {
name = normalizeSchemaName(name);
CodegenModel mdl = super.fromModel(name, schema);
// Detect integer enums - check both the schema type and the dataType
// Detect numeric enums - check both the schema type and the dataType
// Note: "number" type in OpenAPI can include integer values in enums
if (mdl.isEnum) {
String schemaType = schema != null ? schema.getType() : null;
if ("integer".equals(schemaType) || "int".equals(mdl.dataType) || "int64".equals(mdl.dataType)) {
if ("integer".equals(schemaType) || "number".equals(schemaType) ||
"int".equals(mdl.dataType) || "int64".equals(mdl.dataType) ||
"float".equals(mdl.dataType) || "float64".equals(mdl.dataType)) {
mdl.vendorExtensions.put("x-is-integer-enum", true);
}
}
@@ -606,22 +609,38 @@ public class NimClientCodegen extends DefaultCodegen implements CodegenConfig {
return objs;
}
/**
* Resolve a schema reference to its target schema.
* This is needed to properly detect nested maps/arrays when the schema is a $ref.
*/
private Schema resolveSchema(Schema schema) {
if (schema != null && schema.get$ref() != null) {
Schema resolved = ModelUtils.getReferencedSchema(this.openAPI, schema);
return resolved != null ? resolved : schema;
}
return schema;
}
@Override
public String getTypeDeclaration(Schema p) {
if (ModelUtils.isArraySchema(p)) {
Schema inner = ModelUtils.getSchemaItems(p);
// Resolve the schema to check for nested maps/arrays - refs that point to map/array schemas
Schema resolved = resolveSchema(p);
if (ModelUtils.isArraySchema(resolved)) {
Schema inner = ModelUtils.getSchemaItems(resolved);
if (inner == null) {
return null;
}
return "seq[" + getTypeDeclaration(inner) + "]";
} else if (ModelUtils.isMapSchema(p)) {
Schema inner = ModelUtils.getAdditionalProperties(p);
} else if (ModelUtils.isMapSchema(resolved)) {
Schema inner = ModelUtils.getAdditionalProperties(resolved);
if (inner == null) {
inner = new StringSchema();
}
return "Table[string, " + getTypeDeclaration(inner) + "]";
}
// For non-containers, use the original schema to preserve model names
String schemaType = getSchemaType(p);
if (typeMapping.containsKey(schemaType)) {
return typeMapping.get(schemaType);
@@ -719,10 +738,17 @@ public class NimClientCodegen extends DefaultCodegen implements CodegenConfig {
@Override
public String toEnumVarName(String name, String datatype) {
// Handle negative numbers by prefixing with "Neg" to avoid collisions
// e.g., -1 and 1 would both become `1` without this, causing invalid syntax
if (name.startsWith("-")) {
name = "Neg" + name.substring(1);
}
name = name.replace(" ", "_");
name = StringUtils.camelize(name);
// starts with number or contains any character not allowed,see
// starts with number or contains any character not allowed, see
// https://nim-lang.org/docs/manual.html#lexical-analysis-identifiers-amp-keywords
if (isValidIdentifier(name)) {
return name;
} else {

View File

@@ -368,6 +368,45 @@ public class RustClientCodegen extends AbstractRustCodegen implements CodegenCon
break;
}
}
// Compute documentation type for each property
// This matches the actual generated code type, including HashSet for uniqueItems
for (CodegenProperty cp : cm.vars) {
String docType;
if (cp.datatypeWithEnum != null && !cp.datatypeWithEnum.isEmpty()) {
// Use enum type if available (e.g., Vec<UniqueItemArray> instead of Vec<String>)
docType = cp.datatypeWithEnum;
} else {
// Use regular dataType
docType = cp.dataType;
}
// Apply uniqueItems logic (matching model.mustache lines 139, 161)
// Arrays with uniqueItems=true use HashSet instead of Vec in the generated code
if (Boolean.TRUE.equals(cp.getUniqueItems()) && docType.startsWith("Vec<")) {
docType = docType.replace("Vec<", "HashSet<");
}
cp.vendorExtensions.put("x-doc-type", docType);
// Determine if this type should have a doc link
// Only local models should link, not external types from std lib or crates
boolean shouldLink = false;
if (cp.complexType != null && !cp.complexType.isEmpty()) {
// Check if it's an external type by looking for known prefixes
String[] externalPrefixes = {"std::", "serde_json::", "uuid::", "chrono::", "url::"};
boolean isExternal = false;
for (String prefix : externalPrefixes) {
if (cp.complexType.startsWith(prefix)) {
isExternal = true;
break;
}
}
shouldLink = !isExternal;
}
cp.vendorExtensions.put("x-should-link", shouldLink);
}
}
// process enum in models
return postProcessModelsEnum(objs);
@@ -741,7 +780,7 @@ public class RustClientCodegen extends AbstractRustCodegen implements CodegenCon
}
// If we use a file body parameter, we need to include the imports and crates for it
// But they should be added only once per file
// But they should be added only once per file
for (var param: operation.bodyParams) {
if (param.isFile && supportAsync && !useAsyncFileStream) {
useAsyncFileStream = true;
@@ -751,6 +790,18 @@ public class RustClientCodegen extends AbstractRustCodegen implements CodegenCon
}
}
// Also check form params for file uploads (multipart)
if (!useAsyncFileStream) {
for (var param: operation.formParams) {
if (param.isFile && supportAsync) {
useAsyncFileStream = true;
additionalProperties.put("useAsyncFileStream", Boolean.TRUE);
operation.vendorExtensions.put("useAsyncFileStream", Boolean.TRUE);
break;
}
}
}
// http method verb conversion, depending on client library (e.g. Hyper: PUT => Put, Reqwest: PUT => put)
if (HYPER_LIBRARY.equals(getLibrary())) {
operation.httpMethod = StringUtils.camelize(operation.httpMethod.toLowerCase(Locale.ROOT));

View File

@@ -84,7 +84,7 @@ public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCode
public static final String NGPACKAGR_VERSION = "ngPackagrVersion";
public static final String ZONEJS_VERSION = "zonejsVersion";
protected String ngVersion = "20.0.0";
protected String ngVersion = "21.0.0";
@Getter @Setter
protected String npmRepository = null;
@Setter(AccessLevel.PRIVATE) private boolean useSingleRequestParameter = false;
@@ -170,7 +170,7 @@ public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCode
@Override
public String getHelp() {
return "Generates a TypeScript Angular (9.x - 20.x) client library.";
return "Generates a TypeScript Angular (9.x - 21.x) client library.";
}
@Override

View File

@@ -0,0 +1,51 @@
/*
* 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.templating.mustache;
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
import org.openapitools.codegen.utils.CamelizeOption;
import java.io.IOException;
import java.io.Writer;
import static org.openapitools.codegen.utils.StringUtils.camelize;
/**
* Converts text in a fragment to PascalCase.
* <p>
* Register:
* <pre>
* additionalProperties.put("pascalcase", new PascalCaseLambda());
* </pre>
* <p>
* Use:
* <pre>
* {{#pascalcase}}{{name}}{{/pascalcase}}
* </pre>
*/
public class PascalCaseLambda implements Mustache.Lambda {
public PascalCaseLambda() {
}
@Override
public void execute(Template.Fragment fragment, Writer writer) throws IOException {
String text = fragment.execute();
text = camelize(text);
writer.write(text);
}
}

View File

@@ -21,9 +21,11 @@ public class ApiUtil {
public static void setExampleResponse(NativeWebRequest req, String contentType, String example) {
try {
HttpServletResponse res = req.getNativeResponse(HttpServletResponse.class);
res.setCharacterEncoding("UTF-8");
res.addHeader("Content-Type", contentType);
res.getWriter().print(example);
if (res != null) {
res.setCharacterEncoding("UTF-8");
res.addHeader("Content-Type", contentType);
res.getWriter().print(example);
}
} catch (IOException e) {
throw new RuntimeException(e);
}

View File

@@ -349,7 +349,7 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
* Convert the given object to string with each line indented by 4 spaces
* (except the first line).
*/
private String toIndentedString(Object o) {
private String toIndentedString({{>nullableAnnotation}}Object o) {
if (o == null) {
return "null";
}

View File

@@ -261,6 +261,23 @@ public:
{{/isInherited}}
{{/vars}}
{{#additionalPropertiesType}}
/// <summary>
/// Get additional properties (properties not defined in the schema)
/// </summary>
std::map<utility::string_t, web::json::value> getAdditionalProperties() const;
bool additionalPropertiesIsSet() const;
void unsetAdditionalProperties();
/// <summary>
/// Set additional properties
/// </summary>
void setAdditionalProperties(const std::map<utility::string_t, web::json::value>& value);
/// <summary>
/// Add a single additional property
/// </summary>
void addAdditionalProperty(const utility::string_t& key, const web::json::value& value);
{{/additionalPropertiesType}}
protected:
{{#vars}}
@@ -285,6 +302,10 @@ protected:
{{/isInherited}}
{{/vars}}
{{#additionalPropertiesType}}
std::map<utility::string_t, web::json::value> m_AdditionalProperties;
bool m_AdditionalPropertiesIsSet;
{{/additionalPropertiesType}}
};
{{/isEnum}}

View File

@@ -207,6 +207,9 @@ void {{classname}}::setValue({{classname}}::e{{classname}} const value)
{{/isNullable}}
{{/isInherited}}
{{/vars}}
{{#additionalPropertiesType}}
m_AdditionalPropertiesIsSet = false;
{{/additionalPropertiesType}}
}
{{classname}}::~{{classname}}()
@@ -262,6 +265,16 @@ web::json::value {{classname}}::toJson() const
{{/isNullable}}
{{/isInherited}}
{{/vars}}
{{#additionalPropertiesType}}
// Serialize additional properties
if(m_AdditionalPropertiesIsSet)
{
for(const auto& item : m_AdditionalProperties)
{
val[item.first] = item.second;
}
}
{{/additionalPropertiesType}}
return val;
}
@@ -295,6 +308,24 @@ bool {{classname}}::fromJson(const web::json::value& val)
}
{{/isInherited}}
{{/vars}}
{{#additionalPropertiesType}}
// Capture additional properties (keys not defined in the schema)
if(val.is_object())
{
for(const auto& item : val.as_object())
{
// Skip known properties
{{#vars}}
{{^isInherited}}
if(item.first == utility::conversions::to_string_t(_XPLATSTR("{{baseName}}"))) continue;
{{/isInherited}}
{{/vars}}
// This is an additional property
m_AdditionalProperties[item.first] = item.second;
m_AdditionalPropertiesIsSet = true;
}
}
{{/additionalPropertiesType}}
return ok;
}
@@ -515,6 +546,36 @@ void {{classname}}::unset{{name}}()
{{/isNullable}}
}
{{/isInherited}}{{/vars}}
{{#additionalPropertiesType}}
std::map<utility::string_t, web::json::value> {{classname}}::getAdditionalProperties() const
{
return m_AdditionalProperties;
}
void {{classname}}::setAdditionalProperties(const std::map<utility::string_t, web::json::value>& value)
{
m_AdditionalProperties = value;
m_AdditionalPropertiesIsSet = true;
}
void {{classname}}::addAdditionalProperty(const utility::string_t& key, const web::json::value& value)
{
m_AdditionalProperties[key] = value;
m_AdditionalPropertiesIsSet = true;
}
bool {{classname}}::additionalPropertiesIsSet() const
{
return m_AdditionalPropertiesIsSet;
}
void {{classname}}::unsetAdditionalProperties()
{
m_AdditionalProperties.clear();
m_AdditionalPropertiesIsSet = false;
}
{{/additionalPropertiesType}}
{{/isEnum}}
{{/oneOf}}
{{#modelNamespaceDeclarations}}

View File

@@ -17,6 +17,7 @@
#include <cpprest/json.h>
#include <map>
#include <memory>
#include <set>
#include <vector>

View File

@@ -141,14 +141,14 @@ bourne::json
}
{{#vars}}
{{dataType}}{{#isContainer}}{{#isMap}}<string, string>{{/isMap}}{{^isMap}}<{{#items}}{{dataType}}{{/items}}>{{/isMap}}{{/isContainer}}
{{#isMap}}{{{dataType}}}{{/isMap}}{{^isMap}}{{dataType}}{{#isArray}}<{{#items}}{{dataType}}{{/items}}>{{/isArray}}{{/isMap}}
{{classname}}::{{getter}}()
{
return {{name}};
}
void
{{classname}}::{{setter}}({{dataType}} {{#isContainer}}{{#isMap}}<string, string>{{/isMap}}{{^isMap}}<{{#items}}{{dataType}}{{/items}}>{{/isMap}}{{/isContainer}} {{name}})
{{classname}}::{{setter}}({{#isMap}}{{{dataType}}}{{/isMap}}{{^isMap}}{{dataType}}{{#isArray}}<{{#items}}{{dataType}}{{/items}}>{{/isArray}}{{/isMap}} {{name}})
{
this->{{name}} = {{name}};
}

View File

@@ -51,27 +51,32 @@ public:
{{#vars}}
/*! \brief Get {{{description}}}
*/
{{dataType}}{{#isContainer}}{{#isMap}}<std::string, std::string>{{/isMap}}{{^isMap}}<{{#items}}{{dataType}}{{/items}}>{{/isMap}}{{/isContainer}} {{getter}}();
{{#isMap}}{{{dataType}}}{{/isMap}}{{^isMap}}{{dataType}}{{#isArray}}<{{#items}}{{dataType}}{{/items}}>{{/isArray}}{{/isMap}} {{getter}}();
/*! \brief Set {{{description}}}
*/
void {{setter}}({{dataType}} {{#isContainer}}{{#isMap}}<std::string, std::string>{{/isMap}}{{^isMap}}<{{#items}}{{dataType}}{{/items}}>{{/isMap}}{{/isContainer}} {{name}});
void {{setter}}({{#isMap}}{{{dataType}}}{{/isMap}}{{^isMap}}{{dataType}}{{#isArray}}<{{#items}}{{dataType}}{{/items}}>{{/isArray}}{{/isMap}} {{name}});
{{/vars}}
private:
{{#vars}}
{{^isContainer}}
{{#isMap}}
{{{dataType}}} {{name}};
{{/isMap}}
{{^isMap}}
{{#isArray}}
{{dataType}}<{{#items}}{{dataType}}{{/items}}> {{name}};
{{/isArray}}
{{^isArray}}
{{#isPrimitiveType}}
{{dataType}} {{name}}{};
{{/isPrimitiveType}}
{{^isPrimitiveType}}
{{dataType}} {{name}};
{{/isPrimitiveType}}
{{/isContainer}}
{{#isContainer}}
{{dataType}}{{#isMap}}<std::string, std::string>{{/isMap}}{{^isMap}}<{{#items}}{{dataType}}{{/items}}>{{/isMap}} {{name}};
{{/isContainer}}
{{/isArray}}
{{/isMap}}
{{/vars}}
};
{{/model}}

View File

@@ -159,25 +159,25 @@ module {{moduleName}}
# http body (model)
post_body = {{#bodyParam}}{{{paramName}}}.to_json{{/bodyParam}}{{^bodyParam}}nil{{/bodyParam}}
# return_type
return_type = {{#returnType}}"{{{.}}}"{{/returnType}}{{^returnType}}nil{{/returnType}}
# auth_names
auth_names = {{#authMethods}}{{#-first}}[{{/-first}}"{{name}}"{{^-last}}, {{/-last}}{{#-last}}]{{/-last}}{{/authMethods}}{{^authMethods}}[] of String{{/authMethods}}
data, status_code, headers = @api_client.call_api(:{{httpMethod}},
local_var_path,
:"{{classname}}.{{operationId}}",
return_type,
post_body,
auth_names,
header_params,
query_params,
cookie_params,
form_params)
data, status_code, headers = @api_client.call_api(
http_method: :{{httpMethod}},
path: local_var_path,
operation: :"{{classname}}.{{operationId}}",
post_body: post_body,
auth_names: auth_names,
header_params: header_params,
query_params: query_params,
cookie_params: cookie_params,
form_params: form_params
)
if @api_client.config.debugging
Log.debug {"API called: {{classname}}#{{operationId}}\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"}
end
return {{#returnType}}{{{.}}}.from_json(data){{/returnType}}{{^returnType}}nil{{/returnType}}, status_code, headers
end
{{^-last}}

View File

@@ -119,7 +119,7 @@ module {{moduleName}}
#
# @return [Array<(Object, Integer, Hash)>] an array of 3 elements:
# the data deserialized from response body (could be nil), response status code and response headers.
def call_api(http_method : Symbol, path : String, operation : Symbol, return_type : String?, post_body : String?, auth_names = [] of String, header_params = {} of String => String, query_params = {} of String => String, cookie_params = {} of String => String, form_params = {} of Symbol => (String | ::File))
def call_api(http_method : Symbol, path : String, operation : Symbol, post_body : String?, auth_names = [] of String, header_params = {} of String => String, query_params = {} of String => String, cookie_params = {} of String => String, form_params = {} of Symbol => (String | ::File))
#ssl_options = {
# :ca_file => @config.ssl_ca_file,
# :verify => @config.ssl_verify,
@@ -139,8 +139,9 @@ module {{moduleName}}
form_or_body = form_params
end
request = Crest::Request.new(http_method,
build_request_url(path, operation),
request = Crest::Request.new(
method: http_method,
url: build_request_url(path, operation),
params: query_params,
headers: header_params,
cookies: cookie_params,

View File

@@ -1,78 +1,3 @@
# Builds the object from hash
# @param [Hash] attributes Model attributes in the form of hash
# @return [Object] Returns the model itself
def self.build_from_hash(attributes)
new.build_from_hash(attributes)
end
# Builds the object from hash
# @param [Hash] attributes Model attributes in the form of hash
# @return [Object] Returns the model itself
def build_from_hash(attributes)
return nil unless attributes.is_a?(Hash)
{{#parent}}
super(attributes)
{{/parent}}
self.class.openapi_types.each_pair do |key, type|
if !attributes[self.class.attribute_map[key]]? && self.class.openapi_nullable.includes?(key)
self.send("#{key}=", nil)
elsif type =~ /\AArray<(.*)>/i
# check to ensure the input is an array given that the attribute
# is documented as an array but the input is not
if attributes[self.class.attribute_map[key]].is_a?(Array)
self.send("#{key}=", attributes[self.class.attribute_map[key]].map { |v| _deserialize($1, v) })
end
elsif !attributes[self.class.attribute_map[key]].nil?
self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]]))
end
end
self
end
# Deserializes the data based on type
# @param string type Data type
# @param string value Value to be deserialized
# @return [Object] Deserialized data
def _deserialize(type, value)
case type.to_sym
when :Time
Time.parse(value)
when :Date
Date.parse(value)
when :String
value.to_s
when :Integer
value.to_i
when :Float
value.to_f
when :Boolean
if value.to_s =~ /\A(true|t|yes|y|1)\z/i
true
else
false
end
when :Object
# generic object (usually a Hash), return directly
value
when /\AArray<(?<inner_type>.+)>\z/
inner_type = Regexp.last_match[:inner_type]
value.map { |v| _deserialize(inner_type, v) }
when /\AHash<(?<k_type>.+?), (?<v_type>.+)>\z/
k_type = Regexp.last_match[:k_type]
v_type = Regexp.last_match[:v_type]
({} of Symbol => String).tap do |hash|
value.each do |k, v|
hash[_deserialize(k_type, k)] = _deserialize(v_type, v)
end
end
else # model
# models (e.g. Pet) or oneOf
klass = {{moduleName}}.const_get(type)
klass.respond_to?(:openapi_one_of) ? klass.build(value) : klass.build_from_hash(value)
end
end
# Returns the string representation of the object
# @return [String] String presentation of the object
def to_s
@@ -100,6 +25,8 @@
# @param [Object] value Any valid value
# @return [Hash] Returns the value in the form of hash
private def _to_h(value)
return nil if value.nil?
if value.is_a?(Hash)
hash = NetboxClient::RecursiveHash.new
value.each { |k, v| hash[k] = _to_h(v) }

View File

@@ -1,10 +1,5 @@
# {{#lambdaPrefixWithHash}}{{> api_info}}{{/lambdaPrefixWithHash}}
require "big"
require "json"
require "yaml"
require "time"
module {{moduleName}}
{{#models}}
{{#model}}

View File

@@ -56,7 +56,7 @@
{{#vars}}
{{#isEnum}}
{{^isContainer}}
class EnumAttributeValidatorFor{{#lambdaTitlecase}}{{{name}}}{{/lambdaTitlecase}} < EnumAttributeValidator
class EnumAttributeValidatorFor{{#lambdaPascalcase}}{{{name}}}{{/lambdaPascalcase}} < EnumAttributeValidator
@attribute : String
@allowable_values : Array(Int32 | Int64 | Float32 | Float64 | String)
@@ -118,7 +118,7 @@
{{#vars}}
{{#isEnum}}
{{^isContainer}}
{{{name}}}_validator = EnumAttributeValidatorFor{{#lambdaTitlecase}}{{{name}}}{{/lambdaTitlecase}}.new
{{{name}}}_validator = EnumAttributeValidatorFor{{#lambdaPascalcase}}{{{name}}}{{/lambdaPascalcase}}.new
if !{{{name}}}_validator.valid?(@{{{name}}})
message = {{{name}}}_validator.message
invalid_properties.push(message)
@@ -181,7 +181,7 @@
{{#vars}}
{{#isEnum}}
{{^isContainer}}
{{{name}}}_validator = EnumAttributeValidatorFor{{#lambdaTitlecase}}{{{name}}}{{/lambdaTitlecase}}.new
{{{name}}}_validator = EnumAttributeValidatorFor{{#lambdaPascalcase}}{{{name}}}{{/lambdaPascalcase}}.new
return false unless {{{name}}}_validator.valid?(@{{{name}}})
{{/isContainer}}
{{/isEnum}}
@@ -234,7 +234,7 @@
# Custom attribute writer method checking allowed values (enum).
# @param [Object] {{{name}}} Object to be assigned
def {{{name}}}=({{{name}}})
validator = EnumAttributeValidatorFor{{#lambdaTitlecase}}{{{name}}}{{/lambdaTitlecase}}.new
validator = EnumAttributeValidatorFor{{#lambdaPascalcase}}{{{name}}}{{/lambdaPascalcase}}.new
unless validator.valid?({{{name}}})
raise ArgumentError.new(validator.message)
end

View File

@@ -1,135 +1,104 @@
{{#description}}
# {{{.}}}
{{/description}}
module {{classname}}
class {{classname}}
include JSON::Serializable
include YAML::Serializable
class SchemaMismatchError < Exception
end
{{#oneOf}}
{{#-first}}
# List of class defined in oneOf (OpenAPI v3)
def self.openapi_one_of
[
{{/-first}}
:"{{{.}}}"{{^-last}},{{/-last}}
{{{.}}}{{^-last}},{{/-last}}
{{#-last}}
]
end
{{/-last}}
{{/oneOf}}
{{#discriminator}}
{{#propertyName}}
# Discriminator's property name (OpenAPI v3)
def self.openapi_discriminator_name
:"{{{.}}}"
end
{{/propertyName}}
{{#mappedModels}}
{{#-first}}
# Discriminator's mapping (OpenAPI v3)
def self.openapi_discriminator_mapping
{
{{/-first}}
:"{{{mappingName}}}" => :"{{{modelName}}}"{{^-last}},{{/-last}}
{{#-last}}
}
end
{{/-last}}
{{/mappedModels}}
use_yaml_discriminator {{#propertyName}}"{{{.}}}"{{/propertyName}}, {
{{#mappedModels}}{{{mappingName}}}: {{{modelName}}}{{^-last}},{{/-last}}{{/mappedModels}}
}
{{/discriminator}}
# Builds the object
# @param [Mixed] Data to be matched against the list of oneOf items
# @return [Object] Returns the model or the data itself
def self.build(data)
{{#discriminator}}
discriminator_value = data[openapi_discriminator_name]
return nil unless discriminator_value
{{#mappedModels}}
{{#-first}}
klass = openapi_discriminator_mapping[discriminator_value.to_sym]
return nil unless klass
{{moduleName}}.const_get(klass).build_from_hash(data)
{{/-first}}
{{/mappedModels}}
{{^mappedModels}}
{{moduleName}}.const_get(discriminator_value).build_from_hash(data)
{{/mappedModels}}
{{/discriminator}}
{{^discriminator}}
# Go through the list of oneOf items and attempt to identify the appropriate one.
# Note:
# - We do not attempt to check whether exactly one item matches.
# - No advanced validation of types in some cases (e.g. "x: { type: string }" will happily match { x: 123 })
# due to the way the deserialization is made in the base_object template (it just casts without verifying).
# - TODO: scalar values are de facto behaving as if they were nullable.
# - TODO: logging when debugging is set.
openapi_one_of.each do |klass|
begin
next if klass == :AnyType # "nullable: true"
typed_data = find_and_cast_into_type(klass, data)
return typed_data if typed_data
rescue # rescue all errors so we keep iterating even if the current item lookup raises
rescue ex
# rescue all errors so we keep iterating even if the current item lookup raises
Log.trace { ex.message }
end
end
openapi_one_of.includes?(:AnyType) ? data : nil
{{/discriminator}}
nil
end
{{^discriminator}}
SchemaMismatchError = Class.new(StandardError)
# Note: 'File' is missing here because in the regular case we get the data _after_ a call to JSON.parse.
private def self.find_and_cast_into_type(klass, data)
{{#oneOf}}
private def self.find_and_cast_into_type(klass : {{{.}}}.class, data)
return if data.nil?
begin
case klass.to_s
when "Boolean"
return data if data.instance_of?(TrueClass) || data.instance_of?(FalseClass)
when "Float"
return data if data.instance_of?(Float)
when "Integer"
return data if data.instance_of?(Integer)
when "Time"
return Time.parse(data)
when "Date"
return Date.parse(data)
when "String"
return data if data.instance_of?(String)
when "Object" # "type: object"
return data if data.instance_of?(Hash)
when /\AArray<(?<sub_type>.+)>\z/ # "type: array"
if data.instance_of?(Array)
sub_type = Regexp.last_match[:sub_type]
return data.map { |item| find_and_cast_into_type(sub_type, item) }
end
when /\AHash<String, (?<sub_type>.+)>\z/ # "type: object" with "additionalProperties: { ... }"
if data.instance_of?(Hash) && data.keys.all? { |k| k.instance_of?(Symbol) || k.instance_of?(String) }
sub_type = Regexp.last_match[:sub_type]
return data.each_with_object({} of String | Symbol => Bool | Float | Integer | Time | Date | String | Array | Hash) { |(k, v), hsh| hsh[k] = find_and_cast_into_type(sub_type, v) }
end
else # model
const = {{moduleName}}.const_get(klass)
if const
if const.respond_to?(:openapi_one_of) # nested oneOf model
model = const.build(data)
return model if model
else
# raise if data contains keys that are not known to the model
raise unless (data.keys - const.acceptable_attributes).empty?
model = const.build_from_hash(data)
return model if model && model.valid?
end
end
end
Log.trace { "INSPECTING DATA" }
Log.trace { data.inspect }
raise # if no match by now, raise
rescue
raise SchemaMismatchError, "#{data} doesn't match the #{klass} type"
case data
when NetboxClient::RecursiveHash
if value = cast_value(array_data: false, array_class: array_class?(klass), klass: klass, data: data)
return new(value)
end
when Array(NetboxClient::RecursiveHash)
if value = cast_value(array_data: true, array_class: array_class?(klass), klass: klass, data: data)
return new(value)
end
else
raise SchemaMismatchError.new("#{data} doesn't match the #{klass} type")
end
end
{{/oneOf}}
private def self.cast_value(array_data : Bool, array_class : Bool, klass, data)
if array_class == true && array_data == true
Log.debug { "Building array of classes: #{klass} / #{data}" }
klass.from_json(data.to_json)
elsif array_class == false && array_data == false
Log.debug { "Building single class: #{klass} / #{data}" }
klass.from_json(data.to_json)
end
end
private def self.array_class?(klass)
klass.name.starts_with?("Array(")
end
{{#oneOf}}
def initialize(@value : {{{.}}})
end
{{/oneOf}}
delegate :to_yaml, to: @value
delegate :to_json, to: @value
def to_any_h
{"value" => to_h}
end
def to_h
val = @value
if val.is_a?(Int32)
val
else
val.to_h
end
end
{{/discriminator}}
end

View File

@@ -1,10 +1,15 @@
name: {{{shardName}}}
version: {{{shardVersion}}}
authors:
- {{{shardAuthors}}}
description: |
- {{{ shardDescription}}}
crystal: ">= 0.35.1"
dependencies:
any_hash:
github: Sija/any_hash.cr
@@ -13,9 +18,6 @@ dependencies:
version: ~> 1.3.13
development_dependencies:
kemal:
github: kemalcr/kemal
version: ~>1.5.0
ameba:
github: crystal-ameba/ameba
spectator:

View File

@@ -1,9 +1,15 @@
# {{#lambdaPrefixWithHash}}{{> api_info}}{{/lambdaPrefixWithHash}}
# Dependencies
# Stdlib dependencies
require "big"
require "json"
require "log"
require "time"
require "yaml"
# External dependencies
require "any_hash"
require "crest"
require "log"
module {{moduleName}}
Log = ::Log.for("{{moduleName}}") # => Log for {{moduleName}} source

View File

@@ -2,14 +2,4 @@
# load modules
require "spectator"
require "json"
require "time"
require "../src/{{{shardName}}}"
def assert_compilation_error(path : String, message : String) : Nil
buffer = IO::Memory.new
result = Process.run("crystal", ["run", "--no-color", "--no-codegen", path], error: buffer)
result.success?.should be_false
buffer.to_s.should contain message
buffer.close
end

View File

@@ -1,8 +0,0 @@
# {{#lambdaPrefixWithHash}}{{> api_info}}{{/lambdaPrefixWithHash}}
language: crystal
script:
- crystal spec
# uncomment below to check the code format
# - crystal tool format --check

View File

@@ -85,7 +85,8 @@ namespace {{packageName}}.{{clientPackage}}
}
}
internal override async System.Threading.Tasks.ValueTask<TTokenBase> GetAsync(string header = "", System.Threading.CancellationToken cancellation = default{{^netstandard20OrLater}}(global::System.Threading.CancellationToken){{/netstandard20OrLater}})
/// <inheritdoc/>
protected internal override async System.Threading.Tasks.ValueTask<TTokenBase> GetAsync(string header = "", System.Threading.CancellationToken cancellation = default{{^netstandard20OrLater}}(global::System.Threading.CancellationToken){{/netstandard20OrLater}})
{
if (!AvailableTokens.TryGetValue(header, out global::System.Threading.Channels.Channel<TTokenBase>{{nrt?}} tokens))
throw new KeyNotFoundException($"Could not locate a token for header '{header}'.");

View File

@@ -22,7 +22,12 @@ namespace {{packageName}}
/// </summary>
protected TTokenBase[] _tokens;
internal abstract System.Threading.Tasks.ValueTask<TTokenBase> GetAsync(string header = "", System.Threading.CancellationToken cancellation = default{{^netstandard20OrLater}}(global::System.Threading.CancellationToken){{/netstandard20OrLater}});
/// <summary>
/// Gets an authentication token to be used in request authorization.
/// </summary>
/// <param name="header"></param>
/// <param name="cancellation"></param>
protected internal abstract System.Threading.Tasks.ValueTask<TTokenBase> GetAsync(string header = "", System.Threading.CancellationToken cancellation = default{{^netstandard20OrLater}}(global::System.Threading.CancellationToken){{/netstandard20OrLater}});
/// <summary>
/// Instantiates a TokenProvider.
@@ -31,9 +36,6 @@ namespace {{packageName}}
public TokenProvider(IEnumerable<TTokenBase> tokens)
{
_tokens = tokens.ToArray();
if (_tokens.Length == 0)
throw new ArgumentException("You did not provide any tokens.");
}
}
}

View File

@@ -217,16 +217,16 @@ func (a *{{{classname}}}Service) {{nickname}}Execute(r {{#structPrefix}}{{&class
parameterAddToHeaderOrQuery(localVarQueryParams, "{{{baseName}}}", r.{{paramName}}, "{{style}}", "{{collectionFormat}}")
{{/isCollectionFormatMulti}}
{{#defaultValue}}} else {
{{#isArray}}
var defaultValue {{{dataType}}} = {{{dataType}}}{{{.}}}
parameterAddToHeaderOrQuery(localVarQueryParams, "{{{baseName}}}", defaultValue, "{{style}}", "{{collectionFormat}}")
r.{{paramName}} = &defaultValue
{{/isArray}}
{{^isArray}}
var defaultValue {{{dataType}}} = {{{.}}}
parameterAddToHeaderOrQuery(localVarQueryParams, "{{{baseName}}}", defaultValue, "{{style}}", "{{collectionFormat}}")
r.{{paramName}} = &defaultValue
{{/isArray}}
{{#isArray}}
var defaultValue {{{dataType}}} = {{{dataType}}}{{{.}}}
parameterAddToHeaderOrQuery(localVarQueryParams, "{{{baseName}}}", defaultValue, "{{style}}", "{{collectionFormat}}")
r.{{paramName}} = &defaultValue
{{/isArray}}
{{^isArray}}
var defaultValue {{{dataType}}} = {{{.}}}
parameterAddToHeaderOrQuery(localVarQueryParams, "{{{baseName}}}", defaultValue, "{{style}}", "{{collectionFormat}}")
r.{{paramName}} = &defaultValue
{{/isArray}}
{{/defaultValue}}}
{{/required}}
{{/queryParams}}

View File

@@ -592,10 +592,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error {
if err != nil {
return err
}
err = file.Close()
if err != nil {
return err
}
defer file.Close()
part, err := w.CreateFormFile(fieldName, filepath.Base(path))
if err != nil {

View File

@@ -108,14 +108,16 @@ kotlin {
explicitApi()
}
{{/explicitApi}}
{{#jvm-spring-webclient}}{{#useSpringBoot3}}
{{#jvm-spring-webclient}}
{{#useSpringBoot3}}
kotlin {
jvmToolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
{{/useSpringBoot3}}{{/jvm-spring-webclient}}
{{/useSpringBoot3}}
{{/jvm-spring-webclient}}
{{#jvm-spring-restclient}}
kotlin {
@@ -219,7 +221,6 @@ dependencies {
{{/jackson}}
implementation "com.squareup.retrofit2:converter-scalars:$retrofitVersion"
{{/jvm-retrofit2}}
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
{{#jvm-vertx}}
implementation "io.vertx:vertx-web-client:$vertx_version"
implementation "io.vertx:vertx-core:$vertx_version"
@@ -229,6 +230,10 @@ dependencies {
implementation "io.vertx:vertx-lang-kotlin-coroutines:$vertx_version"
{{/useCoroutines}}
{{/jvm-vertx}}
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
{{#jvm-spring}}
testImplementation "org.springframework.boot:spring-boot-test:$spring_boot_version"
{{/jvm-spring}}
}
{{#kotlinx_serialization}}

View File

@@ -106,14 +106,34 @@ import kotlinx.serialization.*
/**
* Returns a valid [{{classname}}] for [data], null otherwise.
*/
{{#jackson}}@JvmStatic
@JsonCreator
{{/jackson}}{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}fun decode(data: kotlin.Any?): {{classname}}? = data?.let {
{{^jackson}}
{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}fun decode(data: kotlin.Any?): {{classname}}? = data?.let {
val normalizedData = "$it".lowercase()
values().firstOrNull { value ->
it == value || normalizedData == "$value".lowercase()
}
}
{{/jackson}}
{{#jackson}}
@JvmStatic
@JsonCreator
{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}fun decode(data: kotlin.Any?): {{classname}}{{#isNullable}}?{{/isNullable}} {
if (data == null) {
{{#isNullable}}
return null
{{/isNullable}}
{{^isNullable}}
throw IllegalArgumentException("Value for {{classname}} cannot be null")
{{/isNullable}}
}
val normalizedData = "$data".lowercase()
return values().firstOrNull { value ->
data == value || normalizedData == "$value".lowercase()
}{{#isNullable}}{{/isNullable}}{{^isNullable}}{{#enumUnknownDefaultCase}}
?: {{#allowableValues}}{{#enumVars}}{{#-last}}{{&name}}{{/-last}}{{/enumVars}}{{/allowableValues}}{{/enumUnknownDefaultCase}}{{^enumUnknownDefaultCase}}
?: throw IllegalArgumentException("Unknown {{classname}} value: $data"){{/enumUnknownDefaultCase}}{{/isNullable}}
}
{{/jackson}}
}
}
{{#kotlinx_serialization}}{{#enumUnknownDefaultCase}}

View File

@@ -30,9 +30,6 @@ import org.springframework.validation.annotation.Validated
{{/useBeanValidation}}
import org.springframework.web.context.request.NativeWebRequest
import org.springframework.beans.factory.annotation.Autowired
{{#useRequestMappingOnController}}
import {{#apiPackage}}{{.}}.{{/apiPackage}}{{classname}}Controller.Companion.BASE_PATH
{{/useRequestMappingOnController}}
{{#useBeanValidation}}
import {{javaxPackage}}.validation.Valid
@@ -60,9 +57,7 @@ import kotlin.collections.Map
@Api(value = "{{{baseName}}}", description = "The {{{baseName}}} API")
{{/swagger1AnnotationLibrary}}
{{#useRequestMappingOnController}}
{{=<% %>=}}
@RequestMapping("\${openapi.<%title%>.base-path:\${api.base-path:$BASE_PATH}}")
<%={{ }}=%>
@RequestMapping("\${api.base-path:{{contextPath}}}")
{{/useRequestMappingOnController}}
{{#operations}}
class {{classname}}Controller({{#serviceInterface}}@Autowired(required = true) val service: {{classname}}Service{{/serviceInterface}}) {

View File

@@ -3,17 +3,12 @@ package {{package}}
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.RequestMapping
import java.util.Optional
{{#useRequestMappingOnController}}
import {{#apiPackage}}{{.}}.{{/apiPackage}}{{classname}}Controller.Companion.BASE_PATH
{{/useRequestMappingOnController}}
{{>generatedAnnotation}}
@Controller{{#beanQualifiers}}("{{package}}.{{classname}}Controller"){{/beanQualifiers}}
{{#useRequestMappingOnController}}
{{=<% %>=}}
@RequestMapping("\${openapi.<%title%>.base-path:\${api.base-path:$BASE_PATH}}")
<%={{ }}=%>
@RequestMapping("\${api.base-path:{{contextPath}}}")
{{/useRequestMappingOnController}}
{{#operations}}
class {{classname}}Controller(

View File

@@ -35,9 +35,6 @@ import org.springframework.validation.annotation.Validated
{{/useBeanValidation}}
import org.springframework.web.context.request.NativeWebRequest
import org.springframework.beans.factory.annotation.Autowired
{{#useRequestMappingOnInterface}}
import {{#apiPackage}}{{.}}.{{/apiPackage}}{{classname}}.Companion.BASE_PATH
{{/useRequestMappingOnInterface}}
{{#useBeanValidation}}
import {{javaxPackage}}.validation.constraints.DecimalMax
@@ -67,9 +64,7 @@ import kotlin.collections.Map
@Api(value = "{{{baseName}}}", description = "The {{{baseName}}} API")
{{/swagger1AnnotationLibrary}}
{{#useRequestMappingOnInterface}}
{{=<% %>=}}
@RequestMapping("\${openapi.<%title%>.base-path:\${api.base-path:$BASE_PATH}}")
<%={{ }}=%>
@RequestMapping("\${api.base-path:{{contextPath}}}")
{{/useRequestMappingOnInterface}}
{{#operations}}
interface {{classname}} {

View File

@@ -6,9 +6,6 @@ package {{package}}
{{#imports}}import {{import}}
{{/imports}}
{{#useRequestMappingOnInterface}}
import {{#apiPackage}}{{.}}.{{/apiPackage}}{{classname}}.Companion.BASE_PATH
{{/useRequestMappingOnInterface}}
{{#swagger2AnnotationLibrary}}
import io.swagger.v3.oas.annotations.*
@@ -51,9 +48,7 @@ import kotlin.collections.List
import kotlin.collections.Map
{{#useRequestMappingOnInterface}}
{{=<% %>=}}
@HttpExchange("\${openapi.<%title%>.base-path:\${api.base-path:$BASE_PATH}}")
<%={{ }}=%>
@HttpExchange("\${api.base-path:{{contextPath}}}")
{{/useRequestMappingOnInterface}}
{{#useBeanValidation}}
@Validated

View File

@@ -131,10 +131,10 @@ use {{invokerPackage}}\ObjectSerializer;
* {{.}}
*
{{/description}}
{{#vendorExtensions.x-group-parameters}}
{{#exts.x-group-parameters}}
* Note: the input parameter is an associative array with the keys listed as the parameter name below
*
{{/vendorExtensions.x-group-parameters}}
{{/exts.x-group-parameters}}
{{#servers}}
{{#-first}}
* This operation contains host(s) defined in the OpenAPI spec. Use 'hostIndex' to select the host.
@@ -156,7 +156,7 @@ use {{invokerPackage}}\ObjectSerializer;
{{/-last}}
{{/servers}}
{{#allParams}}
* @param {{{dataType}}}{{^required}}|null{{/required}} ${{paramName}}{{#description}} {{.}}{{/description}}{{^description}} {{paramName}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}}
* @param {{{dataType}}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{.}}{{/description}}{{^description}} {{paramName}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}}
{{/allParams}}
{{#servers}}
{{#-first}}
@@ -168,15 +168,15 @@ use {{invokerPackage}}\ObjectSerializer;
*
* @throws ApiException on non-2xx response or if the response body is not in the expected format
* @throws InvalidArgumentException
* @return {{{vendorExtensions.x-php-doc-return-type}}}
* @return {{{exts.x-php-doc-return-type}}}
{{#isDeprecated}}
* @deprecated
{{/isDeprecated}}
*/
public function {{operationId}}(
{{^vendorExtensions.x-group-parameters}}
{{^exts.x-group-parameters}}
{{#allParams}}
{{vendorExtensions.x-php-param-type}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}},
{{exts.x-php-param-type}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}},
{{/allParams}}
{{#servers}}
{{#-first}}
@@ -185,14 +185,14 @@ use {{invokerPackage}}\ObjectSerializer;
{{/-first}}
{{/servers}}
string $contentType = self::contentTypes['{{{operationId}}}'][0]
{{/vendorExtensions.x-group-parameters}}
{{#vendorExtensions.x-group-parameters}}
{{/exts.x-group-parameters}}
{{#exts.x-group-parameters}}
array $associative_array
{{/vendorExtensions.x-group-parameters}}
): {{{vendorExtensions.x-php-return-type}}}
{{/exts.x-group-parameters}}
): {{{exts.x-php-return-type}}}
{
{{^vendorExtensions.x-php-return-type-is-void}}list($response) = {{/vendorExtensions.x-php-return-type-is-void}}$this->{{operationId}}WithHttpInfo({{^vendorExtensions.x-group-parameters}}{{#allParams}}${{paramName}}, {{/allParams}}{{#servers}}{{#-first}}$hostIndex, $variables, {{/-first}}{{/servers}}$contentType{{/vendorExtensions.x-group-parameters}}{{#vendorExtensions.x-group-parameters}}$associative_array{{/vendorExtensions.x-group-parameters}});{{^vendorExtensions.x-php-return-type-is-void}}
return $response;{{/vendorExtensions.x-php-return-type-is-void}}
{{^exts.x-php-return-type-is-void}}list($response) = {{/exts.x-php-return-type-is-void}}$this->{{operationId}}WithHttpInfo({{^exts.x-group-parameters}}{{#allParams}}${{paramName}}, {{/allParams}}{{#servers}}{{#-first}}$hostIndex, $variables, {{/-first}}{{/servers}}$contentType{{/exts.x-group-parameters}}{{#exts.x-group-parameters}}$associative_array{{/exts.x-group-parameters}});{{^exts.x-php-return-type-is-void}}
return $response;{{/exts.x-php-return-type-is-void}}
}
/**
@@ -206,10 +206,10 @@ use {{invokerPackage}}\ObjectSerializer;
* {{.}}
*
{{/description}}
{{#vendorExtensions.x-group-parameters}}
{{#exts.x-group-parameters}}
* Note: the input parameter is an associative array with the keys listed as the parameter name below
*
{{/vendorExtensions.x-group-parameters}}
{{/exts.x-group-parameters}}
{{#servers}}
{{#-first}}
* This operation contains host(s) defined in the OpenAPI spec. Use 'hostIndex' to select the host.
@@ -231,7 +231,7 @@ use {{invokerPackage}}\ObjectSerializer;
{{/-last}}
{{/servers}}
{{#allParams}}
* @param {{{dataType}}}{{^required}}|null{{/required}} ${{paramName}}{{#description}} {{.}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}}
* @param {{{dataType}}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{.}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}}
{{/allParams}}
{{#servers}}
{{#-first}}
@@ -249,9 +249,9 @@ use {{invokerPackage}}\ObjectSerializer;
{{/isDeprecated}}
*/
public function {{operationId}}WithHttpInfo(
{{^vendorExtensions.x-group-parameters}}
{{^exts.x-group-parameters}}
{{#allParams}}
{{vendorExtensions.x-php-param-type}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}},
{{exts.x-php-param-type}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}},
{{/allParams}}
{{#servers}}
{{#-first}}
@@ -260,13 +260,13 @@ use {{invokerPackage}}\ObjectSerializer;
{{/-first}}
{{/servers}}
string $contentType = self::contentTypes['{{{operationId}}}'][0]
{{/vendorExtensions.x-group-parameters}}
{{#vendorExtensions.x-group-parameters}}
{{/exts.x-group-parameters}}
{{#exts.x-group-parameters}}
array $associative_array
{{/vendorExtensions.x-group-parameters}}
{{/exts.x-group-parameters}}
): array
{
$request = $this->{{operationId}}Request({{^vendorExtensions.x-group-parameters}}{{#allParams}}${{paramName}}, {{/allParams}}{{#servers}}{{#-first}}$hostIndex, $variables, {{/-first}}{{/servers}}$contentType{{/vendorExtensions.x-group-parameters}}{{#vendorExtensions.x-group-parameters}}$associative_array{{/vendorExtensions.x-group-parameters}});
$request = $this->{{operationId}}Request({{^exts.x-group-parameters}}{{#allParams}}${{paramName}}, {{/allParams}}{{#servers}}{{#-first}}$hostIndex, $variables, {{/-first}}{{/servers}}$contentType{{/exts.x-group-parameters}}{{#exts.x-group-parameters}}$associative_array{{/exts.x-group-parameters}});
try {
$options = $this->createHttpClientOption();
@@ -379,10 +379,10 @@ use {{invokerPackage}}\ObjectSerializer;
* {{.}}
*
{{/description}}
{{#vendorExtensions.x-group-parameters}}
{{#exts.x-group-parameters}}
* Note: the input parameter is an associative array with the keys listed as the parameter name below
*
{{/vendorExtensions.x-group-parameters}}
{{/exts.x-group-parameters}}
{{#servers}}
{{#-first}}
* This operation contains host(s) defined in the OpenAPI spec. Use 'hostIndex' to select the host.
@@ -404,7 +404,7 @@ use {{invokerPackage}}\ObjectSerializer;
{{/-last}}
{{/servers}}
{{#allParams}}
* @param {{{dataType}}}{{^required}}|null{{/required}} ${{paramName}}{{#description}} {{.}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}}
* @param {{{dataType}}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{.}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}}
{{/allParams}}
{{#servers}}
{{#-first}}
@@ -421,9 +421,9 @@ use {{invokerPackage}}\ObjectSerializer;
{{/isDeprecated}}
*/
public function {{operationId}}Async(
{{^vendorExtensions.x-group-parameters}}
{{^exts.x-group-parameters}}
{{#allParams}}
{{vendorExtensions.x-php-param-type}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}},
{{exts.x-php-param-type}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}},
{{/allParams}}
{{#servers}}
{{#-first}}
@@ -432,13 +432,13 @@ use {{invokerPackage}}\ObjectSerializer;
{{/-first}}
{{/servers}}
string $contentType = self::contentTypes['{{{operationId}}}'][0]
{{/vendorExtensions.x-group-parameters}}
{{#vendorExtensions.x-group-parameters}}
{{/exts.x-group-parameters}}
{{#exts.x-group-parameters}}
array $associative_array
{{/vendorExtensions.x-group-parameters}}
{{/exts.x-group-parameters}}
): PromiseInterface
{
return $this->{{operationId}}AsyncWithHttpInfo({{^vendorExtensions.x-group-parameters}}{{#allParams}}${{paramName}}, {{/allParams}}{{#servers}}{{#-first}}$hostIndex, $variables, {{/-first}}{{/servers}}$contentType{{/vendorExtensions.x-group-parameters}}{{#vendorExtensions.x-group-parameters}}$associative_array{{/vendorExtensions.x-group-parameters}})
return $this->{{operationId}}AsyncWithHttpInfo({{^exts.x-group-parameters}}{{#allParams}}${{paramName}}, {{/allParams}}{{#servers}}{{#-first}}$hostIndex, $variables, {{/-first}}{{/servers}}$contentType{{/exts.x-group-parameters}}{{#exts.x-group-parameters}}$associative_array{{/exts.x-group-parameters}})
->then(
function ($response) {
return $response[0];
@@ -457,10 +457,10 @@ use {{invokerPackage}}\ObjectSerializer;
* {{.}}
*
{{/description}}
{{#vendorExtensions.x-group-parameters}}
{{#exts.x-group-parameters}}
* Note: the input parameter is an associative array with the keys listed as the parameter name below
*
{{/vendorExtensions.x-group-parameters}}
{{/exts.x-group-parameters}}
{{#servers}}
{{#-first}}
* This operation contains host(s) defined in the OpenAPI spec. Use 'hostIndex' to select the host.
@@ -482,7 +482,7 @@ use {{invokerPackage}}\ObjectSerializer;
{{/-last}}
{{/servers}}
{{#allParams}}
* @param {{{dataType}}}{{^required}}|null{{/required}} ${{paramName}}{{#description}} {{.}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}}
* @param {{{dataType}}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{.}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}}
{{/allParams}}
{{#servers}}
{{#-first}}
@@ -499,9 +499,9 @@ use {{invokerPackage}}\ObjectSerializer;
{{/isDeprecated}}
*/
public function {{operationId}}AsyncWithHttpInfo(
{{^vendorExtensions.x-group-parameters}}
{{^exts.x-group-parameters}}
{{#allParams}}
{{vendorExtensions.x-php-param-type}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}},
{{exts.x-php-param-type}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}},
{{/allParams}}
{{#servers}}
{{#-first}}
@@ -510,14 +510,14 @@ use {{invokerPackage}}\ObjectSerializer;
{{/-first}}
{{/servers}}
string $contentType = self::contentTypes['{{{operationId}}}'][0]
{{/vendorExtensions.x-group-parameters}}
{{#vendorExtensions.x-group-parameters}}
{{/exts.x-group-parameters}}
{{#exts.x-group-parameters}}
array $associative_array
{{/vendorExtensions.x-group-parameters}}
{{/exts.x-group-parameters}}
): PromiseInterface
{
$returnType = '{{{returnType}}}';
$request = $this->{{operationId}}Request({{^vendorExtensions.x-group-parameters}}{{#allParams}}${{paramName}}, {{/allParams}}{{#servers}}{{#-first}}$hostIndex, $variables, {{/-first}}{{/servers}}$contentType{{/vendorExtensions.x-group-parameters}}{{#vendorExtensions.x-group-parameters}}$associative_array{{/vendorExtensions.x-group-parameters}});
$request = $this->{{operationId}}Request({{^exts.x-group-parameters}}{{#allParams}}${{paramName}}, {{/allParams}}{{#servers}}{{#-first}}$hostIndex, $variables, {{/-first}}{{/servers}}$contentType{{/exts.x-group-parameters}}{{#exts.x-group-parameters}}$associative_array{{/exts.x-group-parameters}});
return $this->client
->sendAsync($request, $this->createHttpClientOption())
@@ -563,10 +563,10 @@ use {{invokerPackage}}\ObjectSerializer;
/**
* Create request for operation '{{{operationId}}}'
*
{{#vendorExtensions.x-group-parameters}}
{{#exts.x-group-parameters}}
* Note: the input parameter is an associative array with the keys listed as the parameter name below
*
{{/vendorExtensions.x-group-parameters}}
{{/exts.x-group-parameters}}
{{#servers}}
{{#-first}}
* This operation contains host(s) defined in the OpenAPI spec. Use 'hostIndex' to select the host.
@@ -588,7 +588,7 @@ use {{invokerPackage}}\ObjectSerializer;
{{/-last}}
{{/servers}}
{{#allParams}}
* @param {{{dataType}}}{{^required}}|null{{/required}} ${{paramName}}{{#description}} {{.}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}}
* @param {{{dataType}}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{paramName}}{{#description}} {{.}}{{/description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#isDeprecated}} (deprecated){{/isDeprecated}}
{{/allParams}}
{{#servers}}
{{#-first}}
@@ -605,9 +605,9 @@ use {{invokerPackage}}\ObjectSerializer;
{{/isDeprecated}}
*/
public function {{operationId}}Request(
{{^vendorExtensions.x-group-parameters}}
{{^exts.x-group-parameters}}
{{#allParams}}
{{vendorExtensions.x-php-param-type}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}},
{{exts.x-php-param-type}} ${{paramName}}{{^required}} = {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}{{/required}},
{{/allParams}}
{{#servers}}
{{#-first}}
@@ -616,13 +616,13 @@ use {{invokerPackage}}\ObjectSerializer;
{{/-first}}
{{/servers}}
string $contentType = self::contentTypes['{{{operationId}}}'][0]
{{/vendorExtensions.x-group-parameters}}
{{#vendorExtensions.x-group-parameters}}
{{/exts.x-group-parameters}}
{{#exts.x-group-parameters}}
array $associative_array
{{/vendorExtensions.x-group-parameters}}
{{/exts.x-group-parameters}}
): Request
{
{{#vendorExtensions.x-group-parameters}}
{{#exts.x-group-parameters}}
// unbox the parameters from the associative array
{{#allParams}}
${{paramName}} = array_key_exists('{{paramName}}', $associative_array) ? $associative_array['{{paramName}}'] : {{{defaultValue}}}{{^defaultValue}}null{{/defaultValue}};
@@ -631,7 +631,7 @@ use {{invokerPackage}}\ObjectSerializer;
$variables = array_key_exists('variables', $associative_array) ? $associative_array['variables'] : [];
{{/servers.0}}
$contentType = $associative_array['contentType'] ?? self::contentTypes['{{{operationId}}}'][0];
{{/vendorExtensions.x-group-parameters}}{{#allParams}}
{{/exts.x-group-parameters}}{{#allParams}}
{{#required}}
// verify the required parameter '{{paramName}}' is set
if (${{paramName}} === null || (is_array(${{paramName}}) && count(${{paramName}}) === 0)) {

View File

@@ -47,14 +47,14 @@ $apiInstance = new {{invokerPackage}}\Api\{{classname}}(
new GuzzleHttp\Client(){{#hasAuthMethods}},
$config{{/hasAuthMethods}}
);
{{^vendorExtensions.x-group-parameters}}
{{^exts.x-group-parameters}}
{{#allParams}}${{paramName}} = {{{example}}}; // {{{dataType}}}{{#description}} | {{{.}}}{{/description}}
{{/allParams}}{{#servers}}{{#-first}}
$hostIndex = 0;
$variables = [{{#variables}}
'{{{name}}}' => '{{{default}}}{{^default}}YOUR_VALUE{{/default}}',{{/variables}}
];
{{/-first}}{{/servers}}{{/vendorExtensions.x-group-parameters}}{{#vendorExtensions.x-group-parameters}}
{{/-first}}{{/servers}}{{/exts.x-group-parameters}}{{#exts.x-group-parameters}}
$associative_array = [
{{#allParams}} '{{paramName}}' => {{{example}}}, // {{{dataType}}}{{#description}} | {{{.}}}{{/description}}
{{/allParams}}
@@ -64,10 +64,10 @@ $associative_array = [
'{{{name}}}' => '{{{default}}}{{^default}}YOUR_VALUE{{/default}}',{{/variables}}
],
{{/-first}}{{/servers}}];
{{/vendorExtensions.x-group-parameters}}
{{/exts.x-group-parameters}}
try {
{{#returnType}}$result = {{/returnType}}$apiInstance->{{{operationId}}}({{^vendorExtensions.x-group-parameters}}{{#allParams}}${{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#servers}}{{#-first}}{{#allParams}}{{#-first}}, {{/-first}}{{/allParams}}$hostIndex, $variables{{/-first}}{{/servers}}{{/vendorExtensions.x-group-parameters}}{{#vendorExtensions.x-group-parameters}}$associate_array{{/vendorExtensions.x-group-parameters}});{{#returnType}}
{{#returnType}}$result = {{/returnType}}$apiInstance->{{{operationId}}}({{^exts.x-group-parameters}}{{#allParams}}${{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#servers}}{{#-first}}{{#allParams}}{{#-first}}, {{/-first}}{{/allParams}}$hostIndex, $variables{{/-first}}{{/servers}}{{/exts.x-group-parameters}}{{#exts.x-group-parameters}}$associate_array{{/exts.x-group-parameters}});{{#returnType}}
print_r($result);{{/returnType}}
} catch (Exception $e) {
echo 'Exception when calling {{classname}}->{{operationId}}: ', $e->getMessage(), PHP_EOL;
@@ -76,10 +76,10 @@ try {
### Parameters
{{#vendorExtensions.x-group-parameters}}
{{#exts.x-group-parameters}}
Note: the input parameter is an associative array with the keys listed as the parameter names below.
{{/vendorExtensions.x-group-parameters}}
{{/exts.x-group-parameters}}
{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}}| Name | Type | Description | Notes |
| ------------- | ------------- | ------------- | ------------- |{{/-last}}{{/allParams}}
{{#allParams}}| **{{paramName}}** | {{#isFile}}**{{{dataType}}}**{{/isFile}}{{#isPrimitiveType}}**{{{dataType}}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}[**{{{dataType}}}**](../Model/{{baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} |{{^required}} [optional]{{/required}}{{#defaultValue}} [default to {{.}}]{{/defaultValue}} |

View File

@@ -68,7 +68,7 @@ use PHPUnit\Framework\TestCase;
* {{{summary}}}.
*
*/
public function test{{vendorExtensions.x-test-operation-id}}()
public function test{{exts.x-test-operation-id}}()
{
// TODO: implement
self::markTestIncomplete('Not implemented');

View File

@@ -1,4 +1,4 @@
enum {{classname}}: {{vendorExtensions.x-php-enum-type}}
enum {{classname}}: {{exts.x-php-enum-type}}
{
{{#allowableValues}}
{{#enumVars}}

View File

@@ -376,12 +376,12 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}}{{/parentSchema}}{{^par
/**
* Gets {{name}}
*
* @return {{{dataType}}}{{^required}}|null{{/required}}
* @return {{{dataType}}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}}
{{#deprecated}}
* @deprecated
{{/deprecated}}
*/
public function {{getter}}(): {{vendorExtensions.x-php-prop-type}}
public function {{getter}}(): {{exts.x-php-prop-type}}
{
return $this->container['{{name}}'];
}
@@ -389,14 +389,14 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}}{{/parentSchema}}{{^par
/**
* Sets {{name}}
*
* @param {{{dataType}}}{{^required}}|null{{/required}} ${{name}}{{#description}} {{{.}}}{{/description}}{{^description}} {{{name}}}{{/description}}
* @param {{{dataType}}}{{#notRequiredOrIsNullable}}|null{{/notRequiredOrIsNullable}} ${{name}}{{#description}} {{{.}}}{{/description}}{{^description}} {{{name}}}{{/description}}
*
* @return $this
{{#deprecated}}
* @deprecated
{{/deprecated}}
*/
public function {{setter}}({{vendorExtensions.x-php-prop-type}} ${{name}}): static
public function {{setter}}({{exts.x-php-prop-type}} ${{name}}): static
{
{{#isNullable}}
if (is_null(${{name}})) {

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