Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1b3afd7
add support for useBeanValidation in spring http interface
Picazsoo Feb 3, 2026
e17a763
generate sample files
Picazsoo Feb 3, 2026
f383400
generate sample files
Picazsoo Feb 3, 2026
418c729
generate sample files
Picazsoo Feb 3, 2026
a61b5d9
generate sample files
Picazsoo Feb 3, 2026
67839a2
generate sample files
Picazsoo Feb 3, 2026
6771de2
fix imports
Picazsoo Feb 3, 2026
b9cb91e
fix samples
Picazsoo Feb 3, 2026
465c959
add unit tests
Picazsoo Mar 5, 2026
8dd296a
Merge branch 'master' into bugfix/22859-Spring-HTTP-Interface-library…
Picazsoo Mar 5, 2026
bf26c2a
fix after merge of master
Picazsoo Mar 5, 2026
d4cccfe
fix validation import
Picazsoo Mar 5, 2026
5d4b1cb
Merge branch 'master' into bugfix/22859-Spring-HTTP-Interface-library…
Picazsoo Mar 16, 2026
6a4db4c
Merge branch 'master' into bugfix/22859-Spring-HTTP-Interface-library…
Picazsoo Mar 18, 2026
8b25a3a
regenerate samples after merge of master
Picazsoo Mar 18, 2026
dcf3e8f
add conditional dependency for spring-boot-starter-validation based o…
Picazsoo Mar 18, 2026
ac13f63
remove unnecessary @Valid annotations from model classes
Picazsoo Mar 18, 2026
d059117
feat: add support for Bean Validation in Spring HTTP Interface and up…
Picazsoo May 15, 2026
a60a814
Merge origin/master into bugfix/22859 - resolve SpringCodegenTest con…
Picazsoo May 15, 2026
22aec6c
update samples after merge of master
Picazsoo May 15, 2026
eb2f495
fix: replace invalid JavaScript regex literal in test spec pattern field
Picazsoo May 16, 2026
65307fd
fix regex pattern in sample open api spec
Picazsoo May 16, 2026
889b377
Merge branch 'master' into bugfix/22859-Spring-HTTP-Interface-library…
Picazsoo May 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions .github/workflows/samples-jdk17.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ on:
- samples/client/petstore/java-helidon-client/v3/mp/**
- samples/client/petstore/java-helidon-client/v3/se/**
- samples/client/petstore/spring-http-interface-reactive/**
- samples/client/petstore/spring-http-interface-reactive-bean-validation/**
- samples/client/petstore/spring-http-interface-bean-validation/**
- samples/client/petstore/spring-http-interface/**
- samples/client/petstore/spring-http-interface-reactive-noResponseEntity/**
- samples/client/petstore/spring-http-interface-noResponseEntity/**
Expand All @@ -27,6 +29,8 @@ on:
- samples/client/petstore/java-helidon-client/v3/mp/**
- samples/client/petstore/java-helidon-client/v3/se/**
- samples/client/petstore/spring-http-interface-reactive/**
- samples/client/petstore/spring-http-interface-reactive-bean-validation/**
- samples/client/petstore/spring-http-interface-bean-validation/**
- samples/client/petstore/spring-http-interface/**
- samples/client/petstore/spring-http-interface-reactive-noResponseEntity/**
- samples/client/petstore/spring-http-interface-noResponseEntity/**
Expand All @@ -53,6 +57,8 @@ jobs:
- samples/client/petstore/java-helidon-client/v3/mp/
- samples/client/petstore/java-helidon-client/v3/se
- samples/client/petstore/spring-http-interface-reactive
- samples/client/petstore/spring-http-interface-reactive-bean-validation
- samples/client/petstore/spring-http-interface-bean-validation
- samples/client/petstore/spring-http-interface
- samples/client/petstore/spring-http-interface-reactive-noResponseEntity
- samples/client/petstore/spring-http-interface-noResponseEntity
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/samples-kotlin-server-jdk17.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ jobs:
- samples/server/petstore/kotlin-server-modelMutable
- samples/server/petstore/kotlin-misk
- samples/server/petstore/kotlin-spring-declarative-interface
- samples/server/petstore/kotlin-spring-declarative-interface-bean-validation
- samples/server/petstore/kotlin-spring-declarative-interface-reactive-coroutines
- samples/server/petstore/kotlin-spring-declarative-interface-reactive-reactor-wrapped
- samples/server/petstore/kotlin-spring-declarative-interface-wrapped
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
generatorName: kotlin-spring
outputDir: samples/server/petstore/kotlin-spring-declarative-interface-bean-validation
library: spring-declarative-http-interface
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/kotlin-spring
additionalProperties:
documentationProvider: springDoc
annotationLibrary: swagger2
useSwaggerUI: "false"
serializableModel: "true"
interfaceOnly: true
reactive: false
useResponseEntity: true
useFlowForArrayReturnType: false
useBeanValidation: "true"
18 changes: 18 additions & 0 deletions bin/configs/spring-http-interface-bean-validation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
generatorName: spring
library: spring-http-interface
outputDir: samples/client/petstore/spring-http-interface-bean-validation
inputSpec: modules/openapi-generator/src/test/resources/3_0/spring/petstore-with-fake-endpoints-models-for-testing.yaml
templateDir: modules/openapi-generator/src/main/resources/JavaSpring
additionalProperties:
artifactId: spring-http-interface
snapshotVersion: "true"
hideGenerationTimestamp: "true"
modelNameSuffix: 'Dto'
generatedConstructorWithRequiredArgs: "false"
# validation should be respected
useBeanValidation: "true"
# documentation provider should be ignored
documentationProvider: "springdoc"
# annotation provider should be ignored
annotationLibrary: "swagger2"
useSpringBoot3: "true"
5 changes: 2 additions & 3 deletions bin/configs/spring-http-interface-noResponseEntity.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ additionalProperties:
documentationProvider: "springdoc"
# annotation provider should be ignored
annotationLibrary: "swagger2"
# validation should be ignored
useBeanValidation: "true"
performBeanValidation: "true"
# useBeanValidation should default to "false" when not specified
# performBeanValidation should default to "false" when not specified
useResponseEntity: "false"
useSpringBoot3: "true"
17 changes: 17 additions & 0 deletions bin/configs/spring-http-interface-reactive-bean-validation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
generatorName: spring
library: spring-http-interface
outputDir: samples/client/petstore/spring-http-interface-reactive-bean-validation
inputSpec: modules/openapi-generator/src/test/resources/3_0/spring/petstore-with-fake-endpoints-models-for-testing.yaml
templateDir: modules/openapi-generator/src/main/resources/JavaSpring
additionalProperties:
artifactId: spring-http-interface-reactive
snapshotVersion: "true"
hideGenerationTimestamp: "true"
reactive: "true"
# validation should be respected
useBeanValidation: "true"
# documentation provider should be ignored
documentationProvider: "springfox"
# annotation provider should be ignored
annotationLibrary: "swagger1"
useSpringBoot3: "true"
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ additionalProperties:
documentationProvider: "springdoc"
# annotation provider should be ignored
annotationLibrary: "swagger1"
# validation should be ignored
useBeanValidation: "true"
performBeanValidation: "true"
# useBeanValidation should default to "false" when not specified
# performBeanValidation should default to "false" when not specified
useResponseEntity: "false"
useSpringBoot3: "true"

5 changes: 2 additions & 3 deletions bin/configs/spring-http-interface-reactive.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ additionalProperties:
documentationProvider: "springdoc"
# annotation provider should be ignored
annotationLibrary: "swagger1"
# validation should be ignored
useBeanValidation: "true"
performBeanValidation: "true"
# useBeanValidation should default to "false" when not specified
# performBeanValidation should default to "false" when not specified
useSpringBoot3: "true"

1 change: 0 additions & 1 deletion bin/configs/spring-http-interface-springboot-4.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ additionalProperties:
documentationProvider: "springdoc"
# annotation provider should be ignored
annotationLibrary: "swagger2"
# validation should be ignored
useBeanValidation: "true"
performBeanValidation: "true"
useSpringBoot4: "true"
Expand Down
5 changes: 2 additions & 3 deletions bin/configs/spring-http-interface.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ additionalProperties:
documentationProvider: "springdoc"
# annotation provider should be ignored
annotationLibrary: "swagger2"
# validation should be ignored
useBeanValidation: "true"
performBeanValidation: "true"
# useBeanValidation should default to "false" when not specified
# performBeanValidation should default to "false" when not specified
useSpringBoot3: "true"
Original file line number Diff line number Diff line change
Expand Up @@ -473,8 +473,18 @@ public void processOpts() {
documentationProvider = DocumentationProvider.NONE;
annotationLibrary = AnnotationLibrary.NONE;
useJakartaEe = true;
useBeanValidation = false;
performBeanValidation = false;
if(additionalProperties.containsKey(USE_BEANVALIDATION)) {
useBeanValidation = convertPropertyToBoolean(USE_BEANVALIDATION);
} else {
//default to false if not specified
useBeanValidation = false;
}
if(additionalProperties.containsKey(PERFORM_BEANVALIDATION)) {
performBeanValidation = convertPropertyToBoolean(PERFORM_BEANVALIDATION);
} else {
//default to false if not specified
performBeanValidation = false;
}

additionalProperties.put(USE_JAKARTA_EE, useJakartaEe);
additionalProperties.put(USE_BEANVALIDATION, useBeanValidation);
Expand All @@ -486,8 +496,7 @@ public void processOpts() {

applyJakartaPackage();

LOGGER.warn("For Spring HTTP Interface following options are disabled: documentProvider, annotationLibrary, useBeanValidation, performBeanValidation. "
+ "useJakartaEe defaulted to 'true'");
LOGGER.warn("For Spring HTTP Interface following options are disabled: documentProvider, annotationLibrary. useJakartaEe defaulted to 'true'. useBeanValidation and performBeanValidation are supported.");
}

// clear model and api doc template as this codegen
Expand Down Expand Up @@ -692,7 +701,6 @@ public void processOpts() {

supportingFiles.add(new SupportingFile(httpInterfacesAbstractConfiguratorFile,
(sourceFolder + File.separator + configPackage).replace(".", java.io.File.separator), "HttpInterfacesAbstractConfigurator.java"));
writePropertyBack(USE_BEANVALIDATION, false);

writePropertyBack(HTTP_INTERFACES_CONFIGURATOR_DEPENDENCY,
useHttpServiceProxyFactoryInterfacesConfigurator ?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.service.annotation.*;
import org.springframework.web.multipart.MultipartFile;
{{#useBeanValidation}}
import {{javaxPackage}}.validation.Valid;
import {{javaxPackage}}.validation.constraints.*;
{{^useSpringBuiltInValidation}}import org.springframework.validation.annotation.Validated;{{/useSpringBuiltInValidation}}
{{/useBeanValidation}}
{{#reactive}}

import org.springframework.http.codec.multipart.Part;
Expand All @@ -29,6 +34,7 @@ import java.util.Optional;
import {{javaxPackage}}.annotation.Generated;


{{#useBeanValidation}}{{^useSpringBuiltInValidation}}@Validated{{/useSpringBuiltInValidation}}{{/useBeanValidation}}
{{>generatedAnnotation}}

{{#operations}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,18 @@
<optional>true</optional>
</dependency>
{{/lombok}}
{{#useBeanValidation}}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
{{/useBeanValidation}}
{{^useBeanValidation}}{{#performBeanValidation}}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
{{/performBeanValidation}}{{/useBeanValidation}}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,18 @@
<optional>true</optional>
</dependency>
{{/lombok}}
{{#useBeanValidation}}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
{{/useBeanValidation}}
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
{{^useBeanValidation}}{{#performBeanValidation}}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
{{/performBeanValidation}}{{/useBeanValidation}}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-{{#reactive}}web{{/reactive}}{{^reactive}}rest{{/reactive}}client-test</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
@Deprecated
{{/deprecated}}
{{#isContainer}}
{{#useBeanValidation}}@Valid{{/useBeanValidation}}
{{#openApiNullable}}
private {{>nullableAnnotation}}{{#isNullable}}{{>nullableDataTypeBeanValidation}} {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>undefined();{{/isNullable}}{{^required}}{{^isNullable}}{{>nullableDataTypeBeanValidation}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};{{/isNullable}}{{/required}}{{#required}}{{^isNullable}}{{>nullableDataTypeBeanValidation}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};{{/isNullable}}{{/required}}
{{/openApiNullable}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<!-- Bean Validation implementation (server-side execution) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>{{/useBeanValidation}}
<dependency>
<groupId>jakarta.annotation</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<!-- Bean Validation implementation (server-side execution) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>{{/useBeanValidation}}
<dependency>
<groupId>jakarta.annotation</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6694,6 +6694,129 @@ public void shouldNotHaveDocumentationAnnotationWhenUsingLibrarySpringHttpInterf
.assertMethod("addPet").assertParameter("pet").assertParameterAnnotations().doesNotContainWithName("Parameter");
}

@Test
public void testSpringHttpInterfaceUseBeanValidationRespected() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
String outputPath = output.getAbsolutePath().replace('\\', '/');

final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/petstore-echo.yaml");
final SpringCodegen codegen = new SpringCodegen();
codegen.setOpenAPI(openAPI);
codegen.setOutputDir(output.getAbsolutePath());
codegen.setLibrary(SPRING_HTTP_INTERFACE);
codegen.setUseSpringBoot3(true);
codegen.additionalProperties().put(BeanValidationFeatures.USE_BEANVALIDATION, "true");

ClientOptInput input = new ClientOptInput();
input.openAPI(openAPI);
input.config(codegen);

DefaultGenerator generator = new DefaultGenerator();
generator.setGenerateMetadata(false);

generator.opts(input).generate().stream()
.collect(Collectors.toMap(File::getName, Function.identity()));

JavaFileAssert.assertThat(Paths.get(outputPath + "/src/main/java/org/openapitools/api/StoreApi.java"))
.hasImports("jakarta.validation.Valid")
.hasImports("jakarta.validation.constraints")
.assertTypeAnnotations().containsWithName("Validated");
}

@Test
public void testSpringHttpInterfaceUseBeanValidationFalseHasNoValidationAnnotations() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
String outputPath = output.getAbsolutePath().replace('\\', '/');

final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/petstore-echo.yaml");
final SpringCodegen codegen = new SpringCodegen();
codegen.setOpenAPI(openAPI);
codegen.setOutputDir(output.getAbsolutePath());
codegen.setLibrary(SPRING_HTTP_INTERFACE);
codegen.setUseSpringBoot3(true);
codegen.additionalProperties().put(BeanValidationFeatures.USE_BEANVALIDATION, "false");

ClientOptInput input = new ClientOptInput();
input.openAPI(openAPI);
input.config(codegen);

DefaultGenerator generator = new DefaultGenerator();
generator.setGenerateMetadata(false);

generator.opts(input).generate().stream()
.collect(Collectors.toMap(File::getName, Function.identity()));

JavaFileAssert.assertThat(Paths.get(outputPath + "/src/main/java/org/openapitools/api/StoreApi.java"))
.hasNoImports("jakarta.validation.Valid")
.hasNoImports("jakarta.validation.constraints")
.assertTypeAnnotations().doesNotContainWithName("Validated");
}

@Test
public void testSpringHttpInterfaceUseBeanValidationDefaultsToFalse() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
String outputPath = output.getAbsolutePath().replace('\\', '/');

final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/petstore-echo.yaml");
final SpringCodegen codegen = new SpringCodegen();
codegen.setOpenAPI(openAPI);
codegen.setOutputDir(output.getAbsolutePath());
codegen.setLibrary(SPRING_HTTP_INTERFACE);
codegen.setUseSpringBoot3(true);
// useBeanValidation not set — should default to false

ClientOptInput input = new ClientOptInput();
input.openAPI(openAPI);
input.config(codegen);

DefaultGenerator generator = new DefaultGenerator();
generator.setGenerateMetadata(false);

generator.opts(input).generate().stream()
.collect(Collectors.toMap(File::getName, Function.identity()));

JavaFileAssert.assertThat(Paths.get(outputPath + "/src/main/java/org/openapitools/api/StoreApi.java"))
.hasNoImports("jakarta.validation.Valid")
.hasNoImports("jakarta.validation.constraints")
.assertTypeAnnotations().doesNotContainWithName("Validated");
}

@Test
public void testSpringHttpInterfaceConstraintAnnotationsOnParams() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
String outputPath = output.getAbsolutePath().replace('\\', '/');

final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/petstore-echo.yaml");
final SpringCodegen codegen = new SpringCodegen();
codegen.setOpenAPI(openAPI);
codegen.setOutputDir(output.getAbsolutePath());
codegen.setLibrary(SPRING_HTTP_INTERFACE);
codegen.setUseSpringBoot3(true);
codegen.additionalProperties().put(BeanValidationFeatures.USE_BEANVALIDATION, "true");

ClientOptInput input = new ClientOptInput();
input.openAPI(openAPI);
input.config(codegen);

DefaultGenerator generator = new DefaultGenerator();
generator.setGenerateMetadata(false);

generator.opts(input).generate().stream()
.collect(Collectors.toMap(File::getName, Function.identity()));

// getOrderById has minimum:1 and maximum:5 on orderId path param
JavaFileAssert.assertThat(Paths.get(outputPath + "/src/main/java/org/openapitools/api/StoreApi.java"))
.assertMethod("getOrderById")
.assertParameter("orderId")
.assertParameterAnnotations()
.containsWithName("Min")
.containsWithName("Max");
}

@DataProvider(name = "jspecifyLibraries")
public Object[][] jspecifyLibraries() {
return new Object[][]{
Expand Down
Loading
Loading