Migration from v1 to v2

This document contains all the breaking changes and migration guidelines for adapting your code to the new version.

Using preferred IDs over anonymous IDs

If you use allOf, and properties need to be merged, Modelina is now using preferred IDs over anonymous IDs. That means if a property has an id/title other than anonymous_schema in one of the schemas in a allOf, it will use the non-anonymous id/title.

Example:

1channels:
2  pet:
3    publish:
4      message:
5        oneOf:
6          - $ref: '#/components/messages/Dog'
7          - $ref: '#/components/messages/Cat'
8components:
9  messages:
10    Dog:
11      payload:
12        title: Dog
13        allOf:
14          - $ref: '#/components/schemas/CloudEvent'
15          - type: object
16            properties:
17              type:
18                title: DogType
19                const: Dog
20    Cat:
21      payload:
22        title: Cat
23        allOf:
24          - $ref: '#/components/schemas/CloudEvent'
25          - type: object
26            properties:
27              type:
28                title: CatType
29                const: Cat
30  schemas:
31    CloudEvent:
32      type: object
33      properties:
34        type:
35          type: string
36      required:
37        - type

The type property in the CloudEvent schema will in this case have an anonymous_schema id. If another schema in the allOf list has the same property and an id other than anonymous_schema, it will now use that id. Meaning, in this example, it will be DogType and CatType.

Accurate array types

For JSON Schema inputs (indirectly for AsyncAPI and OpenAPI), additionalItems was applied to regular array types, when it should only be applied for tuples.

This means that a schema such as:

    "tags": {
      "type": "array",
      "items": {
        "$ref": "http://asyncapi.com/definitions/2.6.0/tag.json"
      },
      "additionalItems": true
    },

Would generate a type such as, in TypeScript:

private _tags?: (Tag | any)[];

Where it now generates:

private _tags?: Tag[];

Creates union type for operation message oneOf

In the example above, where operation.message.oneOf is set, Modelina will now generate a union type for it. Previously, Modelina ignored this union type, and only generated models for the content of operation.message.oneOf. In the example above, that meant models for Dog and Cat. Now, Modelina will generate a union type of Pet in addition to Dog and Cat.

Constraining models with then and else

In v1, required properties defined within then or else (in JSON Schema input variants) would be directly applied to the encapsulating model, meaning it would be as if the if section is always true or false, depending on whether it's defined within then or else respectfully.

In v2, required properties are no longer applied, but the rest of the structure is still.

Constant values

Constant values are now supported.

1type: object
2properties:
3  country:
4    const: 'United States of America'

The country property will not have a setter and will automatically be initialized.

Discriminator

Discriminator is now supported.

1schemas:
2  Pet:
3    type: object
4    discriminator: petType
5    properties:
6      petType:
7        type: string
8      name:
9        type: string
10    required:
11      - petType
12      - name
13  Cat:
14    allOf:
15      - $ref: '#/components/schemas/Pet'
16      - type: object
17        properties:
18          petType:
19            const: Cat
20  Dog:
21    allOf:
22      - $ref: '#/components/schemas/Pet'
23      - type: object
24        properties:
25          petType:
26            const: Dog

This example will generate a model for Cat and Dog where PetType is a shared enum that contains two values (Cat and Dog). PetType will not have a setter, and will automatically be initialized.

Optional properties in Kotlin

In Kotlin, if the property is not required in JSON Schema, it will be nullable with the default value null.

1Response:
2  type: object
3  properties:
4    result:
5      type: string
6    message:
7      type: string
8  required:
9    - result
10  additionalProperties: false

will generate:

1data class Response(
2    val result: String,
3    val message: String? = null
4)

Optional properties in Java

In Java, if the property is not required in JSON Schema, it should use nullable types instead of primitive.

1Response:
2  type: object
3  properties:
4    result:
5      type: number
6    message:
7      type: number
8  required:
9  - result
10  additionalProperties: false

Will now generate:

1public class Response {
2  private double result;
3  private Double message;
4
5  public double getResult() { return this.result; }
6  public void setResult(double result) { this.result = result; }
7
8  public Double getMessage() { return this.message; }
9  public void setMessage(Double message) { this.message = message; }
10}

Interface for objects in oneOf for Java

