From 379c35d3a0fa7bdc114dcacde1504f2945f33364 Mon Sep 17 00:00:00 2001 From: Kenneth Aasan Date: Mon, 20 Nov 2023 18:25:38 +0100 Subject: [PATCH] feat!: adds inheritance with interfaces for java (#1593) --- docs/migrations/version-2-to-3.md | 109 ++++++ 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 ++++- test/generators/java/JavaGenerator.spec.ts | 141 +++++++ .../__snapshots__/JavaGenerator.spec.ts.snap | 355 ++++++++++++++++++ .../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 ++ 16 files changed, 1059 insertions(+), 14 deletions(-) diff --git a/docs/migrations/version-2-to-3.md b/docs/migrations/version-2-to-3.md index 3644f839ec..05f3df9353 100644 --- a/docs/migrations/version-2-to-3.md +++ b/docs/migrations/version-2-to-3.md @@ -114,6 +114,115 @@ public class TestClass { } ``` +#### 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. 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/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/__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/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\\")