Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1758,6 +1758,15 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
&& (cm.hasRequired || cm.vendorExtensions.containsKey("x-java-all-args-constructor"))) {
cm.vendorExtensions.put("x-java-no-args-constructor", true);
}
// the required args constructor only has lombok's signature when every own field is a plain required
// argument: requiredVars also holds inherited properties, a JsonNullable field takes a plain value
// and the additionalProperties map is a field too
if (lombokAnnotations != null && lombokAnnotations.containsKey("AllArgsConstructor")
&& cm.additionalPropertiesType == null
&& cm.requiredVars.size() == cm.vars.size()
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
&& cm.vars.stream().allMatch(p -> p.required && !(openApiNullable && p.isNullable))) {
cm.vendorExtensions.put("x-java-lombok-duplicates-required-args-constructor", true);
}
}

if (substituteGenericPagedModel && !pagedModelRegistry.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
{{/vendorExtensions.x-java-no-args-constructor}}
{{^lombok.Data}}
{{^lombok.RequiredArgsConstructor}}
{{^vendorExtensions.x-java-lombok-duplicates-required-args-constructor}}
{{#generatedConstructorWithRequiredArgs}}
{{#hasRequired}}

Expand All @@ -129,6 +130,7 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
}
{{/hasRequired}}
{{/generatedConstructorWithRequiredArgs}}
{{/vendorExtensions.x-java-lombok-duplicates-required-args-constructor}}
{{/lombok.RequiredArgsConstructor}}
{{#vendorExtensions.x-java-all-args-constructor}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ public class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}{{^parent}}
{{/vendorExtensions.x-java-no-args-constructor}}
{{^lombok.Data}}
{{^lombok.RequiredArgsConstructor}}
{{^vendorExtensions.x-java-lombok-duplicates-required-args-constructor}}
{{#generatedConstructorWithRequiredArgs}}
{{#hasRequired}}

Expand All @@ -140,6 +141,7 @@ public class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}{{^parent}}
}
{{/hasRequired}}
{{/generatedConstructorWithRequiredArgs}}
{{/vendorExtensions.x-java-lombok-duplicates-required-args-constructor}}
{{/lombok.RequiredArgsConstructor}}
{{#vendorExtensions.x-java-all-args-constructor}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5827,6 +5827,78 @@ public void generateAllArgsConstructor() throws IOException {
.hasParameter("count").toConstructor();
}

@Test
public void lombokAllArgsConstructorReplacesRequiredArgsConstructor_issue25395() throws IOException {
Map<String, File> output = generateFromContract(
"src/test/resources/3_0/spring/issue_25395.yaml",
SPRING_BOOT,
Map.of(AbstractJavaCodegen.ADDITIONAL_MODEL_TYPE_ANNOTATIONS, "@lombok.AllArgsConstructor",
CodegenConstants.OPENAPI_NULLABLE, "false",
INTERFACE_ONLY, "true")
);

// lombok generates Pet(Long, String), a generated one would be a duplicate
JavaFileAssert.assertThat(output.get("Pet.java"))
.assertConstructor().toFileAssert()
.hasNoConstructor("Long", "String");
JavaFileAssert.assertThat(output.get("NicknamedPet.java"))
.assertConstructor().toFileAssert()
.hasNoConstructor("Long", "String");
JavaFileAssert.assertThat(output.get("Tag.java"))
.assertConstructor("Long");
}

@Test
public void lombokAllArgsConstructorKeepsRequiredArgsConstructorOfNullableProperty_issue25395() throws IOException {
Map<String, File> output = generateFromContract(
"src/test/resources/3_0/spring/issue_25395.yaml",
SPRING_BOOT,
Map.of(AbstractJavaCodegen.ADDITIONAL_MODEL_TYPE_ANNOTATIONS, "@lombok.AllArgsConstructor",
CodegenConstants.OPENAPI_NULLABLE, "true",
INTERFACE_ONLY, "true")
);

// the field is JsonNullable<String> but the generated parameter is String, so lombok's constructor differs
JavaFileAssert.assertThat(output.get("NicknamedPet.java"))
.assertConstructor("Long", "String");
}

@Test
public void lombokAllArgsConstructorWithInheritedProperties_issue25395() throws IOException {
Map<String, File> output = generateFromContract(
"src/test/resources/3_0/spring/issue_25395.yaml",
SPRING_BOOT,
Map.of(AbstractJavaCodegen.ADDITIONAL_MODEL_TYPE_ANNOTATIONS, "@lombok.AllArgsConstructor",
CodegenConstants.OPENAPI_NULLABLE, "false",
INTERFACE_ONLY, "true")
);

// lombok only covers the own field, the generated constructor also takes the inherited one
JavaFileAssert.assertThat(output.get("Dog.java"))
.assertConstructor("String", "String")
.bodyContainsLines("super(kind)", "this.breed = breed");
// the parent has no required property, so the signature is the same as lombok's
JavaFileAssert.assertThat(output.get("Hound.java"))
.assertConstructor().toFileAssert()
.hasNoConstructor("String");
}

@Test
public void lombokAllArgsConstructorKeepsRequiredArgsConstructorWithAdditionalProperties_issue25395() throws IOException {
Map<String, File> output = generateFromContract(
"src/test/resources/3_0/spring/issue_25395.yaml",
SPRING_BOOT,
Map.of(AbstractJavaCodegen.ADDITIONAL_MODEL_TYPE_ANNOTATIONS, "@lombok.AllArgsConstructor",
CodegenConstants.OPENAPI_NULLABLE, "false",
INTERFACE_ONLY, "true")
);

// the additionalProperties map is a field too, so lombok's constructor takes it as a third parameter
JavaFileAssert.assertThat(output.get("Bag.java"))
.assertConstructor().toFileAssert()
.assertConstructor("Long", "String");
}

@Test
public void allOfDuplicatedProperties() throws IOException {
Map<String, File> output = generateFromContract(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
openapi: 3.0.3
info:
title: Test Issue #25395
version: 1.0.0
paths:
/pets:
get:
operationId: listPets
responses:
'200':
description: default response
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
components:
schemas:
Pet:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
Tag:
type: object
required:
- id
properties:
id:
type: integer
format: int64
label:
type: string
NicknamedPet:
type: object
required:
- id
- nickname
properties:
id:
type: integer
format: int64
nickname:
type: string
nullable: true
Animal:
type: object
required:
- kind
properties:
kind:
type: string
discriminator:
propertyName: kind
Dog:
allOf:
- $ref: '#/components/schemas/Animal'
- type: object
required:
- breed
properties:
breed:
type: string
Base:
type: object
properties:
kind:
type: string
discriminator:
propertyName: kind
Hound:
allOf:
- $ref: '#/components/schemas/Base'
- type: object
required:
- scent
properties:
scent:
type: string
Bag:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
additionalProperties:
type: string
Loading