Skip to content

Commit 2f2a8fd

Browse files
[main] Add possibility of generate code with bean validation annotationin place. (#746)
* Add bean validation option for api and models. Adapt templates for correct bean validation usage. Add integration test for bean validation. * Reformat templates for correct indentation and replace unnecessary imports with fqcn. * Adjust imports, javadoc as well as bean validation integration test pom. * Set default for use-bean-valdiation property to true * Add more assertions for bean validation test and fix validation template * Add assertions on validation annotation values * Format BeanValidationTest
1 parent 3d4e9b3 commit 2f2a8fd

File tree

26 files changed

+463
-72
lines changed

26 files changed

+463
-72
lines changed

client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CodegenConfig.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ public enum ConfigName {
6363
PART_FILENAME_VALUE("part-filename-value"),
6464
USE_FIELD_NAME_IN_PART_FILENAME("use-field-name-in-part-filename"),
6565
ADDITIONAL_PROPERTIES_AS_ATTRIBUTE("additional-properties-as-attribute"),
66-
ADDITIONAL_REQUEST_ARGS("additional-request-args");
66+
ADDITIONAL_REQUEST_ARGS("additional-request-args"),
67+
BEAN_VALIDATION("use-bean-validation");
6768

6869
private final String name;
6970

client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/CommonItemConfig.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public class CommonItemConfig {
4444

4545
/**
4646
* Defines if the enums should have an `UNEXPECTED` member to convey values that cannot be parsed. Default is
47-
* <code>false</code>.
47+
* {@code false}.
4848
*/
4949
@ConfigItem(name = "additional-enum-type-unexpected-member")
5050
public Optional<Boolean> additionalEnumTypeUnexpectedMemberAnnotations;
@@ -62,7 +62,7 @@ public class CommonItemConfig {
6262
public Optional<String> additionalRequestArgs;
6363

6464
/**
65-
* Defines if the methods should return {@link jakarta.ws.rs.core.Response} or a model. Default is <code>false</code>.
65+
* Defines if the methods should return {@link jakarta.ws.rs.core.Response} or a model. Default is {@code false}.
6666
*/
6767
@ConfigItem(name = "return-response")
6868
public Optional<Boolean> returnResponse;
@@ -80,15 +80,16 @@ public class CommonItemConfig {
8080
public Map<String, String> normalizer;
8181

8282
/**
83-
* Enable SmallRye Mutiny support. If you set this to `true`, all return types will be wrapped in `io.smallrye.mutiny.Uni`.
83+
* Enable SmallRye Mutiny support. If you set this to {@code true}, all return types will be wrapped in
84+
* {@link io.smallrye.mutiny.Uni}.
8485
*/
8586
@ConfigItem(name = "mutiny")
8687
public Optional<Boolean> supportMutiny;
8788

8889
/**
8990
* Defines, whether the `PartFilename` ({@link org.jboss.resteasy.reactive.PartFilename} or
9091
* {@link org.jboss.resteasy.annotations.providers.multipart.PartFilename}) annotation should be generated for
91-
* MultipartForm POJOs. By setting to `false`, the annotation will not be generated.
92+
* MultipartForm POJOs. By setting to {@code false}, the annotation will not be generated.
9293
*/
9394
@ConfigItem(name = "generate-part-filename")
9495
public Optional<Boolean> generatePartFilename;
@@ -110,4 +111,11 @@ public class CommonItemConfig {
110111
*/
111112
@ConfigItem(name = "use-field-name-in-part-filename")
112113
public Optional<Boolean> useFieldNameInPartFilename;
113-
}
114+
115+
/**
116+
* Enable bean validation. If you set this to {@code true}, validation annotations are added to generated sources E.g.
117+
* {@code @Size}.
118+
*/
119+
@ConfigItem(name = "use-bean-validation")
120+
public Optional<Boolean> useBeanValidation;
121+
}

client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorCodeGenBase.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,9 @@ protected void generate(final Config config, final Path openApiFilePath, final P
248248
Boolean.class)
249249
.orElse(true));
250250

251+
getValues(config, openApiFilePath, CodegenConfig.ConfigName.BEAN_VALIDATION, Boolean.class)
252+
.ifPresent(generator::withUseBeanValidation);
253+
251254
SmallRyeConfig smallRyeConfig = config.unwrap(SmallRyeConfig.class);
252255

253256
getValues(smallRyeConfig, openApiFilePath, CodegenConfig.ConfigName.TYPE_MAPPINGS, String.class, String.class)

client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapper.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,11 @@ public OpenApiClientGeneratorWrapper withUseFieldNameInPartFilenameConfig(final
225225
return this;
226226
}
227227

228+
public OpenApiClientGeneratorWrapper withUseBeanValidation(Boolean config) {
229+
configurator.addAdditionalProperty("use-bean-validation", config);
230+
return this;
231+
}
232+
228233
public OpenApiClientGeneratorWrapper withApiNameSuffix(final String apiNameSuffix) {
229234
this.configurator.setApiNameSuffix(apiNameSuffix);
230235
return this;

client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/QuarkusJavaClientCodegen.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ public void processOpts() {
5353
this.testFolder = "";
5454
this.embeddedTemplateDir = "templates";
5555

56+
Boolean beanValidation = (Boolean) this.additionalProperties.getOrDefault("use-bean-validation", true);
57+
this.setUseBeanValidation(beanValidation);
58+
this.setPerformBeanValidation(beanValidation);
59+
5660
this.replaceWithQuarkusTemplateFiles();
5761
}
5862

@@ -185,4 +189,4 @@ private void configureAdditionalPropertiesAsAttribute() {
185189
this.supportsAdditionalPropertiesWithComposedSchema = true;
186190
}
187191
}
188-
}
192+
}

client/deployment/src/main/resources/templates/libraries/microprofile/api.qute

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -92,30 +92,28 @@ public interface {classname} {
9292
{/if}
9393
{#if op.hasFormParams}
9494
{#if is-resteasy-reactive}
95-
@jakarta.ws.rs.BeanParam {op.operationIdCamelCase}MultipartForm multipartForm{#if op.hasPathParams},{/if}{!
96-
!}{#for p in op.pathParams}{#include pathParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasQueryParams},{/if}{!
97-
!}{#for p in op.queryParams}{#include queryParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParams},{/if}{!
98-
!}{#for p in op.bodyParams}{#include bodyParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasCookieParams},{/if}{!
99-
!}{#for p in op.cookieParams}{#include cookieParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasHeaderParams},{/if}{!
100-
!}{#for p in op.headerParams}{#include headerParams.qute param=p/}{#if p_hasNext}, {/if}{/for}
95+
@jakarta.ws.rs.BeanParam {op.operationIdCamelCase}MultipartForm multipartForm{#if op.hasPathParams},{/if}
10196
{#else}
102-
@org.jboss.resteasy.annotations.providers.multipart.MultipartForm {op.operationIdCamelCase}MultipartForm multipartForm{#if op.hasPathParams},{/if}{!
103-
!}{#for p in op.pathParams}{#include pathParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasQueryParams},{/if}{!
104-
!}{#for p in op.queryParams}{#include queryParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParams},{/if}{!
105-
!}{#for p in op.bodyParams}{#include bodyParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasCookieParams},{/if}{!
106-
!}{#for p in op.cookieParams}{#include cookieParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasHeaderParams},{/if}{!
107-
!}{#for p in op.headerParams}{#include headerParams.qute param=p/}{#if p_hasNext}, {/if}{/for}
97+
@org.jboss.resteasy.annotations.providers.multipart.MultipartForm {op.operationIdCamelCase}MultipartForm multipartForm{#if op.hasPathParams},{/if}
10898
{/if}
99+
{#if use-bean-validation}
100+
{#for p in op.pathParams}@jakarta.validation.Valid {#include pathParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasQueryParams},{/if}
101+
{#for p in op.queryParams}@jakarta.validation.Valid {#include queryParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasCookieParams},{/if}
102+
{#for p in op.cookieParams}@jakarta.validation.Valid {#include cookieParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasHeaderParams},{/if}
103+
{#for p in op.headerParams}@jakarta.validation.Valid {#include headerParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParams},
104+
{#for p in op.bodyParams}@jakarta.validation.Valid {#include bodyParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{/if}
109105
{#else}
110-
{#for p in op.allParams}{!
111-
!}{#include pathParams.qute param=p/}{!
112-
!}{#include queryParams.qute param=p/}{!
113-
!}{#include bodyParams.qute param=p/}{!
114-
!}{#include cookieParams.qute param=p/}{!
115-
!}{#include headerParams.qute param=p/}{!
116-
!}{#if p_hasNext}, {/if}{!
117-
!}{/for}
106+
{#for p in op.pathParams}{#include pathParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasQueryParams},{/if}
107+
{#for p in op.queryParams}{#include queryParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasCookieParams},{/if}
108+
{#for p in op.cookieParams}{#include cookieParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasHeaderParams},{/if}
109+
{#for p in op.headerParams}{#include headerParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParams},{/if}
110+
{#for p in op.bodyParams}{#include bodyParams.qute param=p/}{#if p_hasNext}, {/if}{/for}
118111
{/if}
112+
{#else}
113+
{#for p in op.allParams}
114+
{#if use-bean-validation}@jakarta.validation.Valid {/if}{!
115+
!}{#include pathParams.qute param=p/}{#include queryParams.qute param=p/}{#include bodyParams.qute param=p/}{#include headerParams.qute param=p/}{#include cookieParams.qute param=p/}{#if p_hasNext}, {/if}
116+
{/for}{/if}
119117
);
120118
{#if op.hasFormParams}
121119

@@ -124,4 +122,4 @@ public interface {classname} {
124122

125123
{/if} {! check deprecated !}
126124
{/for}
127-
}
125+
}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
{#if p.required} @NotNull{/if}
2-
{#include beanValidationCore.qute p=p/}
1+
{#if use-bean-validation}
2+
{#include beanValidationCore.qute p=p/}
3+
{/if}
Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,36 @@
1-
{#if p.pattern} @Pattern(regexp="{p.pattern}"){/if}
2-
{#if p.minLength || p.minItems} @Size(min={p.minLength}{p.minItems}){/if}
3-
{#if p.maxLength || p.maxItems} @Size(max={p.minLength}{p.minItems}){/if}
4-
{#if p.isInteger}{#if p.minimum} @Min({p.minimum}){/if}{#if p.maximum} @Max({p.maximum}){/if}{/if}
5-
{#if p.isLong}{#if p.minimum} @Min({p.minimum}L){/if}{#if p.maximum} @Max({p.maximum}L){/if}{/if}
6-
{#if !p.isInteger && !p.isLong}{#if p.minimum} @DecimalMin("{minimum}"){/if}{#if p.maximum} @DecimalMax("{maximum}"){/if}{/if}
1+
{#if p.required}
2+
@jakarta.validation.constraints.NotNull
3+
{/if}
4+
{#if p.pattern}
5+
@jakarta.validation.constraints.Pattern(regexp = "{p.pattern}")
6+
{/if}
7+
{#if p.minLength || p.minItems}
8+
@jakarta.validation.constraints.Size(min = {p.minLength}{p.minItems})
9+
{/if}
10+
{#if p.maxLength || p.maxItems}
11+
@jakarta.validation.constraints.Size(max = {p.maxLength}{p.maxItems})
12+
{/if}
13+
{#if p.isInteger}
14+
{#if p.minimum}
15+
@jakarta.validation.constraints.Min({p.minimum})
16+
{/if}
17+
{#if p.maximum}
18+
@jakarta.validation.constraints.Max({p.maximum})
19+
{/if}
20+
{/if}
21+
{#if p.isLong}
22+
{#if p.minimum}
23+
@jakarta.validation.constraints.Min({p.minimum}L)
24+
{/if}
25+
{#if p.maximum}
26+
@jakarta.validation.constraints.Max({p.maximum}L)
27+
{/if}
28+
{/if}
29+
{#if !p.isInteger && !p.isLong}
30+
{#if p.minimum}
31+
@jakarta.validation.constraints.DecimalMin("{p.minimum}")
32+
{/if}
33+
{#if p.maximum}
34+
@jakarta.validation.constraints.DecimalMax("{p.maximum}")
35+
{/if}
36+
{/if}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{#if param.required} @NotNull{/if}{#include beanValidationCore.qute p=param/}
1+
{#include beanValidationCore.qute p=param/}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{#if param.isBodyParam}{#if param.useBeanValidation}@Valid {/if}{param.dataType} {param.paramName}{/if}
1+
{#if param.isBodyParam}{param.dataType} {param.paramName}{/if}

0 commit comments

Comments
 (0)