diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/README.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/README.mustache new file mode 100644 index 00000000000..8d2420f4631 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/README.mustache @@ -0,0 +1,34 @@ +# Helidon SE Server with OpenAPI + +## Build and run + +With JDK11+ +```bash +mvn package +java -jar target/{{{artifactId}}}.jar +``` + +## Exercise the application + +``` +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}curl -X {{httpMethod}} {{{basePath}}}{{{path}}} +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +``` + +## Try health and metrics + +``` +curl -s -X GET {{{basePath}}}/health +{"outcome":"UP",... +. . . + +# Prometheus Format +curl -s -X GET {{{basePath}}}/metrics +# TYPE base:gc_g1_young_generation_count gauge +. . . + +# JSON Format +curl -H 'Accept: application/json' -X GET {{{basePath}}}/metrics +{"base":... +. . . +``` \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/additionalEnumTypeAnnotations.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/additionalEnumTypeAnnotations.mustache new file mode 100644 index 00000000000..aa524798b42 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/additionalEnumTypeAnnotations.mustache @@ -0,0 +1,2 @@ +{{#additionalEnumTypeAnnotations}}{{{.}}} +{{/additionalEnumTypeAnnotations}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/additionalModelTypeAnnotations.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/additionalModelTypeAnnotations.mustache new file mode 100644 index 00000000000..f4871c02cc2 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/additionalModelTypeAnnotations.mustache @@ -0,0 +1,2 @@ +{{#additionalModelTypeAnnotations}}{{{.}}} +{{/additionalModelTypeAnnotations}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/api.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/api.mustache new file mode 100644 index 00000000000..c44408d778b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/api.mustache @@ -0,0 +1,78 @@ +package {{package}}; + +{{#imports}}import {{import}}; +{{/imports}} + +{{#useAbstractClass}} +import java.util.Optional; +import java.util.logging.Logger; + +import io.helidon.common.GenericType; +import io.helidon.common.reactive.Single; +{{/useAbstractClass}} +import io.helidon.webserver.Routing; +import io.helidon.webserver.ServerRequest; +import io.helidon.webserver.ServerResponse; +import io.helidon.webserver.Service; + +{{#operations}} +{{^useAbstractClass}}public interface {{classname}} extends Service { {{/useAbstractClass}} +{{#useAbstractClass}}public abstract class {{classname}} implements Service { + + protected static final Logger LOGGER = Logger.getLogger({{classname}}.class.getName()); +{{#jackson}} + protected static final ObjectMapper MAPPER = JsonProvider.objectMapper();{{/jackson}} +{{#jsonb}} + protected static final Jsonb JSONB = JsonbBuilder.create();{{/jsonb}} +{{/useAbstractClass}} + + /** + * A service registers itself by updating the routing rules. + * @param rules the routing rules. + */ + @Override + {{#useAbstractClass}}public{{/useAbstractClass}}{{^useAbstractClass}}default{{/useAbstractClass}} void update(Routing.Rules rules) { +{{#operation}} + rules.{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}("{{{path}}}", {{! + }}{{#bodyParam}}{{#isModel}}Handler.create({{{dataType}}}.class, {{/isModel}}this::{{{operationId}}}){{#isModel}}){{/isModel}}{{/bodyParam}}{{! + }}{{^bodyParam}}this::{{{operationId}}}){{/bodyParam}}; +{{/operation}} + } +{{#useAbstractClass}}{{#isFormParamsFunctions}} + + {{!}}{{>formParamsFunctions}} +{{/isFormParamsFunctions}}{{/useAbstractClass}} +{{#operation}} + + /** + * {{httpMethod}} {{{path}}}{{#summary}} : {{.}}{{/summary}}. + * @param request the server request + * @param response the server response{{#allParams}}{{#isBodyParam}}{{#isModel}} + * @param {{paramName}} {{{description}}}{{^description}}{{paramName}}{{/description}} {{/isModel}}{{/isBodyParam}}{{/allParams}} + */ + void {{{operationId}}}(ServerRequest request, ServerResponse response{{#allParams}}{{#isBodyParam}}{{#isModel}}, {{{dataType}}} {{paramName}}{{/isModel}}{{/isBodyParam}}{{/allParams}}){{^useAbstractClass}};{{/useAbstractClass}}{{#useAbstractClass}} { {{#formParams}}{{#-first}} + {{>formParamsInitial}}{{/-first}}{{/formParams}} + Single.create({{^hasParams}}Single.empty(){{/hasParams}}{{#hasParams}}{{^bodyParam}}{{#formParams}}{{#-first}}formSingle{{/-first}}{{/formParams}}{{^formParams}}Single.empty(){{/formParams}}{{/bodyParam}}{{#bodyParam}}{{^isModel}}request.content().as(new GenericType<{{{dataType}}}>() { }){{/isModel}}{{#isModel}}Single.empty(){{/isModel}}{{/bodyParam}}{{/hasParams}}) + .thenAccept({{#bodyParam}}{{^isModel}}{{paramName}}{{/isModel}}{{#isModel}}val{{/isModel}}{{/bodyParam}}{{^bodyParam}}val{{/bodyParam}} -> { +{{#allParams}} + {{> queryParams }}{{> pathParams }}{{> headerParams}}{{> bodyParams}}{{> formParams}}{{> cookieParams}} +{{/allParams}} + handle{{#lambda.titlecase}}{{{operationId}}}{{/lambda.titlecase}}(request, response{{#allParams}}, {{paramName}}{{/allParams}}); + }) + .exceptionally(throwable -> handleError(request, response, throwable)); + } + + /** + * Handle {{httpMethod}} {{{path}}}{{#summary}} : {{.}}{{/summary}}. + * @param request the server request + * @param response the server response{{#allParams}} + * @param {{paramName}} {{{description}}}{{^description}}{{paramName}}{{/description}} {{/allParams}} + */ + abstract void handle{{#lambda.titlecase}}{{{operationId}}}{{/lambda.titlecase}}(ServerRequest request, ServerResponse response{{#allParams}}, {{>dataType}} {{paramName}}{{/allParams}}); +{{/useAbstractClass}} + +{{/operation}} +{{#useAbstractClass}} abstract Void handleError(ServerRequest request, ServerResponse response, Throwable throwable);{{! +}}{{/useAbstractClass}} +} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/apiImpl.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/apiImpl.mustache new file mode 100644 index 00000000000..b6cf71293f5 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/apiImpl.mustache @@ -0,0 +1,40 @@ +package {{package}}; + +{{#imports}}import {{import}}; +{{/imports}} +{{^useAbstractClass}} +import java.util.logging.Logger;{{/useAbstractClass}} + +import io.helidon.webserver.ServerRequest; +import io.helidon.webserver.ServerResponse; + +{{#operations}} +public class {{classname}}Impl {{^useAbstractClass}}implements{{/useAbstractClass}}{{#useAbstractClass}}extends{{/useAbstractClass}} {{classname}} { + + private static final int HTTP_CODE_NOT_IMPLEMENTED = 501; +{{^useAbstractClass}} + private static final Logger LOGGER = Logger.getLogger({{classname}}.class.getName()); +{{#jackson}} + private static final ObjectMapper MAPPER = JsonProvider.objectMapper();{{/jackson}} +{{#jsonb}} + private static final Jsonb JSONB = JsonbBuilder.create();{{/jsonb}} +{{/useAbstractClass}} +{{#operation}} + +{{#useAbstractClass}} + public void handle{{#lambda.titlecase}}{{{operationId}}}{{/lambda.titlecase}}(ServerRequest request, ServerResponse response{{#allParams}}, {{>dataType}} {{paramName}}{{/allParams}}) { +{{/useAbstractClass}} +{{^useAbstractClass}} + public void {{{operationId}}}(ServerRequest request, ServerResponse response{{#allParams}}{{#isBodyParam}}{{#isModel}}, {{{dataType}}} {{paramName}}{{/isModel}}{{/isBodyParam}}{{/allParams}}) { +{{/useAbstractClass}} + response.status(HTTP_CODE_NOT_IMPLEMENTED).send(); + } +{{/operation}} + +{{#useAbstractClass}} + public Void handleError(ServerRequest request, ServerResponse response, Throwable throwable) { + return response.send(throwable); + } +{{/useAbstractClass}} +} +{{/operations}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/application.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/application.mustache new file mode 100644 index 00000000000..2418f6ef270 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/application.mustache @@ -0,0 +1,3 @@ +server: + port: {{port}}{{^port}}8080{{/port}} + host: {{host}}{{^host}}localhost{{/host}} diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/beanValidationCore.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/beanValidationCore.mustache new file mode 100644 index 00000000000..d0479eb939e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/beanValidationCore.mustache @@ -0,0 +1,16 @@ +{{#pattern}}{{#isString}} + ValidatorUtils.validatePattern({{paramName}}, "{{{pattern}}}");{{/isString}}{{/pattern}}{{#minLength}}{{#maxLength}} + ValidatorUtils.validateSize({{paramName}}, {{minLength}}, {{maxLength}});{{/maxLength}}{{/minLength}}{{#minLength}}{{^maxLength}} + ValidatorUtils.validateSize({{paramName}}, {{minLength}}, null);{{/maxLength}}{{/minLength}}{{^minLength}}{{#maxLength}} + ValidatorUtils.validateSize({{paramName}}, null, {{.}});{{/maxLength}}{{/minLength}}{{#minItems}}{{#maxItems}} + ValidatorUtils.validateSize({{paramName}}, {{minItems}}, {{maxItems}});{{/maxItems}}{{/minItems}}{{#minItems}}{{^maxItems}} + ValidatorUtils.validateSize({{paramName}}, {{minItems}}, null);{{/maxItems}}{{/minItems}}{{^minItems}}{{#maxItems}} + ValidatorUtils.validateSize({{paramName}}, null, {{.}});{{/maxItems}}{{/minItems}}{{#useBeanValidation}}{{#isEmail}} + //RFC 5322 for Email Validation + ValidatorUtils.validatePattern({{paramName}}, "^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$");{{/isEmail}}{{/useBeanValidation}}{{#isInteger}}{{#minimum}} + ValidatorUtils.validateMin({{paramName}}, {{.}});{{/minimum}}{{#maximum}} + ValidatorUtils.validateMax({{paramName}}, {{.}});{{/maximum}}{{/isInteger}}{{#isLong}}{{#minimum}} + ValidatorUtils.validateMin({{paramName}}.intValue(), {{.}});{{/minimum}}{{#maximum}} + ValidatorUtils.validateMax({{paramName}}.intValue(), {{.}});{{/maximum}}{{/isLong}}{{^isInteger}}{{^isLong}}{{#minimum}} + ValidatorUtils.validateMin({{paramName}}, "{{minimum}}", {{#exclusiveMinimum}}false{{/exclusiveMinimum}}{{^exclusiveMinimum}}true{{/exclusiveMinimum}});{{/minimum}}{{#maximum}} + ValidatorUtils.validateMax({{paramName}}, "{{maximum}}", {{#exclusiveMaximum}}false{{/exclusiveMaximum}}{{^exclusiveMaximum}}true{{/exclusiveMaximum}});{{/maximum}}{{/isLong}}{{/isInteger}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/beanValidationHeaderParams.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/beanValidationHeaderParams.mustache new file mode 100644 index 00000000000..b590d59fd0d --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/beanValidationHeaderParams.mustache @@ -0,0 +1,2 @@ +{{#required}}{{!}} + {{!}}ValidatorUtils.checkNonNull({{paramName}});{{/required}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/beanValidationPathParams.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/beanValidationPathParams.mustache new file mode 100644 index 00000000000..60b256f885e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/beanValidationPathParams.mustache @@ -0,0 +1,2 @@ +{{! PathParam is always required, no @NotNull necessary }} +ValidatorUtils.checkNonNull({{paramName}});{{#isPrimitiveType}}{{>beanValidationCore}}{{/isPrimitiveType}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/beanValidationQueryParams.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/beanValidationQueryParams.mustache new file mode 100644 index 00000000000..dfec605af6e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/beanValidationQueryParams.mustache @@ -0,0 +1,2 @@ +{{#required}}{{!}} + {{!}}ValidatorUtils.checkNonNull({{paramName}});{{/required}}{{#isPrimitiveType}}{{>beanValidationCore}}{{/isPrimitiveType}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/bodyParams.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/bodyParams.mustache new file mode 100644 index 00000000000..7994e23e023 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/bodyParams.mustache @@ -0,0 +1 @@ +{{#isBodyParam}}ValidatorUtils.checkNonNull({{paramName}});{{/isBodyParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/build.gradle.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/build.gradle.mustache new file mode 100644 index 00000000000..32b1002ae42 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/build.gradle.mustache @@ -0,0 +1,88 @@ +plugins { + id 'java' + id 'application' +} + +group = '{{{groupId}}}' +version = '{{{artifactVersion}}}' + +{{#appDescription}} +description = """{{.}}""" +{{/appDescription}} + + +sourceCompatibility = 11 +targetCompatibility = 11 + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + +ext { + helidonVersion = '{{{helidonVersion}}}' +{{^interfaceOnly}} + mainClass='{{{invokerPackage}}}.Main' +{{/interfaceOnly}} + validationApiVersion = '2.0.1.Final' +} + +test { + useJUnitPlatform() +} + +repositories { + mavenCentral() + mavenLocal() +} + +dependencies { + // import Helidon BOM + implementation enforcedPlatform("io.helidon:helidon-dependencies:${project.helidonVersion}") + implementation "{{x-helidon-validationArtifactPrefix}}.validation:{{x-helidon-validationArtifactPrefix}}validation-api:${project.validationApiVersion}" + implementation 'io.helidon.webserver:helidon-webserver' + implementation 'io.helidon.media:helidon-media-jsonp' +{{#jackson}} + implementation 'io.helidon.media:helidon-media-jackson' +{{/jackson}} +{{#jsonb}} + implementation 'io.helidon.media:helidon-media-jsonb' +{{/jsonb}} + implementation 'io.helidon.media:helidon-media-multipart' + implementation 'io.helidon.config:helidon-config-yaml' + implementation 'io.helidon.health:helidon-health' + implementation 'io.helidon.health:helidon-health-checks' + implementation 'io.helidon.metrics:helidon-metrics' + implementation 'io.helidon.openapi:helidon-openapi' + testImplementation 'org.junit.jupiter:junit-jupiter-api' + testImplementation 'io.helidon.webclient:helidon-webclient' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' +} + +// define a custom task to copy all dependencies in the runtime classpath +// into build/libs/libs +// uses built-in Copy +task copyLibs(type: Copy) { + from configurations.runtimeClasspath + into 'build/libs/libs' +} + +// add it as a dependency of built-in task 'assemble' +copyLibs.dependsOn jar +assemble.dependsOn copyLibs + +// default jar configuration +// set the main classpath +// add each jar under build/libs/libs into the classpath +jar { + archiveFileName = "${project.name}.jar" + manifest { + attributes ({{^interfaceOnly}}'Main-Class': "${project.mainClass}",{{/interfaceOnly}} + 'Class-Path': configurations.runtimeClasspath.files.collect { "libs/$it.name" }.join(' ') + ) + } +} +{{^interfaceOnly}} +application { +mainClass = "${project.mainClass}" +} +{{/interfaceOnly}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/cookieParams.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/cookieParams.mustache new file mode 100644 index 00000000000..c11ce657251 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/cookieParams.mustache @@ -0,0 +1 @@ +{{#isCookieParam}}{{>dataType}} {{paramName}} = {{>paramValue}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{/isCookieParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/dataType.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/dataType.mustache new file mode 100644 index 00000000000..cc2a3d0d4df --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/dataType.mustache @@ -0,0 +1,11 @@ +{{#isPrimitiveType}}{{^isFile}}{{{dataType}}}{{/isFile}}{{#isFile}}InputStream{{/isFile}}{{/isPrimitiveType}}{{! +}}{{^isPrimitiveType}}{{! + }}{{#isArray}}{{! + }}{{#isBodyParam}}{{{dataType}}}{{/isBodyParam}}{{! + }}{{^isBodyParam}}{{! + }}List<{{#isFormParam}}{{#isFile}}InputStream{{/isFile}}{{^isFile}}String{{/isFile}}{{/isFormParam}}{{! + }}{{^isFormParam}}{{#isModel}}{{{dataType}}}{{/isModel}}{{^isModel}}String{{/isModel}}{{/isFormParam}}>{{! + }}{{/isBodyParam}}{{! + }}{{/isArray}}{{! + }}{{^isArray}}{{^isModel}}String{{/isModel}}{{#isModel}}{{{dataType}}}{{/isModel}}{{/isArray}}{{! +}}{{/isPrimitiveType}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/enumClass.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/enumClass.mustache new file mode 100644 index 00000000000..b9eeee00409 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/enumClass.mustache @@ -0,0 +1,62 @@ + /** + * {{^description}}Gets or Sets {{{name}}}{{/description}}{{{description}}} + */ + {{#jsonb}} + @JsonbTypeSerializer({{datatypeWithEnum}}.Serializer.class) + @JsonbTypeDeserializer({{datatypeWithEnum}}.Deserializer.class) + {{/jsonb}} + {{>additionalEnumTypeAnnotations}}public enum {{{datatypeWithEnum}}} { + {{#allowableValues}}{{#enumVars}}{{{name}}}({{{value}}}){{^-last}}, + {{/-last}}{{#-last}};{{/-last}}{{/enumVars}}{{/allowableValues}} + + private {{{dataType}}} value; + + {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}({{{dataType}}} value) { + this.value = value; + } + + {{#jackson}} + @JsonValue + {{/jackson}} + public {{{dataType}}} getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + {{#jsonb}} + public static final class Deserializer implements JsonbDeserializer<{{datatypeWithEnum}}> { + @Override + public {{datatypeWithEnum}} deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { + for ({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) { + if (String.valueOf(b.value).equals(parser.getString())) { + return b; + } + } + {{#useNullForUnknownEnumValue}}return null;{{/useNullForUnknownEnumValue}}{{^useNullForUnknownEnumValue}}throw new IllegalArgumentException("Unexpected value '" + parser.getString() + "'");{{/useNullForUnknownEnumValue}} + } + } + + public static final class Serializer implements JsonbSerializer<{{datatypeWithEnum}}> { + @Override + public void serialize({{datatypeWithEnum}} obj, JsonGenerator generator, SerializationContext ctx) { + generator.write(obj.value); + } + } + {{/jsonb}} + + {{#jackson}} + @JsonCreator + {{/jackson}} + public static {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} fromValue(String text) { + for ({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) { + if (String.valueOf(b.value).equals(text)) { + return b; + } + } + {{#useNullForUnknownEnumValue}}return null;{{/useNullForUnknownEnumValue}}{{^useNullForUnknownEnumValue}}throw new IllegalArgumentException("Unexpected value '" + text + "'");{{/useNullForUnknownEnumValue}} + } + } diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/enumOuterClass.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/enumOuterClass.mustache new file mode 100644 index 00000000000..a695da1902a --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/enumOuterClass.mustache @@ -0,0 +1,80 @@ +{{#jsonb}}import java.lang.reflect.Type; +import {{rootJavaEEPackage}}.json.bind.annotation.JsonbTypeDeserializer; +import {{rootJavaEEPackage}}.json.bind.annotation.JsonbTypeSerializer; +import {{rootJavaEEPackage}}.json.bind.serializer.DeserializationContext; +import {{rootJavaEEPackage}}.json.bind.serializer.JsonbDeserializer; +import {{rootJavaEEPackage}}.json.bind.serializer.JsonbSerializer; +import {{rootJavaEEPackage}}.json.bind.serializer.SerializationContext; +import {{rootJavaEEPackage}}.json.stream.JsonGenerator; +import {{rootJavaEEPackage}}.json.stream.JsonParser; +import {{rootJavaEEPackage}}.json.bind.annotation.JsonbProperty; +{{#vendorExtensions.x-has-readonly-properties}} +import {{rootJavaEEPackage}}.json.bind.annotation.JsonbCreator; +{{/vendorExtensions.x-has-readonly-properties}}{{/jsonb}} +{{#jackson}} +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue;{{/jackson}} + +/** + * {{^description}}Gets or Sets {{{name}}}{{/description}}{{{description}}} + */ +{{#jsonb}} +@JsonbTypeSerializer({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.Serializer.class) +@JsonbTypeDeserializer({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.Deserializer.class){{/jsonb}} +public enum {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} { + +{{#allowableValues}} + {{#enumVars}} + {{{name}}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/enumVars}} +{{/allowableValues}} + + private {{{dataType}}} value; + + {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}({{{dataType}}} value) { + this.value = value; + } + + {{#jackson}} + @JsonValue + {{/jackson}} + public {{{dataType}}} getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + +{{#jsonb}} + public static final class Deserializer implements JsonbDeserializer<{{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> { + @Override + public {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { + for ({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) { + if (String.valueOf(b.value).equals(parser.getString())) { + return b; + } + } + {{#useNullForUnknownEnumValue}}return null;{{/useNullForUnknownEnumValue}}{{^useNullForUnknownEnumValue}}throw new IllegalArgumentException("Unexpected value '" + parser.getString() + "'");{{/useNullForUnknownEnumValue}} + } + } + + public static final class Serializer implements JsonbSerializer<{{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}> { + @Override + public void serialize({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} obj, JsonGenerator generator, SerializationContext ctx) { + generator.write(obj.value); + } + } +{{/jsonb}} +{{#jackson}} + @JsonCreator{{/jackson}} + public static {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} fromValue(String text) { + for ({{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{{datatypeWithEnum}}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) { + if (String.valueOf(b.value).equals(text)) { + return b; + } + } + {{#useNullForUnknownEnumValue}}return null;{{/useNullForUnknownEnumValue}}{{^useNullForUnknownEnumValue}}throw new IllegalArgumentException("Unexpected value '" + text + "'");{{/useNullForUnknownEnumValue}} + } +} diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/formParams.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/formParams.mustache new file mode 100644 index 00000000000..82c0f5d715c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/formParams.mustache @@ -0,0 +1 @@ +{{#isFormParam}}{{^isFile}}{{>dataType}} {{paramName}} = {{>paramValue}}{{/isFile}}{{#isFile}}{{#isArray}}List<{{/isArray}}InputStream{{#isArray}}>{{/isArray}} {{paramName}} = {{>paramValue}}{{/isFile}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{/isFormParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/formParamsFunctions.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/formParamsFunctions.mustache new file mode 100644 index 00000000000..f4e382d4810 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/formParamsFunctions.mustache @@ -0,0 +1,17 @@ +private void processNonFileFormField(String name, Map> nonFileFormContent, ReadableBodyPart part) { + List content = nonFileFormContent.computeIfAbsent(name, key -> new ArrayList<>()); + part.content().as(String.class).thenAccept(content::add); + } + + private void processFileFormField(String name, Map> fileFormContent, ReadableBodyPart part) { + List content = fileFormContent.computeIfAbsent(name, key -> new ArrayList<>()); + part.content().map(DataChunk::bytes) + .collect(ByteArrayOutputStream::new, (stream, bytes) -> { + try { + stream.write(bytes); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }) + .thenAccept(byteStream -> content.add(new ByteArrayInputStream(byteStream.toByteArray()))); + } \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/formParamsInitial.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/formParamsInitial.mustache new file mode 100644 index 00000000000..08d90802b6c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/formParamsInitial.mustache @@ -0,0 +1,11 @@ +Map> nonFileFormContent = new HashMap<>(); + Map> fileFormContent = new HashMap<>(); + Single formSingle = request.content().asStream(ReadableBodyPart.class) + .forEach(part -> { + String name = part.name();{{#formParams}} + if ("{{baseName}}".equals(name)) { + {{^isFile}}processNonFileFormField(name, nonFileFormContent, part);{{/isFile}}{{! + }}{{#isFile}}processFileFormField(name, fileFormContent, part);{{/isFile}} + }{{/formParams}} + part.drain(); + }); \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/generatedAnnotation.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/generatedAnnotation.mustache new file mode 100644 index 00000000000..6f4eb2cb74f --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/generatedAnnotation.mustache @@ -0,0 +1 @@ +@{{rootJavaEEPackage}}.annotation.Generated(value = "{{{generatorClass}}}"{{^hideGenerationTimestamp}}, date = "{{{generatedDate}}}"{{/hideGenerationTimestamp}}) \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/headerParams.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/headerParams.mustache new file mode 100644 index 00000000000..89fb5e8f51b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/headerParams.mustache @@ -0,0 +1 @@ +{{#isHeaderParam}}{{>dataType}} {{paramName}} = {{>paramValue}}{{#useBeanValidation}}{{>beanValidationHeaderParams}}{{/useBeanValidation}}{{/isHeaderParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/jsonProvider.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/jsonProvider.mustache new file mode 100644 index 00000000000..9a3b6f4c107 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/jsonProvider.mustache @@ -0,0 +1,20 @@ +package {{apiPackage}}; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +public class JsonProvider { + + public static ObjectMapper objectMapper() { + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new JavaTimeModule()); + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false); + return mapper; + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/main.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/main.mustache new file mode 100644 index 00000000000..575d3704448 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/main.mustache @@ -0,0 +1,101 @@ +package {{invokerPackage}}; +{{#apiInfo}}{{#apis}}{{#operations}} +import {{package}}.{{classname}}Impl;{{/operations}}{{/apis}}{{/apiInfo}} + +import io.helidon.common.LogConfig; +import io.helidon.common.reactive.Single; +import io.helidon.config.Config; +import io.helidon.health.HealthSupport; +import io.helidon.health.checks.HealthChecks; +import io.helidon.media.jsonp.JsonpSupport; +{{#jsonb}} +import io.helidon.media.jsonb.JsonbSupport; +{{/jsonb}} +{{#jackson}} +import io.helidon.media.jackson.JacksonSupport; +import {{apiPackage}}.JsonProvider; +{{/jackson}} +import io.helidon.metrics.MetricsSupport; +import io.helidon.openapi.OpenAPISupport; +import io.helidon.webserver.Routing; +import io.helidon.webserver.WebServer; + +/** +* The application main class. +*/ +public final class Main { + + /** + * Cannot be instantiated. + */ + private Main() { + } + + /** + * Application main entry point. + * @param args command line arguments. + */ + public static void main(final String[] args) { + startServer(); + } + + /** + * Start the server. + * @return the created {@link WebServer} instance + */ + static Single startServer() { + + // load logging configuration + LogConfig.configureRuntime(); + + // By default this will pick up application.yaml from the classpath + Config config = Config.create(); + + WebServer server = WebServer.builder(createRouting(config)) + .config(config.get("server")) + .addMediaSupport(JsonpSupport.create()) +{{#jsonb}} + .addMediaSupport(JsonbSupport.create()) +{{/jsonb}} +{{#jackson}} + .addMediaSupport(JacksonSupport.create(JsonProvider.objectMapper())) +{{/jackson}} + .build(); + + Single webserver = server.start(); + + // Try to start the server. If successful, print some info and arrange to + // print a message at shutdown. If unsuccessful, print the exception. + webserver.thenAccept(ws -> { + System.out.println("WEB server is up! {{{basePath}}}"); + ws.whenShutdown().thenRun(() -> System.out.println("WEB server is DOWN. Good bye!")); + }) + .exceptionallyAccept(t -> { + System.err.println("Startup failed: " + t.getMessage()); + t.printStackTrace(System.err); + }); + + return webserver; + } + + /** + * Creates new {@link Routing}. + * + * @return routing configured with JSON support, a health check, and a service + * @param config configuration of this server + */ + private static Routing createRouting(Config config) { + + MetricsSupport metrics = MetricsSupport.create(); + HealthSupport health = HealthSupport.builder() + .addLiveness(HealthChecks.healthChecks()) // Adds a convenient set of checks + .build(); + + return Routing.builder() + .register(OpenAPISupport.create(config.get(OpenAPISupport.Builder.CONFIG_KEY))) + .register(health) // Health at "/health" + .register(metrics) // Metrics at "/metrics"{{#apiInfo}}{{#apis}}{{#operations}} + .register("/", new {{classname}}Impl()){{/operations}}{{/apis}}{{/apiInfo}} + .build(); + } +} diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/mainTest.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/mainTest.mustache new file mode 100644 index 00000000000..78c4e89b589 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/mainTest.mustache @@ -0,0 +1,47 @@ +package {{invokerPackage}}; + +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +import {{rootJavaEEPackage}}.json.Json; +import {{rootJavaEEPackage}}.json.JsonBuilderFactory; + +import io.helidon.media.jsonp.JsonpSupport; +import io.helidon.webclient.WebClient; +import io.helidon.webserver.WebServer; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +@Disabled +public class MainTest { + + private static WebServer webServer; + private static WebClient webClient; + private static final JsonBuilderFactory JSON_BUILDER = Json.createBuilderFactory(Collections.emptyMap()); + + @BeforeAll + public static void startTheServer() throws Exception { + webServer = Main.startServer().await(); + + webClient = WebClient.builder() + .baseUri("http://localhost:" + webServer.port()) + .addMediaSupport(JsonpSupport.create()) + .build(); + } + + @AfterAll + public static void stopServer() throws Exception { + if (webServer != null) { + webServer.shutdown() + .toCompletableFuture() + .get(10, TimeUnit.SECONDS); + } + } + + @Test + public void test() throws Exception { + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/model.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/model.mustache new file mode 100644 index 00000000000..dbe0359ada4 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/model.mustache @@ -0,0 +1,10 @@ +package {{package}}; + +{{#imports}}import {{import}}; +{{/imports}} +{{#models}} +{{#model}}{{#isEnum}} +{{>enumOuterClass}}{{/isEnum}}{{^isEnum}} +{{>pojo}}{{/isEnum}} +{{/model}} +{{/models}} diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/paramValue.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/paramValue.mustache new file mode 100644 index 00000000000..52ad10dd603 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/paramValue.mustache @@ -0,0 +1,14 @@ +{{#isPathParam}}Optional.ofNullable(request.path().param("{{baseName}}")){{/isPathParam}}{{! +}}{{#isQueryParam}}{{#isPrimitiveType}}request.queryParams().toMap().getOrDefault("{{baseName}}", List.of()).stream().findFirst(){{/isPrimitiveType}}{{/isQueryParam}}{{! +}}{{#isQueryParam}}{{^isPrimitiveType}}Optional.ofNullable(request.queryParams().toMap().get("{{baseName}}")){{/isPrimitiveType}}{{/isQueryParam}}{{! +}}{{#isCookieParam}}{{#isPrimitiveType}}request.headers().cookies().toMap().getOrDefault("{{baseName}}", List.of()).stream().findFirst(){{/isPrimitiveType}}{{/isCookieParam}}{{! +}}{{#isCookieParam}}{{^isPrimitiveType}}Optional.ofNullable(request.headers().cookies().toMap().get("{{baseName}}")){{/isPrimitiveType}}{{/isCookieParam}}{{! +}}{{#isHeaderParam}}request.headers().value("{{baseName}}"){{/isHeaderParam}}{{! +}}{{#isFormParam}}Optional.ofNullable({{#isFile}}fileFormContent{{/isFile}}{{^isFile}}nonFileFormContent{{/isFile}}.get("{{baseName}}")){{^isArray}}.flatMap(list->list.stream().findFirst()){{/isArray}}{{/isFormParam}}{{! +}}{{#isPrimitiveType}}{{^isFile}}{{^isString}}.map({{! + }}{{#isDecimal}}BigDecimal::new{{/isDecimal}}{{! + }}{{#isNumber}}BigDecimal::new{{/isNumber}}{{! + }}{{#isDate}}LocalDate::parse{{/isDate}}{{#isDateTime}}OffsetDateTime::parse{{/isDateTime}}{{! + }}{{^isDecimal}}{{^isNumber}}{{^isDate}}{{^isDateTime}}{{{dataType}}}::valueOf{{/isDateTime}}{{/isDate}}{{/isNumber}}{{/isDecimal}}){{! +}}{{/isString}}{{/isFile}}{{/isPrimitiveType}}{{! +}}.orElse(null); \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/pathParams.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/pathParams.mustache new file mode 100644 index 00000000000..14464296f11 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/pathParams.mustache @@ -0,0 +1,2 @@ +{{#isPathParam}}{{> dataType }} {{paramName}} = {{> paramValue }}{{#useBeanValidation}} + {{>beanValidationPathParams}}{{/useBeanValidation}}{{/isPathParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/pojo.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/pojo.mustache new file mode 100644 index 00000000000..944ae482956 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/pojo.mustache @@ -0,0 +1,121 @@ +{{#jsonb}} +import java.lang.reflect.Type; +import {{rootJavaEEPackage}}.json.bind.annotation.JsonbTypeDeserializer; +import {{rootJavaEEPackage}}.json.bind.annotation.JsonbTypeSerializer; +import {{rootJavaEEPackage}}.json.bind.serializer.DeserializationContext; +import {{rootJavaEEPackage}}.json.bind.serializer.JsonbDeserializer; +import {{rootJavaEEPackage}}.json.bind.serializer.JsonbSerializer; +import {{rootJavaEEPackage}}.json.bind.serializer.SerializationContext; +import {{rootJavaEEPackage}}.json.stream.JsonGenerator; +import {{rootJavaEEPackage}}.json.stream.JsonParser; +import {{rootJavaEEPackage}}.json.bind.annotation.JsonbProperty; +{{#vendorExtensions.x-has-readonly-properties}} +import {{rootJavaEEPackage}}.json.bind.annotation.JsonbCreator; +{{/vendorExtensions.x-has-readonly-properties}} +{{/jsonb}} + +{{#description}} +/** + * {{{.}}} + */{{/description}} +public class {{classname}} {{#parent}}extends {{{.}}}{{/parent}} {{#vendorExtensions.x-implements}}{{#-first}}implements {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}} { + +{{#vars}} + {{#isEnum}} + + {{^isContainer}} +{{>enumClass}} + {{/isContainer}} + {{#isContainer}} + {{#mostInnerItems}} +{{>enumClass}} + {{/mostInnerItems}} + {{/isContainer}} + + {{/isEnum}} + private {{{datatypeWithEnum}}} {{{name}}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}; +{{/vars}} + + /** + * Default constructor. + */ + public {{classname}}() { + // JSON-B / Jackson + } + + /** + * Create {{classname}}. + * +{{#vars}} + * @param {{name}} {{description}}{{^description}}{{name}}{{/description}} +{{/vars}} + */ + public {{classname}}( +{{#vars}} + {{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}} +{{/vars}} + ) { +{{#vars}} + this.{{name}} = {{name}}; +{{/vars}} + } + +{{#vars}}{{#vendorExtensions.x-has-readonly-properties}}{{#jsonb}} + @JsonbCreator + public {{classname}}( + {{#readOnlyVars}} + @JsonbProperty("{{baseName}}") {{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}} + {{/readOnlyVars}} + ) { + {{#readOnlyVars}} + this.{{name}} = {{name}}; + {{/readOnlyVars}} + }{{/jsonb}}{{/vendorExtensions.x-has-readonly-properties}} + + /** + {{#description}} + * {{{.}}} + {{/description}} + {{^description}} + * Get {{name}} + {{/description}} + {{#minimum}} + * minimum: {{.}} + {{/minimum}} + {{#maximum}} + * maximum: {{.}} + {{/maximum}} + * @return {{name}} + */ + public {{{datatypeWithEnum}}} {{getter}}() { + return {{name}}; + } + + public void {{setter}}({{{datatypeWithEnum}}} {{name}}) { + this.{{name}} = {{name}}; + }{{/vars}} + + /** + * Create a string representation of this pojo. + **/ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class {{classname}} {\n"); + {{#parent}}sb.append(" ").append(toIndentedString(super.toString())).append("\n");{{/parent}} + {{#vars}}sb.append(" {{name}}: ").append(toIndentedString({{name}})).append("\n"); + {{/vars}}sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private static String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/pom.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/pom.mustache new file mode 100644 index 00000000000..720ea80bde4 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/pom.mustache @@ -0,0 +1,116 @@ + + + 4.0.0 + + io.helidon.applications + helidon-se + {{{helidonVersion}}} + + + {{{groupId}}} + {{{artifactId}}} + {{{artifactVersion}}} + {{{artifactId}}} + {{#appDescription}} + {{.}} + {{/appDescription}} + + + {{{invokerPackage}}}.Main + 0.2.3 + + + + + {{x-helidon-rootJavaEEDepPrefix}}.validation + {{x-helidon-validationArtifactPrefix}}validation-api + + + io.helidon.webserver + helidon-webserver + + + io.helidon.media + helidon-media-jsonp + + + io.helidon.media + helidon-media-multipart + + + io.helidon.config + helidon-config-yaml + + + io.helidon.health + helidon-health + + + io.helidon.health + helidon-health-checks + + + io.helidon.metrics + helidon-metrics + + + io.helidon.openapi + helidon-openapi + +{{#openApiNullable}} + + org.openapitools + jackson-databind-nullable + ${version.jackson.databind.nullable} + +{{/openApiNullable}} +{{#jackson}} + + io.helidon.media + helidon-media-jackson + +{{/jackson}} +{{#jsonb}} + + io.helidon.media + helidon-media-jsonb + ${helidon.version} + +{{/jsonb}} + + org.junit.jupiter + junit-jupiter-api + test + + + io.helidon.webclient + helidon-webclient + test + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-libs + + + + + io.helidon.build-tools + helidon-maven-plugin + + + third-party-license-report + + + + + + diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/queryParams.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/queryParams.mustache new file mode 100644 index 00000000000..6b276964518 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/queryParams.mustache @@ -0,0 +1 @@ +{{#isQueryParam}}{{>dataType}} {{paramName}} = {{>paramValue}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{/isQueryParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/validatorUtils.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/validatorUtils.mustache new file mode 100644 index 00000000000..323407d0c14 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/se/validatorUtils.mustache @@ -0,0 +1,112 @@ +package {{apiPackage}}; + +import java.lang.reflect.Array; +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Map; +import java.util.Objects; + +import {{rootJavaEEPackage}}.validation.ValidationException; + +/** +* Validation utility methods. +*/ +public final class ValidatorUtils { + + public static boolean validateMin(Integer value, Integer min) { + checkNonNull(value); + if (value < min) { + throw new ValidationException(String.format("%s is less than %s", value, min)); + } + return true; + } + + public static boolean validateMax(Integer value, Integer max) { + checkNonNull(value); + if (value > max) { + throw new ValidationException(String.format("%s is more than %s", value, max)); + } + return true; + } + + public static boolean validateSize(Object value, Integer min, Integer max) { + checkNonNull(value); + Integer size = -1; + if (value instanceof Map) { + size = ((Map) value).size(); + } + if (value instanceof CharSequence) { + size = ((CharSequence) value).length(); + } + if (value instanceof Collection) { + size = ((Collection) value).size(); + } + if (value.getClass().isArray()) { + size = Array.getLength(value); + } + if (size == -1) { + throw new ValidationException("Value has incorrect type"); + } + if (min != null) { + validateMin(size, min); + } + if (max != null) { + validateMax(size, max); + } + return true; + } + + public static boolean validatePattern(String value, String pattern) { + checkNonNull(value, pattern); + if (value.matches(pattern)) { + return true; + } + throw new ValidationException(String.format("'%s' does not match the pattern '%s'", value, pattern)); + } + + public static boolean validateMin(BigDecimal value, String stringMinValue, boolean inclusive) { + checkNonNull(value); + BigDecimal minValue = new BigDecimal(stringMinValue); + int result = value.compareTo(minValue); + if (inclusive) { + if (result >= 0) { + return true; + } + } else { + if (result > 0) { + return true; + } + } + throw new ValidationException( + String.format("%s is not valid value. Min value '%s'. Inclusive - %s.", value, stringMinValue, inclusive) + ); + } + + public static boolean validateMax(BigDecimal value, String stringMaxValue, boolean inclusive) { + checkNonNull(value); + BigDecimal maxValue = new BigDecimal(stringMaxValue); + int result = value.compareTo(maxValue); + if (inclusive) { + if (result <= 0) { + return true; + } + } else { + if (result < 0) { + return true; + } + } + throw new ValidationException( + String.format("%s is not valid value. Max value '%s'. Inclusive - %s.", value, stringMaxValue, inclusive) + ); + } + + public static void checkNonNull(Object... args) { + try { + for (Object o : args) { + Objects.requireNonNull(o); + } + } catch (Exception e) { + throw new ValidationException(e); + } + } +} \ No newline at end of file