From 0aab5b9bd20b18e10204745c4eea1ecfcc67f774 Mon Sep 17 00:00:00 2001 From: Jonas Lagoni Date: Thu, 1 Feb 2024 12:30:22 +0900 Subject: [PATCH] feat!: release v3 (#1599) * feat!: convert to use multi parser (#1587) * feat!: adds support for duration in java (#1604) * feat: enable AsyncAPI v3 (#1600) * fix!: add csharp support for DateTime, TimeSpan, Guid (#1612) * feat!: adds extend in common, meta, and constrained models (#1613) * feat!: adds inheritance with interfaces for java (#1593) * chore: remove duplicate version entry for AsyncAPI processor (#1609) * feat!: render python union in pydantic in the pre 3.10 way (#1626) * feat: add file path as input (#1601) * feat!: add options as parameter to constraints (#1667) * fix: (un)marshalling tuple and dictionary unwrapping for Typescript (#1717) * fix: jsonbinpack preset and runtime tests (#1718) * chore: fix linting * chore: remove unused directory * feat!: add useJavascriptReservedKeywords option for TS (#1727) * feat: enable raw properties for interface (#1729) * fix: newtonsoft could not handle enum values (#1731) * chore: update Java runtime tests (#1739) * fix: edge case where references was incorrect handled for JSON Schema (#1754) * fix: recursion bug for get nearest dependencies (#1757) --------- Co-authored-by: Kenneth Aasan Co-authored-by: jano-petras Co-authored-by: Nilkanth Parmar --- .eslintrc | 6 +- docs/languages/TypeScript.md | 9 +- docs/migrations/version-2-to-3.md | 341 ++++++++++++++ examples/README.md | 3 + examples/file-uri-input/README.md | 17 + .../__snapshots__/index.spec.ts.snap | 59 +++ examples/file-uri-input/index.spec.ts | 26 ++ examples/file-uri-input/index.ts | 26 ++ examples/file-uri-input/package-lock.json | 10 + examples/file-uri-input/package.json | 10 + examples/file-uri-input/testasyncapi.yml | 23 + .../__snapshots__/index.spec.ts.snap | 21 + examples/generate-csharp-models/index.ts | 12 + .../generate-csharp-models/package-lock.json | 2 +- .../__snapshots__/index.spec.ts.snap | 10 +- .../overwrite-default-constraint/index.ts | 21 +- .../__snapshots__/index.spec.ts.snap | 11 +- .../__snapshots__/index.spec.ts.snap | 10 +- .../README.md | 17 + .../__snapshots__/index.spec.ts.snap | 9 + .../index.spec.ts | 15 + .../index.ts | 27 ++ .../package-lock.json | 10 + .../package.json | 10 + .../__snapshots__/index.spec.ts.snap | 20 + examples/typescript-use-esm/index.spec.ts | 1 + .../README.md | 17 + .../__snapshots__/index.spec.ts.snap | 18 + .../index.spec.ts | 15 + .../index.ts | 25 ++ .../package-lock.json | 10 + .../package.json | 10 + jest.config.js | 6 +- package-lock.json | 424 ++++++++++++++---- package.json | 11 +- src/generators/AbstractFileGenerator.ts | 2 +- .../cplusplus/CplusplusFileGenerator.ts | 2 +- .../cplusplus/CplusplusGenerator.ts | 18 +- .../constrainer/ConstantConstrainer.ts | 3 +- .../cplusplus/constrainer/EnumConstrainer.ts | 14 +- .../constrainer/ModelNameConstrainer.ts | 5 +- .../constrainer/PropertyKeyConstrainer.ts | 5 +- src/generators/csharp/CSharpConstrainer.ts | 18 +- src/generators/csharp/CSharpFileGenerator.ts | 2 +- src/generators/csharp/CSharpGenerator.ts | 14 +- .../csharp/constrainer/ConstantConstrainer.ts | 4 +- .../csharp/constrainer/EnumConstrainer.ts | 14 +- .../constrainer/ModelNameConstrainer.ts | 5 +- .../constrainer/PropertyKeyConstrainer.ts | 5 +- .../presets/NewtonsoftSerializerPreset.ts | 5 +- src/generators/dart/DartConstrainer.ts | 3 +- src/generators/dart/DartFileGenerator.ts | 2 +- src/generators/dart/DartGenerator.ts | 12 +- .../dart/constrainer/ConstantConstrainer.ts | 4 +- .../dart/constrainer/EnumConstrainer.ts | 14 +- .../dart/constrainer/ModelNameConstrainer.ts | 5 +- .../constrainer/PropertyKeyConstrainer.ts | 5 +- src/generators/go/GoConstrainer.ts | 3 +- src/generators/go/GoFileGenerator.ts | 2 +- src/generators/go/GoGenerator.ts | 12 +- .../go/constrainer/ConstantConstrainer.ts | 4 +- .../go/constrainer/EnumConstrainer.ts | 11 +- .../go/constrainer/ModelNameConstrainer.ts | 5 +- .../go/constrainer/PropertyKeyConstrainer.ts | 5 +- src/generators/java/JavaConstrainer.ts | 5 +- src/generators/java/JavaFileGenerator.ts | 2 +- src/generators/java/JavaGenerator.ts | 14 +- .../java/constrainer/ConstantConstrainer.ts | 4 +- .../java/constrainer/EnumConstrainer.ts | 14 +- .../java/constrainer/ModelNameConstrainer.ts | 5 +- .../constrainer/PropertyKeyConstrainer.ts | 5 +- src/generators/java/presets/CommonPreset.ts | 4 + .../java/presets/ConstraintsPreset.ts | 6 +- .../java/presets/DescriptionPreset.ts | 10 +- src/generators/java/presets/JacksonPreset.ts | 6 +- .../java/renderers/ClassRenderer.ts | 100 ++++- .../javascript/JavaScriptConstrainer.ts | 3 +- .../javascript/JavaScriptFileGenerator.ts | 2 +- .../javascript/JavaScriptGenerator.ts | 18 +- .../constrainer/ConstantConstrainer.ts | 4 +- .../javascript/constrainer/EnumConstrainer.ts | 9 +- .../constrainer/ModelNameConstrainer.ts | 5 +- .../constrainer/PropertyKeyConstrainer.ts | 5 +- src/generators/kotlin/KotlinConstrainer.ts | 3 +- src/generators/kotlin/KotlinFileGenerator.ts | 2 +- src/generators/kotlin/KotlinGenerator.ts | 14 +- .../kotlin/constrainer/ConstantConstrainer.ts | 4 +- .../kotlin/constrainer/EnumConstrainer.ts | 12 +- .../constrainer/ModelNameConstrainer.ts | 5 +- .../constrainer/PropertyKeyConstrainer.ts | 5 +- src/generators/php/PhpFileGenerator.ts | 2 +- src/generators/php/PhpGenerator.ts | 14 +- .../php/constrainer/ConstantConstrainer.ts | 4 +- .../php/constrainer/EnumConstrainer.ts | 11 +- .../php/constrainer/ModelNameConstrainer.ts | 5 +- .../php/constrainer/PropertyKeyConstrainer.ts | 5 +- src/generators/python/PythonConstrainer.ts | 4 +- src/generators/python/PythonFileGenerator.ts | 2 +- src/generators/python/PythonGenerator.ts | 14 +- .../python/constrainer/ConstantConstrainer.ts | 4 +- .../python/constrainer/EnumConstrainer.ts | 14 +- .../constrainer/ModelNameConstrainer.ts | 5 +- .../constrainer/PropertyKeyConstrainer.ts | 5 +- src/generators/python/presets/Pydantic.ts | 26 +- src/generators/rust/RustConstrainer.ts | 4 +- src/generators/rust/RustFileGenerator.ts | 4 +- src/generators/rust/RustGenerator.ts | 14 +- .../rust/constrainer/ConstantConstrainer.ts | 4 +- .../rust/constrainer/EnumConstrainer.ts | 14 +- .../rust/constrainer/ModelNameConstrainer.ts | 5 +- .../constrainer/PropertyKeyConstrainer.ts | 5 +- .../rust/renderers/PackageRenderer.ts | 4 +- src/generators/scala/ScalaConstrainer.ts | 4 +- src/generators/scala/ScalaGenerator.ts | 14 +- .../scala/constrainer/ConstantConstrainer.ts | 4 +- .../scala/constrainer/EnumConstrainer.ts | 16 +- .../scala/constrainer/ModelNameConstrainer.ts | 5 +- .../constrainer/PropertyKeyConstrainer.ts | 5 +- .../template/TemplateConstrainer.ts | 2 +- .../template/TemplateFileGenerator.ts | 2 +- src/generators/template/TemplateGenerator.ts | 15 +- .../constrainer/ConstantConstrainer.ts | 3 +- .../template/constrainer/EnumConstrainer.ts | 8 +- .../constrainer/ModelNameConstrainer.ts | 3 +- .../constrainer/PropertyKeyConstrainer.ts | 3 +- src/generators/typescript/Constants.ts | 18 +- .../typescript/TypeScriptConstrainer.ts | 3 +- .../typescript/TypeScriptFileGenerator.ts | 2 +- .../typescript/TypeScriptGenerator.ts | 27 +- .../typescript/TypeScriptObjectRenderer.ts | 31 +- .../constrainer/ConstantConstrainer.ts | 4 +- .../typescript/constrainer/EnumConstrainer.ts | 30 +- .../constrainer/ModelNameConstrainer.ts | 27 +- .../constrainer/PropertyKeyConstrainer.ts | 28 +- .../typescript/presets/CommonPreset.ts | 310 +------------ .../typescript/presets/JsonBinPackPreset.ts | 9 +- .../presets/utils/MarshalFunction.ts | 262 +++++++++++ .../presets/utils/UnmarshalFunction.ts | 106 +++++ src/helpers/CommonModelToMetaModel.ts | 10 + src/helpers/ConstrainHelpers.ts | 144 ++++-- src/helpers/FilterHelpers.ts | 42 ++ src/helpers/Splitter.ts | 37 +- src/helpers/index.ts | 1 + src/interpreter/InterpretAllOf.ts | 52 ++- src/interpreter/Interpreter.ts | 16 +- src/models/CommonModel.ts | 16 +- src/models/ConstrainedMetaModel.ts | 84 +++- src/models/MetaModel.ts | 2 + src/processors/AsyncAPIInputProcessor.ts | 130 +++--- src/processors/JsonSchemaInputProcessor.ts | 26 +- src/utils/Partials.ts | 13 +- test/blackbox/docs/AsyncAPI-2_6/dummy.json | 15 + .../PropertyKeyConstrainer.spec.ts | 2 - .../csharp/CSharpConstrainer.spec.ts | 39 ++ .../constrainer/EnumConstrainer.spec.ts | 44 +- .../constrainer/ModelNameConstrainer.spec.ts | 26 +- .../PropertyKeyConstrainer.spec.ts | 13 +- .../csharp/presets/CommonPreset.spec.ts | 3 +- .../NewtonsoftSerializerPreset.spec.ts.snap | 5 +- test/generators/dart/DartConstrainer.spec.ts | 3 +- test/generators/dart/DartRenderer.spec.ts | 5 +- .../dart/constrainer/EnumConstrainer.spec.ts | 46 +- .../constrainer/ModelNameConstrainer.spec.ts | 32 +- .../PropertyKeyConstrainer.spec.ts | 13 +- .../go/constrainer/EnumConstrainer.spec.ts | 49 +- .../constrainer/ModelNameConstrainer.spec.ts | 32 +- .../PropertyKeyConstrainer.spec.ts | 13 +- test/generators/java/JavaConstrainer.spec.ts | 13 + test/generators/java/JavaGenerator.spec.ts | 141 ++++++ test/generators/java/JavaRenderer.spec.ts | 6 +- .../__snapshots__/JavaGenerator.spec.ts.snap | 355 +++++++++++++++ .../constrainer/ConstantConstrainer.spec.ts | 15 +- .../java/constrainer/EnumConstrainer.spec.ts | 46 +- .../constrainer/ModelNameConstrainer.spec.ts | 32 +- .../PropertyKeyConstrainer.spec.ts | 13 +- .../java/presets/CommonPreset.spec.ts | 37 ++ .../java/presets/ConstraintsPreset.spec.ts | 28 ++ .../java/presets/DescriptionPreset.spec.ts | 63 +++ .../java/presets/JacksonPreset.spec.ts | 27 ++ .../__snapshots__/CommonPreset.spec.ts.snap | 82 ++++ .../ConstraintsPreset.spec.ts.snap | 22 + .../DescriptionPreset.spec.ts.snap | 59 +++ .../__snapshots__/JacksonPreset.spec.ts.snap | 24 + .../constrainer/EnumConstrainer.spec.ts | 36 +- .../constrainer/ModelNameConstrainer.spec.ts | 26 +- .../PropertyKeyConstrainer.spec.ts | 13 +- .../preset/utils/ExampleFunctions.spec.ts | 1 - test/generators/kotlin/KotlinRenderer.spec.ts | 1 - .../python/presets/Pydantic.spec.ts | 34 ++ .../__snapshots__/Pydantic.spec.ts.snap | 17 + test/generators/rust/RustConstrainer.spec.ts | 4 +- test/generators/rust/RustRenderer.spec.ts | 1 - .../rust/constrainer/EnumConstrainer.spec.ts | 42 +- .../constrainer/ModelNameConstrainer.spec.ts | 27 +- .../PropertyKeyConstrainer.spec.ts | 9 +- .../constrainer/ConstantConstrainer.spec.ts | 15 +- .../constrainer/EnumConstrainer.spec.ts | 46 +- .../constrainer/ModelNameConstrainer.spec.ts | 36 +- .../PropertyKeyConstrainer.spec.ts | 15 +- .../preset/MarshallingPreset.spec.ts | 19 +- .../JsonBinPackPreset.spec.ts.snap | 78 ++-- .../MarshallingPreset.spec.ts.snap | 107 +++-- .../preset/utils/ExampleFunctions.spec.ts | 1 - test/helpers/CommonModelToMetaModel.spec.ts | 15 + test/helpers/ConstrainHelpers.spec.ts | 29 ++ test/helpers/Splitter.spec.ts | 75 ++++ test/helpers/TypeHelpers.spec.ts | 2 - test/interpreter/unit/InterpretAllOf.spec.ts | 1 - test/interpreter/unit/Interpreter.spec.ts | 9 + test/models/CommonModel.spec.ts | 6 +- test/models/ConstrainedMetaModel.spec.ts | 40 +- test/models/OutputModel.spec.ts | 1 - .../processors/AsyncAPIInputProcessor.spec.ts | 44 +- .../AsyncAPIInputProcessor/basic_v3.json | 34 ++ .../AsyncAPIInputProcessor/testasyncapi.yml | 23 + test/processors/InputProcessor.spec.ts | 3 +- .../JsonSchemaInputProcessor.spec.ts | 36 +- .../AsyncAPIInputProcessor.spec.ts.snap | 347 +++++++++++++- test/runtime/generic-input.json | 4 +- test/runtime/runtime-csharp.ts | 52 ++- .../runtime-csharp/runtime-csharp.csproj | 2 + .../test/models/json_serializer/Address.cs | 13 +- .../test/models/newtonsoft/Address.cs | 35 ++ test/runtime/runtime-java.ts | 35 +- test/runtime/runtime-java/.gitignore | 2 +- .../app/{generic => jackson}/AddressTest.java | 21 +- test/runtime/runtime-typescript.ts | 25 +- .../test/JsonBinPackPreset.spec.ts | 9 +- test/utils/Partials.spec.ts | 18 + 229 files changed, 4877 insertions(+), 1249 deletions(-) create mode 100644 docs/migrations/version-2-to-3.md create mode 100644 examples/file-uri-input/README.md create mode 100644 examples/file-uri-input/__snapshots__/index.spec.ts.snap create mode 100644 examples/file-uri-input/index.spec.ts create mode 100644 examples/file-uri-input/index.ts create mode 100644 examples/file-uri-input/package-lock.json create mode 100644 examples/file-uri-input/package.json create mode 100644 examples/file-uri-input/testasyncapi.yml create mode 100644 examples/typescript-generate-raw-properties/README.md create mode 100644 examples/typescript-generate-raw-properties/__snapshots__/index.spec.ts.snap create mode 100644 examples/typescript-generate-raw-properties/index.spec.ts create mode 100644 examples/typescript-generate-raw-properties/index.ts create mode 100644 examples/typescript-generate-raw-properties/package-lock.json create mode 100644 examples/typescript-generate-raw-properties/package.json create mode 100644 examples/typescript-use-js-reserved-keyword/README.md create mode 100644 examples/typescript-use-js-reserved-keyword/__snapshots__/index.spec.ts.snap create mode 100644 examples/typescript-use-js-reserved-keyword/index.spec.ts create mode 100644 examples/typescript-use-js-reserved-keyword/index.ts create mode 100644 examples/typescript-use-js-reserved-keyword/package-lock.json create mode 100644 examples/typescript-use-js-reserved-keyword/package.json create mode 100644 src/generators/typescript/presets/utils/MarshalFunction.ts create mode 100644 src/generators/typescript/presets/utils/UnmarshalFunction.ts create mode 100644 src/helpers/FilterHelpers.ts create mode 100644 test/processors/AsyncAPIInputProcessor/basic_v3.json create mode 100644 test/processors/AsyncAPIInputProcessor/testasyncapi.yml create mode 100644 test/runtime/runtime-csharp/runtime-csharp/test/models/newtonsoft/Address.cs rename test/runtime/runtime-java/src/test/java/com/mycompany/app/{generic => jackson}/AddressTest.java (62%) diff --git a/.eslintrc b/.eslintrc index 52996b74bd..9ac4ab7223 100644 --- a/.eslintrc +++ b/.eslintrc @@ -6,7 +6,8 @@ "security", "github", "jest", - "prettier" + "prettier", + "unused-imports" ], "extends": [ "eslint:recommended", @@ -55,7 +56,7 @@ "no-empty-character-class": 2, "no-self-compare": 2, "valid-typeof": 2, - "no-unused-vars": 0, + "unused-imports/no-unused-imports": 2, "handle-callback-err": 2, "no-shadow-restricted-names": 2, "no-new-require": 2, @@ -138,7 +139,6 @@ "prefer-const": 2, "prefer-spread": 2, "prefer-template": 2, - "@typescript-eslint/no-unused-vars": 2, "prettier/prettier": 2, "sonarjs/no-identical-functions": "off", "sonarjs/prefer-single-boolean-return": "off", diff --git a/docs/languages/TypeScript.md b/docs/languages/TypeScript.md index 1f9ff191f9..97e7c66953 100644 --- a/docs/languages/TypeScript.md +++ b/docs/languages/TypeScript.md @@ -17,6 +17,7 @@ There are special use-cases that each language supports; this document pertains - [Generate example data function](#generate-example-data-function) - [Rendering complete models to a specific module system](#rendering-complete-models-to-a-specific-module-system) - [Rendering comments from description and example fields](#rendering-comments-from-description-and-example-fields) +- [Rendering raw properties for interface](#rendering-raw-properties-for-interface) @@ -101,4 +102,10 @@ Check out this [example for a live demonstration how to generate the complete Ty ## Rendering comments from description and example fields You can use the `TS_DESCRIPTION_PRESET` to generate JSDoc style comments from description and example fields in your model. -See [this example](../../examples/typescript-generate-comments) for how this can be used. \ No newline at end of file +See [this example](../../examples/typescript-generate-comments) for how this can be used. + +## Rendering raw properties for interface + +You can use the `rawPropertyNames: true` and `modelType: 'interface'` together to generate models that use raw properties. + +See [this example](../../examples/typescript-generate-raw-properties) for how this can be used. diff --git a/docs/migrations/version-2-to-3.md b/docs/migrations/version-2-to-3.md new file mode 100644 index 0000000000..2b394ab93e --- /dev/null +++ b/docs/migrations/version-2-to-3.md @@ -0,0 +1,341 @@ +# Migration from v2 to v3 + +This document contains all the breaking changes and migrations guidelines for adapting your code to the new version. + +## allowInheritance set to true will enable inheritance + +This feature introduces a new option called `allowInheritance` in the interpreter options, which controls whether the generated models should inherit when the schema includes an `allOf`. By default, this option is set to false, which means that you'll not be affected if this property is not set. In the `MetaModel` and the `ConstrainedMetaModel` options, there is now an `extend` property (a list of models) and an `isExtended` property (boolean). + +Here is an example of how to use the new feature and the `allowInheritance` option in your code: + +```ts +const generator = new JavaFileGenerator({ + processorOptions: { + interpreter: { + allowInheritance: true + } + } +}); +``` + +## TypeScript + +### JS reserved keywords are no longer applied by default +By default up until now, JS reserved keywords have been checked for TS as well. Which means that something like: + +``` +{ + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + additionalProperties: false, + properties: { + location: { + type: 'string' + } + } +} +``` + +Would be default be rendered as: +```ts +class Root { + private _reservedLocation?: string; + + constructor(input: { + reservedLocation?: string, + }) { + this._reservedLocation = input.reservedLocation; + } + + get reservedLocation(): string | undefined { return this._reservedLocation; } + set reservedLocation(reservedLocation: string | undefined) { this._reservedLocation = reservedLocation; } +} +``` + +However, without setting `useJavascriptReservedKeywords: true` by default the following will be generated: + +```ts +class Root { + private _location?: string; + + constructor(input: { + location?: string, + }) { + this._location = input.location; + } + + get location(): string | undefined { return this._location; } + set location(location: string | undefined) { this._location = location; } +} +``` + +## JavaScript + +Is not affected by this change. + +## C# + +### System.TimeSpan is used when format is time + +This example used to generate a `string`, but is now instead using `System.TimeSpan`. + +```yaml +type: object +properties: + duration: + type: string + format: time +``` + +will generate + +```csharp +public class TestClass { + private System.TimeSpan duration; + ... +} +``` + +### System.DateTime is used when format is date-time + +This example used to generate a `string`, but is now instead using `System.DateTime`. + +```yaml +type: object +properties: + dob: + type: string + format: date-time +``` + +will generate + +```csharp +public class TestClass { + private System.DateTime dob; + ... +} +``` + +### System.Guid is used when format is uuid + +This example used to generate a `string`, but is now instead using `System.Guid`. + +```yaml +type: object +properties: + uniqueId: + type: string + format: uuid +``` + +will generate + +```csharp +public class TestClass { + private System.Guid uniqueId; + ... +} +``` + +## Java + +### java.time.Duration is used when format is duration + +This example used to generate a `String`, but is now instead using `java.time.Duration`. + +```yaml +type: object +properties: + duration: + type: string + format: duration +``` + +will generate + +```java +public class TestClass { + private java.time.Duration duration; + ... +} +``` + +### inheritance will generate interfaces + +Please read the section about [allowInheritance](#allowinheritance-set-to-true-will-enable-inheritance) first. When `allowInheritance` is enabled, interfaces will be generated for schemas that uses `allOf`: + +```yaml +components: + messages: + Vehicle: + payload: + oneOf: + - $ref: '#/components/schemas/Car' + - $ref: '#/components/schemas/Truck' + schemas: + Vehicle: + title: Vehicle + type: object + discriminator: vehicleType + properties: + vehicleType: + title: VehicleType + type: string + length: + type: number + format: float + required: + - vehicleType + Car: + allOf: + - '#/components/schemas/Vehicle' + - type: object + properties: + vehicleType: + const: Car + Truck: + allOf: + - '#/components/schemas/Vehicle' + - type: object + properties: + vehicleType: + const: Truck +``` + +will generate + +```java +public interface NewVehicle { + VehicleType getVehicleType(); +} + +public class Car implements NewVehicle, Vehicle { + private final VehicleType vehicleType = VehicleType.CAR; + private Float length; + private Map additionalProperties; + + public VehicleType getVehicleType() { return this.vehicleType; } + + @Override + public Float getLength() { return this.length; } + @Override + public void setLength(Float length) { this.length = length; } +} + +public enum VehicleType { + CAR((String)\\"Car\\"), TRUCK((String)\\"Truck\\"); + + private String value; + + VehicleType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static VehicleType fromValue(String value) { + for (VehicleType e : VehicleType.values()) { + if (e.value.equals(value)) { + return e; + } + } + throw new IllegalArgumentException(\\"Unexpected value '\\" + value + \\"'\\"); + } + + @Override + public String toString() { + return String.valueOf(value); + } +} + +public interface Vehicle { + public Float getLength(); + public void setLength(Float length); +} + +public class Truck implements NewVehicle, Vehicle { + private final VehicleType vehicleType = VehicleType.TRUCK; + private Float length; + private Map additionalProperties; + + public VehicleType getVehicleType() { return this.vehicleType; } + + @Override + public Float getLength() { return this.length; } + @Override + public void setLength(Float length) { this.length = length; } +} +``` + +## Kotlin + +Is not affected by this change. + +## Rust + +Is not affected by this change. + +## Python + +### Union type for the Pydantic preset supports Python pre 3.10 + +Modelina used to use the newer way of representing unions in Python by using the `|` operator. In the Pydantic preset, this is now adjusted to support Python pre 3.10 by using `Union[Model1, Model2]` instead: + +```yaml +title: UnionTest +type: object + properties: + unionTest: + oneOf: + - title: Union1 + type: object + properties: + testProp1: + type: string + - title: Union2 + type: object + properties: + testProp2: + type: string +``` + +will generate + +```python +class UnionTest(BaseModel): + unionTest: Optional[Union[Union1, Union2]] = Field() + additionalProperties: Optional[dict[Any, Any]] = Field() + +class Union1(BaseModel): + testProp1: Optional[str] = Field() + additionalProperties: Optional[dict[Any, Any]] = Field() + +class Union2(BaseModel): + testProp2: Optional[str] = Field() + additionalProperties: Optional[dict[Any, Any]] = Field() +``` + +## Go + +Is not affected by this change. + +## Dart + +Is not affected by this change. + +## C++ + +Is not affected by this change. + +## Options in constraints + +As part of https://github.com/asyncapi/modelina/issues/1475 we had the need to access options in the constraint logic, therefore all constraints now have direct access to the provided options. + +To make it easier we now expose types for each of the constraints in each language to make it easier to re-use in TS integrations. They can be accessed as following: + +```ts +import { ConstantConstraint, EnumKeyConstraint, EnumValueConstraint, ModelNameConstraint, PropertyKeyConstraint } from @asyncapi/modelina +``` diff --git a/examples/README.md b/examples/README.md index 41d24576c4..13af86f9a4 100644 --- a/examples/README.md +++ b/examples/README.md @@ -137,6 +137,9 @@ These are all specific examples only relevant to the TypeScript generator: - [typescript-use-esm](./typescript-use-esm) - A basic example that generate the models to use ESM module system. - [typescript-use-cjs](./typescript-use-cjs) - A basic example that generate the models to use CJS module system. - [typescript-generate-jsonbinpack](./typescript-generate-jsonbinpack) - A basic example showing how to generate models that include [jsonbinpack](https://github.com/sourcemeta/jsonbinpack) functionality. +- [typescript-generate-raw-properties](./typescript-generate-raw-properties) - A basic example showing how to generate models that use raw properties for interface. +- [typescript-use-js-reserved-keyword](./typescript-use-js-reserved-keyword) - A basic example showing how you can generate the models that take reserved JS keywords into account for model names, properties or enum keys. + ### Kotlin These are all specific examples only relevant to the Kotlin generator: diff --git a/examples/file-uri-input/README.md b/examples/file-uri-input/README.md new file mode 100644 index 0000000000..92f3825ff5 --- /dev/null +++ b/examples/file-uri-input/README.md @@ -0,0 +1,17 @@ +# Generate models using file URI as input + +A basic example of how to generate models using file URI as input for AsyncAPI + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/file-uri-input/__snapshots__/index.spec.ts.snap b/examples/file-uri-input/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..a563fe8ae8 --- /dev/null +++ b/examples/file-uri-input/__snapshots__/index.spec.ts.snap @@ -0,0 +1,59 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to generate models using file URI as input to output folder and should log expected output to console 1`] = ` +Array [ + "class AnonymousSchema_1 { + private _displayName?: string; + private _email?: string; + private _additionalProperties?: Map; + + constructor(input: { + displayName?: string, + email?: string, + additionalProperties?: Map, + }) { + this._displayName = input.displayName; + this._email = input.email; + this._additionalProperties = input.additionalProperties; + } + + get displayName(): string | undefined { return this._displayName; } + set displayName(displayName: string | undefined) { this._displayName = displayName; } + + get email(): string | undefined { return this._email; } + set email(email: string | undefined) { this._email = email; } + + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } +}", +] +`; + +exports[`Should be able to render models using file URI as input and should log expected output to console 1`] = ` +Array [ + "class AnonymousSchema_1 { + private _displayName?: string; + private _email?: string; + private _additionalProperties?: Map; + + constructor(input: { + displayName?: string, + email?: string, + additionalProperties?: Map, + }) { + this._displayName = input.displayName; + this._email = input.email; + this._additionalProperties = input.additionalProperties; + } + + get displayName(): string | undefined { return this._displayName; } + set displayName(displayName: string | undefined) { this._displayName = displayName; } + + get email(): string | undefined { return this._email; } + set email(email: string | undefined) { this._email = email; } + + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } +}", +] +`; diff --git a/examples/file-uri-input/index.spec.ts b/examples/file-uri-input/index.spec.ts new file mode 100644 index 0000000000..428b0cf504 --- /dev/null +++ b/examples/file-uri-input/index.spec.ts @@ -0,0 +1,26 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate, generateToFiles } from './index'; + +describe('Should be able to render models using file URI as input', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); + +describe('Should be able to generate models using file URI as input to output folder', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generateToFiles(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/file-uri-input/index.ts b/examples/file-uri-input/index.ts new file mode 100644 index 0000000000..30fd45356a --- /dev/null +++ b/examples/file-uri-input/index.ts @@ -0,0 +1,26 @@ +import path from 'path'; +import { TypeScriptFileGenerator, TypeScriptGenerator } from '../../src'; + +const generator = new TypeScriptGenerator(); +const fileGenerator = new TypeScriptFileGenerator(); + +export async function generate(): Promise { + const fileUri = `file://${path.resolve(__dirname, './testasyncapi.yml')}`; + const models = await generator.generate(fileUri); + for (const model of models) { + console.log(model.result); + } +} + +export async function generateToFiles(): Promise { + const outputFolder = './examples/file-uri-input/output'; + const fileUri = `file://${path.resolve(__dirname, './testasyncapi.yml')}`; + const models = await fileGenerator.generateToFiles(fileUri, outputFolder); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); + generateToFiles(); +} diff --git a/examples/file-uri-input/package-lock.json b/examples/file-uri-input/package-lock.json new file mode 100644 index 0000000000..56ab4e873b --- /dev/null +++ b/examples/file-uri-input/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "typescript-interface", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/file-uri-input/package.json b/examples/file-uri-input/package.json new file mode 100644 index 0000000000..4dd081d2e1 --- /dev/null +++ b/examples/file-uri-input/package.json @@ -0,0 +1,10 @@ +{ + "config" : { "example_name" : "file-uri-input" }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/file-uri-input/testasyncapi.yml b/examples/file-uri-input/testasyncapi.yml new file mode 100644 index 0000000000..fbb3f891b7 --- /dev/null +++ b/examples/file-uri-input/testasyncapi.yml @@ -0,0 +1,23 @@ +asyncapi: '2.6.0' +info: + title: Account Service + version: 1.0.0 + description: This service is in charge of processing user signups +channels: + user/signedup: + subscribe: + message: + $ref: '#/components/messages/UserSignedUp' +components: + messages: + UserSignedUp: + payload: + type: object + properties: + displayName: + type: string + description: Name of the user + email: + type: string + format: email + description: Email of the user \ No newline at end of file diff --git a/examples/generate-csharp-models/__snapshots__/index.spec.ts.snap b/examples/generate-csharp-models/__snapshots__/index.spec.ts.snap index 8f4d5cb935..aa5cb03b9b 100644 --- a/examples/generate-csharp-models/__snapshots__/index.spec.ts.snap +++ b/examples/generate-csharp-models/__snapshots__/index.spec.ts.snap @@ -5,12 +5,33 @@ Array [ "public class Root { private string? email; + private System.DateTime? today; + private System.TimeSpan? duration; + private System.Guid? userId; public string? Email { get { return email; } set { email = value; } } + + public System.DateTime? Today + { + get { return today; } + set { today = value; } + } + + public System.TimeSpan? Duration + { + get { return duration; } + set { duration = value; } + } + + public System.Guid? UserId + { + get { return userId; } + set { userId = value; } + } }", ] `; diff --git a/examples/generate-csharp-models/index.ts b/examples/generate-csharp-models/index.ts index cccf80e255..48ac558a6b 100644 --- a/examples/generate-csharp-models/index.ts +++ b/examples/generate-csharp-models/index.ts @@ -9,6 +9,18 @@ const jsonSchemaDraft7 = { email: { type: 'string', format: 'email' + }, + today: { + type: 'string', + format: 'date-time' + }, + duration: { + type: 'string', + format: 'time' + }, + userId: { + type: 'string', + format: 'uuid' } } }; diff --git a/examples/generate-csharp-models/package-lock.json b/examples/generate-csharp-models/package-lock.json index 56ab4e873b..c6c4ae4e1d 100644 --- a/examples/generate-csharp-models/package-lock.json +++ b/examples/generate-csharp-models/package-lock.json @@ -1,5 +1,5 @@ { - "name": "typescript-interface", + "name": "generate-csharp-models", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/examples/overwrite-default-constraint/__snapshots__/index.spec.ts.snap b/examples/overwrite-default-constraint/__snapshots__/index.spec.ts.snap index efdd393efc..3c342c2d0b 100644 --- a/examples/overwrite-default-constraint/__snapshots__/index.spec.ts.snap +++ b/examples/overwrite-default-constraint/__snapshots__/index.spec.ts.snap @@ -3,16 +3,16 @@ exports[`Should be able to render models with custom constrain logic and should log expected output to console 1`] = ` Array [ "class MyOwnCustomModelName { - private _email?: string; + private _MyCustomPropertyKey?: string; constructor(input: { - email?: string, + MyCustomPropertyKey?: string, }) { - this._email = input.email; + this._MyCustomPropertyKey = input.MyCustomPropertyKey; } - get email(): string | undefined { return this._email; } - set email(email: string | undefined) { this._email = email; } + get MyCustomPropertyKey(): string | undefined { return this._MyCustomPropertyKey; } + set MyCustomPropertyKey(MyCustomPropertyKey: string | undefined) { this._MyCustomPropertyKey = MyCustomPropertyKey; } }", ] `; diff --git a/examples/overwrite-default-constraint/index.ts b/examples/overwrite-default-constraint/index.ts index 71c9258a4b..4cab0b0ca6 100644 --- a/examples/overwrite-default-constraint/index.ts +++ b/examples/overwrite-default-constraint/index.ts @@ -1,10 +1,27 @@ import { TypeScriptGenerator } from '../../src'; -import { pascalCase } from 'change-case'; const generator = new TypeScriptGenerator({ constraints: { - modelName: ({ modelName }) => { + modelName: ({ modelName, options }) => { return 'MyOwnCustomModelName'; + }, + constant: ({ constrainedMetaModel, options }) => { + return 'MyCustomConst'; + }, + enumKey: ({ constrainedEnumModel, enumKey, enumModel, options }) => { + return 'MyCustomKey'; + }, + enumValue: ({ constrainedEnumModel, enumValue, enumModel, options }) => { + return 'MyCustomValue'; + }, + propertyKey: ({ + constrainedObjectModel, + constrainedObjectPropertyModel, + objectModel, + objectPropertyModel, + options + }) => { + return 'MyCustomPropertyKey'; } } }); diff --git a/examples/typescript-generate-jsonbinpack/__snapshots__/index.spec.ts.snap b/examples/typescript-generate-jsonbinpack/__snapshots__/index.spec.ts.snap index 40553a144b..218f23f291 100644 --- a/examples/typescript-generate-jsonbinpack/__snapshots__/index.spec.ts.snap +++ b/examples/typescript-generate-jsonbinpack/__snapshots__/index.spec.ts.snap @@ -17,10 +17,9 @@ Array [ public marshal() : string { let json = '{' if(this.email !== undefined) { - json += \`\\"email\\": \${typeof this.email === 'number' || typeof this.email === 'boolean' ? this.email : JSON.stringify(this.email)},\`; + json += \`\\"email\\": \${typeof this.email === 'number' || typeof this.email === 'boolean' ? this.email : JSON.stringify(this.email)},\`; } - - + //Remove potential last comma return \`\${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; } @@ -32,12 +31,10 @@ Array [ if (obj[\\"email\\"] !== undefined) { instance.email = obj[\\"email\\"]; } - - - + + return instance; } - public async jsonbinSerialize(): Promise{ const jsonData = JSON.parse(this.marshal()); const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"$schema\\":\\"https://json-schema.org/draft/2020-12/schema\\",\\"$id\\":\\"Test\\",\\"type\\":\\"object\\",\\"additionalProperties\\":false,\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-modelgen-inferred-name\\":\\"email\\"}},\\"x-modelgen-inferred-name\\":\\"root\\"}); diff --git a/examples/typescript-generate-marshalling/__snapshots__/index.spec.ts.snap b/examples/typescript-generate-marshalling/__snapshots__/index.spec.ts.snap index 500812add9..d43de89024 100644 --- a/examples/typescript-generate-marshalling/__snapshots__/index.spec.ts.snap +++ b/examples/typescript-generate-marshalling/__snapshots__/index.spec.ts.snap @@ -17,10 +17,9 @@ Array [ public marshal() : string { let json = '{' if(this.email !== undefined) { - json += \`\\"email\\": \${typeof this.email === 'number' || typeof this.email === 'boolean' ? this.email : JSON.stringify(this.email)},\`; + json += \`\\"email\\": \${typeof this.email === 'number' || typeof this.email === 'boolean' ? this.email : JSON.stringify(this.email)},\`; } - - + //Remove potential last comma return \`\${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; } @@ -32,9 +31,8 @@ Array [ if (obj[\\"email\\"] !== undefined) { instance.email = obj[\\"email\\"]; } - - - + + return instance; } }", diff --git a/examples/typescript-generate-raw-properties/README.md b/examples/typescript-generate-raw-properties/README.md new file mode 100644 index 0000000000..6bdd08ad4a --- /dev/null +++ b/examples/typescript-generate-raw-properties/README.md @@ -0,0 +1,17 @@ +# TypeScript generate with raw properties + +A basic example of how to use Modelina and output a TypeScript models that only use raw properties for interfaces. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/typescript-generate-raw-properties/__snapshots__/index.spec.ts.snap b/examples/typescript-generate-raw-properties/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..6ce9c32053 --- /dev/null +++ b/examples/typescript-generate-raw-properties/__snapshots__/index.spec.ts.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to render raw properties and should log expected output to console 1`] = ` +Array [ + "interface Root { + 'weird name'?: string; +}", +] +`; diff --git a/examples/typescript-generate-raw-properties/index.spec.ts b/examples/typescript-generate-raw-properties/index.spec.ts new file mode 100644 index 0000000000..5b12322270 --- /dev/null +++ b/examples/typescript-generate-raw-properties/index.spec.ts @@ -0,0 +1,15 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to render raw properties', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/typescript-generate-raw-properties/index.ts b/examples/typescript-generate-raw-properties/index.ts new file mode 100644 index 0000000000..a275064f78 --- /dev/null +++ b/examples/typescript-generate-raw-properties/index.ts @@ -0,0 +1,27 @@ +import { TypeScriptGenerator } from '../../src'; + +const generator = new TypeScriptGenerator({ + rawPropertyNames: true, + modelType: 'interface' +}); +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + additionalProperties: false, + properties: { + 'weird name': { + type: 'string', + format: 'email' + } + } +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft7); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/typescript-generate-raw-properties/package-lock.json b/examples/typescript-generate-raw-properties/package-lock.json new file mode 100644 index 0000000000..33b77abbfd --- /dev/null +++ b/examples/typescript-generate-raw-properties/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "typescript-generate-raw-properties", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/typescript-generate-raw-properties/package.json b/examples/typescript-generate-raw-properties/package.json new file mode 100644 index 0000000000..1901d99872 --- /dev/null +++ b/examples/typescript-generate-raw-properties/package.json @@ -0,0 +1,10 @@ +{ + "config" : { "example_name" : "typescript-generate-raw-properties" }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/examples/typescript-use-esm/__snapshots__/index.spec.ts.snap b/examples/typescript-use-esm/__snapshots__/index.spec.ts.snap index a8f7734995..1dad878abf 100644 --- a/examples/typescript-use-esm/__snapshots__/index.spec.ts.snap +++ b/examples/typescript-use-esm/__snapshots__/index.spec.ts.snap @@ -1,6 +1,26 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Should be able to render models to ESM module system and should log expected output to console 1`] = ` +Array [ + "import Person from './Person'; +class Root { + private _person?: Person; + + constructor(input: { + person?: Person, + }) { + this._person = input.person; + } + + get person(): Person | undefined { return this._person; } + set person(person: Person | undefined) { this._person = person; } +} +export default Root; +", +] +`; + +exports[`Should be able to render models to ESM module system and should log expected output to console 2`] = ` Array [ " class Person { diff --git a/examples/typescript-use-esm/index.spec.ts b/examples/typescript-use-esm/index.spec.ts index 987374c1e9..2c4087b2c2 100644 --- a/examples/typescript-use-esm/index.spec.ts +++ b/examples/typescript-use-esm/index.spec.ts @@ -10,6 +10,7 @@ describe('Should be able to render models to ESM module system', () => { test('and should log expected output to console', async () => { await generate(); expect(spy.mock.calls.length).toEqual(2); + expect(spy.mock.calls[0]).toMatchSnapshot(); expect(spy.mock.calls[1]).toMatchSnapshot(); }); }); diff --git a/examples/typescript-use-js-reserved-keyword/README.md b/examples/typescript-use-js-reserved-keyword/README.md new file mode 100644 index 0000000000..94a51be6cb --- /dev/null +++ b/examples/typescript-use-js-reserved-keyword/README.md @@ -0,0 +1,17 @@ +# TypeScript use JS reserved keywords + +This example shows how you can generate the models that ensure reserved JS keywords are not used for model names, properties or enum keys. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/typescript-use-js-reserved-keyword/__snapshots__/index.spec.ts.snap b/examples/typescript-use-js-reserved-keyword/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..db10da51ad --- /dev/null +++ b/examples/typescript-use-js-reserved-keyword/__snapshots__/index.spec.ts.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to render models to not use reserved keywords from JS and should log expected output to console 1`] = ` +Array [ + "class Root { + private _reservedLocation?: string; + + constructor(input: { + reservedLocation?: string, + }) { + this._reservedLocation = input.reservedLocation; + } + + get reservedLocation(): string | undefined { return this._reservedLocation; } + set reservedLocation(reservedLocation: string | undefined) { this._reservedLocation = reservedLocation; } +}", +] +`; diff --git a/examples/typescript-use-js-reserved-keyword/index.spec.ts b/examples/typescript-use-js-reserved-keyword/index.spec.ts new file mode 100644 index 0000000000..c3457258d5 --- /dev/null +++ b/examples/typescript-use-js-reserved-keyword/index.spec.ts @@ -0,0 +1,15 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to render models to not use reserved keywords from JS', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/typescript-use-js-reserved-keyword/index.ts b/examples/typescript-use-js-reserved-keyword/index.ts new file mode 100644 index 0000000000..b297651e06 --- /dev/null +++ b/examples/typescript-use-js-reserved-keyword/index.ts @@ -0,0 +1,25 @@ +import { TypeScriptGenerator } from '../../src'; + +const generator = new TypeScriptGenerator({ + useJavascriptReservedKeywords: true +}); +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + additionalProperties: false, + properties: { + location: { + type: 'string' + } + } +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft7); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/typescript-use-js-reserved-keyword/package-lock.json b/examples/typescript-use-js-reserved-keyword/package-lock.json new file mode 100644 index 0000000000..aa40f29736 --- /dev/null +++ b/examples/typescript-use-js-reserved-keyword/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "typescript-use-js-reserved-keyword", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/typescript-use-js-reserved-keyword/package.json b/examples/typescript-use-js-reserved-keyword/package.json new file mode 100644 index 0000000000..b0f4c229d8 --- /dev/null +++ b/examples/typescript-use-js-reserved-keyword/package.json @@ -0,0 +1,10 @@ +{ + "config" : { "example_name" : "typescript-use-js-reserved-keyword" }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/jest.config.js b/jest.config.js index 4e6ff5d0a0..020994ab94 100644 --- a/jest.config.js +++ b/jest.config.js @@ -27,6 +27,8 @@ module.exports = { modulePathIgnorePatterns: [ '/examples/TEMPLATE', '/test/generators/template', - '/test/processors/TemplateInputProcessor.spec.ts' - ] + '/test/processors/TemplateInputProcessor.spec.ts', + 'modelina-website' + ], + watchPathIgnorePatterns: ['/node_modules'] }; diff --git a/package-lock.json b/package-lock.json index d84f1c3119..538f6f6572 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,18 @@ { "name": "@asyncapi/modelina", - "version": "2.1.3", + "version": "3.0.0-next.15", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@asyncapi/modelina", - "version": "2.1.3", + "version": "3.0.0-next.15", "license": "Apache-2.0", "dependencies": { - "@apidevtools/json-schema-ref-parser": "^9.0.9", + "@apidevtools/json-schema-ref-parser": "^11.1.0", "@apidevtools/swagger-parser": "^10.0.3", - "@asyncapi/avro-schema-parser": "^3.0.11", - "@asyncapi/openapi-schema-parser": "^3.0.10", - "@asyncapi/parser": "^2.1.2", - "@asyncapi/raml-dt-schema-parser": "^4.0.11", + "@asyncapi/parser": "3.0.0-next-major-spec.8", + "@smoya/multi-parser": "^4.0.0", "@swc/core": "^1.3.5", "@swc/jest": "^0.2.23", "@types/node": "^20.3.3", @@ -38,6 +36,7 @@ "eslint-plugin-prettier": "^5.0.1", "eslint-plugin-security": "^1.7.1", "eslint-plugin-sonarjs": "^0.19.0", + "eslint-plugin-unused-imports": "^3.0.0", "jest": "^27.2.5", "markdown-toc": "^1.2.0", "ts-jest": "^27.0.5", @@ -59,14 +58,21 @@ } }, "node_modules/@apidevtools/json-schema-ref-parser": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz", - "integrity": "sha512-GBD2Le9w2+lVFoc4vswGI/TjkNIZSVp7+9xPf+X3uidBfWnAeUWmquteSyt0+VCrhNMWj/FTABISQrD3Z/YA+w==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.1.0.tgz", + "integrity": "sha512-g/VW9ZQEFJAOwAyUb8JFf7MLiLy2uEB4rU270rGzDwICxnxMlPy0O11KVePSgS36K1NI29gSlK84n5INGhd4Ag==", "dependencies": { "@jsdevtools/ono": "^7.1.3", - "@types/json-schema": "^7.0.6", - "call-me-maybe": "^1.0.1", - "js-yaml": "^4.1.0" + "@types/json-schema": "^7.0.13", + "@types/lodash.clonedeep": "^4.5.7", + "js-yaml": "^4.1.0", + "lodash.clonedeep": "^4.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" } }, "node_modules/@apidevtools/openapi-schemas": { @@ -98,22 +104,33 @@ "openapi-types": ">=7" } }, + "node_modules/@apidevtools/swagger-parser/node_modules/@apidevtools/json-schema-ref-parser": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", + "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.6", + "call-me-maybe": "^1.0.1", + "js-yaml": "^4.1.0" + } + }, "node_modules/@asyncapi/avro-schema-parser": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@asyncapi/avro-schema-parser/-/avro-schema-parser-3.0.11.tgz", - "integrity": "sha512-QXSvGoLk8k5OA1Q1lqVe8xYpSVLdaUBgaGGVn0KkLsbpU9Wp1vOw4LVSOmcSuv7V9O3X7s7GoJ/rdP851TYfCg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@asyncapi/avro-schema-parser/-/avro-schema-parser-3.0.4.tgz", + "integrity": "sha512-2EwTS+0deHsqIJG41dJGE2/O9pD1RJZpUCahz24tgZzcK/GQM1HBuoVvqBkPcloS9EeOXo/ZcfCMAvflj5UB/g==", "dependencies": { - "@asyncapi/parser": "^3.0.4", + "@asyncapi/parser": "^2.1.1", "@types/json-schema": "^7.0.11", "avsc": "^5.7.6" } }, "node_modules/@asyncapi/avro-schema-parser/node_modules/@asyncapi/parser": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-3.0.4.tgz", - "integrity": "sha512-xop2ZGCNey6eaNp6ypscrWCQogC++XAjq7yWvaB9v3GfpL0P8Dq+d29NetlvpLFZ1xnkbeuUg9R2m7FBZYkPFw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-2.1.1.tgz", + "integrity": "sha512-FnJ5Du9iMu9MEb5mF90gF7z1ZkdnazisBsm3GHVFr7VaiF8luAoB+bklGYFwoMb+9QWKWr1099orY5VyXULAcQ==", "dependencies": { - "@asyncapi/specs": "^6.3.0", + "@asyncapi/specs": "^5.1.0", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", "@stoplight/json": "^3.20.2", "@stoplight/json-ref-readers": "^1.2.2", @@ -131,13 +148,15 @@ "avsc": "^5.7.5", "js-yaml": "^4.1.0", "jsonpath-plus": "^7.2.0", - "node-fetch": "2.6.7" + "node-fetch": "2.6.7", + "ramldt2jsonschema": "^1.2.3", + "webapi-parser": "^0.5.0" } }, "node_modules/@asyncapi/avro-schema-parser/node_modules/@asyncapi/specs": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.3.0.tgz", - "integrity": "sha512-Ui7Fekd/ruqd4hTSb5ogsTG8eylHEyKR5qKOtg+HwOYWlUEtObOD2UlhbuASiB4YnG5SSfkEKR5YsUJmf/ESYQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-5.1.0.tgz", + "integrity": "sha512-yffhETqehkim43luMnPKOwzY0D0YtU4bKpORIXIaid6p5Y5kDLrMGJaEPkNieQp03HMjhjFrnUPtT8kvqe0+aQ==", "dependencies": { "@types/json-schema": "^7.0.11" } @@ -164,11 +183,11 @@ "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" }, "node_modules/@asyncapi/openapi-schema-parser": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@asyncapi/openapi-schema-parser/-/openapi-schema-parser-3.0.10.tgz", - "integrity": "sha512-kaLeYLicn65iLCKnjrmOYEybjFgxrKBOc2ZjwloCMRahyWX3hQHPU9IqL54JxEQ2R9AGznROPhrKz4iczRfKZw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@asyncapi/openapi-schema-parser/-/openapi-schema-parser-3.0.5.tgz", + "integrity": "sha512-2CPP0arkI/ibXTdae6DqeQyor3xuSf0GOyh0+yFPqVkbiYPBkC8K2UNbQ610vt8KpcEFeijX+7YuV+FTuQ5CHg==", "dependencies": { - "@asyncapi/parser": "^3.0.2", + "@asyncapi/parser": "^2.1.1", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", "ajv": "^8.11.0", "ajv-errors": "^3.0.0", @@ -176,11 +195,11 @@ } }, "node_modules/@asyncapi/openapi-schema-parser/node_modules/@asyncapi/parser": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-3.0.2.tgz", - "integrity": "sha512-AtDFndWwnaqGoXZQY2DRtORT2Ls4BI7MSR+Rg7TRwxf5jxIz/WVvQwc5HElkHuDEkIZslYu+ukFzNq3awdj0aw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-2.1.1.tgz", + "integrity": "sha512-FnJ5Du9iMu9MEb5mF90gF7z1ZkdnazisBsm3GHVFr7VaiF8luAoB+bklGYFwoMb+9QWKWr1099orY5VyXULAcQ==", "dependencies": { - "@asyncapi/specs": "^6.2.0", + "@asyncapi/specs": "^5.1.0", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", "@stoplight/json": "^3.20.2", "@stoplight/json-ref-readers": "^1.2.2", @@ -198,13 +217,15 @@ "avsc": "^5.7.5", "js-yaml": "^4.1.0", "jsonpath-plus": "^7.2.0", - "node-fetch": "2.6.7" + "node-fetch": "2.6.7", + "ramldt2jsonschema": "^1.2.3", + "webapi-parser": "^0.5.0" } }, "node_modules/@asyncapi/openapi-schema-parser/node_modules/@asyncapi/specs": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.2.0.tgz", - "integrity": "sha512-5uf/Rg6pavZHx7rVIkP0TP/icIahJCuHgmY1rdtkrWxHZMXbASDDV3DlTUaonbsUeemwchoqljmrTd1O1xqvxg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-5.1.0.tgz", + "integrity": "sha512-yffhETqehkim43luMnPKOwzY0D0YtU4bKpORIXIaid6p5Y5kDLrMGJaEPkNieQp03HMjhjFrnUPtT8kvqe0+aQ==", "dependencies": { "@types/json-schema": "^7.0.11" } @@ -231,20 +252,16 @@ "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" }, "node_modules/@asyncapi/parser": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-2.1.2.tgz", - "integrity": "sha512-2pHKnr2P8EujcrvZo4x4zNwsEIAg5vb1ZEhl2+OH0YBg8EYH/Xx73XZ+bbwLaYIg1gvFjm29jNB9UL3CMeDU5w==", + "version": "3.0.0-next-major-spec.8", + "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-3.0.0-next-major-spec.8.tgz", + "integrity": "sha512-d8ebYM08BCsx3Q4AeLke6naU/NrcAXFEVpS6b3EWcKRdUDce+v0X5k9aDH+YXWCaQApEF28UzcxhlSOJvhIFgQ==", "dependencies": { - "@asyncapi/specs": "^5.1.0", + "@asyncapi/specs": "^6.0.0-next-major-spec.9", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", - "@stoplight/json": "^3.20.2", - "@stoplight/json-ref-readers": "^1.2.2", "@stoplight/json-ref-resolver": "^3.1.5", "@stoplight/spectral-core": "^1.16.1", "@stoplight/spectral-functions": "^1.7.2", "@stoplight/spectral-parsers": "^1.0.2", - "@stoplight/spectral-ref-resolver": "^1.0.3", - "@stoplight/types": "^13.12.0", "@types/json-schema": "^7.0.11", "@types/urijs": "^1.19.19", "ajv": "^8.11.0", @@ -253,38 +270,19 @@ "avsc": "^5.7.5", "js-yaml": "^4.1.0", "jsonpath-plus": "^7.2.0", - "node-fetch": "2.6.7" + "node-fetch": "2.6.7", + "ramldt2jsonschema": "^1.2.3", + "webapi-parser": "^0.5.0" } }, "node_modules/@asyncapi/parser/node_modules/@asyncapi/specs": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-5.1.0.tgz", - "integrity": "sha512-yffhETqehkim43luMnPKOwzY0D0YtU4bKpORIXIaid6p5Y5kDLrMGJaEPkNieQp03HMjhjFrnUPtT8kvqe0+aQ==", + "version": "6.0.0-next-major-spec.11", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.0.0-next-major-spec.11.tgz", + "integrity": "sha512-TR92zUaGQ5lZfc+f9L8yRCVqP7AVFvrMl2Ebr36ZGGC6cQwv80DZL/sbbgAWXzThAcTDXL1xsHUc0BpecHXoHw==", "dependencies": { "@types/json-schema": "^7.0.11" } }, - "node_modules/@asyncapi/parser/node_modules/@stoplight/json": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.21.0.tgz", - "integrity": "sha512-5O0apqJ/t4sIevXCO3SBN9AHCEKKR/Zb4gaj7wYe5863jme9g02Q0n/GhM7ZCALkL+vGPTe4ZzTETP8TFtsw3g==", - "dependencies": { - "@stoplight/ordered-object-literal": "^1.0.3", - "@stoplight/path": "^1.3.2", - "@stoplight/types": "^13.6.0", - "jsonc-parser": "~2.2.1", - "lodash": "^4.17.21", - "safe-stable-stringify": "^1.1" - }, - "engines": { - "node": ">=8.3.0" - } - }, - "node_modules/@asyncapi/parser/node_modules/safe-stable-stringify": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", - "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" - }, "node_modules/@asyncapi/parserV1": { "name": "@asyncapi/parser", "version": "1.18.1", @@ -303,6 +301,36 @@ "tiny-merge-patch": "^0.1.2" } }, + "node_modules/@asyncapi/parserV1/node_modules/@apidevtools/json-schema-ref-parser": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", + "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", + "dev": true, + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.6", + "call-me-maybe": "^1.0.1", + "js-yaml": "^4.1.0" + } + }, + "node_modules/@asyncapi/parserV1/node_modules/@apidevtools/json-schema-ref-parser/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@asyncapi/parserV1/node_modules/@apidevtools/json-schema-ref-parser/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@asyncapi/parserV1/node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -341,23 +369,90 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/@asyncapi/protobuf-schema-parser": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@asyncapi/protobuf-schema-parser/-/protobuf-schema-parser-3.0.1.tgz", + "integrity": "sha512-w6y7/jvnCBwwhAee7/uv6Cb31YAbDm3I6V9s7Jj+MSStA91WOEq1rOFm4YHdJ23oYFbWaQmc5+8MLTnQBGfc4Q==", + "dependencies": { + "@asyncapi/parser": "^2.1.1", + "@types/protocol-buffers-schema": "^3.4.1", + "protocol-buffers-schema": "^3.6.0" + } + }, + "node_modules/@asyncapi/protobuf-schema-parser/node_modules/@asyncapi/parser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-2.1.1.tgz", + "integrity": "sha512-FnJ5Du9iMu9MEb5mF90gF7z1ZkdnazisBsm3GHVFr7VaiF8luAoB+bklGYFwoMb+9QWKWr1099orY5VyXULAcQ==", + "dependencies": { + "@asyncapi/specs": "^5.1.0", + "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", + "@stoplight/json": "^3.20.2", + "@stoplight/json-ref-readers": "^1.2.2", + "@stoplight/json-ref-resolver": "^3.1.5", + "@stoplight/spectral-core": "^1.16.1", + "@stoplight/spectral-functions": "^1.7.2", + "@stoplight/spectral-parsers": "^1.0.2", + "@stoplight/spectral-ref-resolver": "^1.0.3", + "@stoplight/types": "^13.12.0", + "@types/json-schema": "^7.0.11", + "@types/urijs": "^1.19.19", + "ajv": "^8.11.0", + "ajv-errors": "^3.0.0", + "ajv-formats": "^2.1.1", + "avsc": "^5.7.5", + "js-yaml": "^4.1.0", + "jsonpath-plus": "^7.2.0", + "node-fetch": "2.6.7", + "ramldt2jsonschema": "^1.2.3", + "webapi-parser": "^0.5.0" + } + }, + "node_modules/@asyncapi/protobuf-schema-parser/node_modules/@asyncapi/specs": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-5.1.0.tgz", + "integrity": "sha512-yffhETqehkim43luMnPKOwzY0D0YtU4bKpORIXIaid6p5Y5kDLrMGJaEPkNieQp03HMjhjFrnUPtT8kvqe0+aQ==", + "dependencies": { + "@types/json-schema": "^7.0.11" + } + }, + "node_modules/@asyncapi/protobuf-schema-parser/node_modules/@stoplight/json": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.21.0.tgz", + "integrity": "sha512-5O0apqJ/t4sIevXCO3SBN9AHCEKKR/Zb4gaj7wYe5863jme9g02Q0n/GhM7ZCALkL+vGPTe4ZzTETP8TFtsw3g==", + "dependencies": { + "@stoplight/ordered-object-literal": "^1.0.3", + "@stoplight/path": "^1.3.2", + "@stoplight/types": "^13.6.0", + "jsonc-parser": "~2.2.1", + "lodash": "^4.17.21", + "safe-stable-stringify": "^1.1" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/@asyncapi/protobuf-schema-parser/node_modules/safe-stable-stringify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", + "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" + }, "node_modules/@asyncapi/raml-dt-schema-parser": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/@asyncapi/raml-dt-schema-parser/-/raml-dt-schema-parser-4.0.11.tgz", - "integrity": "sha512-VOEX1kqSe8TEKz4EepevI57G3UPNpp6MrZMpiHTH+EwIaKyLLGol6vBKgPm+2PYEM7OXlfcaXAVJvI7jas/xLA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@asyncapi/raml-dt-schema-parser/-/raml-dt-schema-parser-4.0.4.tgz", + "integrity": "sha512-kKam4jwYYdwqoV5zkEb3YEb8VOrN0785fc4ByazxRd+BT/RnkQTLspjTY/akdDs9DLmU4ChP73Z0vqpek6wojA==", "dependencies": { - "@asyncapi/parser": "^3.0.3", + "@asyncapi/parser": "^2.1.0", "js-yaml": "^4.1.0", "ramldt2jsonschema": "^1.2.3", "webapi-parser": "^0.5.0" } }, "node_modules/@asyncapi/raml-dt-schema-parser/node_modules/@asyncapi/parser": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-3.0.3.tgz", - "integrity": "sha512-nRkzgzcSkqBMy6kL/AWNsL8EJzDePFI4x6949V+4R/zYtbAm1IYY9Q4DEc81pnxoWHrJrVvrSD6O73/VTJ+XoA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-2.1.1.tgz", + "integrity": "sha512-FnJ5Du9iMu9MEb5mF90gF7z1ZkdnazisBsm3GHVFr7VaiF8luAoB+bklGYFwoMb+9QWKWr1099orY5VyXULAcQ==", "dependencies": { - "@asyncapi/specs": "^6.2.1", + "@asyncapi/specs": "^5.1.0", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", "@stoplight/json": "^3.20.2", "@stoplight/json-ref-readers": "^1.2.2", @@ -375,13 +470,15 @@ "avsc": "^5.7.5", "js-yaml": "^4.1.0", "jsonpath-plus": "^7.2.0", - "node-fetch": "2.6.7" + "node-fetch": "2.6.7", + "ramldt2jsonschema": "^1.2.3", + "webapi-parser": "^0.5.0" } }, "node_modules/@asyncapi/raml-dt-schema-parser/node_modules/@asyncapi/specs": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.2.1.tgz", - "integrity": "sha512-rfd0bzJrApEbxYD4U1SUG02GGG6lK6i6lup9LDWpVu1QwwuMNLAgf8+jsmsf3e7OinB/lauxRumGODXCxBu5Jw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-5.1.0.tgz", + "integrity": "sha512-yffhETqehkim43luMnPKOwzY0D0YtU4bKpORIXIaid6p5Y5kDLrMGJaEPkNieQp03HMjhjFrnUPtT8kvqe0+aQ==", "dependencies": { "@types/json-schema": "^7.0.11" } @@ -1983,6 +2080,19 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@smoya/multi-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smoya/multi-parser/-/multi-parser-4.1.0.tgz", + "integrity": "sha512-0q4805I7ZdkKNEbNrbxmBNAdYEAH9aBYCuXiIXCbu8WC0sdRqLWmTsvg/DaNO8ZnsrGhVzy5VITf+8CKlVnGoQ==", + "dependencies": { + "@asyncapi/avro-schema-parser": "^3.0.3", + "@asyncapi/openapi-schema-parser": "^3.0.4", + "@asyncapi/protobuf-schema-parser": "^3.0.0", + "@asyncapi/raml-dt-schema-parser": "^4.0.4", + "parserv2": "npm:@asyncapi/parser@^2.1.0", + "parserv3": "npm:@asyncapi/parser@^3.0.0-next-major-spec.3" + } + }, "node_modules/@stoplight/better-ajv-errors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@stoplight/better-ajv-errors/-/better-ajv-errors-1.0.3.tgz", @@ -2433,6 +2543,19 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==" + }, + "node_modules/@types/lodash.clonedeep": { + "version": "4.5.9", + "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.9.tgz", + "integrity": "sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/node": { "version": "20.8.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.4.tgz", @@ -2447,6 +2570,14 @@ "integrity": "sha512-Fo79ojj3vdEZOHg3wR9ksAMRz4P3S5fDB5e/YWZiFnyFQI1WY2Vftu9XoXVVtJfxB7Bpce/QTqWSSntkz2Znrw==", "dev": true }, + "node_modules/@types/protocol-buffers-schema": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@types/protocol-buffers-schema/-/protocol-buffers-schema-3.4.2.tgz", + "integrity": "sha512-GaQpfsfFk4wGU3//d7uCGy9zy6B8QBEyWYd6+maZH+S6m861QrFvLWS5RyHj4UfIiON9tmqCz9C+oNpebDgGIw==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/semver": { "version": "7.5.3", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", @@ -5243,6 +5374,36 @@ "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/eslint-plugin-unused-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-3.0.0.tgz", + "integrity": "sha512-sduiswLJfZHeeBJ+MQaG+xYzSWdRXoSw61DpU13mzWumCkR0ufD0HmO4kdNokjrkluMHpj/7PJeN35pgbhW3kw==", + "dev": true, + "dependencies": { + "eslint-rule-composer": "^0.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^6.0.0", + "eslint": "^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + } + } + }, + "node_modules/eslint-rule-composer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", + "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/eslint-rule-documentation": { "version": "1.0.23", "resolved": "https://registry.npmjs.org/eslint-rule-documentation/-/eslint-rule-documentation-1.0.23.tgz", @@ -8967,8 +9128,7 @@ "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", - "dev": true + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" }, "node_modules/lodash.get": { "version": "4.4.2", @@ -9610,6 +9770,97 @@ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true }, + "node_modules/parserv2": { + "name": "@asyncapi/parser", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-2.1.1.tgz", + "integrity": "sha512-FnJ5Du9iMu9MEb5mF90gF7z1ZkdnazisBsm3GHVFr7VaiF8luAoB+bklGYFwoMb+9QWKWr1099orY5VyXULAcQ==", + "dependencies": { + "@asyncapi/specs": "^5.1.0", + "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", + "@stoplight/json": "^3.20.2", + "@stoplight/json-ref-readers": "^1.2.2", + "@stoplight/json-ref-resolver": "^3.1.5", + "@stoplight/spectral-core": "^1.16.1", + "@stoplight/spectral-functions": "^1.7.2", + "@stoplight/spectral-parsers": "^1.0.2", + "@stoplight/spectral-ref-resolver": "^1.0.3", + "@stoplight/types": "^13.12.0", + "@types/json-schema": "^7.0.11", + "@types/urijs": "^1.19.19", + "ajv": "^8.11.0", + "ajv-errors": "^3.0.0", + "ajv-formats": "^2.1.1", + "avsc": "^5.7.5", + "js-yaml": "^4.1.0", + "jsonpath-plus": "^7.2.0", + "node-fetch": "2.6.7", + "ramldt2jsonschema": "^1.2.3", + "webapi-parser": "^0.5.0" + } + }, + "node_modules/parserv2/node_modules/@asyncapi/specs": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-5.1.0.tgz", + "integrity": "sha512-yffhETqehkim43luMnPKOwzY0D0YtU4bKpORIXIaid6p5Y5kDLrMGJaEPkNieQp03HMjhjFrnUPtT8kvqe0+aQ==", + "dependencies": { + "@types/json-schema": "^7.0.11" + } + }, + "node_modules/parserv2/node_modules/@stoplight/json": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.21.0.tgz", + "integrity": "sha512-5O0apqJ/t4sIevXCO3SBN9AHCEKKR/Zb4gaj7wYe5863jme9g02Q0n/GhM7ZCALkL+vGPTe4ZzTETP8TFtsw3g==", + "dependencies": { + "@stoplight/ordered-object-literal": "^1.0.3", + "@stoplight/path": "^1.3.2", + "@stoplight/types": "^13.6.0", + "jsonc-parser": "~2.2.1", + "lodash": "^4.17.21", + "safe-stable-stringify": "^1.1" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/parserv2/node_modules/safe-stable-stringify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", + "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" + }, + "node_modules/parserv3": { + "name": "@asyncapi/parser", + "version": "3.0.0-next-major-spec.7", + "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-3.0.0-next-major-spec.7.tgz", + "integrity": "sha512-iCVCd66h6NXrIVu7aK5RwGuJ4g1j+qD88xxejUwczbpGbhJkuZUzF5jReWNNAizETJttnAwW0rBKjZF4HVqOiQ==", + "dependencies": { + "@asyncapi/specs": "^6.0.0-next-major-spec.9", + "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", + "@stoplight/json-ref-resolver": "^3.1.5", + "@stoplight/spectral-core": "^1.16.1", + "@stoplight/spectral-functions": "^1.7.2", + "@stoplight/spectral-parsers": "^1.0.2", + "@types/json-schema": "^7.0.11", + "@types/urijs": "^1.19.19", + "ajv": "^8.11.0", + "ajv-errors": "^3.0.0", + "ajv-formats": "^2.1.1", + "avsc": "^5.7.5", + "js-yaml": "^4.1.0", + "jsonpath-plus": "^7.2.0", + "node-fetch": "2.6.7", + "ramldt2jsonschema": "^1.2.3", + "webapi-parser": "^0.5.0" + } + }, + "node_modules/parserv3/node_modules/@asyncapi/specs": { + "version": "6.0.0-next-major-spec.10", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.0.0-next-major-spec.10.tgz", + "integrity": "sha512-flxUofPHdLDfbnfkLzqMPjbCj4EJTxg8q9Xxkt9hsO7BDRUDQk5C7RRBfe+HxAstl0LghX1DAQcof3+GPEXuFA==", + "dependencies": { + "@types/json-schema": "^7.0.11" + } + }, "node_modules/pascal-case": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", @@ -9872,6 +10123,11 @@ "node": ">= 6" } }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" + }, "node_modules/psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", diff --git a/package.json b/package.json index 19f5b14930..64f8a632e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@asyncapi/modelina", - "version": "2.1.3", + "version": "3.0.0-next.15", "description": "Library for generating data models based on inputs such as AsyncAPI, OpenAPI, or JSON Schema documents", "license": "Apache-2.0", "homepage": "https://www.modelina.org", @@ -35,12 +35,10 @@ "./LICENSE" ], "dependencies": { - "@apidevtools/json-schema-ref-parser": "^9.0.9", + "@apidevtools/json-schema-ref-parser": "^11.1.0", "@apidevtools/swagger-parser": "^10.0.3", - "@asyncapi/avro-schema-parser": "^3.0.11", - "@asyncapi/openapi-schema-parser": "^3.0.10", - "@asyncapi/parser": "^2.1.2", - "@asyncapi/raml-dt-schema-parser": "^4.0.11", + "@asyncapi/parser": "3.0.0-next-major-spec.8", + "@smoya/multi-parser": "^4.0.0", "@swc/core": "^1.3.5", "@swc/jest": "^0.2.23", "@types/node": "^20.3.3", @@ -64,6 +62,7 @@ "eslint-plugin-prettier": "^5.0.1", "eslint-plugin-security": "^1.7.1", "eslint-plugin-sonarjs": "^0.19.0", + "eslint-plugin-unused-imports": "^3.0.0", "jest": "^27.2.5", "markdown-toc": "^1.2.0", "ts-jest": "^27.0.5", diff --git a/src/generators/AbstractFileGenerator.ts b/src/generators/AbstractFileGenerator.ts index 6ce8c1801e..75da970fd6 100644 --- a/src/generators/AbstractFileGenerator.ts +++ b/src/generators/AbstractFileGenerator.ts @@ -7,7 +7,7 @@ export type FileGenerator = (content: string, toFile: string) => Promise; */ export interface AbstractFileGenerator { generateToFiles( - input: Record | InputMetaModel, + input: any | InputMetaModel, outputDirectory: string, options: RenderCompleteModelOptions ): Promise; diff --git a/src/generators/cplusplus/CplusplusFileGenerator.ts b/src/generators/cplusplus/CplusplusFileGenerator.ts index d57022af85..71281a7ccb 100644 --- a/src/generators/cplusplus/CplusplusFileGenerator.ts +++ b/src/generators/cplusplus/CplusplusFileGenerator.ts @@ -16,7 +16,7 @@ export class CplusplusFileGenerator * @param options */ public async generateToFiles( - input: Record | InputMetaModel, + input: any | InputMetaModel, outputDirectory: string, options?: CplusplusRenderCompleteModelOptions, ensureFilesWritten = false diff --git a/src/generators/cplusplus/CplusplusGenerator.ts b/src/generators/cplusplus/CplusplusGenerator.ts index 77654d8e24..9cc74b9da3 100644 --- a/src/generators/cplusplus/CplusplusGenerator.ts +++ b/src/generators/cplusplus/CplusplusGenerator.ts @@ -20,8 +20,13 @@ import { EnumRenderer } from './renderers/EnumRenderer'; import { isReservedCplusplusKeyword } from './Constants'; import { Logger } from '../..'; import { + ConstantConstraint, constrainMetaModel, - Constraints + Constraints, + EnumKeyConstraint, + EnumValueConstraint, + ModelNameConstraint, + PropertyKeyConstraint } from '../../helpers/ConstrainHelpers'; import { CplusplusDefaultConstraints, @@ -33,9 +38,18 @@ import { CplusplusDependencyManager } from './CplusplusDependencyManager'; export interface CplusplusOptions extends CommonGeneratorOptions { typeMapping: TypeMapping; - constraints: Constraints; + constraints: Constraints; namespace: string; } +export type CplusplusConstantConstraint = ConstantConstraint; +export type CplusplusEnumKeyConstraint = EnumKeyConstraint; +export type CplusplusEnumValueConstraint = + EnumValueConstraint; +export type CplusplusModelNameConstraint = + ModelNameConstraint; +export type CplusplusPropertyKeyConstraint = + PropertyKeyConstraint; + // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface CplusplusRenderCompleteModelOptions {} export class CplusplusGenerator extends AbstractGenerator< diff --git a/src/generators/cplusplus/constrainer/ConstantConstrainer.ts b/src/generators/cplusplus/constrainer/ConstantConstrainer.ts index 2c076ad46d..571f14be90 100644 --- a/src/generators/cplusplus/constrainer/ConstantConstrainer.ts +++ b/src/generators/cplusplus/constrainer/ConstantConstrainer.ts @@ -1,6 +1,7 @@ import { ConstantConstraint } from '../../../helpers'; +import { CplusplusOptions } from '../CplusplusGenerator'; -export function defaultConstantConstraints(): ConstantConstraint { +export function defaultConstantConstraints(): ConstantConstraint { return () => { return undefined; }; diff --git a/src/generators/cplusplus/constrainer/EnumConstrainer.ts b/src/generators/cplusplus/constrainer/EnumConstrainer.ts index 63852d5da5..f6a122fe05 100644 --- a/src/generators/cplusplus/constrainer/EnumConstrainer.ts +++ b/src/generators/cplusplus/constrainer/EnumConstrainer.ts @@ -6,12 +6,12 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { - FormatHelpers, - EnumKeyConstraint, - EnumValueConstraint -} from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedCplusplusKeyword } from '../Constants'; +import { + CplusplusEnumKeyConstraint, + CplusplusEnumValueConstraint +} from '../CplusplusGenerator'; export type ModelEnumKeyConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -50,7 +50,7 @@ export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { */ export function defaultEnumKeyConstraints( customConstraints?: Partial -): EnumKeyConstraint { +): CplusplusEnumKeyConstraint { const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; return ({ enumKey, enumModel, constrainedEnumModel }) => { @@ -76,7 +76,7 @@ export function defaultEnumKeyConstraints( /** * Convert the enum value to a value that is compatible with Cplusplus */ -export function defaultEnumValueConstraints(): EnumValueConstraint { +export function defaultEnumValueConstraints(): CplusplusEnumValueConstraint { return ({ enumValue }) => { let constrainedEnumValue; switch (typeof enumValue) { diff --git a/src/generators/cplusplus/constrainer/ModelNameConstrainer.ts b/src/generators/cplusplus/constrainer/ModelNameConstrainer.ts index 8538f20dd7..a9d262f454 100644 --- a/src/generators/cplusplus/constrainer/ModelNameConstrainer.ts +++ b/src/generators/cplusplus/constrainer/ModelNameConstrainer.ts @@ -3,8 +3,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedCplusplusKeyword } from '../Constants'; +import { CplusplusModelNameConstraint } from '../CplusplusGenerator'; export type ModelNameConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -36,7 +37,7 @@ export const DefaultModelNameConstraints: ModelNameConstraints = { */ export function defaultModelNameConstraints( customConstraints?: Partial -): ModelNameConstraint { +): CplusplusModelNameConstraint { const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; return ({ modelName }) => { diff --git a/src/generators/cplusplus/constrainer/PropertyKeyConstrainer.ts b/src/generators/cplusplus/constrainer/PropertyKeyConstrainer.ts index 427170e803..8c71619bd4 100644 --- a/src/generators/cplusplus/constrainer/PropertyKeyConstrainer.ts +++ b/src/generators/cplusplus/constrainer/PropertyKeyConstrainer.ts @@ -4,8 +4,9 @@ import { NO_DUPLICATE_PROPERTIES, NO_EMPTY_VALUE } from '../../../helpers/Constraints'; -import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedCplusplusKeyword } from '../Constants'; +import { CplusplusPropertyKeyConstraint } from '../CplusplusGenerator'; export type PropertyKeyConstraintOptions = { NO_SPECIAL_CHAR: (value: string) => string; @@ -46,7 +47,7 @@ export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { */ export function defaultPropertyKeyConstraints( customConstraints?: Partial -): PropertyKeyConstraint { +): CplusplusPropertyKeyConstraint { const constraints = { ...DefaultPropertyKeyConstraints, ...customConstraints diff --git a/src/generators/csharp/CSharpConstrainer.ts b/src/generators/csharp/CSharpConstrainer.ts index 463cf8b784..e8b7301156 100644 --- a/src/generators/csharp/CSharpConstrainer.ts +++ b/src/generators/csharp/CSharpConstrainer.ts @@ -1,4 +1,3 @@ -import { Constraints } from '../../helpers'; import { ConstrainedEnumValueModel, ConstrainedObjectPropertyModel @@ -55,8 +54,19 @@ export const CSharpDefaultTypeMapping: CSharpTypeMapping = { Integer({ partOfProperty }): string { return getFullTypeDefinition('int', partOfProperty); }, - String({ partOfProperty }): string { - return getFullTypeDefinition('string', partOfProperty); + String({ constrainedModel, partOfProperty }): string { + switch (constrainedModel.options.format) { + case 'time': + return getFullTypeDefinition('System.TimeSpan', partOfProperty); + case 'date': + case 'dateTime': + case 'date-time': + return getFullTypeDefinition('System.DateTime', partOfProperty); + case 'uuid': + return getFullTypeDefinition('System.Guid', partOfProperty); + default: + return getFullTypeDefinition('string', partOfProperty); + } }, Boolean({ partOfProperty }): string { return getFullTypeDefinition('bool', partOfProperty); @@ -102,7 +112,7 @@ export const CSharpDefaultTypeMapping: CSharpTypeMapping = { } }; -export const CSharpDefaultConstraints: Constraints = { +export const CSharpDefaultConstraints = { enumKey: defaultEnumKeyConstraints(), enumValue: defaultEnumValueConstraints(), modelName: defaultModelNameConstraints(), diff --git a/src/generators/csharp/CSharpFileGenerator.ts b/src/generators/csharp/CSharpFileGenerator.ts index da32f8f90e..8ac2c618bb 100644 --- a/src/generators/csharp/CSharpFileGenerator.ts +++ b/src/generators/csharp/CSharpFileGenerator.ts @@ -18,7 +18,7 @@ export class CSharpFileGenerator * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. */ public async generateToFiles( - input: Record | InputMetaModel, + input: any | InputMetaModel, outputDirectory: string, options: DeepPartial, ensureFilesWritten = false diff --git a/src/generators/csharp/CSharpGenerator.ts b/src/generators/csharp/CSharpGenerator.ts index 39d14f7c2e..add9bab4ec 100644 --- a/src/generators/csharp/CSharpGenerator.ts +++ b/src/generators/csharp/CSharpGenerator.ts @@ -19,7 +19,12 @@ import { Constraints, constrainMetaModel, split, - SplitOptions + SplitOptions, + ConstantConstraint, + EnumKeyConstraint, + EnumValueConstraint, + ModelNameConstraint, + PropertyKeyConstraint } from '../../helpers'; import { CSharpPreset, CSHARP_DEFAULT_PRESET } from './CSharpPreset'; import { EnumRenderer } from './renderers/EnumRenderer'; @@ -37,11 +42,16 @@ import { CSharpDependencyManager } from './CSharpDependencyManager'; export interface CSharpOptions extends CommonGeneratorOptions { collectionType: 'List' | 'Array'; typeMapping: TypeMapping; - constraints: Constraints; + constraints: Constraints; autoImplementedProperties: boolean; modelType: 'class' | 'record'; handleNullable: boolean; } +export type CSharpConstantConstraint = ConstantConstraint; +export type CSharpEnumKeyConstraint = EnumKeyConstraint; +export type CSharpEnumValueConstraint = EnumValueConstraint; +export type CSharpModelNameConstraint = ModelNameConstraint; +export type CSharpPropertyKeyConstraint = PropertyKeyConstraint; export type CSharpTypeMapping = TypeMapping< CSharpOptions, CSharpDependencyManager diff --git a/src/generators/csharp/constrainer/ConstantConstrainer.ts b/src/generators/csharp/constrainer/ConstantConstrainer.ts index 2c076ad46d..797006c4ec 100644 --- a/src/generators/csharp/constrainer/ConstantConstrainer.ts +++ b/src/generators/csharp/constrainer/ConstantConstrainer.ts @@ -1,6 +1,6 @@ -import { ConstantConstraint } from '../../../helpers'; +import { CSharpConstantConstraint } from '../CSharpGenerator'; -export function defaultConstantConstraints(): ConstantConstraint { +export function defaultConstantConstraints(): CSharpConstantConstraint { return () => { return undefined; }; diff --git a/src/generators/csharp/constrainer/EnumConstrainer.ts b/src/generators/csharp/constrainer/EnumConstrainer.ts index fa22cc1a75..2b808a9527 100644 --- a/src/generators/csharp/constrainer/EnumConstrainer.ts +++ b/src/generators/csharp/constrainer/EnumConstrainer.ts @@ -5,12 +5,12 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { - EnumKeyConstraint, - EnumValueConstraint, - FormatHelpers -} from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedCSharpKeyword } from '../Constants'; +import { + CSharpEnumKeyConstraint, + CSharpEnumValueConstraint +} from '../CSharpGenerator'; export type ModelEnumKeyConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -49,7 +49,7 @@ export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { export function defaultEnumKeyConstraints( customConstraints?: Partial -): EnumKeyConstraint { +): CSharpEnumKeyConstraint { const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; return ({ enumKey, enumModel, constrainedEnumModel }) => { @@ -72,7 +72,7 @@ export function defaultEnumKeyConstraints( }; } -export function defaultEnumValueConstraints(): EnumValueConstraint { +export function defaultEnumValueConstraints(): CSharpEnumValueConstraint { return ({ enumValue }) => { let normalizedEnumValue; switch (typeof enumValue) { diff --git a/src/generators/csharp/constrainer/ModelNameConstrainer.ts b/src/generators/csharp/constrainer/ModelNameConstrainer.ts index b7a71c1a55..1d704d57fa 100644 --- a/src/generators/csharp/constrainer/ModelNameConstrainer.ts +++ b/src/generators/csharp/constrainer/ModelNameConstrainer.ts @@ -4,8 +4,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedCSharpKeyword } from '../Constants'; +import { CSharpModelNameConstraint } from '../CSharpGenerator'; export type ModelNameConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -35,7 +36,7 @@ export const DefaultModelNameConstraints: ModelNameConstraints = { }; export function defaultModelNameConstraints( customConstraints?: Partial -): ModelNameConstraint { +): CSharpModelNameConstraint { const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; return ({ modelName }) => { diff --git a/src/generators/csharp/constrainer/PropertyKeyConstrainer.ts b/src/generators/csharp/constrainer/PropertyKeyConstrainer.ts index 647d4398d8..5bd1ed9651 100644 --- a/src/generators/csharp/constrainer/PropertyKeyConstrainer.ts +++ b/src/generators/csharp/constrainer/PropertyKeyConstrainer.ts @@ -5,8 +5,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedCSharpKeyword } from '../Constants'; +import { CSharpPropertyKeyConstraint } from '../CSharpGenerator'; export type PropertyKeyConstraintOptions = { NO_SPECIAL_CHAR: (value: string) => string; @@ -51,7 +52,7 @@ export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { export function defaultPropertyKeyConstraints( customConstraints?: Partial -): PropertyKeyConstraint { +): CSharpPropertyKeyConstraint { const constraints = { ...DefaultPropertyKeyConstraints, ...customConstraints diff --git a/src/generators/csharp/presets/NewtonsoftSerializerPreset.ts b/src/generators/csharp/presets/NewtonsoftSerializerPreset.ts index 648e948f4c..8c803ae484 100644 --- a/src/generators/csharp/presets/NewtonsoftSerializerPreset.ts +++ b/src/generators/csharp/presets/NewtonsoftSerializerPreset.ts @@ -29,8 +29,7 @@ function renderSerialize({ model }: { model: ConstrainedObjectModel }): string { var stringEnumValue = enumValue.ToString(); // C# converts booleans to uppercase True and False, which newtonsoft cannot understand var jsonStringCompliant = stringEnumValue == "True" || stringEnumValue == "False" ? stringEnumValue.ToLower() : stringEnumValue; -var jsonToken = JToken.Parse(jsonStringCompliant); -jo.Add("${prop.unconstrainedPropertyName}", jsonToken);`; +jo.Add("${prop.unconstrainedPropertyName}", JToken.FromObject(jsonStringCompliant, serializer));`; } return `if (value.${propertyAccessor} != null) { @@ -93,7 +92,7 @@ function renderDeserialize({ prop.property instanceof ConstrainedReferenceModel && prop.property.ref instanceof ConstrainedEnumModel ) { - toValue = `${prop.property.name}Extensions.To${prop.property.name}(jo["${prop.unconstrainedPropertyName}"])`; + toValue = `${prop.property.name}Extensions.To${prop.property.name}(jo["${prop.unconstrainedPropertyName}"].ToString())`; } return `if(jo["${prop.unconstrainedPropertyName}"] != null) { value.${propertyAccessor} = ${toValue}; diff --git a/src/generators/dart/DartConstrainer.ts b/src/generators/dart/DartConstrainer.ts index cb154fb017..6c1397cf37 100644 --- a/src/generators/dart/DartConstrainer.ts +++ b/src/generators/dart/DartConstrainer.ts @@ -1,4 +1,3 @@ -import { Constraints } from '../../helpers'; import { defaultEnumKeyConstraints, defaultEnumValueConstraints @@ -71,7 +70,7 @@ export const DartDefaultTypeMapping: DartTypeMapping = { } }; -export const DartDefaultConstraints: Constraints = { +export const DartDefaultConstraints = { enumKey: defaultEnumKeyConstraints(), enumValue: defaultEnumValueConstraints(), modelName: defaultModelNameConstraints(), diff --git a/src/generators/dart/DartFileGenerator.ts b/src/generators/dart/DartFileGenerator.ts index f00a800a53..f85d0c374a 100644 --- a/src/generators/dart/DartFileGenerator.ts +++ b/src/generators/dart/DartFileGenerator.ts @@ -17,7 +17,7 @@ export class DartFileGenerator * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. */ public async generateToFiles( - input: Record | InputMetaModel, + input: any | InputMetaModel, outputDirectory: string, options: DartRenderCompleteModelOptions, ensureFilesWritten = false diff --git a/src/generators/dart/DartGenerator.ts b/src/generators/dart/DartGenerator.ts index c924f851ca..08885ba0f5 100644 --- a/src/generators/dart/DartGenerator.ts +++ b/src/generators/dart/DartGenerator.ts @@ -14,8 +14,13 @@ import { InputMetaModel } from '../../models'; import { + ConstantConstraint, constrainMetaModel, Constraints, + EnumKeyConstraint, + EnumValueConstraint, + ModelNameConstraint, + PropertyKeyConstraint, split, SplitOptions, TypeMapping @@ -34,8 +39,13 @@ import { DartDependencyManager } from './DartDependencyManager'; export interface DartOptions extends CommonGeneratorOptions { collectionType?: 'List'; typeMapping: TypeMapping; - constraints: Constraints; + constraints: Constraints; } +export type DartConstantConstraint = ConstantConstraint; +export type DartEnumKeyConstraint = EnumKeyConstraint; +export type DartEnumValueConstraint = EnumValueConstraint; +export type DartModelNameConstraint = ModelNameConstraint; +export type DartPropertyKeyConstraint = PropertyKeyConstraint; export type DartTypeMapping = TypeMapping; export interface DartRenderCompleteModelOptions { diff --git a/src/generators/dart/constrainer/ConstantConstrainer.ts b/src/generators/dart/constrainer/ConstantConstrainer.ts index 2c076ad46d..97938716cf 100644 --- a/src/generators/dart/constrainer/ConstantConstrainer.ts +++ b/src/generators/dart/constrainer/ConstantConstrainer.ts @@ -1,6 +1,6 @@ -import { ConstantConstraint } from '../../../helpers'; +import { DartConstantConstraint } from '../DartGenerator'; -export function defaultConstantConstraints(): ConstantConstraint { +export function defaultConstantConstraints(): DartConstantConstraint { return () => { return undefined; }; diff --git a/src/generators/dart/constrainer/EnumConstrainer.ts b/src/generators/dart/constrainer/EnumConstrainer.ts index 1d27cbe0f8..45ae32c88e 100644 --- a/src/generators/dart/constrainer/EnumConstrainer.ts +++ b/src/generators/dart/constrainer/EnumConstrainer.ts @@ -6,12 +6,12 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { - EnumKeyConstraint, - EnumValueConstraint, - FormatHelpers -} from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedDartKeyword } from '../Constants'; +import { + DartEnumKeyConstraint, + DartEnumValueConstraint +} from '../DartGenerator'; export type ModelEnumKeyConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -45,7 +45,7 @@ export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { export function defaultEnumKeyConstraints( customConstraints?: Partial -): EnumKeyConstraint { +): DartEnumKeyConstraint { const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; return ({ enumKey, enumModel, constrainedEnumModel }) => { @@ -68,7 +68,7 @@ export function defaultEnumKeyConstraints( }; } -export function defaultEnumValueConstraints(): EnumValueConstraint { +export function defaultEnumValueConstraints(): DartEnumValueConstraint { return ({ enumValue }) => { let constrainedEnumValue = enumValue; switch (typeof enumValue) { diff --git a/src/generators/dart/constrainer/ModelNameConstrainer.ts b/src/generators/dart/constrainer/ModelNameConstrainer.ts index 95acebaad6..c90040be6b 100644 --- a/src/generators/dart/constrainer/ModelNameConstrainer.ts +++ b/src/generators/dart/constrainer/ModelNameConstrainer.ts @@ -4,8 +4,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedDartKeyword } from '../Constants'; +import { DartModelNameConstraint } from '../DartGenerator'; export type ModelNameConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -34,7 +35,7 @@ export const DefaultModelNameConstraints: ModelNameConstraints = { export function defaultModelNameConstraints( customConstraints?: Partial -): ModelNameConstraint { +): DartModelNameConstraint { const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; return ({ modelName }) => { diff --git a/src/generators/dart/constrainer/PropertyKeyConstrainer.ts b/src/generators/dart/constrainer/PropertyKeyConstrainer.ts index 6b14ab2452..06abcf4eec 100644 --- a/src/generators/dart/constrainer/PropertyKeyConstrainer.ts +++ b/src/generators/dart/constrainer/PropertyKeyConstrainer.ts @@ -5,8 +5,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedDartKeyword } from '../Constants'; +import { DartPropertyKeyConstraint } from '../DartGenerator'; export type PropertyKeyConstraintOptions = { NO_SPECIAL_CHAR: (value: string) => string; @@ -42,7 +43,7 @@ export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { export function defaultPropertyKeyConstraints( customConstraints?: Partial -): PropertyKeyConstraint { +): DartPropertyKeyConstraint { const constraints = { ...DefaultPropertyKeyConstraints, ...customConstraints diff --git a/src/generators/go/GoConstrainer.ts b/src/generators/go/GoConstrainer.ts index b04f8cacc6..d634c12577 100644 --- a/src/generators/go/GoConstrainer.ts +++ b/src/generators/go/GoConstrainer.ts @@ -1,4 +1,3 @@ -import { Constraints } from '../../helpers'; import { defaultEnumKeyConstraints, defaultEnumValueConstraints @@ -49,7 +48,7 @@ export const GoDefaultTypeMapping: GoTypeMapping = { } }; -export const GoDefaultConstraints: Constraints = { +export const GoDefaultConstraints = { enumKey: defaultEnumKeyConstraints(), enumValue: defaultEnumValueConstraints(), modelName: defaultModelNameConstraints(), diff --git a/src/generators/go/GoFileGenerator.ts b/src/generators/go/GoFileGenerator.ts index 785a47a0de..0324932955 100644 --- a/src/generators/go/GoFileGenerator.ts +++ b/src/generators/go/GoFileGenerator.ts @@ -17,7 +17,7 @@ export class GoFileGenerator * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. */ public async generateToFiles( - input: Record | InputMetaModel, + input: any | InputMetaModel, outputDirectory: string, options: GoRenderCompleteModelOptions, ensureFilesWritten = false diff --git a/src/generators/go/GoGenerator.ts b/src/generators/go/GoGenerator.ts index 8fc5f5b3d3..12fbf55107 100644 --- a/src/generators/go/GoGenerator.ts +++ b/src/generators/go/GoGenerator.ts @@ -14,8 +14,13 @@ import { MetaModel } from '../../models'; import { + ConstantConstraint, constrainMetaModel, Constraints, + EnumKeyConstraint, + EnumValueConstraint, + ModelNameConstraint, + PropertyKeyConstraint, split, SplitOptions, TypeMapping @@ -30,8 +35,13 @@ import { GoDependencyManager } from './GoDependencyManager'; export interface GoOptions extends CommonGeneratorOptions { typeMapping: TypeMapping; - constraints: Constraints; + constraints: Constraints; } +export type GoConstantConstraint = ConstantConstraint; +export type GoEnumKeyConstraint = EnumKeyConstraint; +export type GoEnumValueConstraint = EnumValueConstraint; +export type GoModelNameConstraint = ModelNameConstraint; +export type GoPropertyKeyConstraint = PropertyKeyConstraint; export type GoTypeMapping = TypeMapping; export interface GoRenderCompleteModelOptions { diff --git a/src/generators/go/constrainer/ConstantConstrainer.ts b/src/generators/go/constrainer/ConstantConstrainer.ts index 2c076ad46d..d841ca4ef6 100644 --- a/src/generators/go/constrainer/ConstantConstrainer.ts +++ b/src/generators/go/constrainer/ConstantConstrainer.ts @@ -1,6 +1,6 @@ -import { ConstantConstraint } from '../../../helpers'; +import { GoConstantConstraint } from '../GoGenerator'; -export function defaultConstantConstraints(): ConstantConstraint { +export function defaultConstantConstraints(): GoConstantConstraint { return () => { return undefined; }; diff --git a/src/generators/go/constrainer/EnumConstrainer.ts b/src/generators/go/constrainer/EnumConstrainer.ts index 6eb0384e9c..c52815e2e7 100644 --- a/src/generators/go/constrainer/EnumConstrainer.ts +++ b/src/generators/go/constrainer/EnumConstrainer.ts @@ -5,12 +5,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { - FormatHelpers, - EnumKeyConstraint, - EnumValueConstraint -} from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedGoKeyword } from '../Constants'; +import { GoEnumKeyConstraint, GoEnumValueConstraint } from '../GoGenerator'; export type ModelEnumKeyConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -48,7 +45,7 @@ export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { export function defaultEnumKeyConstraints( customConstraints?: Partial -): EnumKeyConstraint { +): GoEnumKeyConstraint { const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; return ({ enumKey, enumModel, constrainedEnumModel }) => { @@ -80,7 +77,7 @@ export function defaultEnumKeyConstraints( }; } -export function defaultEnumValueConstraints(): EnumValueConstraint { +export function defaultEnumValueConstraints(): GoEnumValueConstraint { return ({ enumValue }) => { let constrainedEnumValue: any = JSON.stringify(enumValue); switch (typeof enumValue) { diff --git a/src/generators/go/constrainer/ModelNameConstrainer.ts b/src/generators/go/constrainer/ModelNameConstrainer.ts index 890eba255f..161b41f0dc 100644 --- a/src/generators/go/constrainer/ModelNameConstrainer.ts +++ b/src/generators/go/constrainer/ModelNameConstrainer.ts @@ -3,8 +3,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedGoKeyword } from '../Constants'; +import { GoModelNameConstraint } from '../GoGenerator'; export type ModelNameConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -35,7 +36,7 @@ export const DefaultModelNameConstraints: ModelNameConstraints = { export function defaultModelNameConstraints( customConstraints?: Partial -): ModelNameConstraint { +): GoModelNameConstraint { const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; return ({ modelName }) => { diff --git a/src/generators/go/constrainer/PropertyKeyConstrainer.ts b/src/generators/go/constrainer/PropertyKeyConstrainer.ts index bb040de0c7..b7c542f27c 100644 --- a/src/generators/go/constrainer/PropertyKeyConstrainer.ts +++ b/src/generators/go/constrainer/PropertyKeyConstrainer.ts @@ -5,8 +5,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedGoKeyword } from '../Constants'; +import { GoPropertyKeyConstraint } from '../GoGenerator'; export type PropertyKeyConstraintOptions = { NO_SPECIAL_CHAR: (value: string) => string; @@ -42,7 +43,7 @@ export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { export function defaultPropertyKeyConstraints( customConstraints?: Partial -): PropertyKeyConstraint { +): GoPropertyKeyConstraint { const constraints = { ...DefaultPropertyKeyConstraints, ...customConstraints diff --git a/src/generators/java/JavaConstrainer.ts b/src/generators/java/JavaConstrainer.ts index 2ea330c1bf..fc850c81cb 100644 --- a/src/generators/java/JavaConstrainer.ts +++ b/src/generators/java/JavaConstrainer.ts @@ -1,4 +1,3 @@ -import { Constraints } from '../../helpers'; import { ConstrainedEnumValueModel, ConstrainedMetaModel, @@ -178,6 +177,8 @@ export const JavaDefaultTypeMapping: JavaTypeMapping = { case 'dateTime': case 'date-time': return 'java.time.OffsetDateTime'; + case 'duration': + return 'java.time.Duration'; case 'binary': return 'byte[]'; default: @@ -238,7 +239,7 @@ export const JavaDefaultTypeMapping: JavaTypeMapping = { } }; -export const JavaDefaultConstraints: Constraints = { +export const JavaDefaultConstraints = { enumKey: defaultEnumKeyConstraints(), enumValue: defaultEnumValueConstraints(), modelName: defaultModelNameConstraints(), diff --git a/src/generators/java/JavaFileGenerator.ts b/src/generators/java/JavaFileGenerator.ts index e9c045fd0b..8aa224e40d 100644 --- a/src/generators/java/JavaFileGenerator.ts +++ b/src/generators/java/JavaFileGenerator.ts @@ -17,7 +17,7 @@ export class JavaFileGenerator * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. */ public async generateToFiles( - input: Record | InputMetaModel, + input: any | InputMetaModel, outputDirectory: string, options: JavaRenderCompleteModelOptions, ensureFilesWritten = false diff --git a/src/generators/java/JavaGenerator.ts b/src/generators/java/JavaGenerator.ts index 00407aed48..c2033ad93d 100644 --- a/src/generators/java/JavaGenerator.ts +++ b/src/generators/java/JavaGenerator.ts @@ -19,7 +19,12 @@ import { SplitOptions, TypeMapping, constrainMetaModel, - Constraints + Constraints, + ConstantConstraint, + EnumKeyConstraint, + EnumValueConstraint, + PropertyKeyConstraint, + ModelNameConstraint } from '../../helpers'; import { JavaPreset, JAVA_DEFAULT_PRESET } from './JavaPreset'; import { ClassRenderer } from './renderers/ClassRenderer'; @@ -38,8 +43,13 @@ import { UnionRenderer } from './renderers/UnionRenderer'; export interface JavaOptions extends CommonGeneratorOptions { collectionType: 'List' | 'Array'; typeMapping: TypeMapping; - constraints: Constraints; + constraints: Constraints; } +export type JavaConstantConstraint = ConstantConstraint; +export type JavaEnumKeyConstraint = EnumKeyConstraint; +export type JavaEnumValueConstraint = EnumValueConstraint; +export type JavaModelNameConstraint = ModelNameConstraint; +export type JavaPropertyKeyConstraint = PropertyKeyConstraint; export type JavaTypeMapping = TypeMapping; export interface JavaRenderCompleteModelOptions { packageName: string; diff --git a/src/generators/java/constrainer/ConstantConstrainer.ts b/src/generators/java/constrainer/ConstantConstrainer.ts index b21eaf5ba6..fc98b58410 100644 --- a/src/generators/java/constrainer/ConstantConstrainer.ts +++ b/src/generators/java/constrainer/ConstantConstrainer.ts @@ -5,8 +5,8 @@ import { ConstrainedReferenceModel, ConstrainedStringModel } from '../../../models'; -import { ConstantConstraint } from '../../../helpers'; import { JavaRenderer } from '../JavaRenderer'; +import { JavaConstantConstraint } from '../JavaGenerator'; const getConstrainedEnumModelConstant = (args: { constrainedMetaModel: ConstrainedMetaModel; @@ -22,7 +22,7 @@ const getConstrainedEnumModelConstant = (args: { } }; -export function defaultConstantConstraints(): ConstantConstraint { +export function defaultConstantConstraints(): JavaConstantConstraint { return ({ constrainedMetaModel }) => { const constOptions = constrainedMetaModel.options.const; diff --git a/src/generators/java/constrainer/EnumConstrainer.ts b/src/generators/java/constrainer/EnumConstrainer.ts index b427be75cd..836683c807 100644 --- a/src/generators/java/constrainer/EnumConstrainer.ts +++ b/src/generators/java/constrainer/EnumConstrainer.ts @@ -6,12 +6,12 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { - FormatHelpers, - EnumKeyConstraint, - EnumValueConstraint -} from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedJavaKeyword } from '../Constants'; +import { + JavaEnumKeyConstraint, + JavaEnumValueConstraint +} from '../JavaGenerator'; export type ModelEnumKeyConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -46,7 +46,7 @@ export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { export function defaultEnumKeyConstraints( customConstraints?: Partial -): EnumKeyConstraint { +): JavaEnumKeyConstraint { const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; return ({ enumKey, enumModel, constrainedEnumModel }) => { @@ -69,7 +69,7 @@ export function defaultEnumKeyConstraints( }; } -export function defaultEnumValueConstraints(): EnumValueConstraint { +export function defaultEnumValueConstraints(): JavaEnumValueConstraint { return ({ enumValue }) => { let constrainedEnumValue = enumValue; switch (typeof enumValue) { diff --git a/src/generators/java/constrainer/ModelNameConstrainer.ts b/src/generators/java/constrainer/ModelNameConstrainer.ts index 2411c1d15d..2f141ca357 100644 --- a/src/generators/java/constrainer/ModelNameConstrainer.ts +++ b/src/generators/java/constrainer/ModelNameConstrainer.ts @@ -3,8 +3,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedJavaKeyword } from '../Constants'; +import { JavaModelNameConstraint } from '../JavaGenerator'; export type ModelNameConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -34,7 +35,7 @@ export const DefaultModelNameConstraints: ModelNameConstraints = { }; export function defaultModelNameConstraints( customConstraints?: Partial -): ModelNameConstraint { +): JavaModelNameConstraint { const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; return ({ modelName }) => { diff --git a/src/generators/java/constrainer/PropertyKeyConstrainer.ts b/src/generators/java/constrainer/PropertyKeyConstrainer.ts index 2301051992..5a3c2d8ffe 100644 --- a/src/generators/java/constrainer/PropertyKeyConstrainer.ts +++ b/src/generators/java/constrainer/PropertyKeyConstrainer.ts @@ -5,8 +5,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedJavaKeyword } from '../Constants'; +import { JavaPropertyKeyConstraint } from '../JavaGenerator'; export type PropertyKeyConstraintOptions = { NO_SPECIAL_CHAR: (value: string) => string; @@ -42,7 +43,7 @@ export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { export function defaultPropertyKeyConstraints( customConstraints?: Partial -): PropertyKeyConstraint { +): JavaPropertyKeyConstraint { const constraints = { ...DefaultPropertyKeyConstraints, ...customConstraints diff --git a/src/generators/java/presets/CommonPreset.ts b/src/generators/java/presets/CommonPreset.ts index 936aaf7703..6858a477c3 100644 --- a/src/generators/java/presets/CommonPreset.ts +++ b/src/generators/java/presets/CommonPreset.ts @@ -194,6 +194,10 @@ function renderUnmarshalling({ export const JAVA_COMMON_PRESET: JavaPreset = { class: { additionalContent({ renderer, model, content, options }) { + if (model.options.isExtended) { + return ''; + } + options = options || {}; const blocks: string[] = []; const shouldContainEqual = diff --git a/src/generators/java/presets/ConstraintsPreset.ts b/src/generators/java/presets/ConstraintsPreset.ts index 7e684a262d..21745a3643 100644 --- a/src/generators/java/presets/ConstraintsPreset.ts +++ b/src/generators/java/presets/ConstraintsPreset.ts @@ -20,7 +20,11 @@ export const JAVA_CONSTRAINTS_PRESET: JavaPreset = { return content; }, // eslint-disable-next-line sonarjs/cognitive-complexity - property({ renderer, property, content }) { + property({ renderer, property, content, model }) { + if (model.options.isExtended) { + return ''; + } + const annotations: string[] = []; if (property.required) { diff --git a/src/generators/java/presets/DescriptionPreset.ts b/src/generators/java/presets/DescriptionPreset.ts index 81553237df..c1634d7c91 100644 --- a/src/generators/java/presets/DescriptionPreset.ts +++ b/src/generators/java/presets/DescriptionPreset.ts @@ -2,6 +2,7 @@ import { JavaRenderer } from '../JavaRenderer'; import { JavaPreset } from '../JavaPreset'; import { FormatHelpers } from '../../../helpers'; import { ConstrainedMetaModel } from '../../../models'; +import { isDiscriminatorOrDictionary } from '../renderers/ClassRenderer'; function renderDescription({ renderer, @@ -38,7 +39,14 @@ export const JAVA_DESCRIPTION_PRESET: JavaPreset = { self({ renderer, model, content }) { return renderDescription({ renderer, content, item: model }); }, - getter({ renderer, property, content }) { + getter({ renderer, property, content, model }) { + if ( + model.options.isExtended && + isDiscriminatorOrDictionary(model, property) + ) { + return ''; + } + return renderDescription({ renderer, content, item: property.property }); } }, diff --git a/src/generators/java/presets/JacksonPreset.ts b/src/generators/java/presets/JacksonPreset.ts index c0a95ed026..f72147b79d 100644 --- a/src/generators/java/presets/JacksonPreset.ts +++ b/src/generators/java/presets/JacksonPreset.ts @@ -19,7 +19,11 @@ export const JAVA_JACKSON_PRESET: JavaPreset = { renderer.dependencyManager.addDependency(JACKSON_ANNOTATION_DEPENDENCY); return content; }, - property({ renderer, property, content }) { + property({ renderer, property, content, model }) { + if (model.options.isExtended) { + return ''; + } + //Properties that are dictionaries with unwrapped options, cannot get the annotation because it cannot be accurately unwrapped by the jackson library. const isDictionary = property.property instanceof ConstrainedDictionaryModel; diff --git a/src/generators/java/renderers/ClassRenderer.ts b/src/generators/java/renderers/ClassRenderer.ts index 072408b3fd..7f33076523 100644 --- a/src/generators/java/renderers/ClassRenderer.ts +++ b/src/generators/java/renderers/ClassRenderer.ts @@ -3,6 +3,7 @@ import { ConstrainedDictionaryModel, ConstrainedObjectModel, ConstrainedObjectPropertyModel, + ConstrainedReferenceModel, ConstrainedUnionModel } from '../../../models'; import { FormatHelpers } from '../../../helpers'; @@ -31,15 +32,25 @@ export class ClassRenderer extends JavaRenderer { this.dependencyManager.addDependency('import java.util.Map;'); } - const parentUnions = this.getParentUnions(); + if (this.model.options.isExtended) { + return `public interface ${this.model.name} { +${this.indent(this.renderBlock(content, 2))} +}`; + } - if (parentUnions) { - for (const parentUnion of parentUnions) { - this.dependencyManager.addModelDependency(parentUnion); + const parentUnions = this.getParentUnions(); + const extend = this.model.options.extend?.filter( + (extend) => extend.options.isExtended + ); + const implement = [...(parentUnions ?? []), ...(extend ?? [])]; + + if (implement.length) { + for (const i of implement) { + this.dependencyManager.addModelDependency(i); } - return `public class ${this.model.name} implements ${parentUnions - .map((pu) => pu.name) + return `public class ${this.model.name} implements ${implement + .map((i) => i.name) .join(', ')} { ${this.indent(this.renderBlock(content, 2))} }`; @@ -121,28 +132,95 @@ ${this.indent(this.renderBlock(content, 2))} } } +const getOverride = ( + model: ConstrainedObjectModel, + property: ConstrainedObjectPropertyModel +) => { + const isOverride = model.options.extend?.find((extend) => { + if ( + !extend.options.isExtended || + isDiscriminatorOrDictionary(model, property) + ) { + return false; + } + + if ( + extend instanceof ConstrainedObjectModel && + extend.properties[property.propertyName] + ) { + return true; + } + + if ( + extend instanceof ConstrainedReferenceModel && + extend.ref instanceof ConstrainedObjectModel && + extend.ref.properties[property.propertyName] + ) { + return true; + } + }); + + return isOverride ? '@Override\n' : ''; +}; + +export const isDiscriminatorOrDictionary = ( + model: ConstrainedObjectModel, + property: ConstrainedObjectPropertyModel +): boolean => + model.options.discriminator?.discriminator === + property.unconstrainedPropertyName || + property.property instanceof ConstrainedDictionaryModel; + export const JAVA_DEFAULT_CLASS_PRESET: ClassPresetType = { self({ renderer }) { return renderer.defaultSelf(); }, - property({ property }) { + property({ property, model }) { + if (model.options.isExtended) { + return ''; + } + if (property.property.options.const?.value) { return `private final ${property.property.type} ${property.propertyName} = ${property.property.options.const.value};`; } return `private ${property.property.type} ${property.propertyName};`; }, - getter({ property }) { + getter({ property, model }) { const getterName = `get${FormatHelpers.toPascalCase( property.propertyName )}`; - return `public ${property.property.type} ${getterName}() { return this.${property.propertyName}; }`; + + if (model.options.isExtended) { + if (isDiscriminatorOrDictionary(model, property)) { + return ''; + } + + return `public ${property.property.type} ${getterName}();`; + } + + return `${getOverride(model, property)}public ${ + property.property.type + } ${getterName}() { return this.${property.propertyName}; }`; }, - setter({ property }) { + setter({ property, model }) { if (property.property.options.const?.value) { return ''; } const setterName = FormatHelpers.toPascalCase(property.propertyName); - return `public void set${setterName}(${property.property.type} ${property.propertyName}) { this.${property.propertyName} = ${property.propertyName}; }`; + + if (model.options.isExtended) { + if (isDiscriminatorOrDictionary(model, property)) { + return ''; + } + + return `public void set${setterName}(${property.property.type} ${property.propertyName});`; + } + + return `${getOverride(model, property)}public void set${setterName}(${ + property.property.type + } ${property.propertyName}) { this.${property.propertyName} = ${ + property.propertyName + }; }`; } }; diff --git a/src/generators/javascript/JavaScriptConstrainer.ts b/src/generators/javascript/JavaScriptConstrainer.ts index 9b579d0048..e8173ad005 100644 --- a/src/generators/javascript/JavaScriptConstrainer.ts +++ b/src/generators/javascript/JavaScriptConstrainer.ts @@ -4,7 +4,6 @@ import { defaultEnumKeyConstraints, defaultEnumValueConstraints } from './constrainer/EnumConstrainer'; -import { Constraints } from '../../helpers'; import { JavaScriptTypeMapping } from './JavaScriptGenerator'; import { defaultConstantConstraints } from './constrainer/ConstantConstrainer'; @@ -47,7 +46,7 @@ export const JavaScriptDefaultTypeMapping: JavaScriptTypeMapping = { } }; -export const JavaScriptDefaultConstraints: Constraints = { +export const JavaScriptDefaultConstraints = { enumKey: defaultEnumKeyConstraints(), enumValue: defaultEnumValueConstraints(), modelName: defaultModelNameConstraints(), diff --git a/src/generators/javascript/JavaScriptFileGenerator.ts b/src/generators/javascript/JavaScriptFileGenerator.ts index ddebdaae5d..cadfdc6406 100644 --- a/src/generators/javascript/JavaScriptFileGenerator.ts +++ b/src/generators/javascript/JavaScriptFileGenerator.ts @@ -17,7 +17,7 @@ export class JavaScriptFileGenerator * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. */ public async generateToFiles( - input: Record | InputMetaModel, + input: any | InputMetaModel, outputDirectory: string, options?: JavaScriptRenderCompleteModelOptions, ensureFilesWritten = false diff --git a/src/generators/javascript/JavaScriptGenerator.ts b/src/generators/javascript/JavaScriptGenerator.ts index 84b8d9cd32..c665555347 100644 --- a/src/generators/javascript/JavaScriptGenerator.ts +++ b/src/generators/javascript/JavaScriptGenerator.ts @@ -17,7 +17,12 @@ import { Constraints, split, constrainMetaModel, - SplitOptions + SplitOptions, + ConstantConstraint, + EnumKeyConstraint, + EnumValueConstraint, + ModelNameConstraint, + PropertyKeyConstraint } from '../../helpers'; import { JavaScriptPreset, JS_DEFAULT_PRESET } from './JavaScriptPreset'; import { ClassRenderer } from './renderers/ClassRenderer'; @@ -31,9 +36,18 @@ import { JavaScriptDependencyManager } from './JavaScriptDependencyManager'; export interface JavaScriptOptions extends CommonGeneratorOptions { typeMapping: TypeMapping; - constraints: Constraints; + constraints: Constraints; moduleSystem: 'ESM' | 'CJS'; } +export type JavaScriptConstantConstraint = + ConstantConstraint; +export type JavaScriptEnumKeyConstraint = EnumKeyConstraint; +export type JavaScriptEnumValueConstraint = + EnumValueConstraint; +export type JavaScriptModelNameConstraint = + ModelNameConstraint; +export type JavaScriptPropertyKeyConstraint = + PropertyKeyConstraint; export type JavaScriptTypeMapping = TypeMapping< JavaScriptOptions, JavaScriptDependencyManager diff --git a/src/generators/javascript/constrainer/ConstantConstrainer.ts b/src/generators/javascript/constrainer/ConstantConstrainer.ts index 2c076ad46d..030ca50470 100644 --- a/src/generators/javascript/constrainer/ConstantConstrainer.ts +++ b/src/generators/javascript/constrainer/ConstantConstrainer.ts @@ -1,6 +1,6 @@ -import { ConstantConstraint } from '../../../helpers'; +import { JavaScriptConstantConstraint } from '../JavaScriptGenerator'; -export function defaultConstantConstraints(): ConstantConstraint { +export function defaultConstantConstraints(): JavaScriptConstantConstraint { return () => { return undefined; }; diff --git a/src/generators/javascript/constrainer/EnumConstrainer.ts b/src/generators/javascript/constrainer/EnumConstrainer.ts index 46e230ea69..4f54000627 100644 --- a/src/generators/javascript/constrainer/EnumConstrainer.ts +++ b/src/generators/javascript/constrainer/EnumConstrainer.ts @@ -1,14 +1,17 @@ -import { EnumKeyConstraint, EnumValueConstraint } from '../../../helpers'; +import { + JavaScriptEnumKeyConstraint, + JavaScriptEnumValueConstraint +} from '../JavaScriptGenerator'; /** * Enums for JS do not have any constraints because we never render anything specific for enums. **/ -export function defaultEnumKeyConstraints(): EnumKeyConstraint { +export function defaultEnumKeyConstraints(): JavaScriptEnumKeyConstraint { return ({ enumKey }) => { return enumKey; }; } -export function defaultEnumValueConstraints(): EnumValueConstraint { +export function defaultEnumValueConstraints(): JavaScriptEnumValueConstraint { return ({ enumValue }) => { return enumValue; }; diff --git a/src/generators/javascript/constrainer/ModelNameConstrainer.ts b/src/generators/javascript/constrainer/ModelNameConstrainer.ts index b016689ae9..2551e783d5 100644 --- a/src/generators/javascript/constrainer/ModelNameConstrainer.ts +++ b/src/generators/javascript/constrainer/ModelNameConstrainer.ts @@ -3,8 +3,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedJavaScriptKeyword } from '../Constants'; +import { JavaScriptModelNameConstraint } from '../JavaScriptGenerator'; export type ModelNameConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -34,7 +35,7 @@ export const DefaultModelNameConstraints: ModelNameConstraints = { }; export function defaultModelNameConstraints( customConstraints?: Partial -): ModelNameConstraint { +): JavaScriptModelNameConstraint { const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; return ({ modelName }) => { diff --git a/src/generators/javascript/constrainer/PropertyKeyConstrainer.ts b/src/generators/javascript/constrainer/PropertyKeyConstrainer.ts index 5299519ea5..190ab233b7 100644 --- a/src/generators/javascript/constrainer/PropertyKeyConstrainer.ts +++ b/src/generators/javascript/constrainer/PropertyKeyConstrainer.ts @@ -5,8 +5,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedJavaScriptKeyword } from '../Constants'; +import { JavaScriptPropertyKeyConstraint } from '../JavaScriptGenerator'; export type PropertyKeyConstraintOptions = { NO_SPECIAL_CHAR: (value: string) => string; @@ -42,7 +43,7 @@ export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { export function defaultPropertyKeyConstraints( customConstraints?: Partial -): PropertyKeyConstraint { +): JavaScriptPropertyKeyConstraint { const constraints = { ...DefaultPropertyKeyConstraints, ...customConstraints diff --git a/src/generators/kotlin/KotlinConstrainer.ts b/src/generators/kotlin/KotlinConstrainer.ts index d85d053e0d..686f483460 100644 --- a/src/generators/kotlin/KotlinConstrainer.ts +++ b/src/generators/kotlin/KotlinConstrainer.ts @@ -1,4 +1,3 @@ -import { Constraints } from '../../helpers'; import { ConstrainedEnumValueModel } from '../../models'; import { defaultEnumKeyConstraints, @@ -147,7 +146,7 @@ export const KotlinDefaultTypeMapping: KotlinTypeMapping = { } }; -export const KotlinDefaultConstraints: Constraints = { +export const KotlinDefaultConstraints = { enumKey: defaultEnumKeyConstraints(), enumValue: defaultEnumValueConstraints(), modelName: defaultModelNameConstraints(), diff --git a/src/generators/kotlin/KotlinFileGenerator.ts b/src/generators/kotlin/KotlinFileGenerator.ts index e0686ecae9..446a08738b 100644 --- a/src/generators/kotlin/KotlinFileGenerator.ts +++ b/src/generators/kotlin/KotlinFileGenerator.ts @@ -17,7 +17,7 @@ export class KotlinFileGenerator * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. */ public async generateToFiles( - input: Record | InputMetaModel, + input: any | InputMetaModel, outputDirectory: string, options: KotlinRenderCompleteModelOptions, ensureFilesWritten = false diff --git a/src/generators/kotlin/KotlinGenerator.ts b/src/generators/kotlin/KotlinGenerator.ts index c9b5ca1523..bc00a3186a 100644 --- a/src/generators/kotlin/KotlinGenerator.ts +++ b/src/generators/kotlin/KotlinGenerator.ts @@ -20,8 +20,13 @@ import { EnumRenderer } from './renderers/EnumRenderer'; import { isReservedKotlinKeyword } from './Constants'; import { Logger } from '../..'; import { + ConstantConstraint, constrainMetaModel, - Constraints + Constraints, + EnumKeyConstraint, + EnumValueConstraint, + ModelNameConstraint, + PropertyKeyConstraint } from '../../helpers/ConstrainHelpers'; import { KotlinDefaultConstraints, @@ -32,9 +37,14 @@ import { KotlinDependencyManager } from './KotlinDependencyManager'; export interface KotlinOptions extends CommonGeneratorOptions { typeMapping: TypeMapping; - constraints: Constraints; + constraints: Constraints; collectionType: 'List' | 'Array'; } +export type KotlinConstantConstraint = ConstantConstraint; +export type KotlinEnumKeyConstraint = EnumKeyConstraint; +export type KotlinEnumValueConstraint = EnumValueConstraint; +export type KotlinModelNameConstraint = ModelNameConstraint; +export type KotlinPropertyKeyConstraint = PropertyKeyConstraint; export type KotlinTypeMapping = TypeMapping< KotlinOptions, KotlinDependencyManager diff --git a/src/generators/kotlin/constrainer/ConstantConstrainer.ts b/src/generators/kotlin/constrainer/ConstantConstrainer.ts index 2c076ad46d..f6696e95c3 100644 --- a/src/generators/kotlin/constrainer/ConstantConstrainer.ts +++ b/src/generators/kotlin/constrainer/ConstantConstrainer.ts @@ -1,6 +1,6 @@ -import { ConstantConstraint } from '../../../helpers'; +import { KotlinConstantConstraint } from '../KotlinGenerator'; -export function defaultConstantConstraints(): ConstantConstraint { +export function defaultConstantConstraints(): KotlinConstantConstraint { return () => { return undefined; }; diff --git a/src/generators/kotlin/constrainer/EnumConstrainer.ts b/src/generators/kotlin/constrainer/EnumConstrainer.ts index 62bad7b4b3..3cd28d2ea8 100644 --- a/src/generators/kotlin/constrainer/EnumConstrainer.ts +++ b/src/generators/kotlin/constrainer/EnumConstrainer.ts @@ -5,11 +5,13 @@ import { NO_DUPLICATE_ENUM_KEYS, NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS, - FormatHelpers, - EnumKeyConstraint, - EnumValueConstraint + FormatHelpers } from '../../../helpers'; import { isInvalidKotlinEnumKey } from '../Constants'; +import { + KotlinEnumKeyConstraint, + KotlinEnumValueConstraint +} from '../KotlinGenerator'; export type ModelEnumKeyConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -44,7 +46,7 @@ export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { export function defaultEnumKeyConstraints( customConstraints?: Partial -): EnumKeyConstraint { +): KotlinEnumKeyConstraint { const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; return ({ enumKey, enumModel, constrainedEnumModel }) => { @@ -67,7 +69,7 @@ export function defaultEnumKeyConstraints( }; } -export function defaultEnumValueConstraints(): EnumValueConstraint { +export function defaultEnumValueConstraints(): KotlinEnumValueConstraint { return ({ enumValue }) => { switch (typeof enumValue) { case 'string': diff --git a/src/generators/kotlin/constrainer/ModelNameConstrainer.ts b/src/generators/kotlin/constrainer/ModelNameConstrainer.ts index 6477940ae5..a1ed73a8fc 100644 --- a/src/generators/kotlin/constrainer/ModelNameConstrainer.ts +++ b/src/generators/kotlin/constrainer/ModelNameConstrainer.ts @@ -3,8 +3,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedKotlinKeyword } from '../Constants'; +import { KotlinModelNameConstraint } from '../KotlinGenerator'; export type ModelNameConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -35,7 +36,7 @@ export const DefaultModelNameConstraints: ModelNameConstraints = { export function defaultModelNameConstraints( customConstraints?: Partial -): ModelNameConstraint { +): KotlinModelNameConstraint { const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; return ({ modelName }) => { diff --git a/src/generators/kotlin/constrainer/PropertyKeyConstrainer.ts b/src/generators/kotlin/constrainer/PropertyKeyConstrainer.ts index 6ae658ff4c..27733491ba 100644 --- a/src/generators/kotlin/constrainer/PropertyKeyConstrainer.ts +++ b/src/generators/kotlin/constrainer/PropertyKeyConstrainer.ts @@ -5,8 +5,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedKotlinKeyword } from '../Constants'; +import { KotlinPropertyKeyConstraint } from '../KotlinGenerator'; export type PropertyKeyConstraintOptions = { NO_SPECIAL_CHAR: (value: string) => string; @@ -42,7 +43,7 @@ export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { export function defaultPropertyKeyConstraints( customConstraints?: Partial -): PropertyKeyConstraint { +): KotlinPropertyKeyConstraint { const constraints = { ...DefaultPropertyKeyConstraints, ...customConstraints diff --git a/src/generators/php/PhpFileGenerator.ts b/src/generators/php/PhpFileGenerator.ts index 377ba57cfe..90dc949295 100644 --- a/src/generators/php/PhpFileGenerator.ts +++ b/src/generators/php/PhpFileGenerator.ts @@ -17,7 +17,7 @@ export class PhpFileGenerator * @param ensureFilesWritten */ public async generateToFiles( - input: Record | InputMetaModel, + input: any | InputMetaModel, outputDirectory: string, options?: PhpRenderCompleteModelOptions, ensureFilesWritten = false diff --git a/src/generators/php/PhpGenerator.ts b/src/generators/php/PhpGenerator.ts index 833b9ebfdc..fa19930ab9 100644 --- a/src/generators/php/PhpGenerator.ts +++ b/src/generators/php/PhpGenerator.ts @@ -20,8 +20,13 @@ import { EnumRenderer } from './renderers/EnumRenderer'; import { isReservedPhpKeyword } from './Constants'; import { Logger } from '../..'; import { + ConstantConstraint, constrainMetaModel, - Constraints + Constraints, + EnumKeyConstraint, + EnumValueConstraint, + ModelNameConstraint, + PropertyKeyConstraint } from '../../helpers/ConstrainHelpers'; import { PhpDefaultConstraints, PhpDefaultTypeMapping } from './PhpConstrainer'; import { DeepPartial, mergePartialAndDefault } from '../../utils/Partials'; @@ -29,8 +34,13 @@ import { PhpDependencyManager } from './PhpDependencyManager'; export interface PhpOptions extends CommonGeneratorOptions { typeMapping: TypeMapping; - constraints: Constraints; + constraints: Constraints; } +export type PhpConstantConstraint = ConstantConstraint; +export type PhpEnumKeyConstraint = EnumKeyConstraint; +export type PhpEnumValueConstraint = EnumValueConstraint; +export type PhpModelNameConstraint = ModelNameConstraint; +export type PhpPropertyKeyConstraint = PropertyKeyConstraint; export interface PhpRenderCompleteModelOptions { namespace: string; declareStrictTypes: boolean; diff --git a/src/generators/php/constrainer/ConstantConstrainer.ts b/src/generators/php/constrainer/ConstantConstrainer.ts index 2c076ad46d..25112e2cfe 100644 --- a/src/generators/php/constrainer/ConstantConstrainer.ts +++ b/src/generators/php/constrainer/ConstantConstrainer.ts @@ -1,6 +1,6 @@ -import { ConstantConstraint } from '../../../helpers'; +import { PhpConstantConstraint } from '../PhpGenerator'; -export function defaultConstantConstraints(): ConstantConstraint { +export function defaultConstantConstraints(): PhpConstantConstraint { return () => { return undefined; }; diff --git a/src/generators/php/constrainer/EnumConstrainer.ts b/src/generators/php/constrainer/EnumConstrainer.ts index 49b23bae76..f73be2ff4d 100644 --- a/src/generators/php/constrainer/EnumConstrainer.ts +++ b/src/generators/php/constrainer/EnumConstrainer.ts @@ -6,12 +6,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { - FormatHelpers, - EnumKeyConstraint, - EnumValueConstraint -} from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedPhpKeyword } from '../Constants'; +import { PhpEnumKeyConstraint, PhpEnumValueConstraint } from '../PhpGenerator'; export type ModelEnumKeyConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -50,7 +47,7 @@ export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { */ export function defaultEnumKeyConstraints( customConstraints?: Partial -): EnumKeyConstraint { +): PhpEnumKeyConstraint { const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; return ({ enumKey, enumModel, constrainedEnumModel }) => { @@ -76,7 +73,7 @@ export function defaultEnumKeyConstraints( /** * Convert the enum value to a value that is compatible with Php */ -export function defaultEnumValueConstraints(): EnumValueConstraint { +export function defaultEnumValueConstraints(): PhpEnumValueConstraint { return ({ enumValue }) => { let constrainedEnumValue = enumValue; switch (typeof enumValue) { diff --git a/src/generators/php/constrainer/ModelNameConstrainer.ts b/src/generators/php/constrainer/ModelNameConstrainer.ts index 004d6b2d10..dab67c12e4 100644 --- a/src/generators/php/constrainer/ModelNameConstrainer.ts +++ b/src/generators/php/constrainer/ModelNameConstrainer.ts @@ -3,8 +3,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedPhpKeyword } from '../Constants'; +import { PhpModelNameConstraint } from '../PhpGenerator'; export type ModelNameConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -38,7 +39,7 @@ export const DefaultModelNameConstraints: ModelNameConstraints = { */ export function defaultModelNameConstraints( customConstraints?: Partial -): ModelNameConstraint { +): PhpModelNameConstraint { const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; return ({ modelName }) => { diff --git a/src/generators/php/constrainer/PropertyKeyConstrainer.ts b/src/generators/php/constrainer/PropertyKeyConstrainer.ts index 565b824058..251f6969c2 100644 --- a/src/generators/php/constrainer/PropertyKeyConstrainer.ts +++ b/src/generators/php/constrainer/PropertyKeyConstrainer.ts @@ -5,8 +5,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedPhpKeyword } from '../Constants'; +import { PhpPropertyKeyConstraint } from '../PhpGenerator'; export type PropertyKeyConstraintOptions = { NO_SPECIAL_CHAR: (value: string) => string; @@ -44,7 +45,7 @@ export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { */ export function defaultPropertyKeyConstraints( customConstraints?: Partial -): PropertyKeyConstraint { +): PhpPropertyKeyConstraint { const constraints = { ...DefaultPropertyKeyConstraints, ...customConstraints diff --git a/src/generators/python/PythonConstrainer.ts b/src/generators/python/PythonConstrainer.ts index e1328c3ac4..26ff8aea7a 100644 --- a/src/generators/python/PythonConstrainer.ts +++ b/src/generators/python/PythonConstrainer.ts @@ -6,7 +6,7 @@ import { import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer'; import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer'; import { defaultConstantConstraints } from './constrainer/ConstantConstrainer'; -import { PythonTypeMapping } from './PythonGenerator'; +import { PythonOptions, PythonTypeMapping } from './PythonGenerator'; export const PythonDefaultTypeMapping: PythonTypeMapping = { Object({ constrainedModel }): string { @@ -55,7 +55,7 @@ export const PythonDefaultTypeMapping: PythonTypeMapping = { } }; -export const PythonDefaultConstraints: Constraints = { +export const PythonDefaultConstraints: Constraints = { enumKey: defaultEnumKeyConstraints(), enumValue: defaultEnumValueConstraints(), modelName: defaultModelNameConstraints(), diff --git a/src/generators/python/PythonFileGenerator.ts b/src/generators/python/PythonFileGenerator.ts index 79854bec8b..46b2fecbc4 100644 --- a/src/generators/python/PythonFileGenerator.ts +++ b/src/generators/python/PythonFileGenerator.ts @@ -17,7 +17,7 @@ export class PythonFileGenerator * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. */ public async generateToFiles( - input: Record | InputMetaModel, + input: any | InputMetaModel, outputDirectory: string, options: PythonRenderCompleteModelOptions, ensureFilesWritten = false diff --git a/src/generators/python/PythonGenerator.ts b/src/generators/python/PythonGenerator.ts index 2d20bd0e5f..f517d1b05d 100644 --- a/src/generators/python/PythonGenerator.ts +++ b/src/generators/python/PythonGenerator.ts @@ -20,8 +20,13 @@ import { ClassRenderer } from './renderers/ClassRenderer'; import { EnumRenderer } from './renderers/EnumRenderer'; import { Logger } from '../..'; import { + ConstantConstraint, constrainMetaModel, - Constraints + Constraints, + EnumKeyConstraint, + EnumValueConstraint, + ModelNameConstraint, + PropertyKeyConstraint } from '../../helpers/ConstrainHelpers'; import { PythonDefaultConstraints, @@ -32,9 +37,14 @@ import { PythonDependencyManager } from './PythonDependencyManager'; export interface PythonOptions extends CommonGeneratorOptions { typeMapping: TypeMapping; - constraints: Constraints; + constraints: Constraints; importsStyle: 'explicit' | 'implicit'; } +export type PythonConstantConstraint = ConstantConstraint; +export type PythonEnumKeyConstraint = EnumKeyConstraint; +export type PythonEnumValueConstraint = EnumValueConstraint; +export type PythonModelNameConstraint = ModelNameConstraint; +export type PythonPropertyKeyConstraint = PropertyKeyConstraint; export type PythonTypeMapping = TypeMapping< PythonOptions, PythonDependencyManager diff --git a/src/generators/python/constrainer/ConstantConstrainer.ts b/src/generators/python/constrainer/ConstantConstrainer.ts index 2c076ad46d..44bd50358d 100644 --- a/src/generators/python/constrainer/ConstantConstrainer.ts +++ b/src/generators/python/constrainer/ConstantConstrainer.ts @@ -1,6 +1,6 @@ -import { ConstantConstraint } from '../../../helpers'; +import { PythonConstantConstraint } from '../PythonGenerator'; -export function defaultConstantConstraints(): ConstantConstraint { +export function defaultConstantConstraints(): PythonConstantConstraint { return () => { return undefined; }; diff --git a/src/generators/python/constrainer/EnumConstrainer.ts b/src/generators/python/constrainer/EnumConstrainer.ts index c6ed1b4ef2..877ad876d4 100644 --- a/src/generators/python/constrainer/EnumConstrainer.ts +++ b/src/generators/python/constrainer/EnumConstrainer.ts @@ -6,12 +6,12 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { - FormatHelpers, - EnumKeyConstraint, - EnumValueConstraint -} from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedPythonKeyword } from '../Constants'; +import { + PythonEnumKeyConstraint, + PythonEnumValueConstraint +} from '../PythonGenerator'; export type ModelEnumKeyConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -50,7 +50,7 @@ export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { */ export function defaultEnumKeyConstraints( customConstraints?: Partial -): EnumKeyConstraint { +): PythonEnumKeyConstraint { const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; return ({ enumKey, enumModel, constrainedEnumModel }) => { @@ -76,7 +76,7 @@ export function defaultEnumKeyConstraints( /** * Convert the enum value to a value that is compatible with Python */ -export function defaultEnumValueConstraints(): EnumValueConstraint { +export function defaultEnumValueConstraints(): PythonEnumValueConstraint { return ({ enumValue }) => { let constrainedEnumValue = enumValue; switch (typeof enumValue) { diff --git a/src/generators/python/constrainer/ModelNameConstrainer.ts b/src/generators/python/constrainer/ModelNameConstrainer.ts index 42e9ac994f..31c940e701 100644 --- a/src/generators/python/constrainer/ModelNameConstrainer.ts +++ b/src/generators/python/constrainer/ModelNameConstrainer.ts @@ -3,8 +3,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedPythonKeyword } from '../Constants'; +import { PythonModelNameConstraint } from '../PythonGenerator'; export type ModelNameConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -38,7 +39,7 @@ export const DefaultModelNameConstraints: ModelNameConstraints = { */ export function defaultModelNameConstraints( customConstraints?: Partial -): ModelNameConstraint { +): PythonModelNameConstraint { const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; return ({ modelName }) => { diff --git a/src/generators/python/constrainer/PropertyKeyConstrainer.ts b/src/generators/python/constrainer/PropertyKeyConstrainer.ts index 93e01576a6..c86f4d8223 100644 --- a/src/generators/python/constrainer/PropertyKeyConstrainer.ts +++ b/src/generators/python/constrainer/PropertyKeyConstrainer.ts @@ -5,8 +5,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedPythonKeyword } from '../Constants'; +import { PythonPropertyKeyConstraint } from '../PythonGenerator'; export type PropertyKeyConstraintOptions = { NO_SPECIAL_CHAR: (value: string) => string; @@ -44,7 +45,7 @@ export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { */ export function defaultPropertyKeyConstraints( customConstraints?: Partial -): PropertyKeyConstraint { +): PythonPropertyKeyConstraint { const constraints = { ...DefaultPropertyKeyConstraints, ...customConstraints diff --git a/src/generators/python/presets/Pydantic.ts b/src/generators/python/presets/Pydantic.ts index b74ef6510a..39e2b7f052 100644 --- a/src/generators/python/presets/Pydantic.ts +++ b/src/generators/python/presets/Pydantic.ts @@ -1,10 +1,11 @@ +import { ConstrainedUnionModel } from '../../../models'; import { PythonOptions } from '../PythonGenerator'; import { ClassPresetType, PythonPreset } from '../PythonPreset'; const PYTHON_PYDANTIC_CLASS_PRESET: ClassPresetType = { async self({ renderer, model }) { renderer.dependencyManager.addDependency( - 'from typing import Optional, Any' + 'from typing import Optional, Any, Union' ); renderer.dependencyManager.addDependency( 'from pydantic import BaseModel, Field' @@ -18,11 +19,24 @@ const PYTHON_PYDANTIC_CLASS_PRESET: ClassPresetType = { ); }, property(params) { - const { propertyName, required, property } = params.property; - const type = required ? property.type : `Optional[${property.type}]`; - const description = property.originalInput['description']; - const alias = description ? `alias='''${description}'''` : ''; - const defaultValue = required ? '' : 'default=None'; + let type = params.property.property.type; + const propertyName = params.property.propertyName; + + if (params.property.property instanceof ConstrainedUnionModel) { + const unionTypes = params.property.property.union.map( + (unionModel) => unionModel.type + ); + type = `Union[${unionTypes.join(', ')}]`; + } + + if (!params.property.required) { + type = `Optional[${type}]`; + } + + const alias = params.property.property.originalInput['description'] + ? `alias='''${params.property.property.originalInput['description']}'''` + : ''; + const defaultValue = params.property.required ? '' : 'default=None'; if (alias && defaultValue) { return `${propertyName}: ${type} = Field(${alias}, ${defaultValue})`; diff --git a/src/generators/rust/RustConstrainer.ts b/src/generators/rust/RustConstrainer.ts index 8253d11030..735dbcd0f4 100644 --- a/src/generators/rust/RustConstrainer.ts +++ b/src/generators/rust/RustConstrainer.ts @@ -5,7 +5,7 @@ import { import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer'; import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer'; import { RustTypeMapping } from './RustGenerator'; -import { FormatHelpers, Constraints } from '../../helpers'; +import { FormatHelpers } from '../../helpers'; import { ConstrainedAnyModel, ConstrainedArrayModel, @@ -345,7 +345,7 @@ export const RustDefaultTypeMapping: RustTypeMapping = { } }; -export const RustDefaultConstraints: Constraints = { +export const RustDefaultConstraints = { enumKey: defaultEnumKeyConstraints(), enumValue: defaultEnumValueConstraints(), modelName: defaultModelNameConstraints(), diff --git a/src/generators/rust/RustFileGenerator.ts b/src/generators/rust/RustFileGenerator.ts index 5264aad16b..8a6b8609c3 100644 --- a/src/generators/rust/RustFileGenerator.ts +++ b/src/generators/rust/RustFileGenerator.ts @@ -17,7 +17,7 @@ export class RustFileGenerator * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. */ public async generateToFiles( - input: Record | InputMetaModel, + input: any | InputMetaModel, outputDirectory: string, options: RustRenderCompleteModelOptions, ensureFilesWritten = false @@ -50,7 +50,7 @@ export class RustFileGenerator * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. */ public async generateToPackage( - input: Record | InputMetaModel, + input: any | InputMetaModel, outputDirectory: string, options: RustRenderCompleteModelOptions, ensureFilesWritten = false diff --git a/src/generators/rust/RustGenerator.ts b/src/generators/rust/RustGenerator.ts index 758f5e2d60..14d743e4da 100644 --- a/src/generators/rust/RustGenerator.ts +++ b/src/generators/rust/RustGenerator.ts @@ -17,9 +17,14 @@ import { ConstrainedUnionModel } from '../../models'; import { + ConstantConstraint, constrainMetaModel, Constraints, + EnumKeyConstraint, + EnumValueConstraint, IndentationTypes, + ModelNameConstraint, + PropertyKeyConstraint, split, SplitOptions, TypeMapping @@ -40,8 +45,13 @@ import { RustDependencyManager } from './RustDependencyManager'; export interface RustOptions extends CommonGeneratorOptions { typeMapping: TypeMapping; - constraints: Constraints; + constraints: Constraints; } +export type RustConstantConstraint = ConstantConstraint; +export type RustEnumKeyConstraint = EnumKeyConstraint; +export type RustEnumValueConstraint = EnumValueConstraint; +export type RustModelNameConstraint = ModelNameConstraint; +export type RustPropertyKeyConstraint = PropertyKeyConstraint; export type RustTypeMapping = TypeMapping; export enum RustPackageFeatures { @@ -419,7 +429,7 @@ export class RustGenerator extends AbstractGenerator< } async generateCompleteSupport( - input: Record | InputMetaModel, + input: any | InputMetaModel, completeModelOptions: Partial, options?: DeepPartial ): Promise { diff --git a/src/generators/rust/constrainer/ConstantConstrainer.ts b/src/generators/rust/constrainer/ConstantConstrainer.ts index 2c076ad46d..16875bdf9c 100644 --- a/src/generators/rust/constrainer/ConstantConstrainer.ts +++ b/src/generators/rust/constrainer/ConstantConstrainer.ts @@ -1,6 +1,6 @@ -import { ConstantConstraint } from '../../../helpers'; +import { RustConstantConstraint } from '../RustGenerator'; -export function defaultConstantConstraints(): ConstantConstraint { +export function defaultConstantConstraints(): RustConstantConstraint { return () => { return undefined; }; diff --git a/src/generators/rust/constrainer/EnumConstrainer.ts b/src/generators/rust/constrainer/EnumConstrainer.ts index 7915d3f893..8ba049d37d 100644 --- a/src/generators/rust/constrainer/EnumConstrainer.ts +++ b/src/generators/rust/constrainer/EnumConstrainer.ts @@ -6,12 +6,12 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { - FormatHelpers, - EnumKeyConstraint, - EnumValueConstraint -} from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedRustKeyword } from '../Constants'; +import { + RustEnumKeyConstraint, + RustEnumValueConstraint +} from '../RustGenerator'; export type ModelEnumKeyConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -46,7 +46,7 @@ export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { export function defaultEnumKeyConstraints( customConstraints?: Partial -): EnumKeyConstraint { +): RustEnumKeyConstraint { const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; return ({ enumKey, enumModel, constrainedEnumModel }) => { @@ -72,7 +72,7 @@ export function defaultEnumKeyConstraints( }; } -export function defaultEnumValueConstraints(): EnumValueConstraint { +export function defaultEnumValueConstraints(): RustEnumValueConstraint { return ({ enumValue }) => { return enumValue; }; diff --git a/src/generators/rust/constrainer/ModelNameConstrainer.ts b/src/generators/rust/constrainer/ModelNameConstrainer.ts index 599c69ab11..78c88efe84 100644 --- a/src/generators/rust/constrainer/ModelNameConstrainer.ts +++ b/src/generators/rust/constrainer/ModelNameConstrainer.ts @@ -3,8 +3,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedRustKeyword } from '../Constants'; +import { RustModelNameConstraint } from '../RustGenerator'; export type ModelNameConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -34,7 +35,7 @@ export const DefaultModelNameConstraints: ModelNameConstraints = { }; export function defaultModelNameConstraints( customConstraints?: Partial -): ModelNameConstraint { +): RustModelNameConstraint { const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; return ({ modelName }) => { diff --git a/src/generators/rust/constrainer/PropertyKeyConstrainer.ts b/src/generators/rust/constrainer/PropertyKeyConstrainer.ts index 656c7a4618..e4a27f4bfb 100644 --- a/src/generators/rust/constrainer/PropertyKeyConstrainer.ts +++ b/src/generators/rust/constrainer/PropertyKeyConstrainer.ts @@ -5,8 +5,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedRustKeyword } from '../Constants'; +import { RustPropertyKeyConstraint } from '../RustGenerator'; export type PropertyKeyConstraintOptions = { NO_SPECIAL_CHAR: (value: string) => string; @@ -42,7 +43,7 @@ export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { export function defaultPropertyKeyConstraints( customConstraints?: Partial -): PropertyKeyConstraint { +): RustPropertyKeyConstraint { const constraints = { ...DefaultPropertyKeyConstraints, ...customConstraints diff --git a/src/generators/rust/renderers/PackageRenderer.ts b/src/generators/rust/renderers/PackageRenderer.ts index 9b4a2461c1..27cad91740 100644 --- a/src/generators/rust/renderers/PackageRenderer.ts +++ b/src/generators/rust/renderers/PackageRenderer.ts @@ -20,14 +20,14 @@ export const RUST_DEFAULT_PACKAGE_PRESET: PackagePresetType = { return renderer.defaultSelf(); }, - lib({ inputModel, renderer }) { + lib({ inputModel, renderer, options }) { const modelNames: string[] = Object.values(inputModel.models).map( (m: MetaModel) => m.name ); const imports = renderer.renderBlock( modelNames .map((modelName) => { - let mod = defaultModelNameConstraints()({ modelName }); + let mod = defaultModelNameConstraints()({ modelName, options }); mod = FormatHelpers.snakeCase(mod); return ` pub mod ${mod}; diff --git a/src/generators/scala/ScalaConstrainer.ts b/src/generators/scala/ScalaConstrainer.ts index f004c279ec..e5b2714930 100644 --- a/src/generators/scala/ScalaConstrainer.ts +++ b/src/generators/scala/ScalaConstrainer.ts @@ -7,7 +7,7 @@ import { import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer'; import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer'; import { defaultConstantConstraints } from './constrainer/ConstantConstrainer'; -import { ScalaTypeMapping } from './ScalaGenerator'; +import { ScalaOptions, ScalaTypeMapping } from './ScalaGenerator'; function enumFormatToNumberType( enumValueModel: ConstrainedEnumValueModel, @@ -147,7 +147,7 @@ export const ScalaDefaultTypeMapping: ScalaTypeMapping = { } }; -export const ScalaDefaultConstraints: Constraints = { +export const ScalaDefaultConstraints: Constraints = { enumKey: defaultEnumKeyConstraints(), enumValue: defaultEnumValueConstraints(), modelName: defaultModelNameConstraints(), diff --git a/src/generators/scala/ScalaGenerator.ts b/src/generators/scala/ScalaGenerator.ts index c765b8af8b..0309e0c756 100644 --- a/src/generators/scala/ScalaGenerator.ts +++ b/src/generators/scala/ScalaGenerator.ts @@ -20,8 +20,13 @@ import { EnumRenderer } from './renderers/EnumRenderer'; import { isReservedScalaKeyword } from './Constants'; import { Logger } from '../..'; import { + ConstantConstraint, constrainMetaModel, - Constraints + Constraints, + EnumKeyConstraint, + EnumValueConstraint, + ModelNameConstraint, + PropertyKeyConstraint } from '../../helpers/ConstrainHelpers'; import { ScalaDefaultConstraints, @@ -30,9 +35,14 @@ import { import { DeepPartial, mergePartialAndDefault } from '../../utils/Partials'; import { ScalaDependencyManager } from './ScalaDependencyManager'; +export type ScalaConstantConstraint = ConstantConstraint; +export type ScalaEnumKeyConstraint = EnumKeyConstraint; +export type ScalaEnumValueConstraint = EnumValueConstraint; +export type ScalaModelNameConstraint = ModelNameConstraint; +export type ScalaPropertyKeyConstraint = PropertyKeyConstraint; export interface ScalaOptions extends CommonGeneratorOptions { typeMapping: TypeMapping; - constraints: Constraints; + constraints: Constraints; collectionType: 'List' | 'Array'; } diff --git a/src/generators/scala/constrainer/ConstantConstrainer.ts b/src/generators/scala/constrainer/ConstantConstrainer.ts index 2c076ad46d..350be54a39 100644 --- a/src/generators/scala/constrainer/ConstantConstrainer.ts +++ b/src/generators/scala/constrainer/ConstantConstrainer.ts @@ -1,6 +1,6 @@ -import { ConstantConstraint } from '../../../helpers'; +import { ScalaConstantConstraint } from '../ScalaGenerator'; -export function defaultConstantConstraints(): ConstantConstraint { +export function defaultConstantConstraints(): ScalaConstantConstraint { return () => { return undefined; }; diff --git a/src/generators/scala/constrainer/EnumConstrainer.ts b/src/generators/scala/constrainer/EnumConstrainer.ts index 9ba2e2cb83..7e3c38b989 100644 --- a/src/generators/scala/constrainer/EnumConstrainer.ts +++ b/src/generators/scala/constrainer/EnumConstrainer.ts @@ -6,12 +6,12 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { - FormatHelpers, - EnumKeyConstraint, - EnumValueConstraint -} from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedScalaKeyword } from '../Constants'; +import { + ScalaEnumKeyConstraint, + ScalaEnumValueConstraint +} from '../ScalaGenerator'; export type ModelEnumKeyConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -50,7 +50,7 @@ export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { */ export function defaultEnumKeyConstraints( customConstraints?: Partial -): EnumKeyConstraint { +): ScalaEnumKeyConstraint { const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; return ({ enumKey, enumModel, constrainedEnumModel }) => { @@ -65,7 +65,7 @@ export function defaultEnumKeyConstraints( constrainedEnumModel, enumModel, constrainedEnumKey, - constraints.NAMING_FORMATTER! + constraints.NAMING_FORMATTER ); } constrainedEnumKey = constraints.NAMING_FORMATTER(constrainedEnumKey); @@ -76,7 +76,7 @@ export function defaultEnumKeyConstraints( /** * Convert the enum value to a value that is compatible with Scala */ -export function defaultEnumValueConstraints(): EnumValueConstraint { +export function defaultEnumValueConstraints(): ScalaEnumValueConstraint { return ({ enumValue }) => { let constrainedEnumValue = enumValue; switch (typeof enumValue) { diff --git a/src/generators/scala/constrainer/ModelNameConstrainer.ts b/src/generators/scala/constrainer/ModelNameConstrainer.ts index c9c523dff2..592a507468 100644 --- a/src/generators/scala/constrainer/ModelNameConstrainer.ts +++ b/src/generators/scala/constrainer/ModelNameConstrainer.ts @@ -3,8 +3,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedScalaKeyword } from '../Constants'; +import { ScalaModelNameConstraint } from '../ScalaGenerator'; export type ModelNameConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -38,7 +39,7 @@ export const DefaultModelNameConstraints: ModelNameConstraints = { */ export function defaultModelNameConstraints( customConstraints?: Partial -): ModelNameConstraint { +): ScalaModelNameConstraint { const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; return ({ modelName }) => { diff --git a/src/generators/scala/constrainer/PropertyKeyConstrainer.ts b/src/generators/scala/constrainer/PropertyKeyConstrainer.ts index 640761546c..e98c521b61 100644 --- a/src/generators/scala/constrainer/PropertyKeyConstrainer.ts +++ b/src/generators/scala/constrainer/PropertyKeyConstrainer.ts @@ -5,8 +5,9 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedScalaKeyword } from '../Constants'; +import { ScalaPropertyKeyConstraint } from '../ScalaGenerator'; export type PropertyKeyConstraintOptions = { NO_SPECIAL_CHAR: (value: string) => string; @@ -44,7 +45,7 @@ export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { */ export function defaultPropertyKeyConstraints( customConstraints?: Partial -): PropertyKeyConstraint { +): ScalaPropertyKeyConstraint { const constraints = { ...DefaultPropertyKeyConstraints, ...customConstraints diff --git a/src/generators/template/TemplateConstrainer.ts b/src/generators/template/TemplateConstrainer.ts index 4465ba6cbc..86fc24e62d 100644 --- a/src/generators/template/TemplateConstrainer.ts +++ b/src/generators/template/TemplateConstrainer.ts @@ -53,7 +53,7 @@ export const TemplateDefaultTypeMapping: TypeMapping< } }; -export const TemplateDefaultConstraints: Constraints = { +export const TemplateDefaultConstraints = { enumKey: defaultEnumKeyConstraints(), enumValue: defaultEnumValueConstraints(), modelName: defaultModelNameConstraints(), diff --git a/src/generators/template/TemplateFileGenerator.ts b/src/generators/template/TemplateFileGenerator.ts index 495a3851ad..cc92e87e1f 100644 --- a/src/generators/template/TemplateFileGenerator.ts +++ b/src/generators/template/TemplateFileGenerator.ts @@ -16,7 +16,7 @@ export class TemplateFileGenerator * @param options */ public async generateToFiles( - input: Record | InputMetaModel, + input: any | InputMetaModel, outputDirectory: string, options?: TemplateRenderCompleteModelOptions, ensureFilesWritten = false diff --git a/src/generators/template/TemplateGenerator.ts b/src/generators/template/TemplateGenerator.ts index afa1f7db91..d7033fcb85 100644 --- a/src/generators/template/TemplateGenerator.ts +++ b/src/generators/template/TemplateGenerator.ts @@ -20,8 +20,13 @@ import { EnumRenderer } from './renderers/EnumRenderer'; import { isReservedTemplateKeyword } from './Constants'; import { Logger } from '../..'; import { + ConstantConstraint, constrainMetaModel, - Constraints + Constraints, + EnumKeyConstraint, + EnumValueConstraint, + ModelNameConstraint, + PropertyKeyConstraint } from '../../helpers/ConstrainHelpers'; import { TemplateDefaultConstraints, @@ -33,8 +38,14 @@ import { TemplateDependencyManager } from './TemplateDependencyManager'; export interface TemplateOptions extends CommonGeneratorOptions { typeMapping: TypeMapping; - constraints: Constraints; + constraints: Constraints; } +export type TemplateConstantConstraint = ConstantConstraint; +export type TemplateEnumKeyConstraint = EnumKeyConstraint; +export type TemplateEnumValueConstraint = EnumValueConstraint; +export type TemplateModelNameConstraint = ModelNameConstraint; +export type TemplatePropertyKeyConstraint = + PropertyKeyConstraint; export interface TemplateRenderCompleteModelOptions { packageName: string; } diff --git a/src/generators/template/constrainer/ConstantConstrainer.ts b/src/generators/template/constrainer/ConstantConstrainer.ts index 2c076ad46d..ad1cf21366 100644 --- a/src/generators/template/constrainer/ConstantConstrainer.ts +++ b/src/generators/template/constrainer/ConstantConstrainer.ts @@ -1,6 +1,7 @@ import { ConstantConstraint } from '../../../helpers'; +import { TemplateConstantConstraint } from '../TemplateGenerator'; -export function defaultConstantConstraints(): ConstantConstraint { +export function defaultConstantConstraints(): TemplateConstantConstraint { return () => { return undefined; }; diff --git a/src/generators/template/constrainer/EnumConstrainer.ts b/src/generators/template/constrainer/EnumConstrainer.ts index eec1a43306..cedc96bd48 100644 --- a/src/generators/template/constrainer/EnumConstrainer.ts +++ b/src/generators/template/constrainer/EnumConstrainer.ts @@ -12,6 +12,10 @@ import { EnumValueConstraint } from '../../../helpers'; import { isReservedTemplateKeyword } from '../Constants'; +import { + TemplateEnumKeyConstraint, + TemplateEnumValueConstraint +} from '../TemplateGenerator'; export type ModelEnumKeyConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -50,7 +54,7 @@ export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { */ export function defaultEnumKeyConstraints( customConstraints?: Partial -): EnumKeyConstraint { +): TemplateEnumKeyConstraint { const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; return ({ enumKey, enumModel, constrainedEnumModel }) => { @@ -76,7 +80,7 @@ export function defaultEnumKeyConstraints( /** * Convert the enum value to a value that is compatible with Template */ -export function defaultEnumValueConstraints(): EnumValueConstraint { +export function defaultEnumValueConstraints(): TemplateEnumValueConstraint { return ({ enumValue }) => { let constrainedEnumValue = enumValue; switch (typeof enumValue) { diff --git a/src/generators/template/constrainer/ModelNameConstrainer.ts b/src/generators/template/constrainer/ModelNameConstrainer.ts index 8f96bc2ef0..f8e87b5cab 100644 --- a/src/generators/template/constrainer/ModelNameConstrainer.ts +++ b/src/generators/template/constrainer/ModelNameConstrainer.ts @@ -5,6 +5,7 @@ import { } from '../../../helpers/Constraints'; import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; import { isReservedTemplateKeyword } from '../Constants'; +import { TemplateModelNameConstraint } from '../TemplateGenerator'; export type ModelNameConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -38,7 +39,7 @@ export const DefaultModelNameConstraints: ModelNameConstraints = { */ export function defaultModelNameConstraints( customConstraints?: Partial -): ModelNameConstraint { +): TemplateModelNameConstraint { const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; return ({ modelName }) => { diff --git a/src/generators/template/constrainer/PropertyKeyConstrainer.ts b/src/generators/template/constrainer/PropertyKeyConstrainer.ts index 19ee7546b6..d3df2e15e5 100644 --- a/src/generators/template/constrainer/PropertyKeyConstrainer.ts +++ b/src/generators/template/constrainer/PropertyKeyConstrainer.ts @@ -7,6 +7,7 @@ import { } from '../../../helpers/Constraints'; import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; import { isReservedTemplateKeyword } from '../Constants'; +import { TemplatePropertyKeyConstraint } from '../TemplateGenerator'; export type PropertyKeyConstraintOptions = { NO_SPECIAL_CHAR: (value: string) => string; @@ -44,7 +45,7 @@ export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { */ export function defaultPropertyKeyConstraints( customConstraints?: Partial -): PropertyKeyConstraint { +): TemplatePropertyKeyConstraint { const constraints = { ...DefaultPropertyKeyConstraints, ...customConstraints diff --git a/src/generators/typescript/Constants.ts b/src/generators/typescript/Constants.ts index a4c81d4bda..4847585790 100644 --- a/src/generators/typescript/Constants.ts +++ b/src/generators/typescript/Constants.ts @@ -1,5 +1,6 @@ import { checkForReservedKeyword } from '../../helpers'; import { isReservedJavaScriptKeyword } from '../javascript/Constants'; +import { TypeScriptOptions } from './TypeScriptGenerator'; export const RESERVED_TYPESCRIPT_KEYWORDS = [ 'break', 'case', @@ -71,13 +72,16 @@ export const RESERVED_TYPESCRIPT_KEYWORDS = [ */ export function isReservedTypeScriptKeyword( word: string, - forceLowerCase = true + forceLowerCase = true, + options: TypeScriptOptions | undefined ): boolean { - return ( - checkForReservedKeyword( - word, - RESERVED_TYPESCRIPT_KEYWORDS, - forceLowerCase - ) || isReservedJavaScriptKeyword(word) + const isTsReserved = checkForReservedKeyword( + word, + RESERVED_TYPESCRIPT_KEYWORDS, + forceLowerCase ); + const isJsReserved = options?.useJavascriptReservedKeywords + ? isReservedJavaScriptKeyword(word) + : false; + return isTsReserved || isJsReserved; } diff --git a/src/generators/typescript/TypeScriptConstrainer.ts b/src/generators/typescript/TypeScriptConstrainer.ts index feec116bff..6eaf9279f1 100644 --- a/src/generators/typescript/TypeScriptConstrainer.ts +++ b/src/generators/typescript/TypeScriptConstrainer.ts @@ -8,7 +8,6 @@ import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer' import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer'; import { defaultConstantConstraints } from './constrainer/ConstantConstrainer'; import { TypeScriptTypeMapping } from './TypeScriptGenerator'; -import { Constraints } from '../../helpers'; function applyNullable(model: ConstrainedMetaModel, type: string) { if (model.options.isNullable) { return `${type} | null`; @@ -92,7 +91,7 @@ export const TypeScriptDefaultTypeMapping: TypeScriptTypeMapping = { } }; -export const TypeScriptDefaultConstraints: Constraints = { +export const TypeScriptDefaultConstraints = { enumKey: defaultEnumKeyConstraints(), enumValue: defaultEnumValueConstraints(), modelName: defaultModelNameConstraints(), diff --git a/src/generators/typescript/TypeScriptFileGenerator.ts b/src/generators/typescript/TypeScriptFileGenerator.ts index 345937d4e4..06b59aa43d 100644 --- a/src/generators/typescript/TypeScriptFileGenerator.ts +++ b/src/generators/typescript/TypeScriptFileGenerator.ts @@ -13,7 +13,7 @@ export class TypeScriptFileGenerator extends TypeScriptGenerator { * @param ensureFilesWritten verify that the files is completely written before returning, this can happen if the file system is swamped with write requests. */ public async generateToFiles( - input: Record | InputMetaModel, + input: any | InputMetaModel, outputDirectory: string, options?: TypeScriptRenderCompleteModelOptions, ensureFilesWritten = false diff --git a/src/generators/typescript/TypeScriptGenerator.ts b/src/generators/typescript/TypeScriptGenerator.ts index e9058f4be8..074bdeb367 100644 --- a/src/generators/typescript/TypeScriptGenerator.ts +++ b/src/generators/typescript/TypeScriptGenerator.ts @@ -14,8 +14,13 @@ import { RenderOutput } from '../../models'; import { + ConstantConstraint, constrainMetaModel, Constraints, + EnumKeyConstraint, + EnumValueConstraint, + ModelNameConstraint, + PropertyKeyConstraint, split, SplitOptions, TypeMapping @@ -44,9 +49,27 @@ export interface TypeScriptOptions enumType: 'enum' | 'union'; mapType: 'indexedObject' | 'map' | 'record'; typeMapping: TypeMapping; - constraints: Constraints; + constraints: Constraints; moduleSystem: TypeScriptModuleSystemType; + /** + * Use JS reserved keywords so the TS output models can easily be transpiled to JS + */ + useJavascriptReservedKeywords: boolean; + /** + * Use raw property names instead of constrained ones, + * where you most likely need to access them with obj["propertyName"] instead of obj.propertyName + */ + rawPropertyNames: boolean; } +export type TypeScriptConstantConstraint = + ConstantConstraint; +export type TypeScriptEnumKeyConstraint = EnumKeyConstraint; +export type TypeScriptEnumValueConstraint = + EnumValueConstraint; +export type TypeScriptModelNameConstraint = + ModelNameConstraint; +export type TypeScriptPropertyKeyConstraint = + PropertyKeyConstraint; export type TypeScriptTypeMapping = TypeMapping< TypeScriptOptions, TypeScriptDependencyManager @@ -72,6 +95,8 @@ export class TypeScriptGenerator extends AbstractGenerator< typeMapping: TypeScriptDefaultTypeMapping, constraints: TypeScriptDefaultConstraints, moduleSystem: 'ESM', + rawPropertyNames: false, + useJavascriptReservedKeywords: true, // Temporarily set dependencyManager: () => { return {} as TypeScriptDependencyManager; diff --git a/src/generators/typescript/TypeScriptObjectRenderer.ts b/src/generators/typescript/TypeScriptObjectRenderer.ts index 79ce45b89f..2adc74de29 100644 --- a/src/generators/typescript/TypeScriptObjectRenderer.ts +++ b/src/generators/typescript/TypeScriptObjectRenderer.ts @@ -1,12 +1,8 @@ -import { TypeScriptGenerator, TypeScriptOptions } from './TypeScriptGenerator'; import { ConstrainedObjectModel, - ConstrainedObjectPropertyModel, - InputMetaModel, - Preset + ConstrainedObjectPropertyModel } from '../../models'; import { TypeScriptRenderer } from './TypeScriptRenderer'; -import { TypeScriptDependencyManager } from './TypeScriptDependencyManager'; /** * Common renderer for TypeScript types @@ -14,17 +10,6 @@ import { TypeScriptDependencyManager } from './TypeScriptDependencyManager'; * @extends AbstractRenderer */ export abstract class TypeScriptObjectRenderer extends TypeScriptRenderer { - constructor( - options: TypeScriptOptions, - generator: TypeScriptGenerator, - presets: Array<[Preset, unknown]>, - model: ConstrainedObjectModel, - inputModel: InputMetaModel, - dependencyManager: TypeScriptDependencyManager - ) { - super(options, generator, presets, model, inputModel, dependencyManager); - } - /** * Render all the properties for the model by calling the property preset per property. */ @@ -41,9 +26,17 @@ export abstract class TypeScriptObjectRenderer extends TypeScriptRenderer { const constOptions = constrainedMetaModel.options.const; diff --git a/src/generators/typescript/constrainer/EnumConstrainer.ts b/src/generators/typescript/constrainer/EnumConstrainer.ts index 06485c62c3..8f275b5bfd 100644 --- a/src/generators/typescript/constrainer/EnumConstrainer.ts +++ b/src/generators/typescript/constrainer/EnumConstrainer.ts @@ -5,12 +5,13 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { - EnumKeyConstraint, - EnumValueConstraint, - FormatHelpers -} from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedTypeScriptKeyword } from '../Constants'; +import { + TypeScriptEnumKeyConstraint, + TypeScriptEnumValueConstraint, + TypeScriptOptions +} from '../TypeScriptGenerator'; export type ModelEnumKeyConstraints = { NO_SPECIAL_CHAR: (value: string) => string; @@ -23,7 +24,7 @@ export type ModelEnumKeyConstraints = { ) => string; NO_EMPTY_VALUE: (value: string) => string; NAMING_FORMATTER: (value: string) => string; - NO_RESERVED_KEYWORDS: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string, options: TypeScriptOptions) => string; }; export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { @@ -38,22 +39,27 @@ export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = { NO_DUPLICATE_KEYS: NO_DUPLICATE_ENUM_KEYS, NO_EMPTY_VALUE, NAMING_FORMATTER: FormatHelpers.toConstantCase, - NO_RESERVED_KEYWORDS: (value: string) => { - return NO_RESERVED_KEYWORDS(value, isReservedTypeScriptKeyword); + NO_RESERVED_KEYWORDS: (value: string, options: TypeScriptOptions) => { + return NO_RESERVED_KEYWORDS(value, (word) => + isReservedTypeScriptKeyword(word, true, options) + ); } }; export function defaultEnumKeyConstraints( customConstraints?: Partial -): EnumKeyConstraint { +): TypeScriptEnumKeyConstraint { const constraints = { ...DefaultEnumKeyConstraints, ...customConstraints }; - return ({ enumKey, enumModel, constrainedEnumModel }) => { + return ({ enumKey, enumModel, constrainedEnumModel, options }) => { let constrainedEnumKey = enumKey; constrainedEnumKey = constraints.NO_SPECIAL_CHAR(constrainedEnumKey); constrainedEnumKey = constraints.NO_NUMBER_START_CHAR(constrainedEnumKey); constrainedEnumKey = constraints.NO_EMPTY_VALUE(constrainedEnumKey); - constrainedEnumKey = constraints.NO_RESERVED_KEYWORDS(constrainedEnumKey); + constrainedEnumKey = constraints.NO_RESERVED_KEYWORDS( + constrainedEnumKey, + options + ); //If the enum key has been manipulated, lets make sure it don't clash with existing keys if (constrainedEnumKey !== enumKey) { constrainedEnumKey = constraints.NO_DUPLICATE_KEYS( @@ -68,7 +74,7 @@ export function defaultEnumKeyConstraints( }; } -export function defaultEnumValueConstraints(): EnumValueConstraint { +export function defaultEnumValueConstraints(): TypeScriptEnumValueConstraint { return ({ enumValue }) => { let normalizedEnumValue; switch (typeof enumValue) { diff --git a/src/generators/typescript/constrainer/ModelNameConstrainer.ts b/src/generators/typescript/constrainer/ModelNameConstrainer.ts index 935b745db6..0af57d959c 100644 --- a/src/generators/typescript/constrainer/ModelNameConstrainer.ts +++ b/src/generators/typescript/constrainer/ModelNameConstrainer.ts @@ -3,19 +3,23 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, ModelNameConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedTypeScriptKeyword } from '../Constants'; +import { + TypeScriptModelNameConstraint, + TypeScriptOptions +} from '../TypeScriptGenerator'; export type ModelNameConstraints = { NO_SPECIAL_CHAR: (value: string) => string; NO_NUMBER_START_CHAR: (value: string) => string; NO_EMPTY_VALUE: (value: string) => string; NAMING_FORMATTER: (value: string) => string; - NO_RESERVED_KEYWORDS: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string, options: TypeScriptOptions) => string; }; export const DefaultModelNameConstraints: ModelNameConstraints = { - NO_SPECIAL_CHAR: (value: string) => { + NO_SPECIAL_CHAR: (value) => { //Exclude ` ` because it gets formatted by NAMING_FORMATTER //Exclude '_', '$' because they are allowed return FormatHelpers.replaceSpecialCharacters(value, { @@ -25,24 +29,29 @@ export const DefaultModelNameConstraints: ModelNameConstraints = { }, NO_NUMBER_START_CHAR, NO_EMPTY_VALUE, - NAMING_FORMATTER: (value: string) => { + NAMING_FORMATTER: (value) => { return FormatHelpers.toPascalCase(value); }, - NO_RESERVED_KEYWORDS: (value: string) => { - return NO_RESERVED_KEYWORDS(value, isReservedTypeScriptKeyword); + NO_RESERVED_KEYWORDS: (value, options) => { + return NO_RESERVED_KEYWORDS(value, (word) => + isReservedTypeScriptKeyword(word, true, options) + ); } }; export function defaultModelNameConstraints( customConstraints?: Partial -): ModelNameConstraint { +): TypeScriptModelNameConstraint { const constraints = { ...DefaultModelNameConstraints, ...customConstraints }; - return ({ modelName }) => { + return ({ modelName, options }) => { let constrainedValue = modelName; constrainedValue = constraints.NO_SPECIAL_CHAR(constrainedValue); constrainedValue = constraints.NO_NUMBER_START_CHAR(constrainedValue); constrainedValue = constraints.NO_EMPTY_VALUE(constrainedValue); - constrainedValue = constraints.NO_RESERVED_KEYWORDS(constrainedValue); + constrainedValue = constraints.NO_RESERVED_KEYWORDS( + constrainedValue, + options + ); constrainedValue = constraints.NAMING_FORMATTER(constrainedValue); return constrainedValue; }; diff --git a/src/generators/typescript/constrainer/PropertyKeyConstrainer.ts b/src/generators/typescript/constrainer/PropertyKeyConstrainer.ts index ca130ad858..9c3f0dfbd3 100644 --- a/src/generators/typescript/constrainer/PropertyKeyConstrainer.ts +++ b/src/generators/typescript/constrainer/PropertyKeyConstrainer.ts @@ -5,8 +5,12 @@ import { NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS } from '../../../helpers/Constraints'; -import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers'; +import { FormatHelpers } from '../../../helpers'; import { isReservedTypeScriptKeyword } from '../Constants'; +import { + TypeScriptOptions, + TypeScriptPropertyKeyConstraint +} from '../TypeScriptGenerator'; export type PropertyKeyConstraintOptions = { NO_SPECIAL_CHAR: (value: string) => string; @@ -19,11 +23,11 @@ export type PropertyKeyConstraintOptions = { ) => string; NO_EMPTY_VALUE: (value: string) => string; NAMING_FORMATTER: (value: string) => string; - NO_RESERVED_KEYWORDS: (value: string) => string; + NO_RESERVED_KEYWORDS: (value: string, options: TypeScriptOptions) => string; }; export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { - NO_SPECIAL_CHAR: (value: string) => { + NO_SPECIAL_CHAR: (value) => { //Exclude ` ` because it gets formatted by NAMING_FORMATTER //Exclude '_', '$' because they are allowed return FormatHelpers.replaceSpecialCharacters(value, { @@ -35,19 +39,26 @@ export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = { NO_DUPLICATE_PROPERTIES, NO_EMPTY_VALUE, NAMING_FORMATTER: FormatHelpers.toCamelCase, - NO_RESERVED_KEYWORDS: (value: string) => { - return NO_RESERVED_KEYWORDS(value, isReservedTypeScriptKeyword); + NO_RESERVED_KEYWORDS: (value, options) => { + return NO_RESERVED_KEYWORDS(value, (word) => + isReservedTypeScriptKeyword(word, true, options) + ); } }; export function defaultPropertyKeyConstraints( customConstraints?: Partial -): PropertyKeyConstraint { +): TypeScriptPropertyKeyConstraint { const constraints = { ...DefaultPropertyKeyConstraints, ...customConstraints }; - return ({ objectPropertyModel, constrainedObjectModel, objectModel }) => { + return ({ + objectPropertyModel, + constrainedObjectModel, + objectModel, + options + }) => { let constrainedPropertyKey = objectPropertyModel.propertyName; constrainedPropertyKey = constraints.NO_SPECIAL_CHAR( @@ -58,7 +69,8 @@ export function defaultPropertyKeyConstraints( ); constrainedPropertyKey = constraints.NO_EMPTY_VALUE(constrainedPropertyKey); constrainedPropertyKey = constraints.NO_RESERVED_KEYWORDS( - constrainedPropertyKey + constrainedPropertyKey, + options ); //If the property name has been manipulated, lets make sure it don't clash with existing properties if (constrainedPropertyKey !== objectPropertyModel.propertyName) { diff --git a/src/generators/typescript/presets/CommonPreset.ts b/src/generators/typescript/presets/CommonPreset.ts index bc1510dee4..5b44ebcb42 100644 --- a/src/generators/typescript/presets/CommonPreset.ts +++ b/src/generators/typescript/presets/CommonPreset.ts @@ -1,319 +1,13 @@ import { TypeScriptPreset } from '../TypeScriptPreset'; -import { - ConstrainedObjectModel, - ConstrainedDictionaryModel, - ConstrainedReferenceModel, - ConstrainedMetaModel, - ConstrainedEnumModel, - ConstrainedUnionModel, - ConstrainedArrayModel -} from '../../../models'; import renderExampleFunction from './utils/ExampleFunction'; -import { ClassRenderer } from '../renderers/ClassRenderer'; +import { renderUnmarshal } from './utils/UnmarshalFunction'; +import { renderMarshal } from './utils/MarshalFunction'; export interface TypeScriptCommonPresetOptions { marshalling: boolean; example: boolean; } -function realizePropertyFactory(prop: string) { - return `$\{typeof ${prop} === 'number' || typeof ${prop} === 'boolean' ? ${prop} : JSON.stringify(${prop})}`; -} - -function renderMarshalProperty( - modelInstanceVariable: string, - model: ConstrainedMetaModel -) { - if ( - model instanceof ConstrainedReferenceModel && - !(model.ref instanceof ConstrainedEnumModel) - ) { - return `$\{${modelInstanceVariable}.marshal()}`; - } - - return realizePropertyFactory(modelInstanceVariable); -} - -function renderUnionSerializationArray( - modelInstanceVariable: string, - prop: string, - unconstrainedProperty: string, - unionModel: ConstrainedUnionModel -) { - const propName = `${prop}JsonValues`; - const allUnionReferences = unionModel.union - .filter((model) => { - return ( - model instanceof ConstrainedReferenceModel && - !(model.ref instanceof ConstrainedEnumModel) - ); - }) - .map((model) => { - return `unionItem instanceof ${model.type}`; - }); - const allUnionReferencesCondition = allUnionReferences.join(' || '); - const hasUnionReference = allUnionReferences.length > 0; - let unionSerialization = `${propName}.push(typeof unionItem === 'number' || typeof unionItem === 'boolean' ? unionItem : JSON.stringify(unionItem))`; - if (hasUnionReference) { - unionSerialization = `if(${allUnionReferencesCondition}) { - ${propName}.push(unionItem.marshal()); - } else { - ${propName}.push(typeof unionItem === 'number' || typeof unionItem === 'boolean' ? unionItem : JSON.stringify(unionItem)) - }`; - } - return `let ${propName}: any[] = []; - for (const unionItem of ${modelInstanceVariable}) { - ${unionSerialization} - } - json += \`"${unconstrainedProperty}": [\${${propName}.join(',')}],\`;`; -} -function renderArraySerialization( - modelInstanceVariable: string, - prop: string, - unconstrainedProperty: string, - arrayModel: ConstrainedArrayModel -) { - const propName = `${prop}JsonValues`; - return `let ${propName}: any[] = []; - for (const unionItem of ${modelInstanceVariable}) { - ${propName}.push(\`${renderMarshalProperty( - 'unionItem', - arrayModel.valueModel - )}\`); - } - json += \`"${unconstrainedProperty}": [\${${propName}.join(',')}],\`;`; -} -function renderUnionSerialization( - modelInstanceVariable: string, - unconstrainedProperty: string, - unionModel: ConstrainedUnionModel -) { - const allUnionReferences = unionModel.union - .filter((model) => { - return ( - model instanceof ConstrainedReferenceModel && - !(model.ref instanceof ConstrainedEnumModel) - ); - }) - .map((model) => { - return `${modelInstanceVariable} instanceof ${model.type}`; - }); - const allUnionReferencesCondition = allUnionReferences.join(' || '); - const hasUnionReference = allUnionReferences.length > 0; - if (hasUnionReference) { - return `if(${allUnionReferencesCondition}) { - json += \`"${unconstrainedProperty}": $\{${modelInstanceVariable}.marshal()},\`; - } else { - json += \`"${unconstrainedProperty}": ${realizePropertyFactory( - modelInstanceVariable - )},\`; - }`; - } - return `json += \`"${unconstrainedProperty}": ${realizePropertyFactory( - modelInstanceVariable - )},\`;`; -} -function renderMarshalProperties(model: ConstrainedObjectModel) { - const properties = model.properties || {}; - const propertyKeys = [...Object.entries(properties)]; - - //These are a bit special as 'unwrap' dictionary models means they have to be unwrapped within the JSON object. - const unwrapDictionaryProperties = []; - const normalProperties = []; - for (const entry of propertyKeys) { - if ( - entry[1].property instanceof ConstrainedDictionaryModel && - entry[1].property.serializationType === 'unwrap' - ) { - unwrapDictionaryProperties.push(entry); - } else { - normalProperties.push(entry); - } - } - - const marshalNormalProperties = normalProperties.map(([prop, propModel]) => { - const modelInstanceVariable = `this.${prop}`; - let marshalCode = ''; - if ( - propModel.property instanceof ConstrainedArrayModel && - propModel.property.valueModel instanceof ConstrainedUnionModel - ) { - marshalCode = renderUnionSerializationArray( - modelInstanceVariable, - prop, - propModel.unconstrainedPropertyName, - propModel.property.valueModel - ); - } else if (propModel.property instanceof ConstrainedUnionModel) { - marshalCode = renderUnionSerialization( - modelInstanceVariable, - propModel.unconstrainedPropertyName, - propModel.property - ); - } else if (propModel.property instanceof ConstrainedArrayModel) { - marshalCode = renderArraySerialization( - modelInstanceVariable, - prop, - propModel.unconstrainedPropertyName, - propModel.property - ); - } else { - const propMarshalCode = renderMarshalProperty( - modelInstanceVariable, - propModel.property - ); - marshalCode = `json += \`"${propModel.unconstrainedPropertyName}": ${propMarshalCode},\`;`; - } - return `if(${modelInstanceVariable} !== undefined) { - ${marshalCode} -}`; - }); - - const marshalUnwrapDictionaryProperties = unwrapDictionaryProperties.map( - ([prop, propModel]) => { - const modelInstanceVariable = 'value'; - const patternPropertyMarshalCode = renderMarshalProperty( - modelInstanceVariable, - (propModel.property as ConstrainedDictionaryModel).value - ); - const marshalCode = `json += \`"$\{key}": ${patternPropertyMarshalCode},\`;`; - return `if(this.${prop} !== undefined) { -for (const [key, value] of this.${prop}.entries()) { - //Only unwrap those who are not already a property in the JSON object - if(Object.keys(this).includes(String(key))) continue; - ${marshalCode} - } -}`; - } - ); - - return ` -${marshalNormalProperties.join('\n')} -${marshalUnwrapDictionaryProperties.join('\n')} -`; -} - -/** - * Render `marshal` function based on model - */ -function renderMarshal({ - renderer, - model -}: { - renderer: ClassRenderer; - model: ConstrainedObjectModel; -}): string { - return `public marshal() : string { - let json = '{' -${renderer.indent(renderMarshalProperties(model))} - //Remove potential last comma - return \`$\{json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; -}`; -} - -function renderUnmarshalProperty( - modelInstanceVariable: string, - model: ConstrainedMetaModel -) { - if ( - model instanceof ConstrainedReferenceModel && - !(model.ref instanceof ConstrainedEnumModel) - ) { - return `${model.type}.unmarshal(${modelInstanceVariable})`; - } - return `${modelInstanceVariable}`; -} -function renderUnmarshalProperties(model: ConstrainedObjectModel) { - const properties = model.properties || {}; - const propertyKeys = [...Object.entries(properties)]; - const originalPropertyNames = propertyKeys.map(([, model]) => { - return model.unconstrainedPropertyName; - }); - //These are a bit special as 'unwrap' dictionary models means they have to be unwrapped within the JSON object. - const unwrapDictionaryProperties = []; - const normalProperties = []; - for (const entry of propertyKeys) { - // if const value exists, we don't need to unmarshal this property because it exist in the class/interface - if (entry[1].property.options.const) { - continue; - } - - if ( - entry[1].property instanceof ConstrainedDictionaryModel && - entry[1].property.serializationType === 'unwrap' - ) { - unwrapDictionaryProperties.push(entry); - } else { - normalProperties.push(entry); - } - } - - const unmarshalNormalProperties = normalProperties.map( - ([prop, propModel]) => { - const modelInstanceVariable = `obj["${propModel.unconstrainedPropertyName}"]`; - const unmarshalCode = renderUnmarshalProperty( - modelInstanceVariable, - propModel.property - ); - return `if (${modelInstanceVariable} !== undefined) { - instance.${prop} = ${unmarshalCode}; -}`; - } - ); - - const setDictionaryProperties = []; - const unmarshalDictionaryProperties = []; - for (const [prop, propModel] of unwrapDictionaryProperties) { - const modelInstanceVariable = 'value as any'; - const unmarshalCode = renderUnmarshalProperty( - modelInstanceVariable, - (propModel.property as ConstrainedDictionaryModel).value - ); - setDictionaryProperties.push( - `if (instance.${prop} === undefined) {instance.${prop} = new Map();}` - ); - unmarshalDictionaryProperties.push( - `instance.${prop}.set(key, ${unmarshalCode});` - ); - } - const corePropertyKeys = originalPropertyNames - .map((propertyKey) => `"${propertyKey}"`) - .join(','); - const unwrappedDictionaryCode = - setDictionaryProperties.length > 0 - ? `${setDictionaryProperties.join('\n')} - for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![${corePropertyKeys}].includes(key);}))) { - ${unmarshalDictionaryProperties.join('\n')} - }` - : ''; - - return ` -${unmarshalNormalProperties.join('\n')} - -${unwrappedDictionaryCode} -`; -} - -/** - * Render `unmarshal` function based on model - */ -function renderUnmarshal({ - renderer, - model -}: { - renderer: ClassRenderer; - model: ConstrainedObjectModel; -}): string { - const unmarshalProperties = renderUnmarshalProperties(model); - return `public static unmarshal(json: string | object): ${model.type} { - const obj = typeof json === "object" ? json : JSON.parse(json); - const instance = new ${model.type}({} as any); - -${renderer.indent(unmarshalProperties)} - return instance; -}`; -} - /** * Preset which adds `marshal`, `unmarshal`, `example` functions to class. * diff --git a/src/generators/typescript/presets/JsonBinPackPreset.ts b/src/generators/typescript/presets/JsonBinPackPreset.ts index 21452e1c5a..551f843676 100644 --- a/src/generators/typescript/presets/JsonBinPackPreset.ts +++ b/src/generators/typescript/presets/JsonBinPackPreset.ts @@ -26,9 +26,8 @@ function getInputSchema(originalInput: any): string { export const TS_JSONBINPACK_PRESET: TypeScriptPreset = { class: { async additionalContent({ renderer, content, model }) { - renderer.dependencyManager.addTypeScriptDependency( - 'jsonbinpack', - 'jsonbinpack' + renderer.dependencyManager.addDependency( + "const jsonbinpack = require('jsonbinpack')" ); const jsonSchema = await alterschema( @@ -36,9 +35,9 @@ export const TS_JSONBINPACK_PRESET: TypeScriptPreset = { getInputSchema(model.originalInput), '2020-12' ); + jsonSchema['$schema'] = 'https://json-schema.org/draft/2020-12/schema'; const json = JSON.stringify(jsonSchema); - const packContent = ` -public async jsonbinSerialize(): Promise{ + const packContent = `public async jsonbinSerialize(): Promise{ const jsonData = JSON.parse(this.marshal()); const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema(${json}); return jsonbinpack.serialize(jsonbinpackEncodedSchema, jsonData); diff --git a/src/generators/typescript/presets/utils/MarshalFunction.ts b/src/generators/typescript/presets/utils/MarshalFunction.ts new file mode 100644 index 0000000000..a2b080a154 --- /dev/null +++ b/src/generators/typescript/presets/utils/MarshalFunction.ts @@ -0,0 +1,262 @@ +import { ClassRenderer } from '../../renderers/ClassRenderer'; +import { + getDictionary, + getNormalProperties, + getOriginalPropertyList +} from '../../../../helpers'; +import { + ConstrainedArrayModel, + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedMetaModel, + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + ConstrainedReferenceModel, + ConstrainedTupleModel, + ConstrainedUnionModel +} from '../../../../models'; + +function realizePropertyFactory(prop: string) { + return `$\{typeof ${prop} === 'number' || typeof ${prop} === 'boolean' ? ${prop} : JSON.stringify(${prop})}`; +} + +function renderMarshalProperty( + modelInstanceVariable: string, + model: ConstrainedMetaModel +) { + if ( + model instanceof ConstrainedReferenceModel && + !(model.ref instanceof ConstrainedEnumModel) + ) { + return `$\{${modelInstanceVariable}.marshal()}`; + } + + return realizePropertyFactory(modelInstanceVariable); +} +/** + * Render marshalling logic for tuples + */ +function renderTupleSerialization( + modelInstanceVariable: string, + unconstrainedProperty: string, + tuple: ConstrainedTupleModel +) { + const t = tuple.tuple.map((tupleEntry) => { + const temp = renderMarshalProperty( + `${modelInstanceVariable}[${tupleEntry.index}]`, + tupleEntry.value + ); + return `if(${modelInstanceVariable}[${tupleEntry.index}]) { + serializedTuple[${tupleEntry.index}] = ${temp} +} else { + serializedTuple[${tupleEntry.index}] = null; +}`; + }); + return `const serializedTuple = []; +${t.join('\n')} +json += \`"${unconstrainedProperty}": [\${serializedTuple.join(',')}],\`;`; +} + +/** + * Render marshalling logic for unions + */ +function renderUnionSerializationArray( + modelInstanceVariable: string, + prop: string, + unconstrainedProperty: string, + unionModel: ConstrainedUnionModel +) { + const propName = `${prop}JsonValues`; + const allUnionReferences = unionModel.union + .filter((model) => { + return ( + model instanceof ConstrainedReferenceModel && + !(model.ref instanceof ConstrainedEnumModel) + ); + }) + .map((model) => { + return `unionItem instanceof ${model.type}`; + }); + const allUnionReferencesCondition = allUnionReferences.join(' || '); + const hasUnionReference = allUnionReferences.length > 0; + let unionSerialization = `${propName}.push(typeof unionItem === 'number' || typeof unionItem === 'boolean' ? unionItem : JSON.stringify(unionItem))`; + if (hasUnionReference) { + unionSerialization = `if(${allUnionReferencesCondition}) { + ${propName}.push(unionItem.marshal()); + } else { + ${propName}.push(typeof unionItem === 'number' || typeof unionItem === 'boolean' ? unionItem : JSON.stringify(unionItem)) + }`; + } + return `const ${propName}: any[] = []; + for (const unionItem of ${modelInstanceVariable}) { + ${unionSerialization} + } + json += \`"${unconstrainedProperty}": [\${${propName}.join(',')}],\`;`; +} + +/** + * Render marshalling logic for Arrays + */ +function renderArraySerialization( + modelInstanceVariable: string, + prop: string, + unconstrainedProperty: string, + arrayModel: ConstrainedArrayModel +) { + const propName = `${prop}JsonValues`; + return `let ${propName}: any[] = []; + for (const unionItem of ${modelInstanceVariable}) { + ${propName}.push(\`${renderMarshalProperty( + 'unionItem', + arrayModel.valueModel + )}\`); + } + json += \`"${unconstrainedProperty}": [\${${propName}.join(',')}],\`;`; +} + +/** + * Render marshalling logic for unions + */ +function renderUnionSerialization( + modelInstanceVariable: string, + unconstrainedProperty: string, + unionModel: ConstrainedUnionModel +) { + const allUnionReferences = unionModel.union + .filter((model) => { + return ( + model instanceof ConstrainedReferenceModel && + !(model.ref instanceof ConstrainedEnumModel) + ); + }) + .map((model) => { + return `${modelInstanceVariable} instanceof ${model.type}`; + }); + const allUnionReferencesCondition = allUnionReferences.join(' || '); + const hasUnionReference = allUnionReferences.length > 0; + if (hasUnionReference) { + return `if(${allUnionReferencesCondition}) { + json += \`"${unconstrainedProperty}": $\{${modelInstanceVariable}.marshal()},\`; + } else { + json += \`"${unconstrainedProperty}": ${realizePropertyFactory( + modelInstanceVariable + )},\`; + }`; + } + return `json += \`"${unconstrainedProperty}": ${realizePropertyFactory( + modelInstanceVariable + )},\`;`; +} + +/** + * Render marshalling logic for dictionary types + */ +function renderDictionarySerialization( + properties: Record +) { + const unwrapDictionaryProperties = getDictionary(properties); + const originalPropertyNames = getOriginalPropertyList(properties); + return unwrapDictionaryProperties.map(([prop, propModel]) => { + let dictionaryValueType; + if ( + (propModel.property as ConstrainedDictionaryModel).value instanceof + ConstrainedUnionModel + ) { + dictionaryValueType = renderUnionSerialization( + 'value', + '${key}', + (propModel.property as ConstrainedDictionaryModel) + .value as ConstrainedUnionModel + ); + } else { + const type = renderMarshalProperty('value', propModel.property); + dictionaryValueType = `json += \`"$\{key}": ${type},\`;`; + } + return `if(this.${prop} !== undefined) { + for (const [key, value] of this.${prop}.entries()) { + //Only unwrap those that are not already a property in the JSON object + if([${originalPropertyNames + .map((value) => `"${value}"`) + .join(',')}].includes(String(key))) continue; + ${dictionaryValueType} + } +}`; + }); +} + +/** + * Render marshalling code for all the normal properties (not dictionaries with unwrap) + */ +function renderNormalProperties( + properties: Record +) { + const normalProperties = getNormalProperties(properties); + + return normalProperties.map(([prop, propModel]) => { + const modelInstanceVariable = `this.${prop}`; + let marshalCode; + if ( + propModel.property instanceof ConstrainedArrayModel && + propModel.property.valueModel instanceof ConstrainedUnionModel + ) { + marshalCode = renderUnionSerializationArray( + modelInstanceVariable, + prop, + propModel.unconstrainedPropertyName, + propModel.property.valueModel + ); + } else if (propModel.property instanceof ConstrainedUnionModel) { + marshalCode = renderUnionSerialization( + modelInstanceVariable, + propModel.unconstrainedPropertyName, + propModel.property + ); + } else if (propModel.property instanceof ConstrainedArrayModel) { + marshalCode = renderArraySerialization( + modelInstanceVariable, + prop, + propModel.unconstrainedPropertyName, + propModel.property + ); + } else if (propModel.property instanceof ConstrainedTupleModel) { + marshalCode = renderTupleSerialization( + modelInstanceVariable, + propModel.unconstrainedPropertyName, + propModel.property + ); + } else { + const propMarshalCode = renderMarshalProperty( + modelInstanceVariable, + propModel.property + ); + marshalCode = `json += \`"${propModel.unconstrainedPropertyName}": ${propMarshalCode},\`;`; + } + return `if(${modelInstanceVariable} !== undefined) { + ${marshalCode} +}`; + }); +} + +/** + * Render `marshal` function based on model + */ +export function renderMarshal({ + renderer, + model +}: { + renderer: ClassRenderer; + model: ConstrainedObjectModel; +}): string { + const properties = model.properties || {}; + const marshalNormalProperties = renderNormalProperties(properties); + const marshalUnwrapDictionaryProperties = + renderDictionarySerialization(properties); + + return `public marshal() : string { + let json = '{' +${renderer.indent(marshalNormalProperties.join('\n'))} +${renderer.indent(marshalUnwrapDictionaryProperties.join('\n'))} + //Remove potential last comma + return \`$\{json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; +}`; +} diff --git a/src/generators/typescript/presets/utils/UnmarshalFunction.ts b/src/generators/typescript/presets/utils/UnmarshalFunction.ts new file mode 100644 index 0000000000..373db05dba --- /dev/null +++ b/src/generators/typescript/presets/utils/UnmarshalFunction.ts @@ -0,0 +1,106 @@ +import { ClassRenderer } from '../../renderers/ClassRenderer'; +import { getDictionary, getNormalProperties } from '../../../../helpers'; +import { + ConstrainedDictionaryModel, + ConstrainedEnumModel, + ConstrainedMetaModel, + ConstrainedObjectModel, + ConstrainedObjectPropertyModel, + ConstrainedReferenceModel +} from '../../../../models'; + +/** + * Render the unmarshalled value + */ +function renderUnmarshalProperty( + modelInstanceVariable: string, + model: ConstrainedMetaModel +) { + if ( + model instanceof ConstrainedReferenceModel && + !(model.ref instanceof ConstrainedEnumModel) + ) { + return `${model.type}.unmarshal(${modelInstanceVariable})`; + } + return `${modelInstanceVariable}`; +} + +/** + * Render the code for unmarshalling of regular properties + */ +function unmarshalRegularProperty(propModel: ConstrainedObjectPropertyModel) { + const modelInstanceVariable = `obj["${propModel.unconstrainedPropertyName}"]`; + const unmarshalCode = renderUnmarshalProperty( + modelInstanceVariable, + propModel.property + ); + return `if (${modelInstanceVariable} !== undefined) { + instance.${propModel.propertyName} = ${unmarshalCode}; +}`; +} + +/** + * Render the code for unmarshalling unwrappable dictionary models + */ +function unmarshalDictionary(model: ConstrainedObjectModel) { + const setDictionaryProperties = []; + const unmarshalDictionaryProperties = []; + const properties = model.properties || {}; + const propertyKeys = [...Object.entries(properties)]; + const originalPropertyNames = propertyKeys.map(([, model]) => { + return model.unconstrainedPropertyName; + }); + const unwrapDictionaryProperties = getDictionary(properties); + + for (const [prop, propModel] of unwrapDictionaryProperties) { + const modelInstanceVariable = 'value as any'; + const unmarshalCode = renderUnmarshalProperty( + modelInstanceVariable, + (propModel.property as ConstrainedDictionaryModel).value + ); + setDictionaryProperties.push(`instance.${prop} = new Map();`); + unmarshalDictionaryProperties.push( + `instance.${prop}.set(key, ${unmarshalCode});` + ); + } + + const corePropertyKeys = originalPropertyNames + .map((propertyKey) => `"${propertyKey}"`) + .join(','); + if (setDictionaryProperties.length > 0) { + return `${setDictionaryProperties.join('\n')} +const propsToCheck = Object.entries(obj).filter((([key,]) => {return ![${corePropertyKeys}].includes(key);})); +for (const [key, value] of propsToCheck) { + ${unmarshalDictionaryProperties.join('\n')} +}`; + } + return ''; +} + +/** + * Render `unmarshal` function based on model + */ +export function renderUnmarshal({ + renderer, + model +}: { + renderer: ClassRenderer; + model: ConstrainedObjectModel; +}): string { + const properties = model.properties || {}; + const normalProperties = getNormalProperties(properties); + const unmarshalNormalProperties = normalProperties.map(([, propModel]) => + unmarshalRegularProperty(propModel) + ); + const unwrappedDictionaryCode = unmarshalDictionary(model); + + return `public static unmarshal(json: string | object): ${model.type} { + const obj = typeof json === "object" ? json : JSON.parse(json); + const instance = new ${model.type}({} as any); + +${renderer.indent(unmarshalNormalProperties.join('\n'))} + +${renderer.indent(unwrappedDictionaryCode)} + return instance; +}`; +} diff --git a/src/helpers/CommonModelToMetaModel.ts b/src/helpers/CommonModelToMetaModel.ts index 2bff87d91a..946681b714 100644 --- a/src/helpers/CommonModelToMetaModel.ts +++ b/src/helpers/CommonModelToMetaModel.ts @@ -514,6 +514,16 @@ export function convertToObjectModel( metaModel.properties[String(propertyName)] = propertyModel; } + if (jsonSchemaModel.extend?.length) { + metaModel.options.extend = []; + + for (const extend of jsonSchemaModel.extend) { + metaModel.options.extend.push( + convertToMetaModel(extend, alreadySeenModels) + ); + } + } + if ( jsonSchemaModel.additionalProperties !== undefined || jsonSchemaModel.patternProperties !== undefined diff --git a/src/helpers/ConstrainHelpers.ts b/src/helpers/ConstrainHelpers.ts index 72409b793a..8370f4ec9f 100644 --- a/src/helpers/ConstrainHelpers.ts +++ b/src/helpers/ConstrainHelpers.ts @@ -15,7 +15,8 @@ import { ConstrainedEnumModel, ConstrainedDictionaryModel, ConstrainedEnumValueModel, - ConstrainedObjectPropertyModel + ConstrainedObjectPropertyModel, + ConstrainedMetaModelOptions } from '../models/ConstrainedMetaModel'; import { AnyModel, @@ -48,46 +49,61 @@ export type ConstrainContext< dependencyManager: DependencyManager; }; -export type EnumKeyContext = { +export type EnumKeyContext = { enumKey: string; constrainedEnumModel: ConstrainedEnumModel; enumModel: EnumModel; + options: Options; }; -export type EnumKeyConstraint = (context: EnumKeyContext) => string; +export type EnumKeyConstraint = ( + context: EnumKeyContext +) => string; -export type EnumValueContext = { +export type EnumValueContext = { enumValue: any; constrainedEnumModel: ConstrainedEnumModel; enumModel: EnumModel; + options: Options; }; -export type EnumValueConstraint = (context: EnumValueContext) => any; +export type EnumValueConstraint = ( + context: EnumValueContext +) => any; -export type ModelNameContext = { +export type ModelNameContext = { modelName: string; + options: Options; }; -export type ModelNameConstraint = (context: ModelNameContext) => string; +export type ModelNameConstraint = ( + context: ModelNameContext +) => string; -export type PropertyKeyContext = { +export type PropertyKeyContext = { constrainedObjectPropertyModel: ConstrainedObjectPropertyModel; objectPropertyModel: ObjectPropertyModel; constrainedObjectModel: ConstrainedObjectModel; objectModel: ObjectModel; + options: Options; }; -export type PropertyKeyConstraint = (context: PropertyKeyContext) => string; +export type PropertyKeyConstraint = ( + context: PropertyKeyContext +) => string; -export type ConstantContext = { +export type ConstantContext = { constrainedMetaModel: ConstrainedMetaModel; + options: Options; }; -export type ConstantConstraint = (context: ConstantContext) => unknown; +export type ConstantConstraint = ( + context: ConstantContext +) => unknown; -export interface Constraints { - enumKey: EnumKeyConstraint; - enumValue: EnumValueConstraint; - modelName: ModelNameConstraint; - propertyKey: PropertyKeyConstraint; - constant: ConstantConstraint; +export interface Constraints { + enumKey: EnumKeyConstraint; + enumValue: EnumValueConstraint; + modelName: ModelNameConstraint; + propertyKey: PropertyKeyConstraint; + constant: ConstantConstraint; } const placeHolderConstrainedObject = new ConstrainedAnyModel( @@ -97,19 +113,33 @@ const placeHolderConstrainedObject = new ConstrainedAnyModel( '' ); +function getConstrainedMetaModelOptions( + metaModel: MetaModel +): ConstrainedMetaModelOptions { + const options: ConstrainedMetaModelOptions = {}; + + options.const = metaModel.options.const; + options.isNullable = metaModel.options.isNullable; + options.discriminator = metaModel.options.discriminator; + options.format = metaModel.options.format; + options.isExtended = metaModel.options.isExtended; + + return options; +} + function constrainReferenceModel< Options, DependencyManager extends AbstractDependencyManager >( typeMapping: TypeMapping, - constrainRules: Constraints, + constrainRules: Constraints, context: ConstrainContext, alreadySeenModels: Map ): ConstrainedReferenceModel { const constrainedModel = new ConstrainedReferenceModel( context.constrainedName, context.metaModel.originalInput, - context.metaModel.options, + getConstrainedMetaModelOptions(context.metaModel), '', placeHolderConstrainedObject ); @@ -131,7 +161,8 @@ function constrainReferenceModel< if (constrainedModel.options.const) { const constrainedConstant = constrainRules.constant({ - constrainedMetaModel: constrainedModel + constrainedMetaModel: constrainedModel, + options: context.options }); constrainedModel.options.const.value = constrainedConstant; } @@ -148,7 +179,7 @@ function constrainAnyModel< const constrainedModel = new ConstrainedAnyModel( context.constrainedName, context.metaModel.originalInput, - context.metaModel.options, + getConstrainedMetaModelOptions(context.metaModel), '' ); constrainedModel.type = getTypeFromMapping(typeMapping, { @@ -169,7 +200,7 @@ function constrainFloatModel< const constrainedModel = new ConstrainedFloatModel( context.constrainedName, context.metaModel.originalInput, - context.metaModel.options, + getConstrainedMetaModelOptions(context.metaModel), '' ); constrainedModel.type = getTypeFromMapping(typeMapping, { @@ -190,7 +221,7 @@ function constrainIntegerModel< const constrainedModel = new ConstrainedIntegerModel( context.constrainedName, context.metaModel.originalInput, - context.metaModel.options, + getConstrainedMetaModelOptions(context.metaModel), '' ); constrainedModel.type = getTypeFromMapping(typeMapping, { @@ -211,7 +242,7 @@ function constrainStringModel< const constrainedModel = new ConstrainedStringModel( context.constrainedName, context.metaModel.originalInput, - context.metaModel.options, + getConstrainedMetaModelOptions(context.metaModel), '' ); constrainedModel.type = getTypeFromMapping(typeMapping, { @@ -232,7 +263,7 @@ function constrainBooleanModel< const constrainedModel = new ConstrainedBooleanModel( context.constrainedName, context.metaModel.originalInput, - context.metaModel.options, + getConstrainedMetaModelOptions(context.metaModel), '' ); constrainedModel.type = getTypeFromMapping(typeMapping, { @@ -248,14 +279,14 @@ function constrainTupleModel< DependencyManager extends AbstractDependencyManager >( typeMapping: TypeMapping, - constrainRules: Constraints, + constrainRules: Constraints, context: ConstrainContext, alreadySeenModels: Map ): ConstrainedTupleModel { const constrainedModel = new ConstrainedTupleModel( context.constrainedName, context.metaModel.originalInput, - context.metaModel.options, + getConstrainedMetaModelOptions(context.metaModel), '', [] ); @@ -284,14 +315,14 @@ function constrainArrayModel< DependencyManager extends AbstractDependencyManager >( typeMapping: TypeMapping, - constrainRules: Constraints, + constrainRules: Constraints, context: ConstrainContext, alreadySeenModels: Map ): ConstrainedArrayModel { const constrainedModel = new ConstrainedArrayModel( context.constrainedName, context.metaModel.originalInput, - context.metaModel.options, + getConstrainedMetaModelOptions(context.metaModel), '', placeHolderConstrainedObject ); @@ -353,14 +384,14 @@ function constrainUnionModel< DependencyManager extends AbstractDependencyManager >( typeMapping: TypeMapping, - constrainRules: Constraints, + constrainRules: Constraints, context: ConstrainContext, alreadySeenModels: Map ): ConstrainedUnionModel { const constrainedModel = new ConstrainedUnionModel( context.constrainedName, context.metaModel.originalInput, - context.metaModel.options, + getConstrainedMetaModelOptions(context.metaModel), '', [] ); @@ -392,14 +423,14 @@ function constrainDictionaryModel< DependencyManager extends AbstractDependencyManager >( typeMapping: TypeMapping, - constrainRules: Constraints, + constrainRules: Constraints, context: ConstrainContext, alreadySeenModels: Map ): ConstrainedDictionaryModel { const constrainedModel = new ConstrainedDictionaryModel( context.constrainedName, context.metaModel.originalInput, - context.metaModel.options, + getConstrainedMetaModelOptions(context.metaModel), '', placeHolderConstrainedObject, placeHolderConstrainedObject, @@ -439,14 +470,35 @@ function constrainObjectModel< DependencyManager extends AbstractDependencyManager >( typeMapping: TypeMapping, - constrainRules: Constraints, + constrainRules: Constraints, context: ConstrainContext, alreadySeenModels: Map ): ConstrainedObjectModel { + const options = getConstrainedMetaModelOptions(context.metaModel); + + if (context.metaModel.options.extend?.length) { + options.extend = []; + + for (const extend of context.metaModel.options.extend) { + options.extend.push( + constrainMetaModel( + typeMapping, + constrainRules, + { + ...context, + metaModel: extend, + partOfProperty: undefined + }, + alreadySeenModels + ) + ); + } + } + const constrainedModel = new ConstrainedObjectModel( context.constrainedName, context.metaModel.originalInput, - context.metaModel.options, + options, '', {} ); @@ -463,7 +515,8 @@ function constrainObjectModel< objectPropertyModel: propertyMetaModel, constrainedObjectPropertyModel: constrainedPropertyModel, constrainedObjectModel: constrainedModel, - objectModel: context.metaModel + objectModel: context.metaModel, + options: context.options }); constrainedPropertyModel.propertyName = constrainedPropertyName; const constrainedProperty = constrainMetaModel( @@ -481,6 +534,7 @@ function constrainObjectModel< constrainedModel.properties[String(constrainedPropertyName)] = constrainedPropertyModel; } + constrainedModel.type = getTypeFromMapping(typeMapping, { constrainedModel, options: context.options, @@ -495,13 +549,13 @@ function ConstrainEnumModel< DependencyManager extends AbstractDependencyManager >( typeMapping: TypeMapping, - constrainRules: Constraints, + constrainRules: Constraints, context: ConstrainContext ): ConstrainedEnumModel { const constrainedModel = new ConstrainedEnumModel( context.constrainedName, context.metaModel.originalInput, - context.metaModel.options, + getConstrainedMetaModelOptions(context.metaModel), '', [] ); @@ -512,12 +566,14 @@ function ConstrainEnumModel< const constrainedEnumKey = constrainRules.enumKey({ enumKey: String(enumValue.key), enumModel: context.metaModel, - constrainedEnumModel: constrainedModel + constrainedEnumModel: constrainedModel, + options: context.options }); const constrainedEnumValue = constrainRules.enumValue({ enumValue: enumValue.value, enumModel: context.metaModel, - constrainedEnumModel: constrainedModel + constrainedEnumModel: constrainedModel, + options: context.options }); return new ConstrainedEnumValueModel( constrainedEnumKey, @@ -548,7 +604,7 @@ export function constrainMetaModel< DependencyManager extends AbstractDependencyManager >( typeMapping: TypeMapping, - constrainRules: Constraints, + constrainRules: Constraints, context: ConstrainContext, alreadySeenModels: Map = new Map() ): ConstrainedMetaModel { @@ -556,7 +612,8 @@ export function constrainMetaModel< return alreadySeenModels.get(context.metaModel) as ConstrainedMetaModel; } const constrainedName = constrainRules.modelName({ - modelName: context.metaModel.name + modelName: context.metaModel.name, + options: context.options }); const newContext = { ...context, constrainedName }; if (newContext.metaModel instanceof ObjectModel) { @@ -638,7 +695,8 @@ export function constrainMetaModel< if (simpleModel !== undefined) { if (simpleModel.options.const) { const constrainedConstant = constrainRules.constant({ - constrainedMetaModel: simpleModel + constrainedMetaModel: simpleModel, + options: context.options }); simpleModel.options.const.value = constrainedConstant; } diff --git a/src/helpers/FilterHelpers.ts b/src/helpers/FilterHelpers.ts new file mode 100644 index 0000000000..7235228dc5 --- /dev/null +++ b/src/helpers/FilterHelpers.ts @@ -0,0 +1,42 @@ +import { + ConstrainedDictionaryModel, + ConstrainedObjectPropertyModel +} from '../models'; + +/** + * Filter out all properties that are dictionary models with unwrap serialization type. + */ +export function getNormalProperties( + properties: Record +): [string, ConstrainedObjectPropertyModel][] { + return Object.entries(properties).filter( + ([, value]) => + !(value.property instanceof ConstrainedDictionaryModel) || + (value.property instanceof ConstrainedDictionaryModel && + value.property.serializationType !== 'unwrap') + ); +} + +/** + * Filter out all properties that are dictionary models with unwrap serialization type. + */ +export function getDictionary( + properties: Record +): [string, ConstrainedObjectPropertyModel][] { + return Object.entries(properties).filter( + ([, value]) => + value.property instanceof ConstrainedDictionaryModel && + value.property.serializationType === 'unwrap' + ); +} + +/** + * Filter properties and return unconstrained property names for each property + */ +export function getOriginalPropertyList( + properties: Record +): string[] { + return Object.entries(properties).map(([, model]) => { + return model.unconstrainedPropertyName; + }); +} diff --git a/src/helpers/Splitter.ts b/src/helpers/Splitter.ts index 96d677adc3..51b0de7d49 100644 --- a/src/helpers/Splitter.ts +++ b/src/helpers/Splitter.ts @@ -53,9 +53,31 @@ const trySplitModel = ( (options.splitDictionary === true && model instanceof DictionaryModel); if (shouldSplit) { - if (!models.includes(model)) { + let hasModel: boolean = false; + + for (const m of models) { + if (m === model) { + hasModel = true; + } + + // If a model with the same name is not extended somewhere, we have to force both not to be extended + if (m.name === model.name) { + // if both are extended we can continue + if (m.options.isExtended && model.options.isExtended) { + continue; + } + + if (m.options.isExtended || model.options.isExtended) { + m.options.isExtended = false; + model.options.isExtended = false; + } + } + } + + if (!hasModel) { models.push(model); } + return new ReferenceModel( model.name, model.originalInput, @@ -95,6 +117,19 @@ export const split = ( ); split(propertyModel, options, models, alreadySeenModels); } + + if (model.options.extend?.length) { + for (let index = 0; index < model.options.extend.length; index++) { + const extendModel = model.options.extend[Number(index)]; + extendModel.options.isExtended = true; + model.options.extend[Number(index)] = trySplitModel( + extendModel, + options, + models + ); + split(extendModel, options, models, alreadySeenModels); + } + } } else if (model instanceof UnionModel) { for (let index = 0; index < model.union.length; index++) { const unionModel = model.union[Number(index)]; diff --git a/src/helpers/index.ts b/src/helpers/index.ts index 4624b77036..c1c75769dc 100644 --- a/src/helpers/index.ts +++ b/src/helpers/index.ts @@ -7,3 +7,4 @@ export * from './Constraints'; export * from './ConstrainHelpers'; export * from './PresetHelpers'; export * from './DependencyHelpers'; +export * from './FilterHelpers'; diff --git a/src/interpreter/InterpretAllOf.ts b/src/interpreter/InterpretAllOf.ts index fe511ee97f..a89e367bf6 100644 --- a/src/interpreter/InterpretAllOf.ts +++ b/src/interpreter/InterpretAllOf.ts @@ -45,31 +45,39 @@ export default function interpretAllOf( for (const allOfSchema of schema.allOf) { const allOfModel = interpreter.interpret(allOfSchema, interpreterOptions); + if (allOfModel === undefined) { continue; } - if ( - isModelObject(allOfModel) === true && - interpreterOptions.allowInheritance === true - ) { - Logger.info( - `Processing allOf, inheritance is enabled, ${model.$id} inherits from ${allOfModel.$id}`, - model, - allOfModel - ); - model.addExtendedModel(allOfModel); - } else { - Logger.info( - 'Processing allOf, inheritance is not enabled. AllOf model is merged together with already interpreted model', - model, - allOfModel - ); - interpreter.interpretAndCombineSchema( - allOfSchema, - model, - schema, - interpreterOptions - ); + + if (interpreterOptions.allowInheritance === true) { + const allOfModelWithoutCache = interpreter.interpret(allOfSchema, { + ...interpreterOptions, + disableCache: true + }); + + if (allOfModelWithoutCache && isModelObject(allOfModelWithoutCache)) { + Logger.info( + `Processing allOf, inheritance is enabled, ${model.$id} inherits from ${allOfModelWithoutCache.$id}`, + model, + allOfModel + ); + + model.addExtendedModel(allOfModelWithoutCache); + } } + + Logger.info( + 'Processing allOf, inheritance is not enabled. AllOf model is merged together with already interpreted model', + model, + allOfModel + ); + + interpreter.interpretAndCombineSchema( + allOfSchema, + model, + schema, + interpreterOptions + ); } } diff --git a/src/interpreter/Interpreter.ts b/src/interpreter/Interpreter.ts index 157a6102f8..ef7df5dd61 100644 --- a/src/interpreter/Interpreter.ts +++ b/src/interpreter/Interpreter.ts @@ -50,6 +50,13 @@ export type InterpreterOptions = { * When interpreting a schema with discriminator set, this property will be set best by the individual interpreters to make sure the discriminator becomes an enum. */ discriminator?: string; + /** + * This options disables the seenSchemas cache in the Interpreter. + * Use this option to disable the seenSchemas cache when interpreting schemas. + * This will affect merging of schemas, and should only be used by the internal interpreters when allowInheritance is set to true. + * This allows the internal interpreters to keep clean copies of the schemas as CommonModel's. + */ + disableCache?: boolean; }; export type InterpreterSchemas = | Draft6Schema @@ -64,7 +71,8 @@ export class Interpreter { static defaultInterpreterOptions: InterpreterOptions = { allowInheritance: false, ignoreAdditionalProperties: false, - ignoreAdditionalItems: false + ignoreAdditionalItems: false, + disableCache: false }; private anonymCounter = 1; @@ -80,7 +88,7 @@ export class Interpreter { schema: InterpreterSchemaType, options: InterpreterOptions = Interpreter.defaultInterpreterOptions ): CommonModel | undefined { - if (this.seenSchemas.has(schema)) { + if (!options.disableCache && this.seenSchemas.has(schema)) { const cachedModel = this.seenSchemas.get(schema); if (cachedModel !== undefined) { return cachedModel; @@ -92,7 +100,9 @@ export class Interpreter { } const model = new CommonModel(); model.originalInput = schema; - this.seenSchemas.set(schema, model); + if (!options.disableCache) { + this.seenSchemas.set(schema, model); + } this.interpretSchema(model, schema, options); return model; } diff --git a/src/models/CommonModel.ts b/src/models/CommonModel.ts index 4e023d6037..aebcd32852 100644 --- a/src/models/CommonModel.ts +++ b/src/models/CommonModel.ts @@ -15,7 +15,7 @@ export const defaultMergingOptions: MergingOptions = { * Common internal representation for a model. */ export class CommonModel { - extend?: string[]; + extend?: CommonModel[]; originalInput?: any; $id?: string; type?: string | string[]; @@ -426,7 +426,10 @@ export class CommonModel { * @param extendedModel */ addExtendedModel(extendedModel: CommonModel): void { - if (extendedModel.$id === undefined) { + if ( + extendedModel.$id === undefined || + CommonModel.idIncludesAnonymousSchema(extendedModel) + ) { Logger.error( 'Found no $id for allOf model and cannot extend the existing model, this should never happen.', this, @@ -434,8 +437,10 @@ export class CommonModel { ); return; } - this.extend = this.extend || []; - if (this.extend.includes(extendedModel.$id)) { + + if ( + this.extend?.find((commonModel) => commonModel.$id === extendedModel.$id) + ) { Logger.info( `${this.$id} model already extends model ${extendedModel.$id}.`, this, @@ -443,7 +448,8 @@ export class CommonModel { ); return; } - this.extend.push(extendedModel.$id); + this.extend = this.extend ?? []; + this.extend.push(extendedModel); } /** diff --git a/src/models/ConstrainedMetaModel.ts b/src/models/ConstrainedMetaModel.ts index b65ac61452..e52a92ee3e 100644 --- a/src/models/ConstrainedMetaModel.ts +++ b/src/models/ConstrainedMetaModel.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { DeepPartial, mergePartialAndDefault } from '../utils'; import { makeUnique } from '../helpers/DependencyHelpers'; import { MetaModel, @@ -20,8 +22,17 @@ export class ConstrainedMetaModelOptions extends MetaModelOptions { const?: ConstrainedMetaModelOptionsConst; discriminator?: ConstrainedMetaModelOptionsDiscriminator; parents?: ConstrainedMetaModel[]; + extend?: ConstrainedMetaModel[]; } +export interface GetNearestDependenciesArgument { + alreadyVisitedNodes: any[]; +} + +const defaultGetNearestDependenciesArgument: GetNearestDependenciesArgument = { + alreadyVisitedNodes: [] +}; + export abstract class ConstrainedMetaModel extends MetaModel { public options: ConstrainedMetaModelOptions; @@ -54,7 +65,9 @@ export abstract class ConstrainedMetaModel extends MetaModel { * * This is often used when you want to know which other models you are referencing. */ - getNearestDependencies(): ConstrainedMetaModel[] { + getNearestDependencies( + arg?: DeepPartial + ): ConstrainedMetaModel[] { return []; } } @@ -92,16 +105,25 @@ export class ConstrainedTupleModel extends ConstrainedMetaModel { super(name, originalInput, options, type); } - getNearestDependencies(): ConstrainedMetaModel[] { + getNearestDependencies( + arg?: DeepPartial + ): ConstrainedMetaModel[] { + const argumentsToUse = mergePartialAndDefault( + defaultGetNearestDependenciesArgument, + arg + ) as GetNearestDependenciesArgument; + argumentsToUse.alreadyVisitedNodes.push(this); let dependencyModels: ConstrainedMetaModel[] = []; for (const tupleModel of Object.values(this.tuple)) { if (tupleModel.value instanceof ConstrainedReferenceModel) { dependencyModels.push(tupleModel.value); - } else { + } else if ( + !argumentsToUse.alreadyVisitedNodes.includes(tupleModel.value) + ) { //Lets check the non-reference model for dependencies dependencyModels = [ ...dependencyModels, - ...tupleModel.value.getNearestDependencies() + ...tupleModel.value.getNearestDependencies(argumentsToUse) ]; } } @@ -131,11 +153,20 @@ export class ConstrainedArrayModel extends ConstrainedMetaModel { super(name, originalInput, options, type); } - getNearestDependencies(): ConstrainedMetaModel[] { + getNearestDependencies( + arg?: DeepPartial + ): ConstrainedMetaModel[] { + const argumentsToUse = mergePartialAndDefault( + defaultGetNearestDependenciesArgument, + arg + ) as GetNearestDependenciesArgument; + argumentsToUse.alreadyVisitedNodes.push(this); if (this.valueModel instanceof ConstrainedReferenceModel) { return [this.valueModel]; + } else if (!argumentsToUse.alreadyVisitedNodes.includes(this.valueModel)) { + return this.valueModel.getNearestDependencies(argumentsToUse); } - return this.valueModel.getNearestDependencies(); + return []; } } export class ConstrainedUnionModel extends ConstrainedMetaModel { @@ -149,16 +180,23 @@ export class ConstrainedUnionModel extends ConstrainedMetaModel { super(name, originalInput, options, type); } - getNearestDependencies(): ConstrainedMetaModel[] { + getNearestDependencies( + arg?: DeepPartial + ): ConstrainedMetaModel[] { + const argumentsToUse = mergePartialAndDefault( + defaultGetNearestDependenciesArgument, + arg + ) as GetNearestDependenciesArgument; + argumentsToUse.alreadyVisitedNodes.push(this); let dependencyModels: ConstrainedMetaModel[] = []; for (const unionModel of Object.values(this.union)) { if (unionModel instanceof ConstrainedReferenceModel) { dependencyModels.push(unionModel); - } else { + } else if (!argumentsToUse.alreadyVisitedNodes.includes(unionModel)) { //Lets check the non-reference model for dependencies dependencyModels = [ ...dependencyModels, - ...unionModel.getNearestDependencies() + ...unionModel.getNearestDependencies(argumentsToUse) ]; } } @@ -200,17 +238,24 @@ export class ConstrainedDictionaryModel extends ConstrainedMetaModel { super(name, originalInput, options, type); } - getNearestDependencies(): ConstrainedMetaModel[] { + getNearestDependencies( + arg?: DeepPartial + ): ConstrainedMetaModel[] { + const argumentsToUse = mergePartialAndDefault( + defaultGetNearestDependenciesArgument, + arg + ) as GetNearestDependenciesArgument; + argumentsToUse.alreadyVisitedNodes.push(this); const dependencies = [this.key, this.value]; let dependencyModels: ConstrainedMetaModel[] = []; for (const model of dependencies) { if (model instanceof ConstrainedReferenceModel) { dependencyModels.push(model); - } else { + } else if (!argumentsToUse.alreadyVisitedNodes.includes(model)) { //Lets check the non-reference model for dependencies dependencyModels = [ ...dependencyModels, - ...model.getNearestDependencies() + ...model.getNearestDependencies(argumentsToUse) ]; } } @@ -233,16 +278,25 @@ export class ConstrainedObjectModel extends ConstrainedMetaModel { super(name, originalInput, options, type); } - getNearestDependencies(): ConstrainedMetaModel[] { + getNearestDependencies( + arg?: DeepPartial + ): ConstrainedMetaModel[] { + const argumentsToUse = mergePartialAndDefault( + defaultGetNearestDependenciesArgument, + arg + ) as GetNearestDependenciesArgument; + argumentsToUse.alreadyVisitedNodes.push(this); let dependencyModels: ConstrainedMetaModel[] = []; for (const modelProperty of Object.values(this.properties)) { if (modelProperty.property instanceof ConstrainedReferenceModel) { dependencyModels.push(modelProperty.property); - } else { + } else if ( + !argumentsToUse.alreadyVisitedNodes.includes(modelProperty.property) + ) { //Lets check the non-reference model for dependencies dependencyModels = [ ...dependencyModels, - ...modelProperty.property.getNearestDependencies() + ...modelProperty.property.getNearestDependencies(argumentsToUse) ]; } } diff --git a/src/models/MetaModel.ts b/src/models/MetaModel.ts index e3233d6d04..ebf5f077fa 100644 --- a/src/models/MetaModel.ts +++ b/src/models/MetaModel.ts @@ -11,6 +11,8 @@ export class MetaModelOptions { discriminator?: MetaModelOptionsDiscriminator; isNullable?: boolean = false; format?: string; + extend?: MetaModel[]; + isExtended?: boolean; } export class MetaModel { diff --git a/src/processors/AsyncAPIInputProcessor.ts b/src/processors/AsyncAPIInputProcessor.ts index a7f96a591d..54c7dd69d5 100644 --- a/src/processors/AsyncAPIInputProcessor.ts +++ b/src/processors/AsyncAPIInputProcessor.ts @@ -1,38 +1,30 @@ /* eslint-disable no-undef */ /* eslint-disable @typescript-eslint/no-var-requires */ -import { - createAsyncAPIDocument, +import Parser, { isAsyncAPIDocument, isOldAsyncAPIDocument, - Parser, AsyncAPIDocumentInterface, SchemaInterface as AsyncAPISchemaInterface, - SchemaV2 as AsyncAPISchema + SchemaV2 as AsyncAPISchema, + fromFile, + createAsyncAPIDocument } from '@asyncapi/parser'; -import { AsyncAPISchemaObject } from '@asyncapi/parser/cjs/spec-types/v2'; -import { AvroSchemaParser } from '@asyncapi/avro-schema-parser'; -import { OpenAPISchemaParser } from '@asyncapi/openapi-schema-parser'; -import { RamlDTSchemaParser } from '@asyncapi/raml-dt-schema-parser'; -import { createDetailedAsyncAPI } from '@asyncapi/parser/cjs/utils'; + import { AbstractInputProcessor } from './AbstractInputProcessor'; import { JsonSchemaInputProcessor } from './JsonSchemaInputProcessor'; -import { InputMetaModel, ProcessorOptions, UnionModel } from '../models'; +import { InputMetaModel, ProcessorOptions } from '../models'; import { Logger } from '../utils'; import { AsyncapiV2Schema } from '../models/AsyncapiV2Schema'; import { convertToMetaModel } from '../helpers'; +import fs from 'fs'; +import { fileURLToPath } from 'url'; +import { NewParser } from '@smoya/multi-parser'; +import { createDetailedAsyncAPI } from '@asyncapi/parser/cjs/utils'; /** * Class for processing AsyncAPI inputs */ export class AsyncAPIInputProcessor extends AbstractInputProcessor { - private parser = new Parser(); - constructor() { - super(); - this.parser.registerSchemaParser(AvroSchemaParser()); - this.parser.registerSchemaParser(OpenAPISchemaParser()); - this.parser.registerSchemaParser(RamlDTSchemaParser()); - } - static supportedVersions = [ '2.0.0', '2.1.0', @@ -40,7 +32,8 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor { '2.3.0', '2.4.0', '2.5.0', - '2.6.0' + '2.6.0', + '3.0.0' ]; /** @@ -53,22 +46,37 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor { input?: any, options?: ProcessorOptions ): Promise { - if (!this.shouldProcess(input)) { + let rawInput = input; + if (this.isFileInput(input)) { + rawInput = await this.getParsedFileInput(input); + } + + if (!this.shouldProcess(rawInput)) { throw new Error( 'Input is not an AsyncAPI document so it cannot be processed.' ); } Logger.debug('Processing input as an AsyncAPI document'); - let doc: AsyncAPIDocumentInterface; + let doc: AsyncAPIDocumentInterface | undefined; const inputModel = new InputMetaModel(); - if (!AsyncAPIInputProcessor.isFromParser(input)) { - const { document, diagnostics } = await this.parser.parse( - input as any, - options?.asyncapi || {} + if (isOldAsyncAPIDocument(rawInput)) { + // Is from old parser + const parsedJSON = rawInput.json(); + const detailed = createDetailedAsyncAPI(parsedJSON, parsedJSON); + doc = createAsyncAPIDocument(detailed); + } else { + const parserOptions = options?.asyncapi || {}; + const parser = NewParser(2, { + parserOptions, + includeSchemaParsers: true + }); + const { document, diagnostics } = await parser.parse( + rawInput, + parserOptions ); if (document) { - doc = document; + doc = document as unknown as AsyncAPIDocumentInterface; } else { const err = new Error( 'Input is not an correct AsyncAPI document so it cannot be processed.' @@ -76,33 +84,14 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor { (err as any).diagnostics = diagnostics; throw err; } - } else if (AsyncAPIInputProcessor.isFromNewParser(input)) { - doc = input as AsyncAPIDocumentInterface; - } else { - // Is from old parser - const parsedJSON = input.json(); - const detailed = createDetailedAsyncAPI(parsedJSON, parsedJSON); - doc = createAsyncAPIDocument(detailed); + } + if (!doc) { + throw new Error('Could not parse input as AsyncAPI document'); } inputModel.originalInput = doc; const addToInputModel = (payload: AsyncAPISchemaInterface) => { - const id = payload.title() || payload.id(); - - for (const model of Object.values(inputModel.models)) { - if (model instanceof UnionModel) { - for (const union of model.union) { - if (union.name === id) { - Logger.warn( - `Model ${id} has already been added to the input model` - ); - return; - } - } - } - } - const schema = AsyncAPIInputProcessor.convertToInternalSchema(payload); const newCommonModel = JsonSchemaInputProcessor.convertSchemaToCommonModel(schema, options); @@ -134,7 +123,7 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor { // treat multiple messages as oneOf if (operationMessages.length > 1) { - const oneOf: AsyncAPISchemaObject[] = []; + const oneOf: any[] = []; for (const message of operationMessages) { const payload = message.payload(); @@ -163,13 +152,6 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor { } } } - - for (const message of doc.messages()) { - const payload = message.payload(); - if (payload) { - addToInputModel(payload); - } - } } else { for (const message of doc.allMessages()) { const payload = message.payload(); @@ -226,21 +208,21 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor { if (schema.allOf()) { convertedSchema.allOf = schema .allOf()! - .map((item) => + .map((item: any) => this.convertToInternalSchema(item, alreadyIteratedSchemas) ); } if (schema.oneOf()) { convertedSchema.oneOf = schema .oneOf()! - .map((item) => + .map((item: any) => this.convertToInternalSchema(item, alreadyIteratedSchemas) ); } if (schema.anyOf()) { convertedSchema.anyOf = schema .anyOf()! - .map((item) => + .map((item: any) => this.convertToInternalSchema(item, alreadyIteratedSchemas) ); } @@ -342,7 +324,7 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor { alreadyIteratedSchemas ); } else { - dependencies[String(dependencyName)] = dependency as string[]; + dependencies[String(dependencyName)] = dependency; } } convertedSchema.dependencies = dependencies; @@ -390,6 +372,9 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor { if (!input) { return false; } + if (this.isFileInput(input)) { + return true; + } const version = this.tryGetVersionOfDocument(input); if (!version) { return false; @@ -409,7 +394,7 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor { if (AsyncAPIInputProcessor.isFromParser(input)) { return input.version(); } - return input && input.asyncapi; + return input?.asyncapi; } /** @@ -429,4 +414,27 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor { static isFromNewParser(input?: any): boolean { return isAsyncAPIDocument(input); } + + isFileInput(input: any): boolean { + // prettier-ignore + return typeof input === 'string' && (/^file:\/\//g).test(input); + } + + async getParsedFileInput(input: string): Promise { + const filePath = fileURLToPath(input); + /* eslint-disable-next-line security/detect-non-literal-fs-filename -- Safe as it just checks file existance */ + if (!fs.existsSync(filePath)) { + throw new Error('File does not exists.'); + } + const parser = new Parser(); + const { document, diagnostics } = await fromFile(parser, filePath).parse(); + if (!document) { + const err = new Error( + 'Input is not an correct AsyncAPI document so it cannot be processed.' + ); + (err as any).diagnostics = diagnostics; + throw err; + } + return document; + } } diff --git a/src/processors/JsonSchemaInputProcessor.ts b/src/processors/JsonSchemaInputProcessor.ts index b64aae1a66..e5b9071464 100644 --- a/src/processors/JsonSchemaInputProcessor.ts +++ b/src/processors/JsonSchemaInputProcessor.ts @@ -1,5 +1,5 @@ import { AbstractInputProcessor } from './AbstractInputProcessor'; -import $RefParser from '@apidevtools/json-schema-ref-parser'; +import { dereference } from '@apidevtools/json-schema-ref-parser'; import path from 'path'; import { CommonModel, @@ -15,6 +15,7 @@ import { import { Logger } from '../utils'; import { Interpreter } from '../interpreter/Interpreter'; import { convertToMetaModel } from '../helpers'; +import { ParserOptions } from '@apidevtools/json-schema-ref-parser/dist/lib/options'; /** * Class for processing JSON Schema @@ -84,7 +85,7 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { {}, 'root', true - ) as any; + ); input = await this.dereferenceInputs(input); const parsedSchema = Draft7Schema.toSchema(input); const newCommonModel = JsonSchemaInputProcessor.convertSchemaToCommonModel( @@ -114,7 +115,7 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { {}, 'root', true - ) as any; + ); input = await this.dereferenceInputs(input); const parsedSchema = Draft4Schema.toSchema(input); const newCommonModel = JsonSchemaInputProcessor.convertSchemaToCommonModel( @@ -144,7 +145,7 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { {}, 'root', true - ) as any; + ); input = await this.dereferenceInputs(input); const parsedSchema = Draft6Schema.toSchema(input); const newCommonModel = JsonSchemaInputProcessor.convertSchemaToCommonModel( @@ -197,12 +198,19 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { public async dereferenceInputs(input: any): Promise { input = this.handleRootReference(input); Logger.debug('Dereferencing all $ref instances'); - const refParser = new $RefParser(); // eslint-disable-next-line no-undef const localPath = `${process.cwd()}${path.sep}`; - const deRefOption: $RefParser.Options = { + const deRefOption: ParserOptions = { continueOnError: true, - dereference: { circular: true } + dereference: { + circular: true, + excludedPathMatcher: (path: string) => { + return ( + path.includes('/examples/') && + !path.includes('/properties/examples/') + ); + } + } }; Logger.debug( `Trying to dereference all $ref instances from input, using option ${JSON.stringify( @@ -210,7 +218,7 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { )}.` ); try { - await refParser.dereference(localPath, input, deRefOption); + await dereference(localPath, input, deRefOption); } catch (e: any) { const errorMessage = `Could not dereference $ref in input, is all the references correct? ${e.message}`; Logger.error(errorMessage, e); @@ -252,7 +260,7 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { return schema; } - schema = Object.assign({}, schema); + schema = { ...schema }; if (isRoot) { namesStack[String(name)] = 0; (schema as any)[this.MODELGEN_INFFERED_NAME] = name; diff --git a/src/utils/Partials.ts b/src/utils/Partials.ts index 5375686f44..d37fc250d2 100644 --- a/src/utils/Partials.ts +++ b/src/utils/Partials.ts @@ -36,17 +36,24 @@ export function mergePartialAndDefault>( customOptional?: DeepPartial ): T { if (customOptional === undefined) { - return defaultNonOptional; + return Object.assign({}, defaultNonOptional); } // create a new object - const target = { ...defaultNonOptional } as Record; + const target = Object.assign({}, defaultNonOptional) as Record; // deep merge the object into the target object for (const [propName, prop] of Object.entries(customOptional)) { const isObjectOrClass = typeof prop === 'object' && target[propName] !== undefined; const isRegularObject = !isClass(prop); - if (isObjectOrClass && isRegularObject) { + const isArray = Array.isArray(prop); + if (isArray) { + // merge array into target with a new array instance so we dont touch the default value + target[propName] = Array(target[propName]); + for (const [index, value] of prop.entries()) { + target[propName][index] = value; + } + } else if (isObjectOrClass && isRegularObject) { target[propName] = mergePartialAndDefault(target[propName], prop); } else if (prop) { target[propName] = prop; diff --git a/test/blackbox/docs/AsyncAPI-2_6/dummy.json b/test/blackbox/docs/AsyncAPI-2_6/dummy.json index 1ed6f19df8..46cfbbd6b8 100644 --- a/test/blackbox/docs/AsyncAPI-2_6/dummy.json +++ b/test/blackbox/docs/AsyncAPI-2_6/dummy.json @@ -58,6 +58,21 @@ "type": "string", "format": "email", "description": "Email of the user" + }, + "signedUpAt": { + "type": "string", + "format": "date-time", + "description": "Sign-up processed date and time" + }, + "sessionDuration": { + "type": "string", + "format": "time", + "description": "Maximum session duration" + }, + "uniqueUserId": { + "type": "string", + "format": "uuid", + "description": "Unique user id" } } } diff --git a/test/generators/cplusplus/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/cplusplus/constrainer/PropertyKeyConstrainer.spec.ts index d4126c7829..994b3d6b57 100644 --- a/test/generators/cplusplus/constrainer/PropertyKeyConstrainer.spec.ts +++ b/test/generators/cplusplus/constrainer/PropertyKeyConstrainer.spec.ts @@ -1,4 +1,3 @@ -import { CplusplusDefaultConstraints } from '../../../../src/generators/cplusplus/CplusplusConstrainer'; import { ConstrainedObjectModel, ConstrainedObjectPropertyModel, @@ -6,7 +5,6 @@ import { ObjectPropertyModel } from '../../../../src'; import { - DefaultPropertyKeyConstraints, PropertyKeyConstraintOptions, defaultPropertyKeyConstraints } from '../../../../src/generators/cplusplus/constrainer/PropertyKeyConstrainer'; diff --git a/test/generators/csharp/CSharpConstrainer.spec.ts b/test/generators/csharp/CSharpConstrainer.spec.ts index e9285dc01a..26fbb989d9 100644 --- a/test/generators/csharp/CSharpConstrainer.spec.ts +++ b/test/generators/csharp/CSharpConstrainer.spec.ts @@ -93,6 +93,45 @@ describe('CSharpConstrainer', () => { }); expect(type).toEqual('string'); }); + test('should render System.DateTime', () => { + const model = new ConstrainedStringModel( + 'test', + undefined, + { format: 'date-time' }, + '' + ); + const type = CSharpDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('System.DateTime'); + }); + test('should render TimeSpan', () => { + const model = new ConstrainedStringModel( + 'test', + undefined, + { format: 'time' }, + '' + ); + const type = CSharpDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('System.TimeSpan'); + }); + test('should render Guid', () => { + const model = new ConstrainedStringModel( + 'test', + undefined, + { format: 'uuid' }, + '' + ); + const type = CSharpDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('System.Guid'); + }); }); describe('Boolean', () => { test('should render type', () => { diff --git a/test/generators/csharp/constrainer/EnumConstrainer.spec.ts b/test/generators/csharp/constrainer/EnumConstrainer.spec.ts index 1ec31923d8..42f72d23d4 100644 --- a/test/generators/csharp/constrainer/EnumConstrainer.spec.ts +++ b/test/generators/csharp/constrainer/EnumConstrainer.spec.ts @@ -1,6 +1,7 @@ import { CSharpDefaultConstraints } from '../../../../src/generators/csharp/CSharpConstrainer'; import { EnumModel } from '../../../../src/models/MetaModel'; import { + CSharpGenerator, ConstrainedEnumModel, ConstrainedEnumValueModel } from '../../../../src'; @@ -24,7 +25,8 @@ describe('EnumConstrainer', () => { const constrainedKey = CSharpDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '%' + enumKey: '%', + options: CSharpGenerator.defaultOptions }); expect(constrainedKey).toEqual('PERCENT'); }); @@ -32,7 +34,8 @@ describe('EnumConstrainer', () => { const constrainedKey = CSharpDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '1' + enumKey: '1', + options: CSharpGenerator.defaultOptions }); expect(constrainedKey).toEqual('NUMBER_1'); }); @@ -52,7 +55,8 @@ describe('EnumConstrainer', () => { const constrainedKey = CSharpDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: CSharpGenerator.defaultOptions }); expect(constrainedKey).toEqual('RESERVED_EMPTY'); }); @@ -60,7 +64,8 @@ describe('EnumConstrainer', () => { const constrainedKey = CSharpDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: CSharpGenerator.defaultOptions }); expect(constrainedKey).toEqual('EMPTY'); }); @@ -68,7 +73,8 @@ describe('EnumConstrainer', () => { const constrainedKey = CSharpDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: 'some weird_value!"#2' + enumKey: 'some weird_value!"#2', + options: CSharpGenerator.defaultOptions }); expect(constrainedKey).toEqual( 'SOME_SPACE_WEIRD_VALUE_EXCLAMATION_QUOTATION_HASH_2' @@ -78,7 +84,8 @@ describe('EnumConstrainer', () => { const constrainedKey = CSharpDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: 'return' + enumKey: 'return', + options: CSharpGenerator.defaultOptions }); expect(constrainedKey).toEqual('RESERVED_RETURN'); }); @@ -88,7 +95,8 @@ describe('EnumConstrainer', () => { const constrainedValue = CSharpDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: 'string value' + enumValue: 'string value', + options: CSharpGenerator.defaultOptions }); expect(constrainedValue).toEqual('"string value"'); }); @@ -96,7 +104,8 @@ describe('EnumConstrainer', () => { const constrainedValue = CSharpDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: true + enumValue: true, + options: CSharpGenerator.defaultOptions }); expect(constrainedValue).toEqual(true); }); @@ -104,7 +113,8 @@ describe('EnumConstrainer', () => { const constrainedValue = CSharpDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: 123 + enumValue: 123, + options: CSharpGenerator.defaultOptions }); expect(constrainedValue).toEqual(123); }); @@ -112,7 +122,8 @@ describe('EnumConstrainer', () => { const constrainedValue = CSharpDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: { test: 'test' } + enumValue: { test: 'test' }, + options: CSharpGenerator.defaultOptions }); expect(constrainedValue).toEqual('"{\\"test\\":\\"test\\"}"'); }); @@ -120,7 +131,8 @@ describe('EnumConstrainer', () => { const constrainedValue = CSharpDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: undefined + enumValue: undefined, + options: CSharpGenerator.defaultOptions }); expect(constrainedValue).toEqual('"undefined"'); }); @@ -137,7 +149,12 @@ describe('EnumConstrainer', () => { const constrainFunction = defaultEnumKeyConstraints( mockedConstraintCallbacks ); - constrainFunction({ enumModel, constrainedEnumModel, enumKey: '' }); + constrainFunction({ + enumModel, + constrainedEnumModel, + enumKey: '', + options: CSharpGenerator.defaultOptions + }); //Expect all callbacks to be called for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { expect(jestMockCallback).toHaveBeenCalled(); @@ -159,7 +176,8 @@ describe('EnumConstrainer', () => { const constrainedValue = constrainFunction({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: CSharpGenerator.defaultOptions }); expect(constrainedValue).toEqual(''); for (const jestMockCallback of spies) { diff --git a/test/generators/csharp/constrainer/ModelNameConstrainer.spec.ts b/test/generators/csharp/constrainer/ModelNameConstrainer.spec.ts index 9aa2f539f9..ac62267856 100644 --- a/test/generators/csharp/constrainer/ModelNameConstrainer.spec.ts +++ b/test/generators/csharp/constrainer/ModelNameConstrainer.spec.ts @@ -1,3 +1,4 @@ +import { CSharpGenerator } from '../../../../src'; import { CSharpDefaultConstraints } from '../../../../src/generators/csharp/CSharpConstrainer'; import { DefaultModelNameConstraints, @@ -7,31 +8,36 @@ import { describe('ModelNameConstrainer', () => { test('should never render special chars', () => { const constrainedKey = CSharpDefaultConstraints.modelName({ - modelName: '%' + modelName: '%', + options: CSharpGenerator.defaultOptions }); expect(constrainedKey).toEqual('Percent'); }); test('should never render number as start char', () => { const constrainedKey = CSharpDefaultConstraints.modelName({ - modelName: '1' + modelName: '1', + options: CSharpGenerator.defaultOptions }); expect(constrainedKey).toEqual('Number_1'); }); test('should never contain empty name', () => { const constrainedKey = CSharpDefaultConstraints.modelName({ - modelName: '' + modelName: '', + options: CSharpGenerator.defaultOptions }); expect(constrainedKey).toEqual('Empty'); }); test('should use constant naming format', () => { const constrainedKey = CSharpDefaultConstraints.modelName({ - modelName: 'some weird_value!"#2' + modelName: 'some weird_value!"#2', + options: CSharpGenerator.defaultOptions }); expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash_2'); }); test('should never render reserved keywords', () => { const constrainedKey = CSharpDefaultConstraints.modelName({ - modelName: 'return' + modelName: 'return', + options: CSharpGenerator.defaultOptions }); expect(constrainedKey).toEqual('ReservedReturn'); }); @@ -47,7 +53,10 @@ describe('ModelNameConstrainer', () => { const constrainFunction = defaultModelNameConstraints( mockedConstraintCallbacks ); - constrainFunction({ modelName: '' }); + constrainFunction({ + modelName: '', + options: CSharpGenerator.defaultOptions + }); //Expect all callbacks to be called for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { expect(jestMockCallback).toHaveBeenCalled(); @@ -65,7 +74,10 @@ describe('ModelNameConstrainer', () => { const constrainFunction = defaultModelNameConstraints({ NAMING_FORMATTER: jestCallback }); - const constrainedValue = constrainFunction({ modelName: '' }); + const constrainedValue = constrainFunction({ + modelName: '', + options: CSharpGenerator.defaultOptions + }); expect(constrainedValue).toEqual(''); for (const jestMockCallback of spies) { expect(jestMockCallback).toHaveBeenCalled(); diff --git a/test/generators/csharp/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/csharp/constrainer/PropertyKeyConstrainer.spec.ts index 2c2e86c94c..2d5291ba40 100644 --- a/test/generators/csharp/constrainer/PropertyKeyConstrainer.spec.ts +++ b/test/generators/csharp/constrainer/PropertyKeyConstrainer.spec.ts @@ -1,5 +1,6 @@ import { CSharpDefaultConstraints } from '../../../../src/generators/csharp/CSharpConstrainer'; import { + CSharpGenerator, ConstrainedObjectModel, ConstrainedObjectPropertyModel, ObjectModel, @@ -36,7 +37,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: CSharpGenerator.defaultOptions }); }; @@ -99,7 +101,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel: objectPropertyModel2, - constrainedObjectPropertyModel: constrainedObjectPropertyModel2 + constrainedObjectPropertyModel: constrainedObjectPropertyModel2, + options: CSharpGenerator.defaultOptions }); expect(constrainedKey).toEqual('reservedReservedReturn'); }); @@ -135,7 +138,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: CSharpGenerator.defaultOptions }); //Expect all callbacks to be called for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { @@ -174,7 +178,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: CSharpGenerator.defaultOptions }); expect(constrainedValue).toEqual(''); expect(jestCallback).toHaveBeenCalled(); diff --git a/test/generators/csharp/presets/CommonPreset.spec.ts b/test/generators/csharp/presets/CommonPreset.spec.ts index 4ba4258fac..6d37a2b93b 100644 --- a/test/generators/csharp/presets/CommonPreset.spec.ts +++ b/test/generators/csharp/presets/CommonPreset.spec.ts @@ -1,7 +1,6 @@ import { CSharpGenerator, - CSHARP_COMMON_PRESET, - CSHARP_DEFAULT_PRESET + CSHARP_COMMON_PRESET } from '../../../../src/generators'; const doc = { $id: 'Test', diff --git a/test/generators/csharp/presets/__snapshots__/NewtonsoftSerializerPreset.spec.ts.snap b/test/generators/csharp/presets/__snapshots__/NewtonsoftSerializerPreset.spec.ts.snap index 3ea6419f36..698c143006 100644 --- a/test/generators/csharp/presets/__snapshots__/NewtonsoftSerializerPreset.spec.ts.snap +++ b/test/generators/csharp/presets/__snapshots__/NewtonsoftSerializerPreset.spec.ts.snap @@ -55,7 +55,7 @@ if(jo[\\"numberProp\\"] != null) { value.NumberProp = jo[\\"numberProp\\"].ToObject(serializer); } if(jo[\\"enumProp\\"] != null) { - value.EnumProp = EnumTestExtensions.ToEnumTest(jo[\\"enumProp\\"]); + value.EnumProp = EnumTestExtensions.ToEnumTest(jo[\\"enumProp\\"].ToString()); } if(jo[\\"objectProp\\"] != null) { value.ObjectProp = jo[\\"objectProp\\"].ToObject(serializer); @@ -88,8 +88,7 @@ if (value.EnumProp != null) var stringEnumValue = enumValue.ToString(); // C# converts booleans to uppercase True and False, which newtonsoft cannot understand var jsonStringCompliant = stringEnumValue == \\"True\\" || stringEnumValue == \\"False\\" ? stringEnumValue.ToLower() : stringEnumValue; -var jsonToken = JToken.Parse(jsonStringCompliant); -jo.Add(\\"enumProp\\", jsonToken); +jo.Add(\\"enumProp\\", JToken.FromObject(jsonStringCompliant, serializer)); } if (value.ObjectProp != null) { diff --git a/test/generators/dart/DartConstrainer.spec.ts b/test/generators/dart/DartConstrainer.spec.ts index 4f2d72bbd2..04050180ef 100644 --- a/test/generators/dart/DartConstrainer.spec.ts +++ b/test/generators/dart/DartConstrainer.spec.ts @@ -13,8 +13,7 @@ import { ConstrainedReferenceModel, ConstrainedStringModel, ConstrainedTupleModel, - ConstrainedUnionModel, - InputMetaModel + ConstrainedUnionModel } from '../../../src/models'; describe('DartConstrainer', () => { diff --git a/test/generators/dart/DartRenderer.spec.ts b/test/generators/dart/DartRenderer.spec.ts index bc41f65695..afdc9e04f2 100644 --- a/test/generators/dart/DartRenderer.spec.ts +++ b/test/generators/dart/DartRenderer.spec.ts @@ -1,7 +1,4 @@ -import { - defaultGeneratorOptions, - DartGenerator -} from '../../../src/generators'; +import { DartGenerator } from '../../../src/generators'; import { DartDependencyManager } from '../../../src/generators/dart/DartDependencyManager'; import { DartRenderer } from '../../../src/generators/dart/DartRenderer'; import { ConstrainedObjectModel, InputMetaModel } from '../../../src/models'; diff --git a/test/generators/dart/constrainer/EnumConstrainer.spec.ts b/test/generators/dart/constrainer/EnumConstrainer.spec.ts index aedbc3dda2..d020608787 100644 --- a/test/generators/dart/constrainer/EnumConstrainer.spec.ts +++ b/test/generators/dart/constrainer/EnumConstrainer.spec.ts @@ -2,7 +2,8 @@ import { DartDefaultConstraints } from '../../../../src/generators/dart/DartCons import { EnumModel } from '../../../../src/models/MetaModel'; import { ConstrainedEnumModel, - ConstrainedEnumValueModel + ConstrainedEnumValueModel, + DartGenerator } from '../../../../src'; import { defaultEnumKeyConstraints, @@ -24,7 +25,8 @@ describe('EnumConstrainer', () => { const constrainedKey = DartDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '%' + enumKey: '%', + options: DartGenerator.defaultOptions }); expect(constrainedKey).toEqual('PERCENT'); }); @@ -32,7 +34,8 @@ describe('EnumConstrainer', () => { const constrainedKey = DartDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '1' + enumKey: '1', + options: DartGenerator.defaultOptions }); expect(constrainedKey).toEqual('NUMBER_1'); }); @@ -52,7 +55,8 @@ describe('EnumConstrainer', () => { const constrainedKey = DartDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: DartGenerator.defaultOptions }); expect(constrainedKey).toEqual('RESERVED_EMPTY'); }); @@ -60,7 +64,8 @@ describe('EnumConstrainer', () => { const constrainedKey = DartDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: DartGenerator.defaultOptions }); expect(constrainedKey).toEqual('EMPTY'); }); @@ -68,7 +73,8 @@ describe('EnumConstrainer', () => { const constrainedKey = DartDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: 'some weird_value!"#2' + enumKey: 'some weird_value!"#2', + options: DartGenerator.defaultOptions }); expect(constrainedKey).toEqual( 'SOME_WEIRD_VALUE_EXCLAMATION_QUOTATION_HASH_2' @@ -78,7 +84,8 @@ describe('EnumConstrainer', () => { const constrainedKey = DartDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: 'return' + enumKey: 'return', + options: DartGenerator.defaultOptions }); expect(constrainedKey).toEqual('RESERVED_RETURN'); }); @@ -88,7 +95,8 @@ describe('EnumConstrainer', () => { const constrainedValue = DartDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: 'string value' + enumValue: 'string value', + options: DartGenerator.defaultOptions }); expect(constrainedValue).toEqual('"string value"'); }); @@ -96,7 +104,8 @@ describe('EnumConstrainer', () => { const constrainedValue = DartDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: true + enumValue: true, + options: DartGenerator.defaultOptions }); expect(constrainedValue).toEqual('"true"'); }); @@ -104,7 +113,8 @@ describe('EnumConstrainer', () => { const constrainedValue = DartDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: 123 + enumValue: 123, + options: DartGenerator.defaultOptions }); expect(constrainedValue).toEqual(123); }); @@ -112,7 +122,8 @@ describe('EnumConstrainer', () => { const constrainedValue = DartDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: { test: 'test' } + enumValue: { test: 'test' }, + options: DartGenerator.defaultOptions }); expect(constrainedValue).toEqual('"{\\"test\\":\\"test\\"}"'); }); @@ -120,7 +131,8 @@ describe('EnumConstrainer', () => { const constrainedValue = DartDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: undefined + enumValue: undefined, + options: DartGenerator.defaultOptions }); expect(constrainedValue).toEqual('"undefined"'); }); @@ -137,7 +149,12 @@ describe('EnumConstrainer', () => { const constrainFunction = defaultEnumKeyConstraints( mockedConstraintCallbacks ); - constrainFunction({ enumModel, constrainedEnumModel, enumKey: '' }); + constrainFunction({ + enumModel, + constrainedEnumModel, + enumKey: '', + options: DartGenerator.defaultOptions + }); //Expect all callbacks to be called for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { expect(jestMockCallback).toHaveBeenCalled(); @@ -159,7 +176,8 @@ describe('EnumConstrainer', () => { const constrainedValue = constrainFunction({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: DartGenerator.defaultOptions }); expect(constrainedValue).toEqual(''); for (const jestMockCallback of spies) { diff --git a/test/generators/dart/constrainer/ModelNameConstrainer.spec.ts b/test/generators/dart/constrainer/ModelNameConstrainer.spec.ts index 1a6ad450a6..9b1f26f482 100644 --- a/test/generators/dart/constrainer/ModelNameConstrainer.spec.ts +++ b/test/generators/dart/constrainer/ModelNameConstrainer.spec.ts @@ -1,3 +1,4 @@ +import { DartGenerator } from '../../../../src'; import { DartDefaultConstraints } from '../../../../src/generators/dart/DartConstrainer'; import { DefaultModelNameConstraints, @@ -6,20 +7,30 @@ import { } from '../../../../src/generators/dart/constrainer/ModelNameConstrainer'; describe('ModelNameConstrainer', () => { test('should never render special chars', () => { - const constrainedKey = DartDefaultConstraints.modelName({ modelName: '%' }); + const constrainedKey = DartDefaultConstraints.modelName({ + modelName: '%', + options: DartGenerator.defaultOptions + }); expect(constrainedKey).toEqual('Percent'); }); test('should never render number as start char', () => { - const constrainedKey = DartDefaultConstraints.modelName({ modelName: '1' }); + const constrainedKey = DartDefaultConstraints.modelName({ + modelName: '1', + options: DartGenerator.defaultOptions + }); expect(constrainedKey).toEqual('Number_1'); }); test('should never contain empty name', () => { - const constrainedKey = DartDefaultConstraints.modelName({ modelName: '' }); + const constrainedKey = DartDefaultConstraints.modelName({ + modelName: '', + options: DartGenerator.defaultOptions + }); expect(constrainedKey).toEqual('Empty'); }); test('should use constant naming format', () => { const constrainedKey = DartDefaultConstraints.modelName({ - modelName: 'some weird_value!"#2' + modelName: 'some weird_value!"#2', + options: DartGenerator.defaultOptions }); expect(constrainedKey).toEqual( 'SomeSpaceWeirdUnderscoreValueExclamationQuotationHash_2' @@ -27,7 +38,8 @@ describe('ModelNameConstrainer', () => { }); test('should never render reserved keywords', () => { const constrainedKey = DartDefaultConstraints.modelName({ - modelName: 'return' + modelName: 'return', + options: DartGenerator.defaultOptions }); expect(constrainedKey).toEqual('ReservedReturn'); }); @@ -43,7 +55,10 @@ describe('ModelNameConstrainer', () => { const constrainFunction = defaultModelNameConstraints( mockedConstraintCallbacks ); - constrainFunction({ modelName: '' }); + constrainFunction({ + modelName: '', + options: DartGenerator.defaultOptions + }); //Expect all callbacks to be called for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { expect(jestMockCallback).toHaveBeenCalled(); @@ -61,7 +76,10 @@ describe('ModelNameConstrainer', () => { const constrainFunction = defaultModelNameConstraints({ NAMING_FORMATTER: jestCallback }); - const constrainedValue = constrainFunction({ modelName: '' }); + const constrainedValue = constrainFunction({ + modelName: '', + options: DartGenerator.defaultOptions + }); expect(constrainedValue).toEqual(''); for (const jestMockCallback of spies) { expect(jestMockCallback).toHaveBeenCalled(); diff --git a/test/generators/dart/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/dart/constrainer/PropertyKeyConstrainer.spec.ts index 4e88ec352c..b110e2ffeb 100644 --- a/test/generators/dart/constrainer/PropertyKeyConstrainer.spec.ts +++ b/test/generators/dart/constrainer/PropertyKeyConstrainer.spec.ts @@ -2,6 +2,7 @@ import { DartDefaultConstraints } from '../../../../src/generators/dart/DartCons import { ConstrainedObjectModel, ConstrainedObjectPropertyModel, + DartGenerator, ObjectModel, ObjectPropertyModel } from '../../../../src'; @@ -36,7 +37,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: DartGenerator.defaultOptions }); }; afterEach(() => { @@ -98,7 +100,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel: objectPropertyModel2, - constrainedObjectPropertyModel: constrainedObjectPropertyModel2 + constrainedObjectPropertyModel: constrainedObjectPropertyModel2, + options: DartGenerator.defaultOptions }); expect(constrainedKey).toEqual('reservedReservedReturn'); }); @@ -133,7 +136,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: DartGenerator.defaultOptions }); //Expect all callbacks to be called for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { @@ -172,7 +176,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: DartGenerator.defaultOptions }); expect(constrainedValue).toEqual(''); expect(jestCallback).toHaveBeenCalled(); diff --git a/test/generators/go/constrainer/EnumConstrainer.spec.ts b/test/generators/go/constrainer/EnumConstrainer.spec.ts index 332856cf13..c4b49bec3f 100644 --- a/test/generators/go/constrainer/EnumConstrainer.spec.ts +++ b/test/generators/go/constrainer/EnumConstrainer.spec.ts @@ -2,7 +2,8 @@ import { GoDefaultConstraints } from '../../../../src/generators/go/GoConstraine import { EnumModel } from '../../../../src/models/MetaModel'; import { ConstrainedEnumModel, - ConstrainedEnumValueModel + ConstrainedEnumValueModel, + GoGenerator } from '../../../../src'; import { defaultEnumKeyConstraints, @@ -24,7 +25,8 @@ describe('EnumConstrainer', () => { const constrainedKey = GoDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '%' + enumKey: '%', + options: GoGenerator.defaultOptions }); expect(constrainedKey).toEqual('TestPercent'); }); @@ -32,7 +34,8 @@ describe('EnumConstrainer', () => { const constrainedKey = GoDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '1' + enumKey: '1', + options: GoGenerator.defaultOptions }); expect(constrainedKey).toEqual('TestNumber_1'); }); @@ -52,7 +55,8 @@ describe('EnumConstrainer', () => { const constrainedKey = GoDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: GoGenerator.defaultOptions }); expect(constrainedKey).toEqual('TestReservedEmpty'); }); @@ -60,7 +64,8 @@ describe('EnumConstrainer', () => { const constrainedKey = GoDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: GoGenerator.defaultOptions }); expect(constrainedKey).toEqual('TestEmpty'); }); @@ -68,7 +73,8 @@ describe('EnumConstrainer', () => { const constrainedKey = GoDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: 'some weird_value!"#2' + enumKey: 'some weird_value!"#2', + options: GoGenerator.defaultOptions }); expect(constrainedKey).toEqual( 'TestSomeSpaceWeirdValueExclamationQuotationHash_2' @@ -78,7 +84,8 @@ describe('EnumConstrainer', () => { const constrainedKey = GoDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: 'return' + enumKey: 'return', + options: GoGenerator.defaultOptions }); expect(constrainedKey).toEqual('TestReservedReturn'); }); @@ -88,7 +95,8 @@ describe('EnumConstrainer', () => { const constrainedValue = GoDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: 'string value' + enumValue: 'string value', + options: GoGenerator.defaultOptions }); expect(constrainedValue).toEqual('"string value"'); }); @@ -96,7 +104,8 @@ describe('EnumConstrainer', () => { const constrainedValue = GoDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: true + enumValue: true, + options: GoGenerator.defaultOptions }); expect(constrainedValue).toEqual('true'); }); @@ -104,7 +113,8 @@ describe('EnumConstrainer', () => { const constrainedValue = GoDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: 123 + enumValue: 123, + options: GoGenerator.defaultOptions }); expect(constrainedValue).toEqual(123); }); @@ -112,7 +122,8 @@ describe('EnumConstrainer', () => { const constrainedValue = GoDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: { test: 'test' } + enumValue: { test: 'test' }, + options: GoGenerator.defaultOptions }); expect(constrainedValue).toEqual('{"test":"test"}'); }); @@ -120,7 +131,8 @@ describe('EnumConstrainer', () => { const constrainedValue = GoDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: undefined + enumValue: undefined, + options: GoGenerator.defaultOptions }); expect(constrainedValue).toEqual(undefined); }); @@ -131,7 +143,8 @@ describe('EnumConstrainer', () => { const value = constrainFunction({ enumModel, constrainedEnumModel, - enumKey: 'TEST' + enumKey: 'TEST', + options: GoGenerator.defaultOptions }); expect(value).toEqual('TestTest'); }); @@ -146,7 +159,12 @@ describe('EnumConstrainer', () => { const constrainFunction = defaultEnumKeyConstraints( mockedConstraintCallbacks ); - constrainFunction({ enumModel, constrainedEnumModel, enumKey: '' }); + constrainFunction({ + enumModel, + constrainedEnumModel, + enumKey: '', + options: GoGenerator.defaultOptions + }); //Expect all callbacks to be called for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { expect(jestMockCallback).toHaveBeenCalled(); @@ -168,7 +186,8 @@ describe('EnumConstrainer', () => { const constrainedValue = constrainFunction({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: GoGenerator.defaultOptions }); expect(constrainedValue).toEqual(''); for (const jestMockCallback of spies) { diff --git a/test/generators/go/constrainer/ModelNameConstrainer.spec.ts b/test/generators/go/constrainer/ModelNameConstrainer.spec.ts index 0be52ccbfe..b4f7610a19 100644 --- a/test/generators/go/constrainer/ModelNameConstrainer.spec.ts +++ b/test/generators/go/constrainer/ModelNameConstrainer.spec.ts @@ -1,3 +1,4 @@ +import { GoGenerator } from '../../../../src'; import { GoDefaultConstraints } from '../../../../src/generators/go/GoConstrainer'; import { DefaultModelNameConstraints, @@ -6,26 +7,37 @@ import { } from '../../../../src/generators/go/constrainer/ModelNameConstrainer'; describe('ModelNameConstrainer', () => { test('should never render special chars', () => { - const constrainedKey = GoDefaultConstraints.modelName({ modelName: '%' }); + const constrainedKey = GoDefaultConstraints.modelName({ + modelName: '%', + options: GoGenerator.defaultOptions + }); expect(constrainedKey).toEqual('Percent'); }); test('should never render number as start char', () => { - const constrainedKey = GoDefaultConstraints.modelName({ modelName: '1' }); + const constrainedKey = GoDefaultConstraints.modelName({ + modelName: '1', + options: GoGenerator.defaultOptions + }); expect(constrainedKey).toEqual('Number_1'); }); test('should never contain empty name', () => { - const constrainedKey = GoDefaultConstraints.modelName({ modelName: '' }); + const constrainedKey = GoDefaultConstraints.modelName({ + modelName: '', + options: GoGenerator.defaultOptions + }); expect(constrainedKey).toEqual('Empty'); }); test('should use constant naming format', () => { const constrainedKey = GoDefaultConstraints.modelName({ - modelName: 'some weird_value!"#2' + modelName: 'some weird_value!"#2', + options: GoGenerator.defaultOptions }); expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash_2'); }); test('should never render reserved keywords', () => { const constrainedKey = GoDefaultConstraints.modelName({ - modelName: 'return' + modelName: 'return', + options: GoGenerator.defaultOptions }); expect(constrainedKey).toEqual('ReservedReturn'); }); @@ -41,7 +53,10 @@ describe('ModelNameConstrainer', () => { const constrainFunction = defaultModelNameConstraints( mockedConstraintCallbacks ); - constrainFunction({ modelName: '' }); + constrainFunction({ + modelName: '', + options: GoGenerator.defaultOptions + }); //Expect all callbacks to be called for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { expect(jestMockCallback).toHaveBeenCalled(); @@ -59,7 +74,10 @@ describe('ModelNameConstrainer', () => { const constrainFunction = defaultModelNameConstraints({ NAMING_FORMATTER: jestCallback }); - const constrainedValue = constrainFunction({ modelName: '' }); + const constrainedValue = constrainFunction({ + modelName: '', + options: GoGenerator.defaultOptions + }); expect(constrainedValue).toEqual(''); for (const jestMockCallback of spies) { expect(jestMockCallback).toHaveBeenCalled(); diff --git a/test/generators/go/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/go/constrainer/PropertyKeyConstrainer.spec.ts index 195587071c..84183e8e3d 100644 --- a/test/generators/go/constrainer/PropertyKeyConstrainer.spec.ts +++ b/test/generators/go/constrainer/PropertyKeyConstrainer.spec.ts @@ -2,6 +2,7 @@ import { GoDefaultConstraints } from '../../../../src/generators/go/GoConstraine import { ConstrainedObjectModel, ConstrainedObjectPropertyModel, + GoGenerator, ObjectModel, ObjectPropertyModel } from '../../../../src'; @@ -36,7 +37,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: GoGenerator.defaultOptions }); }; @@ -95,7 +97,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel: objectPropertyModel2, - constrainedObjectPropertyModel: constrainedObjectPropertyModel2 + constrainedObjectPropertyModel: constrainedObjectPropertyModel2, + options: GoGenerator.defaultOptions }); expect(constrainedKey).toEqual('ReservedReservedReturn'); }); @@ -130,7 +133,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: GoGenerator.defaultOptions }); //Expect all callbacks to be called for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { @@ -169,7 +173,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: GoGenerator.defaultOptions }); expect(constrainedValue).toEqual(''); expect(jestCallback).toHaveBeenCalled(); diff --git a/test/generators/java/JavaConstrainer.spec.ts b/test/generators/java/JavaConstrainer.spec.ts index 033d5fffb2..1a6b1cc975 100644 --- a/test/generators/java/JavaConstrainer.spec.ts +++ b/test/generators/java/JavaConstrainer.spec.ts @@ -348,6 +348,19 @@ describe('JavaConstrainer', () => { }); expect(type).toEqual('byte[]'); }); + test('should render Duration when format has duration format', () => { + const model = new ConstrainedStringModel( + 'test', + {}, + { format: 'duration' }, + '' + ); + const type = JavaDefaultTypeMapping.String({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('java.time.Duration'); + }); }); describe('Boolean', () => { test('should render type', () => { diff --git a/test/generators/java/JavaGenerator.spec.ts b/test/generators/java/JavaGenerator.spec.ts index 78ec573505..ee88e35b71 100644 --- a/test/generators/java/JavaGenerator.spec.ts +++ b/test/generators/java/JavaGenerator.spec.ts @@ -281,6 +281,147 @@ describe('JavaGenerator', () => { ); }); + describe('allowInheritance', () => { + test('should create interface for Pet and CloudEvent', async () => { + const asyncapiDoc = { + asyncapi: '2.5.0', + info: { + title: 'CloudEvent example', + version: '1.0.0' + }, + channels: { + pet: { + publish: { + message: { + oneOf: [ + { + $ref: '#/components/messages/Dog' + }, + { + $ref: '#/components/messages/Cat' + } + ] + } + } + } + }, + components: { + messages: { + Dog: { + payload: { + title: 'Dog', + allOf: [ + { + $ref: '#/components/schemas/CloudEvent' + }, + { + type: 'object', + properties: { + type: { + const: 'Dog' + }, + data: { + type: 'string' + }, + test: { + $ref: '#/components/schemas/TestAllOf' + } + } + } + ] + } + }, + Cat: { + payload: { + title: 'Cat', + allOf: [ + { + $ref: '#/components/schemas/CloudEvent' + }, + { + type: 'object', + properties: { + type: { + const: 'Cat' + }, + test: { + $ref: '#/components/schemas/Test' + } + } + } + ] + } + } + }, + schemas: { + CloudEvent: { + title: 'CloudEvent', + type: 'object', + discriminator: 'type', + properties: { + id: { + type: 'string' + }, + type: { + title: 'CloudEventType', + type: 'string', + description: 'test' + }, + sequencetype: { + title: 'CloudEvent.SequenceType', + type: 'string', + enum: ['Integer'] + } + }, + required: ['id', 'type'] + }, + Test: { + title: 'Test', + type: 'object', + properties: { + testProp: { + type: 'string' + } + } + }, + TestAllOf: { + title: 'TestAllOf', + allOf: [ + { $ref: '#/components/schemas/Test' }, + { + type: 'object', + properties: { + testProp2: { + type: 'string' + } + } + } + ] + } + } + } + }; + + generator = new JavaGenerator({ + presets: [ + JAVA_COMMON_PRESET, + JAVA_JACKSON_PRESET, + JAVA_DESCRIPTION_PRESET, + JAVA_CONSTRAINTS_PRESET + ], + collectionType: 'List', + processorOptions: { + interpreter: { + allowInheritance: true + } + } + }); + + const models = await generator.generate(asyncapiDoc); + expect(models.map((model) => model.result)).toMatchSnapshot(); + }); + }); + describe('oneOf/discriminator', () => { test('should create an interface', async () => { const asyncapiDoc = { diff --git a/test/generators/java/JavaRenderer.spec.ts b/test/generators/java/JavaRenderer.spec.ts index edbf22c03e..cbefc431e0 100644 --- a/test/generators/java/JavaRenderer.spec.ts +++ b/test/generators/java/JavaRenderer.spec.ts @@ -1,11 +1,7 @@ import { JavaGenerator } from '../../../src/generators'; import { JavaDependencyManager } from '../../../src/generators/java/JavaDependencyManager'; import { JavaRenderer } from '../../../src/generators/java/JavaRenderer'; -import { - CommonModel, - ConstrainedObjectModel, - InputMetaModel -} from '../../../src/models'; +import { ConstrainedObjectModel, InputMetaModel } from '../../../src/models'; import { MockJavaRenderer } from '../../TestUtils/TestRenderers'; describe('JavaRenderer', () => { diff --git a/test/generators/java/__snapshots__/JavaGenerator.spec.ts.snap b/test/generators/java/__snapshots__/JavaGenerator.spec.ts.snap index 268963de78..f031f078bf 100644 --- a/test/generators/java/__snapshots__/JavaGenerator.spec.ts.snap +++ b/test/generators/java/__snapshots__/JavaGenerator.spec.ts.snap @@ -1,5 +1,360 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`JavaGenerator allowInheritance should create interface for Pet and CloudEvent 1`] = ` +Array [ + "@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.EXISTING_PROPERTY, property=\\"type\\", visible=true) +@JsonSubTypes({ + @JsonSubTypes.Type(value = Dog.class, name = \\"Dog\\"), + @JsonSubTypes.Type(value = Cat.class, name = \\"Cat\\") +}) +/** + * Pet represents a union of types: Dog, Cat + */ +public interface Pet { + CloudEventType getType(); +}", + "public class Dog implements Pet, CloudEvent { + @NotNull + @JsonProperty(\\"id\\") + private String id; + @NotNull + @JsonProperty(\\"type\\") + private final CloudEventType type = CloudEventType.DOG; + @JsonProperty(\\"sequencetype\\") + @JsonInclude(JsonInclude.Include.NON_NULL) + private CloudEventDotSequenceType sequencetype; + @JsonProperty(\\"data\\") + @JsonInclude(JsonInclude.Include.NON_NULL) + private String data; + @JsonProperty(\\"test\\") + @JsonInclude(JsonInclude.Include.NON_NULL) + private TestAllOf test; + @JsonInclude(JsonInclude.Include.NON_NULL) + private Map additionalProperties; + + @Override + public String getId() { return this.id; } + @Override + public void setId(String id) { this.id = id; } + + public CloudEventType getType() { return this.type; } + + @Override + public CloudEventDotSequenceType getSequencetype() { return this.sequencetype; } + @Override + public void setSequencetype(CloudEventDotSequenceType sequencetype) { this.sequencetype = sequencetype; } + + public String getData() { return this.data; } + public void setData(String data) { this.data = data; } + + public TestAllOf getTest() { return this.test; } + public void setTest(TestAllOf test) { this.test = test; } + + public Map getAdditionalProperties() { return this.additionalProperties; } + public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Dog self = (Dog) o; + return + Objects.equals(this.id, self.id) && + Objects.equals(this.type, self.type) && + Objects.equals(this.sequencetype, self.sequencetype) && + Objects.equals(this.data, self.data) && + Objects.equals(this.test, self.test) && + Objects.equals(this.additionalProperties, self.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash((Object)id, (Object)type, (Object)sequencetype, (Object)data, (Object)test, (Object)additionalProperties); + } + + @Override + public String toString() { + return \\"class Dog {\\\\n\\" + + \\" id: \\" + toIndentedString(id) + \\"\\\\n\\" + + \\" type: \\" + toIndentedString(type) + \\"\\\\n\\" + + \\" sequencetype: \\" + toIndentedString(sequencetype) + \\"\\\\n\\" + + \\" data: \\" + toIndentedString(data) + \\"\\\\n\\" + + \\" test: \\" + toIndentedString(test) + \\"\\\\n\\" + + \\" additionalProperties: \\" + toIndentedString(additionalProperties) + \\"\\\\n\\" + + \\"}\\"; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return \\"null\\"; + } + return o.toString().replace(\\"\\\\n\\", \\"\\\\n \\"); + } +}", + "public enum CloudEventType { + DOG((String)\\"Dog\\"), CAT((String)\\"Cat\\"); + + private String value; + + CloudEventType(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @JsonCreator + public static CloudEventType fromValue(String value) { + for (CloudEventType e : CloudEventType.values()) { + if (e.value.equals(value)) { + return e; + } + } + throw new IllegalArgumentException(\\"Unexpected value '\\" + value + \\"'\\"); + } + + @Override + public String toString() { + return String.valueOf(value); + } +}", + "public enum CloudEventDotSequenceType { + INTEGER((String)\\"Integer\\"); + + private String value; + + CloudEventDotSequenceType(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @JsonCreator + public static CloudEventDotSequenceType fromValue(String value) { + for (CloudEventDotSequenceType e : CloudEventDotSequenceType.values()) { + if (e.value.equals(value)) { + return e; + } + } + throw new IllegalArgumentException(\\"Unexpected value '\\" + value + \\"'\\"); + } + + @Override + public String toString() { + return String.valueOf(value); + } +}", + "public class TestAllOf { + @JsonProperty(\\"testProp\\") + @JsonInclude(JsonInclude.Include.NON_NULL) + private String testProp; + @JsonProperty(\\"testProp2\\") + @JsonInclude(JsonInclude.Include.NON_NULL) + private String testProp2; + @JsonInclude(JsonInclude.Include.NON_NULL) + private Map additionalProperties; + + public String getTestProp() { return this.testProp; } + public void setTestProp(String testProp) { this.testProp = testProp; } + + public String getTestProp2() { return this.testProp2; } + public void setTestProp2(String testProp2) { this.testProp2 = testProp2; } + + public Map getAdditionalProperties() { return this.additionalProperties; } + public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TestAllOf self = (TestAllOf) o; + return + Objects.equals(this.testProp, self.testProp) && + Objects.equals(this.testProp2, self.testProp2) && + Objects.equals(this.additionalProperties, self.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash((Object)testProp, (Object)testProp2, (Object)additionalProperties); + } + + @Override + public String toString() { + return \\"class TestAllOf {\\\\n\\" + + \\" testProp: \\" + toIndentedString(testProp) + \\"\\\\n\\" + + \\" testProp2: \\" + toIndentedString(testProp2) + \\"\\\\n\\" + + \\" additionalProperties: \\" + toIndentedString(additionalProperties) + \\"\\\\n\\" + + \\"}\\"; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return \\"null\\"; + } + return o.toString().replace(\\"\\\\n\\", \\"\\\\n \\"); + } +}", + "public class Test { + @JsonProperty(\\"testProp\\") + @JsonInclude(JsonInclude.Include.NON_NULL) + private String testProp; + @JsonInclude(JsonInclude.Include.NON_NULL) + private Map additionalProperties; + + public String getTestProp() { return this.testProp; } + public void setTestProp(String testProp) { this.testProp = testProp; } + + public Map getAdditionalProperties() { return this.additionalProperties; } + public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Test self = (Test) o; + return + Objects.equals(this.testProp, self.testProp) && + Objects.equals(this.additionalProperties, self.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash((Object)testProp, (Object)additionalProperties); + } + + @Override + public String toString() { + return \\"class Test {\\\\n\\" + + \\" testProp: \\" + toIndentedString(testProp) + \\"\\\\n\\" + + \\" additionalProperties: \\" + toIndentedString(additionalProperties) + \\"\\\\n\\" + + \\"}\\"; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return \\"null\\"; + } + return o.toString().replace(\\"\\\\n\\", \\"\\\\n \\"); + } +}", + "public interface CloudEvent { + public String getId(); + public void setId(String id); + + public CloudEventDotSequenceType getSequencetype(); + public void setSequencetype(CloudEventDotSequenceType sequencetype); +}", + "public class Cat implements Pet, CloudEvent { + @NotNull + @JsonProperty(\\"id\\") + private String id; + @NotNull + @JsonProperty(\\"type\\") + private final CloudEventType type = CloudEventType.CAT; + @JsonProperty(\\"sequencetype\\") + @JsonInclude(JsonInclude.Include.NON_NULL) + private CloudEventDotSequenceType sequencetype; + @JsonProperty(\\"test\\") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Test test; + @JsonInclude(JsonInclude.Include.NON_NULL) + private Map additionalProperties; + + @Override + public String getId() { return this.id; } + @Override + public void setId(String id) { this.id = id; } + + public CloudEventType getType() { return this.type; } + + @Override + public CloudEventDotSequenceType getSequencetype() { return this.sequencetype; } + @Override + public void setSequencetype(CloudEventDotSequenceType sequencetype) { this.sequencetype = sequencetype; } + + public Test getTest() { return this.test; } + public void setTest(Test test) { this.test = test; } + + public Map getAdditionalProperties() { return this.additionalProperties; } + public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Cat self = (Cat) o; + return + Objects.equals(this.id, self.id) && + Objects.equals(this.type, self.type) && + Objects.equals(this.sequencetype, self.sequencetype) && + Objects.equals(this.test, self.test) && + Objects.equals(this.additionalProperties, self.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash((Object)id, (Object)type, (Object)sequencetype, (Object)test, (Object)additionalProperties); + } + + @Override + public String toString() { + return \\"class Cat {\\\\n\\" + + \\" id: \\" + toIndentedString(id) + \\"\\\\n\\" + + \\" type: \\" + toIndentedString(type) + \\"\\\\n\\" + + \\" sequencetype: \\" + toIndentedString(sequencetype) + \\"\\\\n\\" + + \\" test: \\" + toIndentedString(test) + \\"\\\\n\\" + + \\" additionalProperties: \\" + toIndentedString(additionalProperties) + \\"\\\\n\\" + + \\"}\\"; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return \\"null\\"; + } + return o.toString().replace(\\"\\\\n\\", \\"\\\\n \\"); + } +}", +] +`; + exports[`JavaGenerator oneOf/discriminator should create an interface 1`] = ` Array [ "/** diff --git a/test/generators/java/constrainer/ConstantConstrainer.spec.ts b/test/generators/java/constrainer/ConstantConstrainer.spec.ts index c66822deba..302321c51c 100644 --- a/test/generators/java/constrainer/ConstantConstrainer.spec.ts +++ b/test/generators/java/constrainer/ConstantConstrainer.spec.ts @@ -2,7 +2,8 @@ import { ConstrainedEnumModel, ConstrainedEnumValueModel, ConstrainedReferenceModel, - ConstrainedStringModel + ConstrainedStringModel, + JavaGenerator } from '../../../../src'; import { defaultConstantConstraints } from '../../../../src/generators/java/constrainer/ConstantConstrainer'; @@ -14,7 +15,8 @@ describe('ConstantConstrainer', () => { undefined, {}, 'String' - ) + ), + options: JavaGenerator.defaultOptions }); expect(constrainedConstant).toBeUndefined(); }); @@ -38,7 +40,8 @@ describe('ConstantConstrainer', () => { 'TestEnumType', [new ConstrainedEnumValueModel('testKey', 'testValue', 'testValue')] ) - ) + ), + options: JavaGenerator.defaultOptions }); expect(constrainedConstant).toEqual('TestRefType.testKey'); }); @@ -53,7 +56,8 @@ describe('ConstantConstrainer', () => { { const: { originalInput: 'testValue' } }, 'TestEnumType', [new ConstrainedEnumValueModel('testKey', 'testValue', 'testValue')] - ) + ), + options: JavaGenerator.defaultOptions }); expect(constrainedConstant).toEqual('TestEnumType.testKey'); }); @@ -67,7 +71,8 @@ describe('ConstantConstrainer', () => { undefined, { const: { originalInput: 'testValue' } }, 'String' - ) + ), + options: JavaGenerator.defaultOptions }); expect(constrainedConstant).toEqual('"testValue"'); }); diff --git a/test/generators/java/constrainer/EnumConstrainer.spec.ts b/test/generators/java/constrainer/EnumConstrainer.spec.ts index 0741014e53..fc0155cde1 100644 --- a/test/generators/java/constrainer/EnumConstrainer.spec.ts +++ b/test/generators/java/constrainer/EnumConstrainer.spec.ts @@ -2,7 +2,8 @@ import { JavaDefaultConstraints } from '../../../../src/generators/java/JavaCons import { EnumModel } from '../../../../src/models/MetaModel'; import { ConstrainedEnumModel, - ConstrainedEnumValueModel + ConstrainedEnumValueModel, + JavaGenerator } from '../../../../src'; import { defaultEnumKeyConstraints, @@ -24,7 +25,8 @@ describe('EnumConstrainer', () => { const constrainedKey = JavaDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '%' + enumKey: '%', + options: JavaGenerator.defaultOptions }); expect(constrainedKey).toEqual('PERCENT'); }); @@ -32,7 +34,8 @@ describe('EnumConstrainer', () => { const constrainedKey = JavaDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '1' + enumKey: '1', + options: JavaGenerator.defaultOptions }); expect(constrainedKey).toEqual('NUMBER_1'); }); @@ -52,7 +55,8 @@ describe('EnumConstrainer', () => { const constrainedKey = JavaDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: JavaGenerator.defaultOptions }); expect(constrainedKey).toEqual('RESERVED_EMPTY'); }); @@ -60,7 +64,8 @@ describe('EnumConstrainer', () => { const constrainedKey = JavaDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: JavaGenerator.defaultOptions }); expect(constrainedKey).toEqual('EMPTY'); }); @@ -68,7 +73,8 @@ describe('EnumConstrainer', () => { const constrainedKey = JavaDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: 'some weird_value!"#2' + enumKey: 'some weird_value!"#2', + options: JavaGenerator.defaultOptions }); expect(constrainedKey).toEqual( 'SOME_WEIRD_VALUE_EXCLAMATION_QUOTATION_HASH_2' @@ -78,7 +84,8 @@ describe('EnumConstrainer', () => { const constrainedKey = JavaDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: 'return' + enumKey: 'return', + options: JavaGenerator.defaultOptions }); expect(constrainedKey).toEqual('RESERVED_RETURN'); }); @@ -88,7 +95,8 @@ describe('EnumConstrainer', () => { const constrainedValue = JavaDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: 'string value' + enumValue: 'string value', + options: JavaGenerator.defaultOptions }); expect(constrainedValue).toEqual('"string value"'); }); @@ -96,7 +104,8 @@ describe('EnumConstrainer', () => { const constrainedValue = JavaDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: true + enumValue: true, + options: JavaGenerator.defaultOptions }); expect(constrainedValue).toEqual('"true"'); }); @@ -104,7 +113,8 @@ describe('EnumConstrainer', () => { const constrainedValue = JavaDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: 123 + enumValue: 123, + options: JavaGenerator.defaultOptions }); expect(constrainedValue).toEqual(123); }); @@ -112,7 +122,8 @@ describe('EnumConstrainer', () => { const constrainedValue = JavaDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: { test: 'test' } + enumValue: { test: 'test' }, + options: JavaGenerator.defaultOptions }); expect(constrainedValue).toEqual('"{\\"test\\":\\"test\\"}"'); }); @@ -120,7 +131,8 @@ describe('EnumConstrainer', () => { const constrainedValue = JavaDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: undefined + enumValue: undefined, + options: JavaGenerator.defaultOptions }); expect(constrainedValue).toEqual('"undefined"'); }); @@ -137,7 +149,12 @@ describe('EnumConstrainer', () => { const constrainFunction = defaultEnumKeyConstraints( mockedConstraintCallbacks ); - constrainFunction({ enumModel, constrainedEnumModel, enumKey: '' }); + constrainFunction({ + enumModel, + constrainedEnumModel, + enumKey: '', + options: JavaGenerator.defaultOptions + }); //Expect all callbacks to be called for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { expect(jestMockCallback).toHaveBeenCalled(); @@ -159,7 +176,8 @@ describe('EnumConstrainer', () => { const constrainedValue = constrainFunction({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: JavaGenerator.defaultOptions }); expect(constrainedValue).toEqual(''); for (const jestMockCallback of spies) { diff --git a/test/generators/java/constrainer/ModelNameConstrainer.spec.ts b/test/generators/java/constrainer/ModelNameConstrainer.spec.ts index 11f7eaa8f5..c483754a64 100644 --- a/test/generators/java/constrainer/ModelNameConstrainer.spec.ts +++ b/test/generators/java/constrainer/ModelNameConstrainer.spec.ts @@ -1,3 +1,4 @@ +import { JavaGenerator } from '../../../../src'; import { JavaDefaultConstraints } from '../../../../src/generators/java/JavaConstrainer'; import { DefaultModelNameConstraints, @@ -6,26 +7,37 @@ import { } from '../../../../src/generators/java/constrainer/ModelNameConstrainer'; describe('ModelNameConstrainer', () => { test('should never render special chars', () => { - const constrainedKey = JavaDefaultConstraints.modelName({ modelName: '%' }); + const constrainedKey = JavaDefaultConstraints.modelName({ + modelName: '%', + options: JavaGenerator.defaultOptions + }); expect(constrainedKey).toEqual('Percent'); }); test('should never render number as start char', () => { - const constrainedKey = JavaDefaultConstraints.modelName({ modelName: '1' }); + const constrainedKey = JavaDefaultConstraints.modelName({ + modelName: '1', + options: JavaGenerator.defaultOptions + }); expect(constrainedKey).toEqual('Number_1'); }); test('should never contain empty name', () => { - const constrainedKey = JavaDefaultConstraints.modelName({ modelName: '' }); + const constrainedKey = JavaDefaultConstraints.modelName({ + modelName: '', + options: JavaGenerator.defaultOptions + }); expect(constrainedKey).toEqual('Empty'); }); test('should use constant naming format', () => { const constrainedKey = JavaDefaultConstraints.modelName({ - modelName: 'some weird_value!"#2' + modelName: 'some weird_value!"#2', + options: JavaGenerator.defaultOptions }); expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash_2'); }); test('should never render reserved keywords', () => { const constrainedKey = JavaDefaultConstraints.modelName({ - modelName: 'return' + modelName: 'return', + options: JavaGenerator.defaultOptions }); expect(constrainedKey).toEqual('ReservedReturn'); }); @@ -41,7 +53,10 @@ describe('ModelNameConstrainer', () => { const constrainFunction = defaultModelNameConstraints( mockedConstraintCallbacks ); - constrainFunction({ modelName: '' }); + constrainFunction({ + modelName: '', + options: JavaGenerator.defaultOptions + }); //Expect all callbacks to be called for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { expect(jestMockCallback).toHaveBeenCalled(); @@ -59,7 +74,10 @@ describe('ModelNameConstrainer', () => { const constrainFunction = defaultModelNameConstraints({ NAMING_FORMATTER: jestCallback }); - const constrainedValue = constrainFunction({ modelName: '' }); + const constrainedValue = constrainFunction({ + modelName: '', + options: JavaGenerator.defaultOptions + }); expect(constrainedValue).toEqual(''); for (const jestMockCallback of spies) { expect(jestMockCallback).toHaveBeenCalled(); diff --git a/test/generators/java/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/java/constrainer/PropertyKeyConstrainer.spec.ts index 3338834422..21e10256e1 100644 --- a/test/generators/java/constrainer/PropertyKeyConstrainer.spec.ts +++ b/test/generators/java/constrainer/PropertyKeyConstrainer.spec.ts @@ -2,6 +2,7 @@ import { JavaDefaultConstraints } from '../../../../src/generators/java/JavaCons import { ConstrainedObjectModel, ConstrainedObjectPropertyModel, + JavaGenerator, ObjectModel, ObjectPropertyModel } from '../../../../src'; @@ -36,7 +37,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: JavaGenerator.defaultOptions }); }; afterEach(() => { @@ -98,7 +100,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel: objectPropertyModel2, - constrainedObjectPropertyModel: constrainedObjectPropertyModel2 + constrainedObjectPropertyModel: constrainedObjectPropertyModel2, + options: JavaGenerator.defaultOptions }); expect(constrainedKey).toEqual('reservedReservedReturn'); }); @@ -133,7 +136,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: JavaGenerator.defaultOptions }); //Expect all callbacks to be called for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { @@ -172,7 +176,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: JavaGenerator.defaultOptions }); expect(constrainedValue).toEqual(''); expect(jestCallback).toHaveBeenCalled(); diff --git a/test/generators/java/presets/CommonPreset.spec.ts b/test/generators/java/presets/CommonPreset.spec.ts index f8e24de703..cf433fd2bd 100644 --- a/test/generators/java/presets/CommonPreset.spec.ts +++ b/test/generators/java/presets/CommonPreset.spec.ts @@ -32,6 +32,7 @@ describe('JAVA_COMMON_PRESET', () => { expect(models).toHaveLength(1); expect(models[0].result).toMatchSnapshot(); }); + describe('with option', () => { test('should render all functions', async () => { const generator = new JavaGenerator({ @@ -161,5 +162,41 @@ describe('JAVA_COMMON_PRESET', () => { 'import java.util.Map;' ]); }); + test('should not render anything when isExtended is true', async () => { + const extend = { + $id: 'extend', + type: 'object', + properties: { + extendProp: { + type: 'string' + } + } + }; + const extendDoc = { + $id: 'extendDoc', + allOf: [extend] + }; + const generator = new JavaGenerator({ + presets: [ + { + preset: JAVA_COMMON_PRESET, + options: { + equal: true, + hashCode: true, + classToString: true, + marshalling: true + } + } + ], + processorOptions: { + interpreter: { + allowInheritance: true + } + } + }); + const models = await generator.generate(extendDoc); + expect(models).toHaveLength(2); + expect(models.map((model) => model.result)).toMatchSnapshot(); + }); }); }); diff --git a/test/generators/java/presets/ConstraintsPreset.spec.ts b/test/generators/java/presets/ConstraintsPreset.spec.ts index d83d322881..13a74fa289 100644 --- a/test/generators/java/presets/ConstraintsPreset.spec.ts +++ b/test/generators/java/presets/ConstraintsPreset.spec.ts @@ -35,4 +35,32 @@ describe('JAVA_CONSTRAINTS_PRESET', () => { expect(models[0].result).toMatchSnapshot(); expect(models[0].dependencies).toEqual(expectedDependencies); }); + + test('should not render anything when isExtended is true', async () => { + const extend = { + $id: 'extend', + type: 'object', + properties: { + extendProp: { + type: 'string' + } + }, + required: ['extendProp'] + }; + const extendDoc = { + $id: 'extendDoc', + allOf: [extend] + }; + const generator = new JavaGenerator({ + presets: [JAVA_CONSTRAINTS_PRESET], + processorOptions: { + interpreter: { + allowInheritance: true + } + } + }); + const models = await generator.generate(extendDoc); + expect(models).toHaveLength(2); + expect(models.map((model) => model.result)).toMatchSnapshot(); + }); }); diff --git a/test/generators/java/presets/DescriptionPreset.spec.ts b/test/generators/java/presets/DescriptionPreset.spec.ts index 2116b2a477..3d6634e684 100644 --- a/test/generators/java/presets/DescriptionPreset.spec.ts +++ b/test/generators/java/presets/DescriptionPreset.spec.ts @@ -44,4 +44,67 @@ describe('JAVA_DESCRIPTION_PRESET', () => { expect(models[0].result).toMatchSnapshot(); expect(models[0].dependencies).toEqual([]); }); + + test('should not render anything when isExtended is true and model is discriminator or dictionary', async () => { + const asyncapiDoc = { + asyncapi: '2.6.0', + info: { + title: 'Test', + version: '1.0.0' + }, + channels: {}, + components: { + messages: { + extendDoc: { + payload: { + title: 'extendDoc', + allOf: [ + { $ref: '#/components/schemas/extend' }, + { + type: 'object', + properties: { + type: { + const: 'ExtendDoc' + }, + test2: { + type: 'string', + description: 'test', + examples: ['test'] + } + } + } + ] + } + } + }, + schemas: { + extend: { + type: 'object', + discriminator: 'type', + properties: { + type: { + title: 'discriminatorTest', + type: 'string' + }, + test3: { + type: 'string' + } + }, + required: ['type'] + } + } + } + }; + const generator = new JavaGenerator({ + presets: [JAVA_DESCRIPTION_PRESET], + processorOptions: { + interpreter: { + allowInheritance: true + } + } + }); + const models = await generator.generate(asyncapiDoc); + expect(models).toHaveLength(3); + expect(models.map((model) => model.result)).toMatchSnapshot(); + }); }); diff --git a/test/generators/java/presets/JacksonPreset.spec.ts b/test/generators/java/presets/JacksonPreset.spec.ts index 7cf5bb25c9..8223123054 100644 --- a/test/generators/java/presets/JacksonPreset.spec.ts +++ b/test/generators/java/presets/JacksonPreset.spec.ts @@ -44,6 +44,33 @@ describe('JAVA_JACKSON_PRESET', () => { expect(models[0].dependencies).toEqual(expectedDependencies); }); + test('should not render anything when isExtended is true', async () => { + const extend = { + $id: 'extend', + type: 'object', + properties: { + extendProp: { + type: 'string' + } + } + }; + const extendDoc = { + $id: 'extendDoc', + allOf: [extend] + }; + const generator = new JavaGenerator({ + presets: [JAVA_JACKSON_PRESET], + processorOptions: { + interpreter: { + allowInheritance: true + } + } + }); + const models = await generator.generate(extendDoc); + expect(models).toHaveLength(2); + expect(models.map((model) => model.result)).toMatchSnapshot(); + }); + describe('union', () => { test('handle oneOf with AsyncAPI discriminator with Jackson', async () => { const asyncapiDoc = { diff --git a/test/generators/java/presets/__snapshots__/CommonPreset.spec.ts.snap b/test/generators/java/presets/__snapshots__/CommonPreset.spec.ts.snap index accc8c5036..e592f6ed74 100644 --- a/test/generators/java/presets/__snapshots__/CommonPreset.spec.ts.snap +++ b/test/generators/java/presets/__snapshots__/CommonPreset.spec.ts.snap @@ -173,6 +173,88 @@ exports[`JAVA_COMMON_PRESET with option should not render any functions when all }" `; +exports[`JAVA_COMMON_PRESET with option should not render anything when isExtended is true 1`] = ` +Array [ + "public class ExtendDoc implements Extend { + private String extendProp; + private Map additionalProperties; + + @Override + public String getExtendProp() { return this.extendProp; } + @Override + public void setExtendProp(String extendProp) { this.extendProp = extendProp; } + + public Map getAdditionalProperties() { return this.additionalProperties; } + public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ExtendDoc self = (ExtendDoc) o; + return + Objects.equals(this.extendProp, self.extendProp) && + Objects.equals(this.additionalProperties, self.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash((Object)extendProp, (Object)additionalProperties); + } + + @Override + public String toString() { + return \\"class ExtendDoc {\\\\n\\" + + \\" extendProp: \\" + toIndentedString(extendProp) + \\"\\\\n\\" + + \\" additionalProperties: \\" + toIndentedString(additionalProperties) + \\"\\\\n\\" + + \\"}\\"; + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return \\"null\\"; + } + return o.toString().replace(\\"\\\\n\\", \\"\\\\n \\"); + } + + public String marshal() { + List propList = new ArrayList(); + if(this.extendProp != null) { + propList.add(\\"extendProp:\\"+this.extendProp.toString()); + } + if(this.additionalProperties != null) { + propList.add(\\"additionalProperties:\\"+this.additionalProperties.toString()); + } + return propList.stream().collect(Collectors.joining(\\",\\")); + } + + public static ExtendDoc unmarshal(String json) { + ExtendDoc result = new ExtendDoc(); + JSONObject jsonObject = new JSONObject(json); + if(jsonObject.has(\\"extendProp\\")) { + result.setExtendProp(jsonObject.getString(\\"extendProp\\")); + } + if(jsonObject.has(\\"additionalProperties\\")) { + result.setAdditionalProperties(jsonObject.getMap(\\"additionalProperties\\")); + } + return result; + } +}", + "public interface Extend { + public String getExtendProp(); + public void setExtendProp(String extendProp); +}", +] +`; + exports[`JAVA_COMMON_PRESET with option should render all functions 1`] = ` "public class Clazz { private boolean requiredProp; diff --git a/test/generators/java/presets/__snapshots__/ConstraintsPreset.spec.ts.snap b/test/generators/java/presets/__snapshots__/ConstraintsPreset.spec.ts.snap index a9652ad936..4101fcd470 100644 --- a/test/generators/java/presets/__snapshots__/ConstraintsPreset.spec.ts.snap +++ b/test/generators/java/presets/__snapshots__/ConstraintsPreset.spec.ts.snap @@ -1,5 +1,27 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`JAVA_CONSTRAINTS_PRESET should not render anything when isExtended is true 1`] = ` +Array [ + "public class ExtendDoc implements Extend { + @NotNull + private String extendProp; + private Map additionalProperties; + + @Override + public String getExtendProp() { return this.extendProp; } + @Override + public void setExtendProp(String extendProp) { this.extendProp = extendProp; } + + public Map getAdditionalProperties() { return this.additionalProperties; } + public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } +}", + "public interface Extend { + public String getExtendProp(); + public void setExtendProp(String extendProp); +}", +] +`; + exports[`JAVA_CONSTRAINTS_PRESET should render constraints annotations 1`] = ` "public class Clazz { @NotNull diff --git a/test/generators/java/presets/__snapshots__/DescriptionPreset.spec.ts.snap b/test/generators/java/presets/__snapshots__/DescriptionPreset.spec.ts.snap index a0cffbd923..848955ac55 100644 --- a/test/generators/java/presets/__snapshots__/DescriptionPreset.spec.ts.snap +++ b/test/generators/java/presets/__snapshots__/DescriptionPreset.spec.ts.snap @@ -1,5 +1,64 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`JAVA_DESCRIPTION_PRESET should not render anything when isExtended is true and model is discriminator or dictionary 1`] = ` +Array [ + "public class ExtendDoc implements Extend { + private final DiscriminatorTest type = DiscriminatorTest.EXTEND_DOC; + private String test3; + private String test2; + private Map additionalProperties; + + public DiscriminatorTest getType() { return this.type; } + + @Override + public String getTest3() { return this.test3; } + @Override + public void setTest3(String test3) { this.test3 = test3; } + + /** + * test + * Examples: test + */ + public String getTest2() { return this.test2; } + public void setTest2(String test2) { this.test2 = test2; } + + public Map getAdditionalProperties() { return this.additionalProperties; } + public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } +}", + "public enum DiscriminatorTest { + EXTEND_DOC((String)\\"ExtendDoc\\"); + + private String value; + + DiscriminatorTest(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static DiscriminatorTest fromValue(String value) { + for (DiscriminatorTest e : DiscriminatorTest.values()) { + if (e.value.equals(value)) { + return e; + } + } + throw new IllegalArgumentException(\\"Unexpected value '\\" + value + \\"'\\"); + } + + @Override + public String toString() { + return String.valueOf(value); + } +}", + "public interface Extend { + public String getTest3(); + public void setTest3(String test3); +}", +] +`; + exports[`JAVA_DESCRIPTION_PRESET should render description and examples for class 1`] = ` "/** * Description for class diff --git a/test/generators/java/presets/__snapshots__/JacksonPreset.spec.ts.snap b/test/generators/java/presets/__snapshots__/JacksonPreset.spec.ts.snap index f7d1757aab..d4e5689c20 100644 --- a/test/generators/java/presets/__snapshots__/JacksonPreset.spec.ts.snap +++ b/test/generators/java/presets/__snapshots__/JacksonPreset.spec.ts.snap @@ -1,5 +1,29 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`JAVA_JACKSON_PRESET should not render anything when isExtended is true 1`] = ` +Array [ + "public class ExtendDoc implements Extend { + @JsonProperty(\\"extendProp\\") + @JsonInclude(JsonInclude.Include.NON_NULL) + private String extendProp; + @JsonInclude(JsonInclude.Include.NON_NULL) + private Map additionalProperties; + + @Override + public String getExtendProp() { return this.extendProp; } + @Override + public void setExtendProp(String extendProp) { this.extendProp = extendProp; } + + public Map getAdditionalProperties() { return this.additionalProperties; } + public void setAdditionalProperties(Map additionalProperties) { this.additionalProperties = additionalProperties; } +}", + "public interface Extend { + public String getExtendProp(); + public void setExtendProp(String extendProp); +}", +] +`; + exports[`JAVA_JACKSON_PRESET should render Jackson annotations for class 1`] = ` "public class Clazz { @JsonProperty(\\"min_number_prop\\") diff --git a/test/generators/javascript/constrainer/EnumConstrainer.spec.ts b/test/generators/javascript/constrainer/EnumConstrainer.spec.ts index e5d08d778e..9104f87737 100644 --- a/test/generators/javascript/constrainer/EnumConstrainer.spec.ts +++ b/test/generators/javascript/constrainer/EnumConstrainer.spec.ts @@ -2,7 +2,8 @@ import { JavaScriptDefaultConstraints } from '../../../../src/generators/javascr import { EnumModel } from '../../../../src/models/MetaModel'; import { ConstrainedEnumModel, - ConstrainedEnumValueModel + ConstrainedEnumValueModel, + JavaScriptGenerator } from '../../../../src'; describe('EnumConstrainer', () => { const enumModel = new EnumModel('test', undefined, {}, []); @@ -19,7 +20,8 @@ describe('EnumConstrainer', () => { const constrainedKey = JavaScriptDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '%' + enumKey: '%', + options: JavaScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('%'); }); @@ -27,7 +29,8 @@ describe('EnumConstrainer', () => { const constrainedKey = JavaScriptDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '1' + enumKey: '1', + options: JavaScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('1'); }); @@ -47,7 +50,8 @@ describe('EnumConstrainer', () => { const constrainedKey = JavaScriptDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: JavaScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual(''); }); @@ -55,7 +59,8 @@ describe('EnumConstrainer', () => { const constrainedKey = JavaScriptDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: JavaScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual(''); }); @@ -63,7 +68,8 @@ describe('EnumConstrainer', () => { const constrainedKey = JavaScriptDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: 'some weird_value!"#2' + enumKey: 'some weird_value!"#2', + options: JavaScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('some weird_value!"#2'); }); @@ -71,7 +77,8 @@ describe('EnumConstrainer', () => { const constrainedKey = JavaScriptDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: 'return' + enumKey: 'return', + options: JavaScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('return'); }); @@ -81,7 +88,8 @@ describe('EnumConstrainer', () => { const constrainedValue = JavaScriptDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: 'string value' + enumValue: 'string value', + options: JavaScriptGenerator.defaultOptions }); expect(constrainedValue).toEqual('string value'); }); @@ -89,7 +97,8 @@ describe('EnumConstrainer', () => { const constrainedValue = JavaScriptDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: true + enumValue: true, + options: JavaScriptGenerator.defaultOptions }); expect(constrainedValue).toEqual(true); }); @@ -97,7 +106,8 @@ describe('EnumConstrainer', () => { const constrainedValue = JavaScriptDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: 123 + enumValue: 123, + options: JavaScriptGenerator.defaultOptions }); expect(constrainedValue).toEqual(123); }); @@ -105,7 +115,8 @@ describe('EnumConstrainer', () => { const constrainedValue = JavaScriptDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: { test: 'test' } + enumValue: { test: 'test' }, + options: JavaScriptGenerator.defaultOptions }); expect(constrainedValue).toEqual({ test: 'test' }); }); @@ -113,7 +124,8 @@ describe('EnumConstrainer', () => { const constrainedValue = JavaScriptDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: undefined + enumValue: undefined, + options: JavaScriptGenerator.defaultOptions }); expect(constrainedValue).toEqual(undefined); }); diff --git a/test/generators/javascript/constrainer/ModelNameConstrainer.spec.ts b/test/generators/javascript/constrainer/ModelNameConstrainer.spec.ts index edd4531e86..19117aaf7c 100644 --- a/test/generators/javascript/constrainer/ModelNameConstrainer.spec.ts +++ b/test/generators/javascript/constrainer/ModelNameConstrainer.spec.ts @@ -1,3 +1,4 @@ +import { JavaScriptGenerator } from '../../../../src'; import { JavaScriptDefaultConstraints } from '../../../../src/generators/javascript/JavaScriptConstrainer'; import { DefaultModelNameConstraints, @@ -7,31 +8,36 @@ import { describe('ModelNameConstrainer', () => { test('should never render special chars', () => { const constrainedKey = JavaScriptDefaultConstraints.modelName({ - modelName: '%' + modelName: '%', + options: JavaScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('Percent'); }); test('should never render number as start char', () => { const constrainedKey = JavaScriptDefaultConstraints.modelName({ - modelName: '1' + modelName: '1', + options: JavaScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('Number_1'); }); test('should never contain empty name', () => { const constrainedKey = JavaScriptDefaultConstraints.modelName({ - modelName: '' + modelName: '', + options: JavaScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('Empty'); }); test('should use pascal naming format', () => { const constrainedKey = JavaScriptDefaultConstraints.modelName({ - modelName: 'some weird_value!"#2' + modelName: 'some weird_value!"#2', + options: JavaScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash_2'); }); test('should never render reserved keywords', () => { const constrainedKey = JavaScriptDefaultConstraints.modelName({ - modelName: 'return' + modelName: 'return', + options: JavaScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('ReservedReturn'); }); @@ -47,7 +53,10 @@ describe('ModelNameConstrainer', () => { const constrainFunction = defaultModelNameConstraints( mockedConstraintCallbacks ); - constrainFunction({ modelName: '' }); + constrainFunction({ + modelName: '', + options: JavaScriptGenerator.defaultOptions + }); //Expect all callbacks to be called for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { expect(jestMockCallback).toHaveBeenCalled(); @@ -65,7 +74,10 @@ describe('ModelNameConstrainer', () => { const constrainFunction = defaultModelNameConstraints({ NAMING_FORMATTER: jestCallback }); - const constrainedValue = constrainFunction({ modelName: '' }); + const constrainedValue = constrainFunction({ + modelName: '', + options: JavaScriptGenerator.defaultOptions + }); expect(constrainedValue).toEqual(''); for (const jestMockCallback of spies) { expect(jestMockCallback).toHaveBeenCalled(); diff --git a/test/generators/javascript/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/javascript/constrainer/PropertyKeyConstrainer.spec.ts index f53bd83d87..3ad6fa7200 100644 --- a/test/generators/javascript/constrainer/PropertyKeyConstrainer.spec.ts +++ b/test/generators/javascript/constrainer/PropertyKeyConstrainer.spec.ts @@ -2,6 +2,7 @@ import { JavaScriptDefaultConstraints } from '../../../../src/generators/javascr import { ConstrainedObjectModel, ConstrainedObjectPropertyModel, + JavaScriptGenerator, ObjectModel, ObjectPropertyModel } from '../../../../src'; @@ -36,7 +37,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: JavaScriptGenerator.defaultOptions }); }; @@ -95,7 +97,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel: objectPropertyModel2, - constrainedObjectPropertyModel: constrainedObjectPropertyModel2 + constrainedObjectPropertyModel: constrainedObjectPropertyModel2, + options: JavaScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('reservedReservedReturn'); }); @@ -130,7 +133,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: JavaScriptGenerator.defaultOptions }); //Expect all callbacks to be called for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { @@ -169,7 +173,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: JavaScriptGenerator.defaultOptions }); expect(constrainedValue).toEqual(''); expect(jestCallback).toHaveBeenCalled(); diff --git a/test/generators/javascript/preset/utils/ExampleFunctions.spec.ts b/test/generators/javascript/preset/utils/ExampleFunctions.spec.ts index ee3a16cf79..a433dbc8f6 100644 --- a/test/generators/javascript/preset/utils/ExampleFunctions.spec.ts +++ b/test/generators/javascript/preset/utils/ExampleFunctions.spec.ts @@ -1,6 +1,5 @@ import { renderValueFromModel } from '../../../../../src/generators/javascript/presets/utils/ExampleFunction'; import { - CommonModel, ConstrainedAnyModel, ConstrainedArrayModel, ConstrainedBooleanModel, diff --git a/test/generators/kotlin/KotlinRenderer.spec.ts b/test/generators/kotlin/KotlinRenderer.spec.ts index b71827f6cb..7eb33d2cd1 100644 --- a/test/generators/kotlin/KotlinRenderer.spec.ts +++ b/test/generators/kotlin/KotlinRenderer.spec.ts @@ -2,7 +2,6 @@ import { KotlinGenerator } from '../../../src/generators/kotlin'; import { KotlinRenderer } from '../../../src/generators/kotlin/KotlinRenderer'; import { ConstrainedObjectModel, InputMetaModel } from '../../../src/models'; import { MockKotlinRenderer } from '../../TestUtils/TestRenderers'; -import { prefix } from 'concurrently/dist/src/defaults'; describe('KotlinRenderer', () => { let renderer: KotlinRenderer; diff --git a/test/generators/python/presets/Pydantic.spec.ts b/test/generators/python/presets/Pydantic.spec.ts index 9801720c31..b3442d2048 100644 --- a/test/generators/python/presets/Pydantic.spec.ts +++ b/test/generators/python/presets/Pydantic.spec.ts @@ -31,4 +31,38 @@ describe('PYTHON_PYDANTIC_PRESET', () => { expect(models).toHaveLength(1); expect(models[0].result).toMatchSnapshot(); }); + + test('should render union to support Python < 3.10', async () => { + const doc = { + title: 'UnionTest', + type: 'object', + properties: { + unionTest: { + oneOf: [ + { + title: 'Union1', + type: 'object', + properties: { + testProp1: { + type: 'string' + } + } + }, + { + title: 'Union2', + type: 'object', + properties: { + testProp2: { + type: 'string' + } + } + } + ] + } + } + }; + + const models = await generator.generate(doc); + expect(models.map((model) => model.result)).toMatchSnapshot(); + }); }); diff --git a/test/generators/python/presets/__snapshots__/Pydantic.spec.ts.snap b/test/generators/python/presets/__snapshots__/Pydantic.spec.ts.snap index a569da8c3b..5c77bd41f9 100644 --- a/test/generators/python/presets/__snapshots__/Pydantic.spec.ts.snap +++ b/test/generators/python/presets/__snapshots__/Pydantic.spec.ts.snap @@ -9,3 +9,20 @@ exports[`PYTHON_PYDANTIC_PRESET should render pydantic for class 1`] = ` additionalProperties: Optional[dict[Any, Any]] = Field(default=None) " `; + +exports[`PYTHON_PYDANTIC_PRESET should render union to support Python < 3.10 1`] = ` +Array [ + "class UnionTest(BaseModel): + unionTest: Optional[Union[Union1, Union2]] = Field(default=None) + additionalProperties: Optional[dict[Any, Any]] = Field(default=None) +", + "class Union1(BaseModel): + testProp1: Optional[str] = Field(default=None) + additionalProperties: Optional[dict[Any, Any]] = Field(default=None) +", + "class Union2(BaseModel): + testProp2: Optional[str] = Field(default=None) + additionalProperties: Optional[dict[Any, Any]] = Field(default=None) +", +] +`; diff --git a/test/generators/rust/RustConstrainer.spec.ts b/test/generators/rust/RustConstrainer.spec.ts index 9273e10c2c..2fd738756f 100644 --- a/test/generators/rust/RustConstrainer.spec.ts +++ b/test/generators/rust/RustConstrainer.spec.ts @@ -20,9 +20,7 @@ import { ConstrainedTupleModel, ConstrainedUnionModel, RustGenerator, - RustOptions, - ConstrainedObjectPropertyModel, - ConstrainedMetaModel + ConstrainedObjectPropertyModel } from '../../../src'; import { RustDependencyManager } from '../../../src/generators/rust/RustDependencyManager'; describe('RustConstrainer', () => { diff --git a/test/generators/rust/RustRenderer.spec.ts b/test/generators/rust/RustRenderer.spec.ts index 88cd4ef2e1..434713beb3 100644 --- a/test/generators/rust/RustRenderer.spec.ts +++ b/test/generators/rust/RustRenderer.spec.ts @@ -1,7 +1,6 @@ import { RustGenerator } from '../../../src/generators'; import { RustRenderer } from '../../../src/generators/rust/RustRenderer'; import { - CommonModel, ConstrainedObjectModel, ConstrainedEnumModel, ConstrainedStringModel, diff --git a/test/generators/rust/constrainer/EnumConstrainer.spec.ts b/test/generators/rust/constrainer/EnumConstrainer.spec.ts index 7c70f115ad..ec8f433a57 100644 --- a/test/generators/rust/constrainer/EnumConstrainer.spec.ts +++ b/test/generators/rust/constrainer/EnumConstrainer.spec.ts @@ -3,9 +3,9 @@ import { EnumModel } from '../../../../src/models/MetaModel'; import { ConstrainedEnumModel, ConstrainedEnumValueModel, - EnumKeyConstraint + RustEnumKeyConstraint, + RustGenerator } from '../../../../src'; -import { RESERVED_RUST_KEYWORDS } from '../../../../src/generators/rust/Constants'; import { defaultEnumKeyConstraints } from '../../../../src/generators/rust/constrainer/EnumConstrainer'; describe('EnumConstrainer', () => { const enumModel = new EnumModel('test', undefined, {}, []); @@ -22,7 +22,8 @@ describe('EnumConstrainer', () => { const constrainedKey = RustDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '%' + enumKey: '%', + options: RustGenerator.defaultOptions }); expect(constrainedKey).toEqual('Percent'); }); @@ -30,7 +31,8 @@ describe('EnumConstrainer', () => { const constrainedKey = RustDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '1' + enumKey: '1', + options: RustGenerator.defaultOptions }); expect(constrainedKey).toEqual('Number_1'); }); @@ -50,7 +52,8 @@ describe('EnumConstrainer', () => { const constrainedKey = RustDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: RustGenerator.defaultOptions }); expect(constrainedKey).toEqual('Empty'); }); @@ -58,7 +61,8 @@ describe('EnumConstrainer', () => { const constrainedKey = RustDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: RustGenerator.defaultOptions }); expect(constrainedKey).toEqual('Empty'); }); @@ -66,7 +70,8 @@ describe('EnumConstrainer', () => { const constrainedKey = RustDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: 'some weird_value!"#2' + enumKey: 'some weird_value!"#2', + options: RustGenerator.defaultOptions }); expect(constrainedKey).toEqual( 'SomeWeirdValueExclamationQuotationHash_2' @@ -76,21 +81,23 @@ describe('EnumConstrainer', () => { const constrainedKey = RustDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: 'return' + enumKey: 'return', + options: RustGenerator.defaultOptions }); expect(constrainedKey).toEqual('Return'); }); describe('custom constraints', () => { test('should make sure reserved keywords cannot be rendered', () => { - const customNamingFormat: Partial = { + const customNamingFormat: Partial = { NAMING_FORMATTER: (value) => value }; const constrainFunction = defaultEnumKeyConstraints(customNamingFormat); const constrainedKey = constrainFunction({ enumModel, constrainedEnumModel, - enumKey: 'return' + enumKey: 'return', + options: RustGenerator.defaultOptions }); expect(constrainedKey).toEqual('reserved_return'); }); @@ -101,7 +108,8 @@ describe('EnumConstrainer', () => { const constrainedValue = RustDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: 'string value' + enumValue: 'string value', + options: RustGenerator.defaultOptions }); expect(constrainedValue).toEqual('string value'); }); @@ -109,7 +117,8 @@ describe('EnumConstrainer', () => { const constrainedValue = RustDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: true + enumValue: true, + options: RustGenerator.defaultOptions }); expect(constrainedValue).toEqual(true); }); @@ -117,7 +126,8 @@ describe('EnumConstrainer', () => { const constrainedValue = RustDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: 123 + enumValue: 123, + options: RustGenerator.defaultOptions }); expect(constrainedValue).toEqual(123); }); @@ -125,7 +135,8 @@ describe('EnumConstrainer', () => { const constrainedValue = RustDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: { test: 'test' } + enumValue: { test: 'test' }, + options: RustGenerator.defaultOptions }); expect(constrainedValue).toEqual({ test: 'test' }); }); @@ -133,7 +144,8 @@ describe('EnumConstrainer', () => { const constrainedValue = RustDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: undefined + enumValue: undefined, + options: RustGenerator.defaultOptions }); expect(constrainedValue).toEqual(undefined); }); diff --git a/test/generators/rust/constrainer/ModelNameConstrainer.spec.ts b/test/generators/rust/constrainer/ModelNameConstrainer.spec.ts index e196b5024b..33894480b4 100644 --- a/test/generators/rust/constrainer/ModelNameConstrainer.spec.ts +++ b/test/generators/rust/constrainer/ModelNameConstrainer.spec.ts @@ -1,3 +1,4 @@ +import { RustGenerator } from '../../../../src'; import { RustDefaultConstraints } from '../../../../src/generators/rust/RustConstrainer'; import { defaultModelNameConstraints, @@ -5,26 +6,37 @@ import { } from '../../../../src/generators/rust/constrainer/ModelNameConstrainer'; describe('ModelNameConstrainer', () => { test('should never render special chars', () => { - const constrainedKey = RustDefaultConstraints.modelName({ modelName: '%' }); + const constrainedKey = RustDefaultConstraints.modelName({ + modelName: '%', + options: RustGenerator.defaultOptions + }); expect(constrainedKey).toEqual('Percent'); }); test('should never render number as start char', () => { - const constrainedKey = RustDefaultConstraints.modelName({ modelName: '1' }); + const constrainedKey = RustDefaultConstraints.modelName({ + modelName: '1', + options: RustGenerator.defaultOptions + }); expect(constrainedKey).toEqual('Number1'); }); test('should never contain empty name', () => { - const constrainedKey = RustDefaultConstraints.modelName({ modelName: '' }); + const constrainedKey = RustDefaultConstraints.modelName({ + modelName: '', + options: RustGenerator.defaultOptions + }); expect(constrainedKey).toEqual('Empty'); }); test('should use constant naming format', () => { const constrainedKey = RustDefaultConstraints.modelName({ - modelName: 'some weird_value!"#2' + modelName: 'some weird_value!"#2', + options: RustGenerator.defaultOptions }); expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash2'); }); test('Reserved keywords should not take effect when naming formatter changes its format', () => { const constrainedKey = RustDefaultConstraints.modelName({ - modelName: 'return' + modelName: 'return', + options: RustGenerator.defaultOptions }); expect(constrainedKey).toEqual('Return'); }); @@ -34,7 +46,10 @@ describe('ModelNameConstrainer', () => { NAMING_FORMATTER: (value) => value }; const constrainFunction = defaultModelNameConstraints(customNamingFormat); - const constrainedKey = constrainFunction({ modelName: 'return' }); + const constrainedKey = constrainFunction({ + modelName: 'return', + options: RustGenerator.defaultOptions + }); expect(constrainedKey).toEqual('reserved_return'); }); }); diff --git a/test/generators/rust/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/rust/constrainer/PropertyKeyConstrainer.spec.ts index 81ffc61575..b4a6add402 100644 --- a/test/generators/rust/constrainer/PropertyKeyConstrainer.spec.ts +++ b/test/generators/rust/constrainer/PropertyKeyConstrainer.spec.ts @@ -3,7 +3,8 @@ import { ConstrainedObjectModel, ConstrainedObjectPropertyModel, ObjectModel, - ObjectPropertyModel + ObjectPropertyModel, + RustGenerator } from '../../../../src'; describe('PropertyKeyConstrainer', () => { const objectModel = new ObjectModel('test', undefined, {}, {}); @@ -31,7 +32,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: RustGenerator.defaultOptions }); }; @@ -92,7 +94,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel: objectPropertyModel2, - constrainedObjectPropertyModel: constrainedObjectPropertyModel2 + constrainedObjectPropertyModel: constrainedObjectPropertyModel2, + options: RustGenerator.defaultOptions }); expect(constrainedKey).toEqual('reserved_reserved_return'); }); diff --git a/test/generators/typescript/constrainer/ConstantConstrainer.spec.ts b/test/generators/typescript/constrainer/ConstantConstrainer.spec.ts index dcce0beb88..3139b2d0a4 100644 --- a/test/generators/typescript/constrainer/ConstantConstrainer.spec.ts +++ b/test/generators/typescript/constrainer/ConstantConstrainer.spec.ts @@ -2,7 +2,8 @@ import { ConstrainedEnumModel, ConstrainedEnumValueModel, ConstrainedReferenceModel, - ConstrainedStringModel + ConstrainedStringModel, + TypeScriptGenerator } from '../../../../src'; import { defaultConstantConstraints } from '../../../../src/generators/typescript/constrainer/ConstantConstrainer'; @@ -14,7 +15,8 @@ describe('ConstantConstrainer', () => { undefined, {}, 'String' - ) + ), + options: TypeScriptGenerator.defaultOptions }); expect(constrainedConstant).toBeUndefined(); }); @@ -38,7 +40,8 @@ describe('ConstantConstrainer', () => { 'TestEnumType', [new ConstrainedEnumValueModel('testKey', 'testValue', 'testValue')] ) - ) + ), + options: TypeScriptGenerator.defaultOptions }); expect(constrainedConstant).toEqual('TestRefType.testKey'); }); @@ -53,7 +56,8 @@ describe('ConstantConstrainer', () => { { const: { originalInput: 'testValue' } }, 'TestEnumType', [new ConstrainedEnumValueModel('testKey', 'testValue', 'testValue')] - ) + ), + options: TypeScriptGenerator.defaultOptions }); expect(constrainedConstant).toEqual('TestEnumType.testKey'); }); @@ -67,7 +71,8 @@ describe('ConstantConstrainer', () => { undefined, { const: { originalInput: 'testValue' } }, 'String' - ) + ), + options: TypeScriptGenerator.defaultOptions }); expect(constrainedConstant).toEqual("'testValue'"); }); diff --git a/test/generators/typescript/constrainer/EnumConstrainer.spec.ts b/test/generators/typescript/constrainer/EnumConstrainer.spec.ts index 561436cfda..56314455bb 100644 --- a/test/generators/typescript/constrainer/EnumConstrainer.spec.ts +++ b/test/generators/typescript/constrainer/EnumConstrainer.spec.ts @@ -2,7 +2,8 @@ import { TypeScriptDefaultConstraints } from '../../../../src/generators/typescr import { EnumModel } from '../../../../src/models/MetaModel'; import { ConstrainedEnumModel, - ConstrainedEnumValueModel + ConstrainedEnumValueModel, + TypeScriptGenerator } from '../../../../src'; import { defaultEnumKeyConstraints, @@ -24,7 +25,8 @@ describe('EnumConstrainer', () => { const constrainedKey = TypeScriptDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '%' + enumKey: '%', + options: TypeScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('PERCENT'); }); @@ -32,7 +34,8 @@ describe('EnumConstrainer', () => { const constrainedKey = TypeScriptDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '1' + enumKey: '1', + options: TypeScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('NUMBER_1'); }); @@ -52,7 +55,8 @@ describe('EnumConstrainer', () => { const constrainedKey = TypeScriptDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: TypeScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('RESERVED_EMPTY'); }); @@ -60,7 +64,8 @@ describe('EnumConstrainer', () => { const constrainedKey = TypeScriptDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: TypeScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('EMPTY'); }); @@ -68,7 +73,8 @@ describe('EnumConstrainer', () => { const constrainedKey = TypeScriptDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: 'some weird_value!"#2' + enumKey: 'some weird_value!"#2', + options: TypeScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual( 'SOME_SPACE_WEIRD_VALUE_EXCLAMATION_QUOTATION_HASH_2' @@ -78,7 +84,8 @@ describe('EnumConstrainer', () => { const constrainedKey = TypeScriptDefaultConstraints.enumKey({ enumModel, constrainedEnumModel, - enumKey: 'return' + enumKey: 'return', + options: TypeScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('RESERVED_RETURN'); }); @@ -88,7 +95,8 @@ describe('EnumConstrainer', () => { const constrainedValue = TypeScriptDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: 'string value' + enumValue: 'string value', + options: TypeScriptGenerator.defaultOptions }); expect(constrainedValue).toEqual('"string value"'); }); @@ -96,7 +104,8 @@ describe('EnumConstrainer', () => { const constrainedValue = TypeScriptDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: true + enumValue: true, + options: TypeScriptGenerator.defaultOptions }); expect(constrainedValue).toEqual('"true"'); }); @@ -104,7 +113,8 @@ describe('EnumConstrainer', () => { const constrainedValue = TypeScriptDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: 123 + enumValue: 123, + options: TypeScriptGenerator.defaultOptions }); expect(constrainedValue).toEqual('123'); }); @@ -112,7 +122,8 @@ describe('EnumConstrainer', () => { const constrainedValue = TypeScriptDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: { test: 'test' } + enumValue: { test: 'test' }, + options: TypeScriptGenerator.defaultOptions }); expect(constrainedValue).toEqual('\'{"test":"test"}\''); }); @@ -120,7 +131,8 @@ describe('EnumConstrainer', () => { const constrainedValue = TypeScriptDefaultConstraints.enumValue({ enumModel, constrainedEnumModel, - enumValue: undefined + enumValue: undefined, + options: TypeScriptGenerator.defaultOptions }); expect(constrainedValue).toEqual('undefined'); }); @@ -137,7 +149,12 @@ describe('EnumConstrainer', () => { const constrainFunction = defaultEnumKeyConstraints( mockedConstraintCallbacks ); - constrainFunction({ enumModel, constrainedEnumModel, enumKey: '' }); + constrainFunction({ + enumModel, + constrainedEnumModel, + enumKey: '', + options: TypeScriptGenerator.defaultOptions + }); //Expect all callbacks to be called for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { expect(jestMockCallback).toHaveBeenCalled(); @@ -159,7 +176,8 @@ describe('EnumConstrainer', () => { const constrainedValue = constrainFunction({ enumModel, constrainedEnumModel, - enumKey: '' + enumKey: '', + options: TypeScriptGenerator.defaultOptions }); expect(constrainedValue).toEqual(''); for (const jestMockCallback of spies) { diff --git a/test/generators/typescript/constrainer/ModelNameConstrainer.spec.ts b/test/generators/typescript/constrainer/ModelNameConstrainer.spec.ts index 11bcd88ba5..efc75f0b89 100644 --- a/test/generators/typescript/constrainer/ModelNameConstrainer.spec.ts +++ b/test/generators/typescript/constrainer/ModelNameConstrainer.spec.ts @@ -1,3 +1,4 @@ +import { TypeScriptGenerator } from '../../../../src'; import { TypeScriptDefaultConstraints } from '../../../../src/generators/typescript/TypeScriptConstrainer'; import { DefaultModelNameConstraints, @@ -7,34 +8,49 @@ import { describe('ModelNameConstrainer', () => { test('should never render special chars', () => { const constrainedKey = TypeScriptDefaultConstraints.modelName({ - modelName: '%' + modelName: '%', + options: TypeScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('Percent'); }); test('should never render number as start char', () => { const constrainedKey = TypeScriptDefaultConstraints.modelName({ - modelName: '1' + modelName: '1', + options: TypeScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('Number_1'); }); test('should never contain empty name', () => { const constrainedKey = TypeScriptDefaultConstraints.modelName({ - modelName: '' + modelName: '', + options: TypeScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('Empty'); }); test('should use constant naming format', () => { const constrainedKey = TypeScriptDefaultConstraints.modelName({ - modelName: 'some weird_value!"#2' + modelName: 'some weird_value!"#2', + options: TypeScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash_2'); }); test('should never render reserved keywords', () => { const constrainedKey = TypeScriptDefaultConstraints.modelName({ - modelName: 'return' + modelName: 'return', + options: TypeScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('ReservedReturn'); }); + test('should never render reserved JS keywords by default', () => { + const constrainedKey = TypeScriptDefaultConstraints.modelName({ + modelName: 'location', + options: { + ...TypeScriptGenerator.defaultOptions, + useJavascriptReservedKeywords: true + } + }); + expect(constrainedKey).toEqual('ReservedLocation'); + }); describe('custom constraints', () => { test('should be able to overwrite all hooks', () => { const mockedConstraintCallbacks: ModelNameConstraints = { @@ -47,7 +63,10 @@ describe('ModelNameConstrainer', () => { const constrainFunction = defaultModelNameConstraints( mockedConstraintCallbacks ); - constrainFunction({ modelName: '' }); + constrainFunction({ + modelName: '', + options: TypeScriptGenerator.defaultOptions + }); //Expect all callbacks to be called for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { expect(jestMockCallback).toHaveBeenCalled(); @@ -65,7 +84,10 @@ describe('ModelNameConstrainer', () => { const constrainFunction = defaultModelNameConstraints({ NAMING_FORMATTER: jestCallback }); - const constrainedValue = constrainFunction({ modelName: '' }); + const constrainedValue = constrainFunction({ + modelName: '', + options: TypeScriptGenerator.defaultOptions + }); expect(constrainedValue).toEqual(''); for (const jestMockCallback of spies) { expect(jestMockCallback).toHaveBeenCalled(); diff --git a/test/generators/typescript/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/typescript/constrainer/PropertyKeyConstrainer.spec.ts index ef9198919b..e893e40a86 100644 --- a/test/generators/typescript/constrainer/PropertyKeyConstrainer.spec.ts +++ b/test/generators/typescript/constrainer/PropertyKeyConstrainer.spec.ts @@ -3,7 +3,8 @@ import { ConstrainedObjectModel, ConstrainedObjectPropertyModel, ObjectModel, - ObjectPropertyModel + ObjectPropertyModel, + TypeScriptGenerator } from '../../../../src'; import { DefaultPropertyKeyConstraints, @@ -36,7 +37,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: TypeScriptGenerator.defaultOptions }); }; @@ -95,7 +97,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel: objectPropertyModel2, - constrainedObjectPropertyModel: constrainedObjectPropertyModel2 + constrainedObjectPropertyModel: constrainedObjectPropertyModel2, + options: TypeScriptGenerator.defaultOptions }); expect(constrainedKey).toEqual('reservedReservedReturn'); }); @@ -130,7 +133,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: TypeScriptGenerator.defaultOptions }); //Expect all callbacks to be called for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { @@ -169,7 +173,8 @@ describe('PropertyKeyConstrainer', () => { constrainedObjectModel, objectModel, objectPropertyModel, - constrainedObjectPropertyModel + constrainedObjectPropertyModel, + options: TypeScriptGenerator.defaultOptions }); expect(constrainedValue).toEqual(''); expect(jestCallback).toHaveBeenCalled(); diff --git a/test/generators/typescript/preset/MarshallingPreset.spec.ts b/test/generators/typescript/preset/MarshallingPreset.spec.ts index 80e6e32f4e..fa1b2d90f3 100644 --- a/test/generators/typescript/preset/MarshallingPreset.spec.ts +++ b/test/generators/typescript/preset/MarshallingPreset.spec.ts @@ -14,7 +14,9 @@ const doc = { }, $id: 'Test', type: 'object', - additionalProperties: { $ref: '#/definitions/NestedTest' }, + additionalProperties: { + oneOf: [{ $ref: '#/definitions/NestedTest' }, { type: 'string' }] + }, required: ['string prop'], properties: { 'string prop': { type: 'string' }, @@ -28,6 +30,9 @@ const doc = { oneOf: [ { $ref: '#/definitions/NestedTest' + }, + { + type: 'string' } ] }, @@ -51,6 +56,18 @@ const doc = { items: { $ref: '#/definitions/NestedTest' } + }, + tupleTest: { + type: 'array', + additionalItems: false, + items: [ + { + $ref: '#/definitions/NestedTest' + }, + { + type: 'string' + } + ] } } }; diff --git a/test/generators/typescript/preset/__snapshots__/JsonBinPackPreset.spec.ts.snap b/test/generators/typescript/preset/__snapshots__/JsonBinPackPreset.spec.ts.snap index 8e23a3f4c3..352da7b51d 100644 --- a/test/generators/typescript/preset/__snapshots__/JsonBinPackPreset.spec.ts.snap +++ b/test/generators/typescript/preset/__snapshots__/JsonBinPackPreset.spec.ts.snap @@ -22,16 +22,15 @@ exports[`JsonBinPack preset should work fine with AsyncAPI inputs 1`] = ` public marshal() : string { let json = '{' if(this.email !== undefined) { - json += \`\\"email\\": \${typeof this.email === 'number' || typeof this.email === 'boolean' ? this.email : JSON.stringify(this.email)},\`; + json += \`\\"email\\": \${typeof this.email === 'number' || typeof this.email === 'boolean' ? this.email : JSON.stringify(this.email)},\`; } if(this.additionalProperties !== undefined) { - for (const [key, value] of this.additionalProperties.entries()) { - //Only unwrap those who are not already a property in the JSON object - if(Object.keys(this).includes(String(key))) continue; + for (const [key, value] of this.additionalProperties.entries()) { + //Only unwrap those that are not already a property in the JSON object + if([\\"email\\",\\"additionalProperties\\"].includes(String(key))) continue; json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`; } } - //Remove potential last comma return \`\${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; } @@ -43,23 +42,22 @@ exports[`JsonBinPack preset should work fine with AsyncAPI inputs 1`] = ` if (obj[\\"email\\"] !== undefined) { instance.email = obj[\\"email\\"]; } - - if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();} - for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"email\\",\\"additionalProperties\\"].includes(key);}))) { - instance.additionalProperties.set(key, value as any); - } - + + instance.additionalProperties = new Map(); + const propsToCheck = Object.entries(obj).filter((([key,]) => {return ![\\"email\\",\\"additionalProperties\\"].includes(key);})); + for (const [key, value] of propsToCheck) { + instance.additionalProperties.set(key, value as any); + } return instance; } - public async jsonbinSerialize(): Promise{ const jsonData = JSON.parse(this.marshal()); - const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-parser-schema-id\\":\\"\\",\\"x-modelgen-inferred-name\\":\\"anonymous_schema_2\\"}},\\"x-parser-schema-id\\":\\"\\",\\"x-modelgen-inferred-name\\":\\"anonymous_schema_1\\"}); + const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-parser-schema-id\\":\\"\\",\\"x-modelgen-inferred-name\\":\\"anonymous_schema_2\\"}},\\"x-parser-schema-id\\":\\"\\",\\"x-modelgen-inferred-name\\":\\"anonymous_schema_1\\",\\"$schema\\":\\"https://json-schema.org/draft/2020-12/schema\\"}); return jsonbinpack.serialize(jsonbinpackEncodedSchema, jsonData); } public static async jsonbinDeserialize(buffer: Buffer): Promise { - const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-parser-schema-id\\":\\"\\",\\"x-modelgen-inferred-name\\":\\"anonymous_schema_2\\"}},\\"x-parser-schema-id\\":\\"\\",\\"x-modelgen-inferred-name\\":\\"anonymous_schema_1\\"}); + const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-parser-schema-id\\":\\"\\",\\"x-modelgen-inferred-name\\":\\"anonymous_schema_2\\"}},\\"x-parser-schema-id\\":\\"\\",\\"x-modelgen-inferred-name\\":\\"anonymous_schema_1\\",\\"$schema\\":\\"https://json-schema.org/draft/2020-12/schema\\"}); const json = jsonbinpack.deserialize(jsonbinpackEncodedSchema, buffer); return AnonymousSchema_1.unmarshal(json); } @@ -88,16 +86,15 @@ exports[`JsonBinPack preset should work fine with JSON Schema draft 4 1`] = ` public marshal() : string { let json = '{' if(this.email !== undefined) { - json += \`\\"email\\": \${typeof this.email === 'number' || typeof this.email === 'boolean' ? this.email : JSON.stringify(this.email)},\`; + json += \`\\"email\\": \${typeof this.email === 'number' || typeof this.email === 'boolean' ? this.email : JSON.stringify(this.email)},\`; } if(this.additionalProperties !== undefined) { - for (const [key, value] of this.additionalProperties.entries()) { - //Only unwrap those who are not already a property in the JSON object - if(Object.keys(this).includes(String(key))) continue; + for (const [key, value] of this.additionalProperties.entries()) { + //Only unwrap those that are not already a property in the JSON object + if([\\"email\\",\\"additionalProperties\\"].includes(String(key))) continue; json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`; } } - //Remove potential last comma return \`\${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; } @@ -109,23 +106,22 @@ exports[`JsonBinPack preset should work fine with JSON Schema draft 4 1`] = ` if (obj[\\"email\\"] !== undefined) { instance.email = obj[\\"email\\"]; } - - if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();} - for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"email\\",\\"additionalProperties\\"].includes(key);}))) { - instance.additionalProperties.set(key, value as any); - } - + + instance.additionalProperties = new Map(); + const propsToCheck = Object.entries(obj).filter((([key,]) => {return ![\\"email\\",\\"additionalProperties\\"].includes(key);})); + for (const [key, value] of propsToCheck) { + instance.additionalProperties.set(key, value as any); + } return instance; } - public async jsonbinSerialize(): Promise{ const jsonData = JSON.parse(this.marshal()); - const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"$schema\\":\\"http://json-schema.org/draft-04/schema\\",\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-modelgen-inferred-name\\":\\"email\\"}},\\"x-modelgen-inferred-name\\":\\"root\\"}); + const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"$schema\\":\\"https://json-schema.org/draft/2020-12/schema\\",\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-modelgen-inferred-name\\":\\"email\\"}},\\"x-modelgen-inferred-name\\":\\"root\\"}); return jsonbinpack.serialize(jsonbinpackEncodedSchema, jsonData); } public static async jsonbinDeserialize(buffer: Buffer): Promise { - const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"$schema\\":\\"http://json-schema.org/draft-04/schema\\",\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-modelgen-inferred-name\\":\\"email\\"}},\\"x-modelgen-inferred-name\\":\\"root\\"}); + const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"$schema\\":\\"https://json-schema.org/draft/2020-12/schema\\",\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-modelgen-inferred-name\\":\\"email\\"}},\\"x-modelgen-inferred-name\\":\\"root\\"}); const json = jsonbinpack.deserialize(jsonbinpackEncodedSchema, buffer); return Root.unmarshal(json); } @@ -154,16 +150,15 @@ exports[`JsonBinPack preset should work fine with JSON Schema draft 6 1`] = ` public marshal() : string { let json = '{' if(this.email !== undefined) { - json += \`\\"email\\": \${typeof this.email === 'number' || typeof this.email === 'boolean' ? this.email : JSON.stringify(this.email)},\`; + json += \`\\"email\\": \${typeof this.email === 'number' || typeof this.email === 'boolean' ? this.email : JSON.stringify(this.email)},\`; } if(this.additionalProperties !== undefined) { - for (const [key, value] of this.additionalProperties.entries()) { - //Only unwrap those who are not already a property in the JSON object - if(Object.keys(this).includes(String(key))) continue; + for (const [key, value] of this.additionalProperties.entries()) { + //Only unwrap those that are not already a property in the JSON object + if([\\"email\\",\\"additionalProperties\\"].includes(String(key))) continue; json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`; } } - //Remove potential last comma return \`\${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; } @@ -175,23 +170,22 @@ exports[`JsonBinPack preset should work fine with JSON Schema draft 6 1`] = ` if (obj[\\"email\\"] !== undefined) { instance.email = obj[\\"email\\"]; } - - if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();} - for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"email\\",\\"additionalProperties\\"].includes(key);}))) { - instance.additionalProperties.set(key, value as any); - } - + + instance.additionalProperties = new Map(); + const propsToCheck = Object.entries(obj).filter((([key,]) => {return ![\\"email\\",\\"additionalProperties\\"].includes(key);})); + for (const [key, value] of propsToCheck) { + instance.additionalProperties.set(key, value as any); + } return instance; } - public async jsonbinSerialize(): Promise{ const jsonData = JSON.parse(this.marshal()); - const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"$schema\\":\\"http://json-schema.org/draft-06/schema\\",\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-modelgen-inferred-name\\":\\"email\\"}},\\"x-modelgen-inferred-name\\":\\"root\\"}); + const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"$schema\\":\\"https://json-schema.org/draft/2020-12/schema\\",\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-modelgen-inferred-name\\":\\"email\\"}},\\"x-modelgen-inferred-name\\":\\"root\\"}); return jsonbinpack.serialize(jsonbinpackEncodedSchema, jsonData); } public static async jsonbinDeserialize(buffer: Buffer): Promise { - const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"$schema\\":\\"http://json-schema.org/draft-06/schema\\",\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-modelgen-inferred-name\\":\\"email\\"}},\\"x-modelgen-inferred-name\\":\\"root\\"}); + const jsonbinpackEncodedSchema = await jsonbinpack.compileSchema({\\"$schema\\":\\"https://json-schema.org/draft/2020-12/schema\\",\\"type\\":\\"object\\",\\"properties\\":{\\"email\\":{\\"type\\":\\"string\\",\\"format\\":\\"email\\",\\"x-modelgen-inferred-name\\":\\"email\\"}},\\"x-modelgen-inferred-name\\":\\"root\\"}); const json = jsonbinpack.deserialize(jsonbinpackEncodedSchema, buffer); return Root.unmarshal(json); } diff --git a/test/generators/typescript/preset/__snapshots__/MarshallingPreset.spec.ts.snap b/test/generators/typescript/preset/__snapshots__/MarshallingPreset.spec.ts.snap index 8342b7fb67..763b32ab54 100644 --- a/test/generators/typescript/preset/__snapshots__/MarshallingPreset.spec.ts.snap +++ b/test/generators/typescript/preset/__snapshots__/MarshallingPreset.spec.ts.snap @@ -6,20 +6,22 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` private _enumProp?: EnumTest; private _numberProp?: number; private _nestedObject?: NestedTest; - private _unionTest?: NestedTest; + private _unionTest?: NestedTest | string; private _unionArrayTest?: (NestedTest | string)[]; private _arrayTest?: NestedTest[]; - private _additionalProperties?: Map; + private _tupleTest?: [NestedTest, string]; + private _additionalProperties?: Map; constructor(input: { stringProp: string, enumProp?: EnumTest, numberProp?: number, nestedObject?: NestedTest, - unionTest?: NestedTest, + unionTest?: NestedTest | string, unionArrayTest?: (NestedTest | string)[], arrayTest?: NestedTest[], - additionalProperties?: Map, + tupleTest?: [NestedTest, string], + additionalProperties?: Map, }) { this._stringProp = input.stringProp; this._enumProp = input.enumProp; @@ -28,6 +30,7 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` this._unionTest = input.unionTest; this._unionArrayTest = input.unionArrayTest; this._arrayTest = input.arrayTest; + this._tupleTest = input.tupleTest; this._additionalProperties = input.additionalProperties; } @@ -43,8 +46,8 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` get nestedObject(): NestedTest | undefined { return this._nestedObject; } set nestedObject(nestedObject: NestedTest | undefined) { this._nestedObject = nestedObject; } - get unionTest(): NestedTest | undefined { return this._unionTest; } - set unionTest(unionTest: NestedTest | undefined) { this._unionTest = unionTest; } + get unionTest(): NestedTest | string | undefined { return this._unionTest; } + set unionTest(unionTest: NestedTest | string | undefined) { this._unionTest = unionTest; } get unionArrayTest(): (NestedTest | string)[] | undefined { return this._unionArrayTest; } set unionArrayTest(unionArrayTest: (NestedTest | string)[] | undefined) { this._unionArrayTest = unionArrayTest; } @@ -52,32 +55,35 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` get arrayTest(): NestedTest[] | undefined { return this._arrayTest; } set arrayTest(arrayTest: NestedTest[] | undefined) { this._arrayTest = arrayTest; } - get additionalProperties(): Map | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } + get tupleTest(): [NestedTest, string] | undefined { return this._tupleTest; } + set tupleTest(tupleTest: [NestedTest, string] | undefined) { this._tupleTest = tupleTest; } + + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } public marshal() : string { let json = '{' if(this.stringProp !== undefined) { - json += \`\\"string prop\\": \${typeof this.stringProp === 'number' || typeof this.stringProp === 'boolean' ? this.stringProp : JSON.stringify(this.stringProp)},\`; + json += \`\\"string prop\\": \${typeof this.stringProp === 'number' || typeof this.stringProp === 'boolean' ? this.stringProp : JSON.stringify(this.stringProp)},\`; } if(this.enumProp !== undefined) { - json += \`\\"enumProp\\": \${typeof this.enumProp === 'number' || typeof this.enumProp === 'boolean' ? this.enumProp : JSON.stringify(this.enumProp)},\`; + json += \`\\"enumProp\\": \${typeof this.enumProp === 'number' || typeof this.enumProp === 'boolean' ? this.enumProp : JSON.stringify(this.enumProp)},\`; } if(this.numberProp !== undefined) { - json += \`\\"numberProp\\": \${typeof this.numberProp === 'number' || typeof this.numberProp === 'boolean' ? this.numberProp : JSON.stringify(this.numberProp)},\`; + json += \`\\"numberProp\\": \${typeof this.numberProp === 'number' || typeof this.numberProp === 'boolean' ? this.numberProp : JSON.stringify(this.numberProp)},\`; } if(this.nestedObject !== undefined) { - json += \`\\"nestedObject\\": \${this.nestedObject.marshal()},\`; + json += \`\\"nestedObject\\": \${this.nestedObject.marshal()},\`; } if(this.unionTest !== undefined) { if(this.unionTest instanceof NestedTest) { - json += \`\\"unionTest\\": \${this.unionTest.marshal()},\`; - } else { - json += \`\\"unionTest\\": \${typeof this.unionTest === 'number' || typeof this.unionTest === 'boolean' ? this.unionTest : JSON.stringify(this.unionTest)},\`; - } + json += \`\\"unionTest\\": \${this.unionTest.marshal()},\`; + } else { + json += \`\\"unionTest\\": \${typeof this.unionTest === 'number' || typeof this.unionTest === 'boolean' ? this.unionTest : JSON.stringify(this.unionTest)},\`; + } } if(this.unionArrayTest !== undefined) { - let unionArrayTestJsonValues: any[] = []; + const unionArrayTestJsonValues: any[] = []; for (const unionItem of this.unionArrayTest) { if(unionItem instanceof NestedTest) { unionArrayTestJsonValues.push(unionItem.marshal()); @@ -85,23 +91,40 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` unionArrayTestJsonValues.push(typeof unionItem === 'number' || typeof unionItem === 'boolean' ? unionItem : JSON.stringify(unionItem)) } } - json += \`\\"unionArrayTest\\": [\${unionArrayTestJsonValues.join(',')}],\`; + json += \`\\"unionArrayTest\\": [\${unionArrayTestJsonValues.join(',')}],\`; } if(this.arrayTest !== undefined) { let arrayTestJsonValues: any[] = []; for (const unionItem of this.arrayTest) { arrayTestJsonValues.push(\`\${unionItem.marshal()}\`); } - json += \`\\"arrayTest\\": [\${arrayTestJsonValues.join(',')}],\`; + json += \`\\"arrayTest\\": [\${arrayTestJsonValues.join(',')}],\`; + } + if(this.tupleTest !== undefined) { + const serializedTuple = []; + if(this.tupleTest[0]) { + serializedTuple[0] = \${this.tupleTest[0].marshal()} + } else { + serializedTuple[0] = null; + } + if(this.tupleTest[1]) { + serializedTuple[1] = \${typeof this.tupleTest[1] === 'number' || typeof this.tupleTest[1] === 'boolean' ? this.tupleTest[1] : JSON.stringify(this.tupleTest[1])} + } else { + serializedTuple[1] = null; + } + json += \`\\"tupleTest\\": [\${serializedTuple.join(',')}],\`; } if(this.additionalProperties !== undefined) { - for (const [key, value] of this.additionalProperties.entries()) { - //Only unwrap those who are not already a property in the JSON object - if(Object.keys(this).includes(String(key))) continue; - json += \`\\"\${key}\\": \${value.marshal()},\`; + for (const [key, value] of this.additionalProperties.entries()) { + //Only unwrap those that are not already a property in the JSON object + if([\\"string prop\\",\\"enumProp\\",\\"numberProp\\",\\"nestedObject\\",\\"unionTest\\",\\"unionArrayTest\\",\\"arrayTest\\",\\"tupleTest\\",\\"additionalProperties\\"].includes(String(key))) continue; + if(value instanceof NestedTest) { + json += \`\\"\${key}\\": \${value.marshal()},\`; + } else { + json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`; + } } } - //Remove potential last comma return \`\${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; } @@ -131,12 +154,15 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` if (obj[\\"arrayTest\\"] !== undefined) { instance.arrayTest = obj[\\"arrayTest\\"]; } - - if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();} - for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"string prop\\",\\"enumProp\\",\\"numberProp\\",\\"nestedObject\\",\\"unionTest\\",\\"unionArrayTest\\",\\"arrayTest\\",\\"additionalProperties\\"].includes(key);}))) { - instance.additionalProperties.set(key, NestedTest.unmarshal(value as any)); - } - + if (obj[\\"tupleTest\\"] !== undefined) { + instance.tupleTest = obj[\\"tupleTest\\"]; + } + + instance.additionalProperties = new Map(); + const propsToCheck = Object.entries(obj).filter((([key,]) => {return ![\\"string prop\\",\\"enumProp\\",\\"numberProp\\",\\"nestedObject\\",\\"unionTest\\",\\"unionArrayTest\\",\\"arrayTest\\",\\"tupleTest\\",\\"additionalProperties\\"].includes(key);})); + for (const [key, value] of propsToCheck) { + instance.additionalProperties.set(key, value as any); + } return instance; } }" @@ -173,16 +199,15 @@ exports[`Marshalling preset should render un/marshal code 3`] = ` public marshal() : string { let json = '{' if(this.stringProp !== undefined) { - json += \`\\"stringProp\\": \${typeof this.stringProp === 'number' || typeof this.stringProp === 'boolean' ? this.stringProp : JSON.stringify(this.stringProp)},\`; + json += \`\\"stringProp\\": \${typeof this.stringProp === 'number' || typeof this.stringProp === 'boolean' ? this.stringProp : JSON.stringify(this.stringProp)},\`; } if(this.additionalProperties !== undefined) { - for (const [key, value] of this.additionalProperties.entries()) { - //Only unwrap those who are not already a property in the JSON object - if(Object.keys(this).includes(String(key))) continue; + for (const [key, value] of this.additionalProperties.entries()) { + //Only unwrap those that are not already a property in the JSON object + if([\\"stringProp\\",\\"additionalProperties\\"].includes(String(key))) continue; json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`; } } - //Remove potential last comma return \`\${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; } @@ -194,12 +219,12 @@ exports[`Marshalling preset should render un/marshal code 3`] = ` if (obj[\\"stringProp\\"] !== undefined) { instance.stringProp = obj[\\"stringProp\\"]; } - - if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();} - for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"stringProp\\",\\"additionalProperties\\"].includes(key);}))) { - instance.additionalProperties.set(key, value as any); - } - + + instance.additionalProperties = new Map(); + const propsToCheck = Object.entries(obj).filter((([key,]) => {return ![\\"stringProp\\",\\"additionalProperties\\"].includes(key);})); + for (const [key, value] of propsToCheck) { + instance.additionalProperties.set(key, value as any); + } return instance; } }" diff --git a/test/generators/typescript/preset/utils/ExampleFunctions.spec.ts b/test/generators/typescript/preset/utils/ExampleFunctions.spec.ts index 9f681fbd21..1859280b2e 100644 --- a/test/generators/typescript/preset/utils/ExampleFunctions.spec.ts +++ b/test/generators/typescript/preset/utils/ExampleFunctions.spec.ts @@ -1,6 +1,5 @@ import { renderValueFromModel } from '../../../../../src/generators/typescript/presets/utils/ExampleFunction'; import { - CommonModel, ConstrainedAnyModel, ConstrainedArrayModel, ConstrainedBooleanModel, diff --git a/test/helpers/CommonModelToMetaModel.spec.ts b/test/helpers/CommonModelToMetaModel.spec.ts index 27c9d3e4ea..dea6b7be29 100644 --- a/test/helpers/CommonModelToMetaModel.spec.ts +++ b/test/helpers/CommonModelToMetaModel.spec.ts @@ -263,6 +263,21 @@ describe('CommonModelToMetaModel', () => { (model as ObjectModel).properties['reserved_additionalProperties'] ).not.toBeUndefined(); }); + test('should convert to object model and add extend model', () => { + const extend = new CommonModel(); + extend.type = 'object'; + extend.$id = 'extend'; + const cm = new CommonModel(); + cm.type = 'object'; + cm.$id = 'test'; + cm.extend = [extend]; + + const model = convertToMetaModel(cm); + + expect(model instanceof ObjectModel).toEqual(true); + expect(model.options.extend?.length).toEqual(1); + expect(model.options.extend?.at(0) instanceof ObjectModel).toEqual(true); + }); test('should merge both patternProperties and additionalProperties into one property', () => { const stringCM = new CommonModel(); diff --git a/test/helpers/ConstrainHelpers.spec.ts b/test/helpers/ConstrainHelpers.spec.ts index c35add83b7..8e64b34f83 100644 --- a/test/helpers/ConstrainHelpers.spec.ts +++ b/test/helpers/ConstrainHelpers.spec.ts @@ -119,6 +119,35 @@ describe('ConstrainHelpers', () => { ); expect(constrainedModel instanceof ConstrainedObjectModel).toEqual(true); }); + + test('should handle extend', () => { + const extendModel = new ObjectModel('extend', undefined, {}, {}); + const metaModel = new ObjectModel( + 'test', + undefined, + { + extend: [extendModel] + }, + {} + ); + const constrainedModel = constrainMetaModel( + mockedTypeMapping, + mockedConstraints, + { + metaModel, + options: {}, + constrainedName: '', + dependencyManager: undefined as never + } + ); + expect(constrainedModel instanceof ConstrainedObjectModel).toEqual(true); + expect(constrainedModel.options.extend?.length).toEqual(1); + expect( + constrainedModel.options.extend?.at(0) instanceof ConstrainedObjectModel + ).toEqual(true); + expect(mockedConstraints.modelName).toHaveBeenCalledTimes(2); + expect(mockedTypeMapping.Object).toHaveBeenCalledTimes(2); + }); }); describe('constrain ReferenceModel', () => { test('should constrain correctly', () => { diff --git a/test/helpers/Splitter.spec.ts b/test/helpers/Splitter.spec.ts index 51b308e64e..91145087d3 100644 --- a/test/helpers/Splitter.spec.ts +++ b/test/helpers/Splitter.spec.ts @@ -174,4 +174,79 @@ describe('Splitter', () => { expect(splittedModels.length).toEqual(1); expect(splittedModels[0]).toEqual(model); }); + + describe('extend', () => { + test('should split models when extend exists in object model', () => { + const extendModel = new ObjectModel('extend', undefined, {}, {}); + const model = new ObjectModel( + 'testObj', + undefined, + { + extend: [extendModel] + }, + {} + ); + + const options: SplitOptions = { + splitObject: true + }; + + const splittedModels = split(model, options); + + expect(splittedModels.length).toEqual(2); + expect(splittedModels.at(0) instanceof ObjectModel).toEqual(true); + expect(splittedModels[0]).toEqual(model); + expect(splittedModels[0].options.extend).toEqual([ + new ReferenceModel( + extendModel.name, + extendModel.originalInput, + { + isExtended: true + }, + extendModel + ) + ]); + expect(splittedModels.at(1) instanceof ObjectModel).toEqual(true); + expect(splittedModels[1]).toEqual(extendModel); + expect(splittedModels[1].options.extend).toBeUndefined(); + expect(splittedModels[1].options.isExtended).toEqual(true); + }); + + test('should not set isExtended if a model with the same name is not extended somewhere', () => { + const extendModel = new ObjectModel('test', undefined, {}, {}); + const model = new ObjectModel( + 'model', + undefined, + { + extend: [extendModel] + }, + {} + ); + + const options: SplitOptions = { + splitObject: true + }; + + const splittedModels = split( + new ObjectModel( + '', + undefined, + {}, + { + model: new ObjectPropertyModel(model.name, true, model), + extendModel: new ObjectPropertyModel( + 'test', + true, + new ObjectModel('test', undefined, {}, {}) + ) + } + ), + options + ); + + expect(splittedModels.length).toEqual(4); + expect(splittedModels.at(2)?.options.isExtended).toEqual(false); + expect(splittedModels.at(3)?.options.isExtended).toEqual(false); + }); + }); }); diff --git a/test/helpers/TypeHelpers.spec.ts b/test/helpers/TypeHelpers.spec.ts index 13df93ac86..c57d89efa6 100644 --- a/test/helpers/TypeHelpers.spec.ts +++ b/test/helpers/TypeHelpers.spec.ts @@ -1,6 +1,5 @@ import { getTypeFromMapping, TypeMapping } from '../../src/helpers'; import { - CommonModel, ConstrainedAnyModel, ConstrainedArrayModel, ConstrainedBooleanModel, @@ -10,7 +9,6 @@ import { ConstrainedIntegerModel, ConstrainedMetaModel, ConstrainedObjectModel, - ConstrainedObjectPropertyModel, ConstrainedReferenceModel, ConstrainedStringModel, ConstrainedTupleModel, diff --git a/test/interpreter/unit/InterpretAllOf.spec.ts b/test/interpreter/unit/InterpretAllOf.spec.ts index 5f385b2448..22f8cc5ad1 100644 --- a/test/interpreter/unit/InterpretAllOf.spec.ts +++ b/test/interpreter/unit/InterpretAllOf.spec.ts @@ -107,7 +107,6 @@ describe('Interpretation of allOf', () => { interpreterOptionsAllowInheritance ); - expect(interpreter.interpretAndCombineSchema).not.toHaveBeenCalled(); expect(isModelObject).toHaveBeenCalled(); expect(model.addExtendedModel).toHaveBeenCalledWith(interpretedModel); }); diff --git a/test/interpreter/unit/Interpreter.spec.ts b/test/interpreter/unit/Interpreter.spec.ts index dd066f7cd4..f4049322d1 100644 --- a/test/interpreter/unit/Interpreter.spec.ts +++ b/test/interpreter/unit/Interpreter.spec.ts @@ -313,4 +313,13 @@ describe('Interpreter', () => { expect(discriminator).toBe('OpenapiV3SchemaDiscriminatorPropertyName'); }); }); + test('should not use cache if disableCache is set', () => { + const schema = { type: 'object' }; + const interpreter = new Interpreter(); + const model1 = interpreter.interpret(schema, { disableCache: false }); + expect(model1).not.toBeUndefined(); + const model2 = interpreter.interpret(schema, { disableCache: true }); + expect(model2).not.toBeUndefined(); + expect(model1).not.toBe(model2); + }); }); diff --git a/test/models/CommonModel.spec.ts b/test/models/CommonModel.spec.ts index ce01f83445..181dae1972 100644 --- a/test/models/CommonModel.spec.ts +++ b/test/models/CommonModel.spec.ts @@ -277,7 +277,7 @@ describe('CommonModel', () => { const doc = {}; let doc1 = CommonModel.toCommonModel(doc); const doc2 = CommonModel.toCommonModel(doc); - doc2.extend = ['test']; + doc2.extend = [doc1]; doc1 = CommonModel.mergeCommonModels(doc1, doc2, doc); expect(doc1.extend).toEqual(doc2.extend); }); @@ -895,7 +895,7 @@ describe('CommonModel', () => { extendedModel.$id = 'test'; const model = new CommonModel(); model.addExtendedModel(extendedModel); - expect(model.extend).toEqual(['test']); + expect(model.extend).toEqual([extendedModel]); }); test('should ignore model if it has no $id', () => { const extendedModel = new CommonModel(); @@ -909,7 +909,7 @@ describe('CommonModel', () => { const model = new CommonModel(); model.addExtendedModel(extendedModel); model.addExtendedModel(extendedModel); - expect(model.extend).toEqual(['test']); + expect(model.extend).toEqual([extendedModel]); }); }); describe('setTypes', () => { diff --git a/test/models/ConstrainedMetaModel.spec.ts b/test/models/ConstrainedMetaModel.spec.ts index 53e5d96729..6b49ca4bbb 100644 --- a/test/models/ConstrainedMetaModel.spec.ts +++ b/test/models/ConstrainedMetaModel.spec.ts @@ -38,7 +38,7 @@ describe('ConstrainedMetaModel', () => { originalInput: 'testConst' }, discriminator: { - originalInput: 'testDiscriminator' + discriminator: 'testDiscriminator' } }); @@ -692,6 +692,44 @@ describe('ConstrainedMetaModel', () => { expect(dependencies[0]).toEqual(model.union[0]); }); + test('should handle shallow recursive models', () => { + const stringModel = new StringModel('', undefined, {}); + const referenceModel = new ReferenceModel('', undefined, {}, stringModel); + const rawModel = new UnionModel('test', undefined, {}, [referenceModel]); + rawModel.union.push(rawModel); + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined, + dependencyManager: undefined as never + }) as ConstrainedUnionModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + expect(dependencies[0]).toEqual(model.union[0]); + }); + + test('should handle deep recursive models', () => { + const stringModel = new StringModel('', undefined, {}); + const referenceModel = new ReferenceModel('', undefined, {}, stringModel); + const rawModel = new UnionModel('test', undefined, {}, [ + stringModel, + referenceModel + ]); + rawModel.union.push(rawModel); + referenceModel.ref = rawModel; + + const model = constrainMetaModel(mockedTypeMapping, mockedConstraints, { + metaModel: rawModel, + constrainedName: '', + options: undefined, + dependencyManager: undefined as never + }) as ConstrainedUnionModel; + const dependencies = model.getNearestDependencies(); + expect(dependencies).toHaveLength(1); + expect(dependencies[0]).toEqual(model.union[1]); + }); + test('should not return duplicate dependencies when different reference instances', () => { const stringModel = new StringModel('', undefined, {}); const referenceModel = new ReferenceModel('', undefined, {}, stringModel); diff --git a/test/models/OutputModel.spec.ts b/test/models/OutputModel.spec.ts index c4a65b2c9f..5a427df586 100644 --- a/test/models/OutputModel.spec.ts +++ b/test/models/OutputModel.spec.ts @@ -1,6 +1,5 @@ import { InputMetaModel, - CommonModel, ToOutputModelArg, OutputModel, ConstrainedAnyModel diff --git a/test/processors/AsyncAPIInputProcessor.spec.ts b/test/processors/AsyncAPIInputProcessor.spec.ts index d509c82b36..6ff561be93 100644 --- a/test/processors/AsyncAPIInputProcessor.spec.ts +++ b/test/processors/AsyncAPIInputProcessor.spec.ts @@ -2,11 +2,16 @@ import * as fs from 'fs'; import * as path from 'path'; import { Parser } from '@asyncapi/parser'; import { AsyncAPIInputProcessor } from '../../src/processors/AsyncAPIInputProcessor'; +import { InputMetaModel } from '../../src/models'; const basicDocString = fs.readFileSync( path.resolve(__dirname, './AsyncAPIInputProcessor/basic.json'), 'utf8' ); +const basicV3DocString = fs.readFileSync( + path.resolve(__dirname, './AsyncAPIInputProcessor/basic_v3.json'), + 'utf8' +); const operationOneOf1DocString = fs.readFileSync( path.resolve(__dirname, './AsyncAPIInputProcessor/operation_oneof1.json'), 'utf8' @@ -15,6 +20,10 @@ const operationOneOf2DocString = fs.readFileSync( path.resolve(__dirname, './AsyncAPIInputProcessor/operation_oneof2.json'), 'utf8' ); +const ymlFileURI = `file://${path.resolve( + __dirname, + './AsyncAPIInputProcessor/testasyncapi.yml' +)}`; jest.mock('../../src/utils/LoggingInterface'); describe('AsyncAPIInputProcessor', () => { @@ -30,6 +39,9 @@ describe('AsyncAPIInputProcessor', () => { const { document } = await parser.parse(basicDocString); expect(processor.shouldProcess(document)).toEqual(true); }); + test('should be able to detect file', () => { + expect(processor.shouldProcess(ymlFileURI)).toEqual(true); + }); test('should be able to process AsyncAPI 2.0.0', () => { const parsedObject = { asyncapi: '2.0.0' }; expect(processor.shouldProcess(parsedObject)).toEqual(true); @@ -58,14 +70,14 @@ describe('AsyncAPIInputProcessor', () => { const parsedObject = { asyncapi: '2.6.0' }; expect(processor.shouldProcess(parsedObject)).toEqual(true); }); + test('should be able to process AsyncAPI 3.x', () => { + const parsedObject = { asyncapi: '3.0.0' }; + expect(processor.shouldProcess(parsedObject)).toEqual(true); + }); test('should not be able to process unsupported AsyncAPI 2.x', () => { const parsedObject = { asyncapi: '2.123.0' }; expect(processor.shouldProcess(parsedObject)).toEqual(false); }); - test('should not be able to process AsyncAPI 3.x', () => { - const parsedObject = { asyncapi: '3.0.0' }; - expect(processor.shouldProcess(parsedObject)).toEqual(false); - }); }); describe('tryGetVersionOfDocument()', () => { const processor = new AsyncAPIInputProcessor(); @@ -80,6 +92,10 @@ describe('AsyncAPIInputProcessor', () => { const { document } = await parser.parse(basicDocString); expect(processor.tryGetVersionOfDocument(document)).toEqual('2.0.0'); }); + test('should be able to find AsyncAPI version for v3', () => { + const basicDoc = JSON.parse(basicV3DocString); + expect(processor.tryGetVersionOfDocument(basicDoc)).toEqual('3.0.0'); + }); }); describe('isFromParser()', () => { test('should be able to detect pure object', () => { @@ -109,12 +125,25 @@ describe('AsyncAPIInputProcessor', () => { ); }); + test('should throw error when file does not exists', async () => { + const processor = new AsyncAPIInputProcessor(); + await expect(processor.process(`${ymlFileURI}test`)).rejects.toThrow( + 'File does not exists.' + ); + }); + test('should be able to process pure object', async () => { const basicDoc = JSON.parse(basicDocString); const processor = new AsyncAPIInputProcessor(); const commonInputModel = await processor.process(basicDoc); expect(commonInputModel).toMatchSnapshot(); }); + test('should be able to process pure object for v3', async () => { + const basicDoc = JSON.parse(basicV3DocString); + const processor = new AsyncAPIInputProcessor(); + const commonInputModel = await processor.process(basicDoc); + expect(commonInputModel).toMatchSnapshot(); + }); test('should be able to process parsed objects', async () => { const { document } = await parser.parse(basicDocString); @@ -123,6 +152,13 @@ describe('AsyncAPIInputProcessor', () => { expect(commonInputModel).toMatchSnapshot(); }); + test('should be able to process file', async () => { + const processor = new AsyncAPIInputProcessor(); + const commonInputModel = await processor.process(ymlFileURI); + expect(commonInputModel instanceof InputMetaModel).toBeTruthy(); + expect(commonInputModel.models).toMatchSnapshot(); + }); + test('should be able to process operation with oneOf #1', async () => { const { document } = await parser.parse(operationOneOf1DocString); const processor = new AsyncAPIInputProcessor(); diff --git a/test/processors/AsyncAPIInputProcessor/basic_v3.json b/test/processors/AsyncAPIInputProcessor/basic_v3.json new file mode 100644 index 0000000000..1051bd8836 --- /dev/null +++ b/test/processors/AsyncAPIInputProcessor/basic_v3.json @@ -0,0 +1,34 @@ +{ + "asyncapi": "3.0.0", + "defaultContentType": "application/json", + "info": { + "title": "Signup service example (internal)", + "version": "0.1.0" + }, + "channels": { + "userSignedUp": { + "address": "/user/signedup", + "messages": { + "userSignUpMessage": { + "payload": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + } + } + } + } + } + } + }, + "operations": { + "userSignup": { + "action": "send", + "channel": { + "$ref": "#/channels/userSignedUp" + } + } + } +} \ No newline at end of file diff --git a/test/processors/AsyncAPIInputProcessor/testasyncapi.yml b/test/processors/AsyncAPIInputProcessor/testasyncapi.yml new file mode 100644 index 0000000000..fbb3f891b7 --- /dev/null +++ b/test/processors/AsyncAPIInputProcessor/testasyncapi.yml @@ -0,0 +1,23 @@ +asyncapi: '2.6.0' +info: + title: Account Service + version: 1.0.0 + description: This service is in charge of processing user signups +channels: + user/signedup: + subscribe: + message: + $ref: '#/components/messages/UserSignedUp' +components: + messages: + UserSignedUp: + payload: + type: object + properties: + displayName: + type: string + description: Name of the user + email: + type: string + format: email + description: Email of the user \ No newline at end of file diff --git a/test/processors/InputProcessor.spec.ts b/test/processors/InputProcessor.spec.ts index 1e9e61bb59..f09b22eeb2 100644 --- a/test/processors/InputProcessor.spec.ts +++ b/test/processors/InputProcessor.spec.ts @@ -6,8 +6,7 @@ import { AsyncAPIInputProcessor, JsonSchemaInputProcessor, InputProcessor, - SwaggerInputProcessor, - TypeScriptInputProcessor + SwaggerInputProcessor } from '../../src/processors'; import { OpenAPIInputProcessor } from '../../src/processors/OpenAPIInputProcessor'; diff --git a/test/processors/JsonSchemaInputProcessor.spec.ts b/test/processors/JsonSchemaInputProcessor.spec.ts index 769ad658f6..cc3e856a4b 100644 --- a/test/processors/JsonSchemaInputProcessor.spec.ts +++ b/test/processors/JsonSchemaInputProcessor.spec.ts @@ -1,11 +1,11 @@ import * as fs from 'fs'; import * as path from 'path'; import { JsonSchemaInputProcessor } from '../../src/processors/JsonSchemaInputProcessor'; -import { AnyModel, CommonModel, StringModel } from '../../src/models'; +import { AnyModel, CommonModel } from '../../src/models'; jest.mock('../../src/utils/LoggingInterface'); jest.spyOn(JsonSchemaInputProcessor, 'convertSchemaToCommonModel'); let mockedReturnModels = [new CommonModel()]; -const mockedMetaModel = new AnyModel('test', undefined); +const mockedMetaModel = new AnyModel('test', undefined, {}); jest.mock('../../src/helpers/CommonModelToMetaModel', () => { return { convertToMetaModel: jest.fn().mockImplementation(() => { @@ -261,6 +261,38 @@ describe('JsonSchemaInputProcessor', () => { 'Cannot handle input, because it has a root `$ref`, please manually resolve the first reference.' ); }); + test('should not resolve example containing $ref keyword', async () => { + const processor = new JsonSchemaInputProcessor(); + const schema = { + examples: [{ $ref: '#/none/existing/reference' }], + properties: { + $ref: { + type: 'string' + } + } + }; + const dereferencedSchema = await processor.dereferenceInputs(schema); + expect(dereferencedSchema.examples[0]).toEqual({ + $ref: '#/none/existing/reference' + }); + }); + test('should resolve example containing $ref keyword if part of properties', async () => { + const processor = new JsonSchemaInputProcessor(); + const schema = { + definitions: { + reference: { + type: 'string' + } + }, + properties: { + $ref: { $ref: '#/definitions/reference' } + } + }; + const dereferencedSchema = await processor.dereferenceInputs(schema); + expect(dereferencedSchema.properties.$ref).toEqual({ + type: 'string' + }); + }); }); describe('convertSchemaToCommonModel()', () => { diff --git a/test/processors/__snapshots__/AsyncAPIInputProcessor.spec.ts.snap b/test/processors/__snapshots__/AsyncAPIInputProcessor.spec.ts.snap index 4f35c47b35..2b7fab40bb 100644 --- a/test/processors/__snapshots__/AsyncAPIInputProcessor.spec.ts.snap +++ b/test/processors/__snapshots__/AsyncAPIInputProcessor.spec.ts.snap @@ -1,5 +1,102 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`AsyncAPIInputProcessor process() should be able to process file 1`] = ` +Object { + "anonymous_schema_1": ObjectModel { + "name": "anonymous_schema_1", + "options": Object { + "isNullable": false, + }, + "originalInput": AsyncapiV2Schema { + "properties": Object { + "displayName": AsyncapiV2Schema { + "description": "Name of the user", + "type": "string", + "x-modelgen-inferred-name": "anonymous_schema_2", + "x-parser-schema-id": "", + }, + "email": AsyncapiV2Schema { + "description": "Email of the user", + "format": "email", + "type": "string", + "x-modelgen-inferred-name": "anonymous_schema_3", + "x-parser-schema-id": "", + }, + }, + "type": "object", + "x-modelgen-inferred-name": "anonymous_schema_1", + "x-parser-schema-id": "", + }, + "properties": Object { + "additionalProperties": ObjectPropertyModel { + "property": DictionaryModel { + "key": StringModel { + "name": "additionalProperties", + "options": Object { + "isNullable": false, + }, + "originalInput": Array [ + true, + ], + }, + "name": "additionalProperties", + "options": Object { + "isNullable": false, + }, + "originalInput": Array [ + true, + ], + "serializationType": "unwrap", + "value": AnyModel { + "name": "undefined", + "options": Object { + "isNullable": true, + }, + "originalInput": true, + }, + }, + "propertyName": "additionalProperties", + "required": false, + }, + "displayName": ObjectPropertyModel { + "property": StringModel { + "name": "anonymous_schema_2", + "options": Object { + "isNullable": false, + }, + "originalInput": AsyncapiV2Schema { + "description": "Name of the user", + "type": "string", + "x-modelgen-inferred-name": "anonymous_schema_2", + "x-parser-schema-id": "", + }, + }, + "propertyName": "displayName", + "required": false, + }, + "email": ObjectPropertyModel { + "property": StringModel { + "name": "anonymous_schema_3", + "options": Object { + "format": "email", + "isNullable": false, + }, + "originalInput": AsyncapiV2Schema { + "description": "Email of the user", + "format": "email", + "type": "string", + "x-modelgen-inferred-name": "anonymous_schema_3", + "x-parser-schema-id": "", + }, + }, + "propertyName": "email", + "required": false, + }, + }, + }, +} +`; + exports[`AsyncAPIInputProcessor process() should be able to process operation with oneOf #1 1`] = ` InputMetaModel { "models": Object { @@ -1510,7 +1607,7 @@ InputMetaModel { "title": "CloudEvent example", "version": "1.0.0", }, - "x-parser-api-version": 1, + "x-parser-api-version": 2, "x-parser-spec-parsed": true, }, "_meta": Object { @@ -1943,7 +2040,7 @@ InputMetaModel { "title": "CloudEvent example", "version": "1.0.0", }, - "x-parser-api-version": 1, + "x-parser-api-version": 2, "x-parser-spec-parsed": true, }, "semver": Object { @@ -3476,7 +3573,7 @@ InputMetaModel { "title": "CloudEvent example", "version": "1.0.0", }, - "x-parser-api-version": 1, + "x-parser-api-version": 2, "x-parser-spec-parsed": true, }, "_meta": Object { @@ -3909,7 +4006,7 @@ InputMetaModel { "title": "CloudEvent example", "version": "1.0.0", }, - "x-parser-api-version": 1, + "x-parser-api-version": 2, "x-parser-spec-parsed": true, }, "semver": Object { @@ -4027,7 +4124,7 @@ InputMetaModel { "title": "Signup service example (internal)", "version": "0.1.0", }, - "x-parser-api-version": 1, + "x-parser-api-version": 2, "x-parser-spec-parsed": true, }, "_meta": Object { @@ -4084,7 +4181,7 @@ InputMetaModel { "title": "Signup service example (internal)", "version": "0.1.0", }, - "x-parser-api-version": 1, + "x-parser-api-version": 2, "x-parser-spec-parsed": true, }, "semver": Object { @@ -4202,7 +4299,7 @@ InputMetaModel { "title": "Signup service example (internal)", "version": "0.1.0", }, - "x-parser-api-version": 1, + "x-parser-api-version": 2, "x-parser-spec-parsed": true, }, "_meta": Object { @@ -4259,7 +4356,7 @@ InputMetaModel { "title": "Signup service example (internal)", "version": "0.1.0", }, - "x-parser-api-version": 1, + "x-parser-api-version": 2, "x-parser-spec-parsed": true, }, "semver": Object { @@ -4276,3 +4373,237 @@ InputMetaModel { }, } `; + +exports[`AsyncAPIInputProcessor process() should be able to process pure object for v3 1`] = ` +InputMetaModel { + "models": Object { + "anonymous_schema_1": ObjectModel { + "name": "anonymous_schema_1", + "options": Object { + "isNullable": false, + }, + "originalInput": AsyncapiV2Schema { + "properties": Object { + "email": AsyncapiV2Schema { + "format": "email", + "type": "string", + "x-modelgen-inferred-name": "anonymous_schema_2", + "x-parser-schema-id": "", + }, + }, + "type": "object", + "x-modelgen-inferred-name": "anonymous_schema_1", + "x-parser-schema-id": "", + }, + "properties": Object { + "additionalProperties": ObjectPropertyModel { + "property": DictionaryModel { + "key": StringModel { + "name": "additionalProperties", + "options": Object { + "isNullable": false, + }, + "originalInput": Array [ + true, + ], + }, + "name": "additionalProperties", + "options": Object { + "isNullable": false, + }, + "originalInput": Array [ + true, + ], + "serializationType": "unwrap", + "value": AnyModel { + "name": "undefined", + "options": Object { + "isNullable": true, + }, + "originalInput": true, + }, + }, + "propertyName": "additionalProperties", + "required": false, + }, + "email": ObjectPropertyModel { + "property": StringModel { + "name": "anonymous_schema_2", + "options": Object { + "format": "email", + "isNullable": false, + }, + "originalInput": AsyncapiV2Schema { + "format": "email", + "type": "string", + "x-modelgen-inferred-name": "anonymous_schema_2", + "x-parser-schema-id": "", + }, + }, + "propertyName": "email", + "required": false, + }, + }, + }, + }, + "originalInput": AsyncAPIDocument { + "_json": Object { + "asyncapi": "3.0.0", + "channels": Object { + "userSignedUp": Object { + "address": "/user/signedup", + "messages": Object { + "userSignUpMessage": Object { + "payload": Object { + "properties": Object { + "email": Object { + "format": "email", + "type": "string", + "x-parser-schema-id": "", + }, + }, + "type": "object", + "x-parser-schema-id": "", + }, + "x-parser-message-name": "userSignUpMessage", + }, + }, + }, + }, + "defaultContentType": "application/json", + "info": Object { + "title": "Signup service example (internal)", + "version": "0.1.0", + }, + "operations": Object { + "userSignup": Object { + "action": "send", + "channel": Object { + "address": "/user/signedup", + "messages": Object { + "userSignUpMessage": Object { + "payload": Object { + "properties": Object { + "email": Object { + "format": "email", + "type": "string", + "x-parser-schema-id": "", + }, + }, + "type": "object", + "x-parser-schema-id": "", + }, + "x-parser-message-name": "userSignUpMessage", + }, + }, + }, + }, + }, + "x-parser-api-version": 2, + "x-parser-spec-parsed": true, + }, + "_meta": Object { + "asyncapi": Object { + "input": Object { + "asyncapi": "3.0.0", + "channels": Object { + "userSignedUp": Object { + "address": "/user/signedup", + "messages": Object { + "userSignUpMessage": Object { + "payload": Object { + "properties": Object { + "email": Object { + "format": "email", + "type": "string", + }, + }, + "type": "object", + }, + }, + }, + }, + }, + "defaultContentType": "application/json", + "info": Object { + "title": "Signup service example (internal)", + "version": "0.1.0", + }, + "operations": Object { + "userSignup": Object { + "action": "send", + "channel": Object { + "$ref": "#/channels/userSignedUp", + }, + }, + }, + }, + "parsed": Object { + "asyncapi": "3.0.0", + "channels": Object { + "userSignedUp": Object { + "address": "/user/signedup", + "messages": Object { + "userSignUpMessage": Object { + "payload": Object { + "properties": Object { + "email": Object { + "format": "email", + "type": "string", + "x-parser-schema-id": "", + }, + }, + "type": "object", + "x-parser-schema-id": "", + }, + "x-parser-message-name": "userSignUpMessage", + }, + }, + }, + }, + "defaultContentType": "application/json", + "info": Object { + "title": "Signup service example (internal)", + "version": "0.1.0", + }, + "operations": Object { + "userSignup": Object { + "action": "send", + "channel": Object { + "address": "/user/signedup", + "messages": Object { + "userSignUpMessage": Object { + "payload": Object { + "properties": Object { + "email": Object { + "format": "email", + "type": "string", + "x-parser-schema-id": "", + }, + }, + "type": "object", + "x-parser-schema-id": "", + }, + "x-parser-message-name": "userSignUpMessage", + }, + }, + }, + }, + }, + "x-parser-api-version": 2, + "x-parser-spec-parsed": true, + }, + "semver": Object { + "major": 3, + "minor": 0, + "patch": 0, + "rc": undefined, + "version": "3.0.0", + }, + "source": undefined, + }, + "pointer": "/", + }, + }, +} +`; diff --git a/test/runtime/generic-input.json b/test/runtime/generic-input.json index 3d153ba97b..afe3656468 100644 --- a/test/runtime/generic-input.json +++ b/test/runtime/generic-input.json @@ -44,6 +44,9 @@ "type": "string" } } + }, + "enumTest": { + "enum": ["test", "test2"] } }, "patternProperties": { @@ -52,7 +55,6 @@ } }, "required": [ - "street_name", "house_number", "array_type" ] diff --git a/test/runtime/runtime-csharp.ts b/test/runtime/runtime-csharp.ts index 97f5c1c441..12598805c9 100644 --- a/test/runtime/runtime-csharp.ts +++ b/test/runtime/runtime-csharp.ts @@ -1,17 +1,43 @@ -import { CSHARP_JSON_SERIALIZER_PRESET, CSharpFileGenerator } from '../../'; +import { CSHARP_JSON_SERIALIZER_PRESET, CSHARP_NEWTONSOFT_SERIALIZER_PRESET, CSharpFileGenerator } from '../../'; import path from 'path'; import input from './generic-input.json'; -const generator = new CSharpFileGenerator({ - presets: [CSHARP_JSON_SERIALIZER_PRESET] -}); +async function generateJsonSerializer() { + const generator = new CSharpFileGenerator({ + presets: [CSHARP_JSON_SERIALIZER_PRESET] + }); + + await generator.generateToFiles( + input, + path.resolve( + // eslint-disable-next-line no-undef + __dirname, + './runtime-csharp/runtime-csharp/src/models/json_serializer' + ), + { namespace: 'com.mycompany.app.json_serializer' } + ); +} +async function generateNewtonsoft() { + const generator = new CSharpFileGenerator({ + presets: [CSHARP_NEWTONSOFT_SERIALIZER_PRESET] + }); + await generator.generateToFiles( + input, + path.resolve( + // eslint-disable-next-line no-undef + __dirname, + './runtime-csharp/runtime-csharp/src/models/newtonsoft' + ), + { namespace: 'com.mycompany.app.newtonsoft' } + ); +} -generator.generateToFiles( - input, - path.resolve( - // eslint-disable-next-line no-undef - __dirname, - './runtime-csharp/runtime-csharp/src/models/json_serializer' - ), - { namespace: 'com.mycompany.app.generic' } -); +async function generateEverything() { + await Promise.all( + [ + generateJsonSerializer(), + generateNewtonsoft() + ] + ) +} +generateEverything(); \ No newline at end of file diff --git a/test/runtime/runtime-csharp/runtime-csharp/runtime-csharp.csproj b/test/runtime/runtime-csharp/runtime-csharp/runtime-csharp.csproj index 7cb79cdc30..a16ebd4679 100644 --- a/test/runtime/runtime-csharp/runtime-csharp/runtime-csharp.csproj +++ b/test/runtime/runtime-csharp/runtime-csharp/runtime-csharp.csproj @@ -15,6 +15,7 @@ + @@ -24,6 +25,7 @@ + diff --git a/test/runtime/runtime-csharp/runtime-csharp/test/models/json_serializer/Address.cs b/test/runtime/runtime-csharp/runtime-csharp/test/models/json_serializer/Address.cs index 4275e8d7d7..bb50cce423 100644 --- a/test/runtime/runtime-csharp/runtime-csharp/test/models/json_serializer/Address.cs +++ b/test/runtime/runtime-csharp/runtime-csharp/test/models/json_serializer/Address.cs @@ -1,7 +1,7 @@ -using System.Text.Json; -using com.mycompany.app.generic; +using System.Text.Json; +using com.mycompany.app.json_serializer; -namespace runtime_csharp; +namespace runtime_csharp.json_serializer; public class AddressTests { @@ -18,13 +18,16 @@ public void TestSerializingFullModel() NestedObject nestedObject = new NestedObject(); nestedObject.Test = "test"; address.NestedObject = nestedObject; - address.StreetName = "test"; address.Marriage = true; address.Members = 2; address.HouseNumber = 1; address.ArrayType = new dynamic[] { 1, "test" }; + address.EnumTest = EnumTest.TEST; + address.AdditionalProperties = new Dictionary(); + address.AdditionalProperties.Add("test_not_used", 2); + string actualJsonString = JsonSerializer.Serialize(address); - string expectedJsonString = "{\"street_name\":\"test\",\"house_number\":1,\"marriage\":true,\"members\":2,\"array_type\":[1,\"test\"],\"nestedObject\":{\"test\":\"test\"}}"; + string expectedJsonString = "{\"house_number\":1,\"marriage\":true,\"members\":2,\"array_type\":[1,\"test\"],\"nestedObject\":{\"test\":\"test\"},\"enumTest\":\"test\",\"test_not_used\":2}"; Assert.That(actualJsonString, Is.EqualTo(expectedJsonString)); } } diff --git a/test/runtime/runtime-csharp/runtime-csharp/test/models/newtonsoft/Address.cs b/test/runtime/runtime-csharp/runtime-csharp/test/models/newtonsoft/Address.cs new file mode 100644 index 0000000000..896823abbb --- /dev/null +++ b/test/runtime/runtime-csharp/runtime-csharp/test/models/newtonsoft/Address.cs @@ -0,0 +1,35 @@ +using System.Text.Json; +using com.mycompany.app.newtonsoft; +using Newtonsoft.Json; + +namespace runtime_csharp.newtonsoft; + +public class AddressTests +{ + + [SetUp] + public void Setup() + { + } + + [Test] + public void TestSerializingFullModel() + { + Address address = new Address(); + NestedObject nestedObject = new NestedObject(); + nestedObject.Test = "test"; + address.NestedObject = nestedObject; + address.Marriage = true; + address.Members = 2; + address.HouseNumber = 1; + address.ArrayType = new dynamic[] { 1, "test" }; + address.EnumTest = EnumTest.TEST; + address.AdditionalProperties = new Dictionary(); + address.AdditionalProperties.Add("test_not_used", 2); + + string actualJsonString = JsonConvert.SerializeObject(address); + string expectedJsonString = "{\"house_number\":1.0,\"marriage\":true,\"members\":2,\"array_type\":[1,\"test\"],\"nestedObject\":{\"test\":\"test\"},\"enumTest\":\"test\",\"test_not_used\":2}"; + Assert.That(actualJsonString, Is.EqualTo(expectedJsonString)); + } +} + diff --git a/test/runtime/runtime-java.ts b/test/runtime/runtime-java.ts index 8e0dd85980..af0a734621 100644 --- a/test/runtime/runtime-java.ts +++ b/test/runtime/runtime-java.ts @@ -2,16 +2,27 @@ import { JAVA_JACKSON_PRESET, JavaFileGenerator } from '../../'; import path from 'path'; import input from './generic-input.json'; -const generator = new JavaFileGenerator({ - presets: [JAVA_JACKSON_PRESET] -}); +async function generateJacksonModels() { + const generator = new JavaFileGenerator({ + presets: [JAVA_JACKSON_PRESET] + }); + + await generator.generateToFiles( + input, + path.resolve( + // eslint-disable-next-line no-undef + __dirname, + './runtime-java/src/main/java/com/mycompany/app/jackson' + ), + { packageName: 'com.mycompany.app.jackson' } + ); +} -generator.generateToFiles( - input, - path.resolve( - // eslint-disable-next-line no-undef - __dirname, - './runtime-java/src/main/java/com/mycompany/app/generic' - ), - { packageName: 'com.mycompany.app.generic' } -); +async function generateEverything() { + await Promise.all( + [ + generateJacksonModels() + ] + ) +} +generateEverything(); \ No newline at end of file diff --git a/test/runtime/runtime-java/.gitignore b/test/runtime/runtime-java/.gitignore index 19ec31de0d..6133fc37c1 100644 --- a/test/runtime/runtime-java/.gitignore +++ b/test/runtime/runtime-java/.gitignore @@ -17,7 +17,7 @@ buildNumber.properties # JDT-specific (Eclipse Java Development Tools) .classpath -src/main/java/com/mycompany/app/generic +src/main bin .settings diff --git a/test/runtime/runtime-java/src/test/java/com/mycompany/app/generic/AddressTest.java b/test/runtime/runtime-java/src/test/java/com/mycompany/app/jackson/AddressTest.java similarity index 62% rename from test/runtime/runtime-java/src/test/java/com/mycompany/app/generic/AddressTest.java rename to test/runtime/runtime-java/src/test/java/com/mycompany/app/jackson/AddressTest.java index 736245104c..4f0478b638 100644 --- a/test/runtime/runtime-java/src/test/java/com/mycompany/app/generic/AddressTest.java +++ b/test/runtime/runtime-java/src/test/java/com/mycompany/app/jackson/AddressTest.java @@ -1,5 +1,5 @@ -package com.mycompany.app.generic; +package com.mycompany.app.jackson; import static org.junit.Assert.*; import static org.hamcrest.CoreMatchers.*; @@ -9,6 +9,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.*; +import com.mycompany.app.jackson.Address; +import com.mycompany.app.jackson.NestedObject; /** * Unit test for Address. @@ -33,19 +35,8 @@ public void shouldBeAbleToSerializeModel() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); String json = objectMapper.writeValueAsString(address); - assertTrue( json != null ); - assertTrue(json.length() != 0); - } - - @Test - public void shouldNotContainAdditionalPropertiesWhenSerialized() throws JsonProcessingException - { - /** - * additionalProperties should be unwrapped when serialized - */ - ObjectMapper objectMapper = new ObjectMapper(); - String json = objectMapper.writeValueAsString(address); - assertTrue( true ); - assertThat( json, not(containsString("additionalProperties"))); + String expectedJson = "{\"street_name\":\"Test address 2\",\"house_number\":2.0,\"marriage\":true,\"members\":2,\"array_type\":[2,\"test\"],\"nestedObject\":{\"test\":\"test\"}}"; + assertNotNull(json); + assertEquals(json, expectedJson); } } diff --git a/test/runtime/runtime-typescript.ts b/test/runtime/runtime-typescript.ts index c1614f7a35..344b844111 100644 --- a/test/runtime/runtime-typescript.ts +++ b/test/runtime/runtime-typescript.ts @@ -2,7 +2,6 @@ import { TS_COMMON_PRESET, TS_JSONBINPACK_PRESET, TypeScriptFileGenerator } from import path from 'path'; import input from './generic-input.json'; - async function generateNamedExport() { const generator = new TypeScriptFileGenerator({}); @@ -16,6 +15,7 @@ async function generateNamedExport() { { exportType: 'named' } ); } + async function generateDefaultExport() { const generator = new TypeScriptFileGenerator({}); @@ -63,8 +63,29 @@ async function generateJsonBinPack() { TS_JSONBINPACK_PRESET ] }); + const simplifiedInput = { + "$id": "Address", + "description": "This object contains all types of MetaModel generations", + "type": "object", + "properties": { + "street_name": { + "type": "string" + }, + "house_number": { + "type": "number" + }, + "marriage": { + "type": "boolean", + "description": "Status if marriage live in given house" + }, + }, + "required": [ + "street_name", + "house_number", + ] + } await generator.generateToFiles( - input, + simplifiedInput, path.resolve( // eslint-disable-next-line no-undef __dirname, diff --git a/test/runtime/runtime-typescript/test/JsonBinPackPreset.spec.ts b/test/runtime/runtime-typescript/test/JsonBinPackPreset.spec.ts index eca9ab5217..b01f1b9ca3 100644 --- a/test/runtime/runtime-typescript/test/JsonBinPackPreset.spec.ts +++ b/test/runtime/runtime-typescript/test/JsonBinPackPreset.spec.ts @@ -1,17 +1,10 @@ import { Address } from '../src/jsonbinpack/Address'; -import { NestedObject } from '../src/jsonbinpack/NestedObject'; describe('Address', () => { - const nestedObj = new NestedObject({ - test: 'test' - }); const address = new Address({ streetName: 'test', houseNumber: 1, - marriage: true, - members: 2, - arrayType: [1, 'test'], - nestedObject: nestedObj + marriage: true }); test('be able to serialize model and turning it back to a model with the same values', async () => { const serialized = await address.jsonbinSerialize(); diff --git a/test/utils/Partials.spec.ts b/test/utils/Partials.spec.ts index 19e4889557..a4b7630aa0 100644 --- a/test/utils/Partials.spec.ts +++ b/test/utils/Partials.spec.ts @@ -100,4 +100,22 @@ describe('mergePartialAndDefault', () => { expect(realizedOptions.nestedObject instanceof TestClass).toEqual(true); expect(realizedOptions.nestedObject.test()).toEqual(true); }); + + test('should not return default options instance', () => { + interface TestType { + array: string[]; + } + const defaultOptions: TestType = { + array: [] + }; + const partialOptions: DeepPartial = { + array: ['test'] + }; + const realizedOptions = mergePartialAndDefault( + defaultOptions, + partialOptions + ) as TestType; + expect(defaultOptions).toEqual({ array: [] }); + expect(realizedOptions).toEqual({ array: ['test'] }); + }); });