Add Helidon SE server resources

Signed-off-by: aserkes <andrii.serkes@oracle.com>
This commit is contained in:
aserkes 2022-09-23 17:11:15 -05:00 committed by tim.quinn@oracle.com
parent 224f42e291
commit 1e029ae935
31 changed files with 999 additions and 0 deletions

View File

@ -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":...
. . .
```

View File

@ -0,0 +1,2 @@
{{#additionalEnumTypeAnnotations}}{{{.}}}
{{/additionalEnumTypeAnnotations}}

View File

@ -0,0 +1,2 @@
{{#additionalModelTypeAnnotations}}{{{.}}}
{{/additionalModelTypeAnnotations}}

View File

@ -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}}

View File

@ -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}}

View File

@ -0,0 +1,3 @@
server:
port: {{port}}{{^port}}8080{{/port}}
host: {{host}}{{^host}}localhost{{/host}}

View File

@ -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}}

View File

@ -0,0 +1,2 @@
{{#required}}{{!}}
{{!}}ValidatorUtils.checkNonNull({{paramName}});{{/required}}

View File

@ -0,0 +1,2 @@
{{! PathParam is always required, no @NotNull necessary }}
ValidatorUtils.checkNonNull({{paramName}});{{#isPrimitiveType}}{{>beanValidationCore}}{{/isPrimitiveType}}

View File

@ -0,0 +1,2 @@
{{#required}}{{!}}
{{!}}ValidatorUtils.checkNonNull({{paramName}});{{/required}}{{#isPrimitiveType}}{{>beanValidationCore}}{{/isPrimitiveType}}

View File

@ -0,0 +1 @@
{{#isBodyParam}}ValidatorUtils.checkNonNull({{paramName}});{{/isBodyParam}}

View File

@ -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}}

View File

@ -0,0 +1 @@
{{#isCookieParam}}{{>dataType}} {{paramName}} = {{>paramValue}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{/isCookieParam}}

View File

@ -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}}

View File

@ -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}}
}
}

View File

@ -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}}
}
}

View File

@ -0,0 +1 @@
{{#isFormParam}}{{^isFile}}{{>dataType}} {{paramName}} = {{>paramValue}}{{/isFile}}{{#isFile}}{{#isArray}}List<{{/isArray}}InputStream{{#isArray}}>{{/isArray}} {{paramName}} = {{>paramValue}}{{/isFile}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{/isFormParam}}

View File

@ -0,0 +1,17 @@
private void processNonFileFormField(String name, Map<String, List<String>> nonFileFormContent, ReadableBodyPart part) {
List<String> content = nonFileFormContent.computeIfAbsent(name, key -> new ArrayList<>());
part.content().as(String.class).thenAccept(content::add);
}
private void processFileFormField(String name, Map<String, List<InputStream>> fileFormContent, ReadableBodyPart part) {
List<InputStream> 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())));
}

View File

@ -0,0 +1,11 @@
Map<String, List<String>> nonFileFormContent = new HashMap<>();
Map<String, List<InputStream>> fileFormContent = new HashMap<>();
Single<Void> 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();
});

View File

@ -0,0 +1 @@
@{{rootJavaEEPackage}}.annotation.Generated(value = "{{{generatorClass}}}"{{^hideGenerationTimestamp}}, date = "{{{generatedDate}}}"{{/hideGenerationTimestamp}})

View File

@ -0,0 +1 @@
{{#isHeaderParam}}{{>dataType}} {{paramName}} = {{>paramValue}}{{#useBeanValidation}}{{>beanValidationHeaderParams}}{{/useBeanValidation}}{{/isHeaderParam}}

View File

@ -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;
}
}

View File

@ -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<WebServer> 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> 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();
}
}

View File

@ -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 {
}
}

View File

@ -0,0 +1,10 @@
package {{package}};
{{#imports}}import {{import}};
{{/imports}}
{{#models}}
{{#model}}{{#isEnum}}
{{>enumOuterClass}}{{/isEnum}}{{^isEnum}}
{{>pojo}}{{/isEnum}}
{{/model}}
{{/models}}

View File

@ -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);

View File

@ -0,0 +1,2 @@
{{#isPathParam}}{{> dataType }} {{paramName}} = {{> paramValue }}{{#useBeanValidation}}
{{>beanValidationPathParams}}{{/useBeanValidation}}{{/isPathParam}}

View File

@ -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 ");
}
}

View File

@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.helidon.applications</groupId>
<artifactId>helidon-se</artifactId>
<version>{{{helidonVersion}}}</version>
<relativePath/>
</parent>
<groupId>{{{groupId}}}</groupId>
<artifactId>{{{artifactId}}}</artifactId>
<version>{{{artifactVersion}}}</version>
<name>{{{artifactId}}}</name>
{{#appDescription}}
<description>{{.}}</description>
{{/appDescription}}
<properties>
<mainClass>{{{invokerPackage}}}.Main</mainClass>
<version.jackson.databind.nullable>0.2.3</version.jackson.databind.nullable>
</properties>
<dependencies>
<dependency>
<groupId>{{x-helidon-rootJavaEEDepPrefix}}.validation</groupId>
<artifactId>{{x-helidon-validationArtifactPrefix}}validation-api</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.webserver</groupId>
<artifactId>helidon-webserver</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.media</groupId>
<artifactId>helidon-media-jsonp</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.media</groupId>
<artifactId>helidon-media-multipart</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-yaml</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.health</groupId>
<artifactId>helidon-health</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.health</groupId>
<artifactId>helidon-health-checks</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.metrics</groupId>
<artifactId>helidon-metrics</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.openapi</groupId>
<artifactId>helidon-openapi</artifactId>
</dependency>
{{#openApiNullable}}
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>jackson-databind-nullable</artifactId>
<version>${version.jackson.databind.nullable}</version>
</dependency>
{{/openApiNullable}}
{{#jackson}}
<dependency>
<groupId>io.helidon.media</groupId>
<artifactId>helidon-media-jackson</artifactId>
</dependency>
{{/jackson}}
{{#jsonb}}
<dependency>
<groupId>io.helidon.media</groupId>
<artifactId>helidon-media-jsonb</artifactId>
<version>${helidon.version}</version>
</dependency>
{{/jsonb}}
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.helidon.webclient</groupId>
<artifactId>helidon-webclient</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-libs</id>
</execution>
</executions>
</plugin>
<plugin>
<groupId>io.helidon.build-tools</groupId>
<artifactId>helidon-maven-plugin</artifactId>
<executions>
<execution>
<id>third-party-license-report</id>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1 @@
{{#isQueryParam}}{{>dataType}} {{paramName}} = {{>paramValue}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{/isQueryParam}}

View File

@ -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);
}
}
}