In Java, if a oneOf includes objects, there will be created an interface. All the classes that are part of the oneOf, implements the interface. The Jackson preset includes support for unions by setting @JsonTypeInfo and @JsonSubTypes annotations.

1components:
2  messages:
3    Vehicle:
4      payload:
5        title: Vehicle
6        type: object
7        discriminator: vehicleType
8        properties:
9          vehicleType:
10            title: VehicleType
11            type: string
12        required:
13          - vehicleType
14        oneOf:
15          - $ref: '#/components/schemas/Car'
16          - $ref: '#/components/schemas/Truck'
17  schemas:
18    Car:
19      type: object
20      properties:
21        vehicleType:
22          const: Car
23    Truck:
24      type: object
25      properties:
26        vehicleType:
27          const: Truck

will generate

1@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.EXISTING_PROPERTY, property="vehicleType")
2@JsonSubTypes({
3  @JsonSubTypes.Type(value = Car.class, name = "Car"),
4  @JsonSubTypes.Type(value = Truck.class, name = "Truck")
5})
6/**
7 * Vehicle represents a union of types: Car, Truck
8 */
9public interface Vehicle {
10  VehicleType getVehicleType();
11}
12
13public class Car implements Vehicle {
14  @NotNull
15  @JsonProperty(\\"vehicleType\\")
16  private final VehicleType vehicleType = VehicleType.CAR;
17  private Map<String, Object> additionalProperties;
18
19  public VehicleType getVehicleType() { return this.vehicleType; }
20
21  public Map<String, Object> getAdditionalProperties() { return this.additionalProperties; }
22  public void setAdditionalProperties(Map<String, Object> additionalProperties) { this.additionalProperties = additionalProperties; }
23}
24
25public enum VehicleType {
26  CAR((String)"Car"), TRUCK((String)"Truck");
27
28  private String value;
29
30  VehicleType(String value) {
31    this.value = value;
32  }
33
34  @JsonValue
35  public String getValue() {
36    return value;
37  }
38
39  @JsonCreator
40  public static VehicleType fromValue(String value) {
41    for (VehicleType e : VehicleType.values()) {
42      if (e.value.equals(value)) {
43        return e;
44      }
45    }
46    throw new IllegalArgumentException("Unexpected value '" + value + "'");
47  }
48
49  @Override
50  public String toString() {
51    return String.valueOf(value);
52  }
53}
54
55public class Truck implements Vehicle {
56  @NotNull
57  @JsonProperty("vehicleType")
58  private final VehicleType vehicleType = VehicleType.TRUCK;
59  private Map<String, Object> additionalProperties;
60
61  public VehicleType getVehicleType() { return this.vehicleType; }
62
63  public Map<String, Object> getAdditionalProperties() { return this.additionalProperties; }
64  public void setAdditionalProperties(Map<String, Object> additionalProperties) { this.additionalProperties = additionalProperties; }
65}

OpenAPI inputs now generate parameters as models

For a given OpenAPI input, it now generates parameter models on top of the payload models as the default behavior.

renderCompleteModel and render now use object arguments

As parameters grow, regular arguments become cumbersome, so we are starting to switch over to using object arguments.

This means that the following functions are going to change from:

1generator.renderCompleteModel(
2  constrainedModel,
3  inputModel,
4  completeOptions,
5  options
6);
7
8generator.render(
9  constrainedModel,
10  inputModel,
11  options
12);

To

1generator.renderCompleteModel({
2  constrainedModel,
3  inputModel,
4  completeOptions,
5  options
6});
7
8generator.render({
9  constrainedModel,
10  inputModel,
11  options
12});

Nullable models

Each meta model up until now was not able to be marked as nullable, but now they can be through isNullable. Here are the different outputs and how they now apply nullable types.

TypeScript

Across all models, if they are nullable they will get the union x | null.

JavaScript

Is not affected by this change.

C#

Is not affected by this change.

Java

With this update, Float, Integer, and Boolean meta models are rendered between their primitive types (for example float) and wrapper classes (for example Float) for nullable.

Double now also respects being nullable.

Kotlin

Is not affected by this change.

Rust

Is not affected by this change.

Python

Is not affected by this change.

Go

Is not affected by this change.

Dart

Is not affected by this change.

C++

Is not affected by this change.

Upgrading to Node v18

As Node v14 is unmaintained we have upgraded to use Node v18.