diff --git a/src/main/java/io/swagger/codegen/v3/generators/java/JavaVertXServerCodegen.java b/src/main/java/io/swagger/codegen/v3/generators/java/JavaVertXServerCodegen.java
new file mode 100644
index 0000000000..4f9e53c09f
--- /dev/null
+++ b/src/main/java/io/swagger/codegen/v3/generators/java/JavaVertXServerCodegen.java
@@ -0,0 +1,401 @@
+package io.swagger.codegen.v3.generators.java;
+
+import static io.swagger.codegen.v3.CodegenConstants.HAS_ENUMS_EXT_NAME;
+import static io.swagger.codegen.v3.CodegenConstants.IS_ENUM_EXT_NAME;
+import static io.swagger.codegen.v3.generators.handlebars.ExtensionHelper.getBooleanValue;
+
+import com.github.jknack.handlebars.Lambda;
+import com.google.common.collect.ImmutableMap;
+import io.swagger.codegen.v3.CliOption;
+import io.swagger.codegen.v3.CodegenModel;
+import io.swagger.codegen.v3.CodegenProperty;
+import io.swagger.codegen.v3.CodegenType;
+import io.swagger.codegen.v3.SupportingFile;
+import io.swagger.codegen.v3.generators.features.BeanValidationFeatures;
+import io.swagger.codegen.v3.generators.features.NotNullAnnotationFeatures;
+import io.swagger.codegen.v3.generators.handlebars.lambda.UppercaseLambda;
+import io.swagger.codegen.v3.utils.URLPathUtil;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.Operation;
+import io.swagger.v3.oas.models.PathItem;
+import io.swagger.v3.oas.models.PathItem.HttpMethod;
+import io.swagger.v3.oas.models.Paths;
+import io.swagger.v3.oas.models.media.Schema;
+import java.io.File;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JavaVertXServerCodegen extends AbstractJavaCodegen implements BeanValidationFeatures, NotNullAnnotationFeatures {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(JavaVertXServerCodegen.class);
+
+ private static final String ROOT_PACKAGE = "rootPackage";
+ private static final String VERTICLE_PACKAGE = "verticlePackage";
+ private static final String SPEC_LOCATION = "openapi.yaml";
+ private static final String OPENAPI_EXTENSION = "x-vertx-event-bus";
+ private static final String OPENAPI_EXTENSION_ADDRESS = "address";
+ private static final String OPENAPI_EXTENSION_METHOD_NAME = "method";
+ private static final String TITLE = "title";
+
+ public static final String RX_INTERFACE_OPTION = "rxInterface";
+ public static final String USE_DATAOBJECT_OPTION = "useDataObject";
+ public static final String MOUNT_OPERATION_FROM_OPTION = "mountOperationFrom";
+ public static final String MOUNT_OPERATION_FROM_EXTENSIONS = "mountFromExtensions";
+ public static final String MOUNT_OPERATION_FROM_INTERFACE = "mountFromInterface";
+ public static final String SPEC_LOCATION_OPTION = "specLocation";
+
+ protected String rootPackage = "io.swagger.server.api";
+ protected String apiVerticle;
+ protected String apiVersion = "1.0.0-SNAPSHOT";
+
+ protected boolean useDataObject = false;
+ protected boolean mountFromExtensions = false;
+ protected boolean mountFromInterface = false;
+ protected String title = null;
+ protected boolean useBeanValidation = false;
+ protected boolean notNullJacksonAnnotation = false;
+
+ /**
+ * A Java Vert.X generator. It can be configured with CLI options :
+ *
+ * - rxInterface : type Boolean if true, API interfaces are generated with RX and methods return
+ * Single and Comparable. default : false
+ *
+ * - useDataObject : type Boolean if true, models objects are generated with @DataObject
+ *
+ * - mountOperationFrom : type String, define how routes are mounted.
+ *
+ * - specLocation : define spec location, default as {@link JavaVertXServerCodegen#SPEC_LOCATION}.
+ *
+ */
+ public JavaVertXServerCodegen() {
+ super();
+
+ // set the output folder here
+ outputFolder = "generated-code" + File.separator + "javaVertXServer";
+
+ apiPackage = rootPackage + ".service";
+ apiVerticle = rootPackage + ".verticle";
+ modelPackage = rootPackage + ".model";
+
+ additionalProperties.put(ROOT_PACKAGE, rootPackage);
+ additionalProperties.put(VERTICLE_PACKAGE, apiVerticle);
+
+ groupId = "io.swagger";
+ artifactId = "swagger-java-vertx-server";
+ artifactVersion = apiVersion;
+
+ cliOptions.add(CliOption.newBoolean(RX_INTERFACE_OPTION,
+ "When specified, API interfaces are generated with RX "
+ + "and methods return Single<> and Comparable."));
+ cliOptions.add(CliOption.newBoolean(USE_DATAOBJECT_OPTION,
+ "When specified, models objects are generated with @DataObject"));
+
+ // add option to mount with operation id ?
+ CliOption operationsOption = CliOption.newString(MOUNT_OPERATION_FROM_OPTION,"When specified, defines how operations are mounted. Default with @WebApiServiceGen");
+ Map mountOperationFromEnum = new HashMap<>();
+ mountOperationFromEnum.put(MOUNT_OPERATION_FROM_EXTENSIONS, "Mount operations from extensions with web-api-service module & @WebApiServiceGen. open api contract must define x-vertx-event-bus extension to be mounted");
+ mountOperationFromEnum.put(MOUNT_OPERATION_FROM_INTERFACE, "Mount operations from interface with web-api-service module & Interfaces implementing operations. event bus address will be #{tag}.address");
+ operationsOption.setEnum(mountOperationFromEnum);
+ operationsOption.setDefault(MOUNT_OPERATION_FROM_EXTENSIONS);
+ cliOptions.add(operationsOption);
+
+ CliOption specLocation = CliOption.newString(SPEC_LOCATION_OPTION,
+ "When specified, define spec location. Default as " + SPEC_LOCATION);
+ specLocation.setDefault(SPEC_LOCATION);
+ cliOptions.add(specLocation);
+
+ cliOptions.add(CliOption.newBoolean(USE_BEANVALIDATION, "Use BeanValidation API annotations"));
+ }
+
+ /**
+ * Configures the type of generator.
+ *
+ * @return the CodegenType for this generator
+ * @see CodegenType
+ */
+ public CodegenType getTag() {
+ return CodegenType.SERVER;
+ }
+
+ /**
+ * Configures a friendly name for the generator. This will be used by the generator to select
+ * the library with the -l flag.
+ *
+ * @return the friendly name for the generator
+ */
+ public String getName() {
+ return "java-vertx";
+ }
+
+ /**
+ * Returns human-friendly help for the generator. Provide the consumer with help tips,
+ * parameters here
+ *
+ * @return A string value for the help message
+ */
+ public String getHelp() {
+ return "Generates a java-Vert.X Server library.";
+ }
+
+ @Override
+ public String getDefaultTemplateDir() {
+ return "JavaVertXServer";
+ }
+
+ @Override
+ public void processOpts() {
+
+ if (dateLibrary.equals("legacy")){
+ // can't use legacy format
+ LOGGER.warn("Legacy date library could not be used. Replaced to java8 as default");
+ setDateLibrary("java8");
+ }
+
+ super.processOpts();
+
+ modelTemplateFiles.clear();
+ modelTemplateFiles.put("model.mustache", ".java");
+
+ apiTemplateFiles.clear();
+ apiTemplateFiles.put("api.mustache", ".java");
+
+ apiTestTemplateFiles.clear();
+ modelDocTemplateFiles.clear();
+ apiDocTemplateFiles.clear();
+
+ if (additionalProperties.containsKey(USE_DATAOBJECT_OPTION)) {
+ this.useDataObject = (Boolean.valueOf(additionalProperties.get(USE_DATAOBJECT_OPTION).toString()));
+ }
+
+
+ if (additionalProperties.containsKey(MOUNT_OPERATION_FROM_OPTION)) {
+ if (MOUNT_OPERATION_FROM_INTERFACE.equals(additionalProperties.get(MOUNT_OPERATION_FROM_OPTION))) {
+ this.mountFromInterface = true;
+ additionalProperties.put(MOUNT_OPERATION_FROM_INTERFACE, true);
+ }
+ }
+ if (!this.mountFromInterface) {
+ this.mountFromExtensions = true;
+ additionalProperties.put(MOUNT_OPERATION_FROM_EXTENSIONS, true);
+ }
+
+
+ if (!additionalProperties.containsKey(SPEC_LOCATION_OPTION)) {
+ additionalProperties.put(SPEC_LOCATION_OPTION, SPEC_LOCATION);
+ }
+
+ supportingFiles.clear();
+ supportingFiles.add(new SupportingFile("MainApiVerticle.mustache", sourceFolder + File.separator + rootPackage.replace(".", File.separator), "MainApiVerticle.java"));
+
+ supportingFiles.add(new SupportingFile("package-info-service.mustache", sourceFolder + File.separator + apiPackage.replace(".", File.separator), "package-info.java"));
+
+ if (this.useDataObject) {
+ supportingFiles.add(new SupportingFile("package-info-model.mustache", sourceFolder + File.separator + modelPackage.replace(".", File.separator), "package-info.java"));
+ supportingFiles.add(new SupportingFile("json-mappers.mustache", projectFolder + File.separator + "resources/META-INF/vertx", "json-mappers.properties"));
+ supportingFiles.add(new SupportingFile("DataObjectMapper.mustache", sourceFolder + File.separator + modelPackage.replace(".", File.separator), "DataObjectMapper.java"));
+ }
+
+ writeOptional(outputFolder, new SupportingFile("pom.mustache", "", "pom.xml"));
+ writeOptional(outputFolder, new SupportingFile("README.mustache", "", "README.md"));
+
+ addHandlebarsLambdas(additionalProperties);
+ }
+
+ @Override
+ public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
+ super.postProcessModelProperty(model, property);
+
+ boolean isEnum = getBooleanValue(model, IS_ENUM_EXT_NAME);
+ if (!Boolean.TRUE.equals(isEnum)) {
+ model.imports.add("JsonProperty");
+ boolean hasEnums = getBooleanValue(model, HAS_ENUMS_EXT_NAME);
+ if (Boolean.TRUE.equals(hasEnums)) {
+ model.imports.add("JsonValue");
+ }
+ }
+
+ // not use
+ model.imports.remove("Schema");
+ }
+
+ @Override
+ public CodegenModel fromModel(String name, Schema schema, Map allSchemas) {
+ CodegenModel codegenModel = super.fromModel(name, schema, allSchemas);
+ codegenModel.imports.remove("ApiModel");
+ codegenModel.imports.remove("ApiModelProperty");
+ return codegenModel;
+ }
+
+ @Override
+ public void preprocessOpenAPI(OpenAPI openAPI) {
+ super.preprocessOpenAPI(openAPI);
+
+ final URL urlInfo = URLPathUtil.getServerURL(openAPI);
+ String port = "8080";
+ if (urlInfo != null && urlInfo.getPort() > 0) {
+ port = String.valueOf(urlInfo.getPort());
+ }
+
+ this.additionalProperties.put("serverPort", port);
+
+ // From the title, compute a reasonable name for the package and the API
+ String title = openAPI.getInfo().getTitle();
+
+ // Drop any API suffix
+ title = title.trim().replace(" ", "-");
+ if (title.toUpperCase().endsWith("API")) {
+ title = title.substring(0, title.length() - 3);
+ }
+
+ title = camelize(sanitizeName(title));
+ additionalProperties.put(TITLE, title);
+ supportingFiles.add(new SupportingFile("apiVerticle.mustache", sourceFolder + File.separator + apiVerticle.replace(".", File.separator), title + "Verticle.java"));
+
+ /*
+ * manage operation & custom serviceId because operationId field is not
+ * required and may be empty
+ */
+ Paths paths = openAPI.getPaths();
+ if (paths != null) {
+ for (Entry entry : paths.entrySet()) {
+ manageOperations(entry.getValue(), entry.getKey());
+ }
+ }
+ this.additionalProperties.remove("gson");
+ }
+
+ public void setUseBeanValidation(boolean useBeanValidation) {
+ this.useBeanValidation = useBeanValidation;
+ }
+
+ @Override
+ public void setNotNullJacksonAnnotation(boolean notNullJacksonAnnotation) {
+ this.notNullJacksonAnnotation = notNullJacksonAnnotation;
+ }
+
+ @Override
+ public boolean isNotNullJacksonAnnotation() {
+ return notNullJacksonAnnotation;
+ }
+
+ private void addHandlebarsLambdas(Map objs) {
+ Map lambdas = new ImmutableMap.Builder()
+ .put("uppercase", new UppercaseLambda())
+ .build();
+
+ if (objs.containsKey("lambda")) {
+ LOGGER.warn("An property named 'lambda' already exists. Mustache lambdas renamed from 'lambda' to '_lambda'. " +
+ "You'll likely need to use a custom template, " +
+ "see https://github.com/swagger-api/swagger-codegen#modifying-the-client-library-format. ");
+ objs.put("_lambda", lambdas);
+ } else {
+ objs.put("lambda", lambdas);
+ }
+ }
+
+ private void manageOperations(PathItem pathItem, String pathname) {
+ Map operationMap = pathItem.readOperationsMap();
+ if (operationMap != null) {
+ for (Entry entry : operationMap.entrySet()) {
+ String serviceId = null;
+
+ if (this.mountFromExtensions) {
+ // read extension "x-vertx-event-bus" to write api service from address, not by tag
+ // Cases: from vertx doc
+ // 1. both strings or path extension null: operation extension overrides all
+ // 2. path extension map and operation extension string: path extension interpreted as delivery options and operation extension as address
+ // 3. path extension string and operation extension map: path extension interpreted as address
+ // 4. both maps: extension map overrides path map elements
+ // 5. operation extension null: path extension overrides all
+ Object pathExtension = getExtension(pathItem.getExtensions());
+ Object operationExtension = getExtension(entry.getValue().getExtensions());
+
+ String address = null;
+
+ if ((operationExtension instanceof String && pathExtension instanceof String) || pathExtension == null) {
+ if (operationExtension instanceof String) {
+ address = (String) operationExtension;
+ } else if (operationExtension instanceof Map) {
+ address = (String) ((Map) operationExtension).get(
+ OPENAPI_EXTENSION_ADDRESS);
+ serviceId = (String) ((Map) operationExtension).get(OPENAPI_EXTENSION_METHOD_NAME);
+ }
+ } else if (operationExtension instanceof String && pathExtension instanceof Map) {
+ address = (((Map, ?>) pathExtension).containsKey(OPENAPI_EXTENSION_ADDRESS))
+ ? (String) ((Map) pathExtension).get(OPENAPI_EXTENSION_ADDRESS)
+ : (String) operationExtension;
+ } else if (operationExtension instanceof Map && pathExtension instanceof String) {
+ address = (String) pathExtension;
+ serviceId = (String) ((Map) operationExtension).get(OPENAPI_EXTENSION_METHOD_NAME);
+ } else if (operationExtension instanceof Map && pathExtension instanceof Map) {
+ Map busExtension = new LinkedHashMap<>(
+ (Map) pathExtension);
+ busExtension.putAll((Map) operationExtension);
+ address = (String) (busExtension).get(OPENAPI_EXTENSION_ADDRESS);
+ serviceId = (String) (busExtension).get(OPENAPI_EXTENSION_METHOD_NAME);
+ } else if (operationExtension == null && pathExtension instanceof String) {
+ address = (String) pathExtension;
+ } else if (operationExtension == null && pathExtension instanceof Map) {
+ address = (String) ((Map) pathExtension).get(OPENAPI_EXTENSION_ADDRESS);
+ serviceId = (String) ((Map) pathExtension).get(OPENAPI_EXTENSION_METHOD_NAME);
+ }
+
+ if (null != address) {
+ entry.getValue().addExtension("x-event-bus-address", address);
+ // codegen use tag to generate api, so we save tags & replace with event bus address
+ entry.getValue().setTags(Collections.singletonList(address));
+ } else {
+ LOGGER.warn("event bus address not found on operationId {}, will not be mount", entry.getValue().getOperationId());
+ }
+ }
+
+ if (null == serviceId) {
+ serviceId = computeServiceId(pathname, entry);
+ }
+ entry.getValue().addExtension("x-serviceid", sanitizeOperationId(serviceId));
+ }
+ }
+ }
+
+ private Object getExtension(Map extensions) {
+ return null != extensions ? extensions.get(OPENAPI_EXTENSION) : null;
+ }
+
+ /**
+ * @see io.vertx.ext.web.openapi.impl.OpenAPI3Utils#sanitizeOperationId
+ */
+ private String sanitizeOperationId(String operationId) {
+ StringBuilder result = new StringBuilder();
+ for (int i = 0; i < operationId.length(); i++) {
+ char c = operationId.charAt(i);
+ if (c == '-' || c == ' ' || c == '_') {
+ try {
+ while (c == '-' || c == ' ' || c == '_') {
+ i++;
+ c = operationId.charAt(i);
+ }
+ result.append(Character.toUpperCase(operationId.charAt(i)));
+ } catch (StringIndexOutOfBoundsException e) {
+ }
+ } else {
+ result.append(c);
+ }
+ }
+ return result.toString();
+ }
+
+ private String computeServiceId(String pathname, Entry entry) {
+ String operationId = entry.getValue().getOperationId();
+ return (operationId != null) ? operationId
+ : entry.getKey().name()
+ + pathname.replaceAll("-", "_").replaceAll("/", "_").replaceAll("[{}]", "");
+ }
+
+}
diff --git a/src/main/resources/META-INF/services/io.swagger.codegen.v3.CodegenConfig b/src/main/resources/META-INF/services/io.swagger.codegen.v3.CodegenConfig
index 69ee5fda50..2adb275cf1 100644
--- a/src/main/resources/META-INF/services/io.swagger.codegen.v3.CodegenConfig
+++ b/src/main/resources/META-INF/services/io.swagger.codegen.v3.CodegenConfig
@@ -17,6 +17,7 @@ io.swagger.codegen.v3.generators.java.JavaJerseyServerCodegen
io.swagger.codegen.v3.generators.java.JavaJerseyDIServerCodegen
io.swagger.codegen.v3.generators.java.JavaResteasyEapServerCodegen
io.swagger.codegen.v3.generators.java.JavaResteasyServerCodegen
+io.swagger.codegen.v3.generators.java.JavaVertXServerCodegen
io.swagger.codegen.v3.generators.java.MicronautCodegen
io.swagger.codegen.v3.generators.java.SpringCodegen
io.swagger.codegen.v3.generators.nodejs.NodeJSServerCodegen
diff --git a/src/main/resources/handlebars/JavaVertXServer/DataObjectMapper.mustache b/src/main/resources/handlebars/JavaVertXServer/DataObjectMapper.mustache
new file mode 100644
index 0000000000..9126854940
--- /dev/null
+++ b/src/main/resources/handlebars/JavaVertXServer/DataObjectMapper.mustache
@@ -0,0 +1,55 @@
+package {{modelPackage}};
+
+{{#java8}}
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+{{/java8}}
+{{#threetenbp}}
+import org.threeten.bp.Instant;
+import org.threeten.bp.OffsetDateTime;
+import org.threeten.bp.ZonedDateTime;
+{{/threetenbp}}
+{{#joda}}
+import org.joda.time.LocalDate;
+import org.joda.time.DateTime;
+{{/joda}}
+
+public class DataObjectMapper {
+ {{#java8}}
+ public static String serializeOffsetDateTime(OffsetDateTime value) {
+ return value.toString();
+ }
+
+ public static String serializeLocalDateTime(LocalDateTime value) {
+ return value.toString();
+ }
+
+ public static String serializeLocalDate(LocalDate value) {
+ return value.toString();
+ }
+ {{/java8}}
+ {{#threetenbp}}
+ public static String serializeThreetenbpInstant(org.threeten.bp.Instant value) {
+ return value.toString();
+ }
+
+ public static String serializeThreetenbpOffsetDateTime(org.threeten.bp.OffsetDateTime value) {
+ return value.toString();
+ }
+
+ public static String serializeThreetenbpZonedDateTime(org.threeten.bp.ZonedDateTime value) {
+ return value.toString();
+ }
+ {{/threetenbp}}
+ {{#joda}}
+ public static String serializeJodaLocalDate(org.joda.time.LocalDate value) {
+ return value.toString();
+ }
+
+ public static String serializeJodaDateTime(org.joda.time.DateTime value) {
+ return value.toString();
+ }
+ {{/joda}}
+
+}
diff --git a/src/main/resources/handlebars/JavaVertXServer/MainApiVerticle.mustache b/src/main/resources/handlebars/JavaVertXServer/MainApiVerticle.mustache
new file mode 100644
index 0000000000..be3c5c1c62
--- /dev/null
+++ b/src/main/resources/handlebars/JavaVertXServer/MainApiVerticle.mustache
@@ -0,0 +1,36 @@
+package {{rootPackage}};
+
+{{#rxInterface}}
+import io.reactivex.Completable;
+import io.vertx.reactivex.core.AbstractVerticle;
+{{/rxInterface}}
+{{^rxInterface}}
+import io.vertx.core.AbstractVerticle;
+import io.vertx.core.Promise;
+{{/rxInterface}}
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MainApiVerticle extends AbstractVerticle {
+ static final Logger LOGGER = LoggerFactory.getLogger(MainApiVerticle.class);
+
+ @Override
+ {{^rxInterface}}public void start(Promise startPromise) throws Exception {
+ vertx.deployVerticle("{{verticlePackage}}.{{title}}Verticle")
+ .onFailure(error -> {
+ LOGGER.error("{{title}}Verticle : Deployment failed");
+ startPromise.fail(error);
+ })
+ .onSuccess(server -> {
+ LOGGER.info("{{title}}Verticle : Deployed");
+ startPromise.complete();
+ });
+ }{{/rxInterface}}
+ {{#rxInterface}}public Completable rxStart() {
+ return vertx.rxDeployVerticle("{{verticlePackage}}.{{title}}Verticle")
+ .doOnError(error -> LOGGER.error("{{title}}Verticle : Deployment failed"))
+ .doOnSuccess(server -> LOGGER.info("{{title}}Verticle : Deployed"))
+ .ignoreElement();
+ }{{/rxInterface}}
+
+}
diff --git a/src/main/resources/handlebars/JavaVertXServer/README.mustache b/src/main/resources/handlebars/JavaVertXServer/README.mustache
new file mode 100644
index 0000000000..ac03f0bc7c
--- /dev/null
+++ b/src/main/resources/handlebars/JavaVertXServer/README.mustache
@@ -0,0 +1 @@
+Project generated on : {{generatedDate}}
diff --git a/src/main/resources/handlebars/JavaVertXServer/api.mustache b/src/main/resources/handlebars/JavaVertXServer/api.mustache
new file mode 100644
index 0000000000..e0ddaebc98
--- /dev/null
+++ b/src/main/resources/handlebars/JavaVertXServer/api.mustache
@@ -0,0 +1,31 @@
+package {{package}};
+
+{{#imports}}import {{import}};
+{{/imports}}
+
+import io.vertx.core.Future;
+import io.vertx.ext.web.validation.RequestParameter;
+import io.vertx.ext.web.api.service.ServiceRequest;
+import io.vertx.ext.web.api.service.ServiceResponse;
+{{#mountFromExtensions}}
+import io.vertx.ext.web.api.service.WebApiServiceGen;
+{{/mountFromExtensions}}
+
+import java.util.List;
+import java.util.Map;
+
+{{#mountFromExtensions}}@WebApiServiceGen{{/mountFromExtensions}}
+public interface {{classname}} {
+
+{{#operations}}{{#operation}}{{#@first}} String WEBSERVICE_ADDRESS_{{#lambda.uppercase}}{{classname}}{{/lambda.uppercase}} = "{{#mountFromExtensions}}{{#vendorExtensions}}{{x-event-bus-address}}{{/vendorExtensions}}{{/mountFromExtensions}}{{#mountFromInterface}}{{baseName}}.address{{/mountFromInterface}}";{{/@first}}{{/operation}}{{/operations}}
+{{#operations}}{{#operation}} String OPERATION_ID_{{#lambda.uppercase}}{{operationId}}{{/lambda.uppercase}} = "{{operationId}}";
+{{/operation}}{{/operations}}
+
+{{#operations}}
+{{#operation}}
+{{#contents}}
+ Future {{#vendorExtensions}}{{x-serviceid}}{{/vendorExtensions}}({{#useDataObject}}{{#parameters}}{{^isBodyParam}}{{^isEnum}}{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{dataType}}}{{/datatypeWithEnum}}{{/isEnum}}{{#isEnum}}{{{dataType}}}{{/isEnum}} {{paramName}}{{/isBodyParam}}{{#isBodyParam}}{{^isBinary}}{{^parent}}{{^children}}{{{dataType}}} body{{/children}}{{/parent}}{{/isBinary}}{{/isBodyParam}}{{#hasMore}}{{^isBinary}}, {{/isBinary}}{{/hasMore}}{{^hasMore}}{{^isBinary}}, {{/isBinary}}{{/hasMore}}{{/parameters}}{{/useDataObject}}{{^useDataObject}}RequestParameter body, {{/useDataObject}}ServiceRequest request);
+{{/contents}}
+{{/operation}}
+{{/operations}}
+}
diff --git a/src/main/resources/handlebars/JavaVertXServer/apiVerticle.mustache b/src/main/resources/handlebars/JavaVertXServer/apiVerticle.mustache
new file mode 100644
index 0000000000..b303058fe1
--- /dev/null
+++ b/src/main/resources/handlebars/JavaVertXServer/apiVerticle.mustache
@@ -0,0 +1,94 @@
+package {{verticlePackage}};
+
+{{#mountFromInterface}}{{#apiInfo}}{{#apis}}import {{{package}}}.{{{classFilename}}};
+import static {{{package}}}.{{{classFilename}}}.WEBSERVICE_ADDRESS_{{#lambda.uppercase}}{{{classFilename}}}{{/lambda.uppercase}};
+{{/apis}}{{/apiInfo}}{{/mountFromInterface}}
+
+{{#rxInterface}}
+import io.reactivex.Completable;
+import io.vertx.reactivex.core.AbstractVerticle;
+import io.vertx.reactivex.core.eventbus.MessageConsumer;
+import io.vertx.reactivex.core.http.HttpServer;
+import io.vertx.reactivex.ext.web.Router;
+import io.vertx.reactivex.ext.web.openapi.RouterBuilder;
+{{/rxInterface}}
+{{^rxInterface}}
+import io.vertx.core.AbstractVerticle;
+import io.vertx.core.Future;
+import io.vertx.core.Promise;
+import io.vertx.core.eventbus.MessageConsumer;
+import io.vertx.core.http.HttpServer;
+import io.vertx.ext.web.Router;
+import io.vertx.ext.web.openapi.RouterBuilder;
+{{/rxInterface}}
+import io.vertx.core.http.HttpServerOptions;
+import io.vertx.core.json.JsonObject;
+import io.vertx.ext.web.openapi.RouterBuilderOptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class {{title}}Verticle extends AbstractVerticle {
+ static final Logger LOGGER = LoggerFactory.getLogger({{title}}Verticle.class);
+
+ MessageConsumer consumer;
+ HttpServer server;
+
+ @Override
+ {{^rxInterface}}public void start(Promise startPromise) throws Exception {
+ RouterBuilder.create(this.vertx, "{{specLocation}}")
+ .flatMap(routerBuilder -> { {{/rxInterface}}
+ {{#rxInterface}}public Completable rxStart() {
+ return RouterBuilder.rxCreate(this.vertx, "{{specLocation}}")
+ .map(routerBuilder -> { {{/rxInterface}}
+ RouterBuilderOptions factoryOptions = new RouterBuilderOptions()
+ .setRequireSecurityHandlers(false)
+ .setMountResponseContentTypeHandler(true);
+ routerBuilder.setOptions(factoryOptions);
+
+{{#mountFromExtensions}} routerBuilder.mountServicesFromExtensions();{{/mountFromExtensions}}
+{{#mountFromInterface}}{{#apiInfo}}{{#apis}} routerBuilder.mountServiceInterface({{{classFilename}}}.class, WEBSERVICE_ADDRESS_{{#lambda.uppercase}}{{{classFilename}}}{{/lambda.uppercase}});
+ {{/apis}}{{/apiInfo}}{{/mountFromInterface}}
+
+ return {{#rxInterface}}routerBuilder.createRouter(){{/rxInterface}}{{^rxInterface}}Future.succeededFuture(routerBuilder.createRouter()){{/rxInterface}};
+ })
+ .flatMap(openapiRouter -> {
+ Router router = Router.router(vertx);
+
+ server = vertx.createHttpServer(new HttpServerOptions().setPort({{serverPort}}).setHost("localhost"))
+ .requestHandler(router);
+
+ router.mountSubRouter("/", openapiRouter);
+
+ router.route().last().handler(context ->
+ context.response()
+ .setStatusCode(404)
+ .end(new JsonObject()
+ .put("message", "Resource not found")
+ .encode())
+ );
+
+ {{#rxInterface}}return server.rxListen()
+ .doOnSuccess(server -> LOGGER.info("SwaggerPetstoreVerticle started on port " + server.actualPort()));
+ })
+ .ignoreElement();
+ }
+
+ @Override
+ public Completable rxStop() {
+ return this.consumer.rxUnregister()
+ .andThen(this.server.rxClose());
+ }{{/rxInterface}}
+ {{^rxInterface}}return server.listen()
+ .onSuccess(server -> LOGGER.info("{{title}}Verticle started on port " + server.actualPort()));
+ })
+ .onSuccess(server -> startPromise.complete())
+ .onFailure(startPromise::fail);
+ }
+
+ @Override
+ public void stop() {
+ this.server.close();
+ this.consumer.unregister();
+ }{{/rxInterface}}
+
+}
diff --git a/src/main/resources/handlebars/JavaVertXServer/beanValidation.mustache b/src/main/resources/handlebars/JavaVertXServer/beanValidation.mustache
new file mode 100644
index 0000000000..3e4ef612a4
--- /dev/null
+++ b/src/main/resources/handlebars/JavaVertXServer/beanValidation.mustache
@@ -0,0 +1,6 @@
+{{#required}}
+ @NotNull
+{{/required}}{{#isContainer}}{{^isPrimitiveType}}{{^isEnum}}
+ @Valid{{/isEnum}}{{/isPrimitiveType}}{{/isContainer}}{{#isNotContainer}}{{^isPrimitiveType}}
+ @Valid{{/isPrimitiveType}}{{/isNotContainer}}
+{{>beanValidationCore}}
diff --git a/src/main/resources/handlebars/JavaVertXServer/beanValidationCore.mustache b/src/main/resources/handlebars/JavaVertXServer/beanValidationCore.mustache
new file mode 100644
index 0000000000..29d043cc77
--- /dev/null
+++ b/src/main/resources/handlebars/JavaVertXServer/beanValidationCore.mustache
@@ -0,0 +1,20 @@
+{{#pattern}}@Pattern(regexp="{{{pattern}}}"{{#vendorExtensions.x-pattern-message}}, message="{{vendorExtensions.x-pattern-message}}"{{/vendorExtensions.x-pattern-message}}) {{/pattern}}{{!
+minLength && maxLength set
+}}{{#minLength}}{{#maxLength}}@Size(min={{minLength}},max={{maxLength}}) {{/maxLength}}{{/minLength}}{{!
+minLength set, maxLength not
+}}{{#minLength}}{{^maxLength}}@Size(min={{minLength}}) {{/maxLength}}{{/minLength}}{{!
+minLength not set, maxLength set
+}}{{^minLength}}{{#maxLength}}@Size(max={{maxLength}}) {{/maxLength}}{{/minLength}}{{!
+@Size: minItems && maxItems set
+}}{{#minItems}}{{#maxItems}}@Size(min={{minItems}},max={{maxItems}}) {{/maxItems}}{{/minItems}}{{!
+@Size: minItems set, maxItems not
+}}{{#minItems}}{{^maxItems}}@Size(min={{minItems}}) {{/maxItems}}{{/minItems}}{{!
+@Size: minItems not set && maxItems set
+}}{{^minItems}}{{#maxItems}}@Size(max={{maxItems}}) {{/maxItems}}{{/minItems}}{{!
+check for integer or long / all others=decimal type with @Decimal*
+isInteger set
+}}{{#isInteger}}{{#minimum}}@Min({{minimum}}){{/minimum}}{{#maximum}} @Max({{maximum}}) {{/maximum}}{{/isInteger}}{{!
+isLong set
+}}{{#isLong}}{{#minimum}}@Min({{minimum}}L){{/minimum}}{{#maximum}} @Max({{maximum}}L) {{/maximum}}{{/isLong}}{{!
+Not Integer, not Long => we have a decimal value!
+}}{{^isInteger}}{{^isLong}}{{#minimum}}@DecimalMin("{{minimum}}"){{/minimum}}{{#maximum}} @DecimalMax("{{maximum}}") {{/maximum}}{{/isLong}}{{/isInteger}}
\ No newline at end of file
diff --git a/src/main/resources/handlebars/JavaVertXServer/enumClass.mustache b/src/main/resources/handlebars/JavaVertXServer/enumClass.mustache
new file mode 100644
index 0000000000..17981cd6d2
--- /dev/null
+++ b/src/main/resources/handlebars/JavaVertXServer/enumClass.mustache
@@ -0,0 +1,35 @@
+ /**
+ * {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{{description}}}{{/description}}
+ */
+ public enum {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} {
+ {{#allowableValues}}{{#enumVars}}{{{name}}}({{{value}}}){{^@last}},
+ {{/@last}}{{#@last}};{{/@last}}{{/enumVars}}{{/allowableValues}}
+
+ private {{{datatype}}} value;
+
+ {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}({{{datatype}}} value) {
+ this.value = value;
+ }
+
+ public {{{datatype}}} getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+
+ public static {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} fromValue({{{datatype}}} value) {
+ for ({{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) {
+ if (b.value.equals(value)) {
+ return b;
+ }
+ }
+ {{^errorOnUnknownEnum}}return null;{{/errorOnUnknownEnum}}{{#errorOnUnknownEnum}}throw new IllegalArgumentException("Unexpected value '" + value + "' for '{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}' enum.");{{/errorOnUnknownEnum}}
+ }
+ {{#useDataObject}}
+ public static {{{datatype}}} serialize({{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} enumValue) {
+ return enumValue.getValue();
+ }{{/useDataObject}}
+ }
diff --git a/src/main/resources/handlebars/JavaVertXServer/enumOuterClass.mustache b/src/main/resources/handlebars/JavaVertXServer/enumOuterClass.mustache
new file mode 100644
index 0000000000..f90c462a72
--- /dev/null
+++ b/src/main/resources/handlebars/JavaVertXServer/enumOuterClass.mustache
@@ -0,0 +1,35 @@
+/**
+ * {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{description}}{{/description}}
+ */
+public enum {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} {
+ {{#allowableValues}}{{#enumVars}}{{{name}}}({{{value}}}){{^@last}},
+ {{/@last}}{{#@last}};{{/@last}}{{/enumVars}}{{/allowableValues}}
+
+ private {{{dataType}}} value;
+
+ {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}({{{dataType}}} value) {
+ this.value = value;
+ }
+
+ public {{{dataType}}} getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+
+ public static {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} fromValue({{{dataType}}} value) {
+ for ({{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} b : {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.values()) {
+ if (b.value.equals(value)) {
+ return b;
+ }
+ }
+ {{^errorOnUnknownEnum}}return null;{{/errorOnUnknownEnum}}{{#errorOnUnknownEnum}}throw new IllegalArgumentException("Unexpected value '" + value + "' for '{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}' enum.");{{/errorOnUnknownEnum}}
+ }
+ {{#useDataObject}}
+ public static {{{dataType}}} serialize({{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} enumValue) {
+ return enumValue.getValue();
+ }{{/useDataObject}}
+}
diff --git a/src/main/resources/handlebars/JavaVertXServer/generatedAnnotation.mustache b/src/main/resources/handlebars/JavaVertXServer/generatedAnnotation.mustache
new file mode 100644
index 0000000000..ad17a426e9
--- /dev/null
+++ b/src/main/resources/handlebars/JavaVertXServer/generatedAnnotation.mustache
@@ -0,0 +1,3 @@
+{{^hideGenerationTimestamp}}
+@javax.annotation.Generated(value = "{{generatorClass}}", date = "{{generatedDate}}")
+{{/hideGenerationTimestamp}}
\ No newline at end of file
diff --git a/src/main/resources/handlebars/JavaVertXServer/json-mappers.mustache b/src/main/resources/handlebars/JavaVertXServer/json-mappers.mustache
new file mode 100644
index 0000000000..a15f23ed1a
--- /dev/null
+++ b/src/main/resources/handlebars/JavaVertXServer/json-mappers.mustache
@@ -0,0 +1,38 @@
+{{#models}}
+{{#model}}
+{{#isEnum}}
+{{modelPackage}}.{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.serializer={{modelPackage}}.{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}#serialize
+{{modelPackage}}.{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.deserializer={{modelPackage}}.{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}#fromValue
+{{/isEnum}}
+{{^isEnum}}
+ {{#vars}}
+ {{#isEnum}}
+{{modelPackage}}.{{classname}}.{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.serializer={{modelPackage}}.{{classname}}.{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}#serialize
+{{modelPackage}}.{{classname}}.{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.deserializer={{modelPackage}}.{{classname}}.{{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}#fromValue
+ {{/isEnum}}
+ {{/vars}}
+{{/isEnum}}
+{{/model}}
+{{/models}}
+{{#java8}}
+java.time.LocalDate.serializer={{modelPackage}}.DataObjectMapper#serializeLocalDate
+java.time.LocalDate.deserializer=java.time.LocalDate#parse
+java.time.LocalDateTime.serializer={{modelPackage}}.DataObjectMapper#serializeLocalDateTime
+java.time.LocalDateTime.deserializer=java.time.LocalDateTime#parse
+java.time.OffsetDateTime.serializer={{modelPackage}}.DataObjectMapper#serializeOffsetDateTime
+java.time.OffsetDateTime.deserializer=java.time.OffsetDateTime#parse
+{{/java8}}
+{{#threetenbp}}
+org.threeten.bp.Instant.serializer={{modelPackage}}.DataObjectMapper#serializeThreetenbpInstant
+org.threeten.bp.Instant.deserializer=org.threeten.bp.Instant#parse
+org.threeten.bp.OffsetDateTime.serializer={{modelPackage}}.DataObjectMapper#serializeThreetenbpOffsetDateTime
+org.threeten.bp.OffsetDateTime.deserializer=org.threeten.bp.OffsetDateTime#parse
+org.threeten.bp.ZonedDateTime.serializer={{modelPackage}}.DataObjectMapper#serializeThreetenbpZonedDateTime
+org.threeten.bp.ZonedDateTime.deserializer=org.threeten.bp.ZonedDateTime#parse
+{{/threetenbp}}
+{{#joda}}
+org.joda.time.LocalDate.serializer={{modelPackage}}.DataObjectMapper#serializeJodaLocalDate
+org.joda.time.LocalDate.deserializer=org.joda.time.LocalDate#parse
+org.joda.time.DateTime.serializer={{modelPackage}}.DataObjectMapper#serializeJodaDateTime
+org.joda.time.DateTime.deserializer=org.joda.time.DateTime#parse
+{{/joda}}
diff --git a/src/main/resources/handlebars/JavaVertXServer/model.mustache b/src/main/resources/handlebars/JavaVertXServer/model.mustache
new file mode 100644
index 0000000000..ee70e82101
--- /dev/null
+++ b/src/main/resources/handlebars/JavaVertXServer/model.mustache
@@ -0,0 +1,31 @@
+package {{package}};
+
+{{^x-is-composed-model}}
+import java.util.Objects;
+{{#imports}}import {{import}};
+{{/imports}}
+{{#serializableModel}}
+import java.io.Serializable;
+{{/serializableModel}}
+{{#useBeanValidation}}
+import javax.validation.Valid;
+import javax.validation.constraints.*;
+{{/useBeanValidation}}
+{{/x-is-composed-model}}
+import io.vertx.core.json.JsonObject;
+import io.vertx.codegen.annotations.DataObject;
+{{#models}}
+{{#model}}
+{{#isComposedModel}}
+{{>interface}}
+{{/isComposedModel}}
+{{^isComposedModel}}
+{{#isEnum}}
+{{>enumOuterClass}}
+{{/isEnum}}
+{{^isEnum}}
+{{>pojo}}
+{{/isEnum}}
+{{/isComposedModel}}
+{{/model}}
+{{/models}}
diff --git a/src/main/resources/handlebars/JavaVertXServer/package-info-model.mustache b/src/main/resources/handlebars/JavaVertXServer/package-info-model.mustache
new file mode 100644
index 0000000000..4d5382212e
--- /dev/null
+++ b/src/main/resources/handlebars/JavaVertXServer/package-info-model.mustache
@@ -0,0 +1,4 @@
+@ModuleGen(name = "model", groupPackage = "{{modelPackage}}")
+package {{modelPackage}};
+
+import io.vertx.codegen.annotations.ModuleGen;
diff --git a/src/main/resources/handlebars/JavaVertXServer/package-info-service.mustache b/src/main/resources/handlebars/JavaVertXServer/package-info-service.mustache
new file mode 100644
index 0000000000..6f049c3da3
--- /dev/null
+++ b/src/main/resources/handlebars/JavaVertXServer/package-info-service.mustache
@@ -0,0 +1,4 @@
+@ModuleGen(name = "service", groupPackage = "{{apiPackage}}", useFutures = true)
+package {{apiPackage}};
+
+import io.vertx.codegen.annotations.ModuleGen;
diff --git a/src/main/resources/handlebars/JavaVertXServer/pojo.mustache b/src/main/resources/handlebars/JavaVertXServer/pojo.mustache
new file mode 100644
index 0000000000..930b070c71
--- /dev/null
+++ b/src/main/resources/handlebars/JavaVertXServer/pojo.mustache
@@ -0,0 +1,147 @@
+/**
+ * {{#description}}{{.}}{{/description}}{{^description}}{{classname}}{{/description}}
+ */
+{{#useBeanValidation}}@Valid{{/useBeanValidation}}
+{{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}
+{{#notNullJacksonAnnotation}}@JsonInclude(JsonInclude.Include.NON_NULL){{/notNullJacksonAnnotation}}
+{{#useDataObject}}@DataObject(generateConverter = true, publicConverter = false){{/useDataObject}}
+
+public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#serializableModel}}implements Serializable {{#interfaceModels}}, {{classname}}{{^@last}}, {{/@last}}{{#@last}} {{/@last}}{{/interfaceModels}}{{/serializableModel}}{{^serializableModel}}{{#interfaceModels}}{{#@first}}implements {{/@first}}{{classname}}{{^@last}}, {{/@last}}{{#@last}}{{/@last}}{{/interfaceModels}}{{/serializableModel}} {
+{{#serializableModel}}
+ private static final long serialVersionUID = 1L;
+
+{{/serializableModel}}
+ {{#vars}}
+ {{#baseItems this}}
+ {{#isEnum}}
+{{>enumClass}}
+ {{/isEnum}}
+ {{/baseItems}}
+ {{#jackson}}
+ {{#vendorExtensions.x-is-discriminator-property}}
+ @JsonTypeId
+ {{/vendorExtensions.x-is-discriminator-property}}
+ {{^vendorExtensions.x-is-discriminator-property}}
+ @JsonProperty("{{baseName}}")
+ {{/vendorExtensions.x-is-discriminator-property}}
+ {{/jackson}}
+ {{#isContainer}}
+ {{#useBeanValidation}}@Valid{{/useBeanValidation}}
+ private {{{datatypeWithEnum}}} {{name}}{{#required}} = {{{defaultValue}}}{{/required}}{{^required}} = null{{/required}};
+ {{/isContainer}}
+ {{^isContainer}}
+ private {{{datatypeWithEnum}}} {{name}} = {{{defaultValue}}};
+ {{/isContainer}}
+
+ {{/vars}}
+{{#useDataObject}}
+ public {{classname}}(JsonObject json) {
+ {{#parent}}{{#hasVars}}super(json);{{/hasVars}}{{/parent}}
+ {{classname}}Converter.fromJson(json, this);
+ }
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ {{classname}}Converter.toJson(this, json);
+ return json;
+ }
+{{/useDataObject}}
+ {{#vars}}
+ public {{classname}} {{name}}({{{datatypeWithEnum}}} {{name}}) {
+ this.{{name}} = {{name}};
+ return this;
+ }
+ {{#isListContainer}}
+
+ public {{classname}} add{{nameInCamelCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) {
+ {{^required}}
+ if (this.{{name}} == null) {
+ this.{{name}} = {{{defaultValue}}};
+ }
+ {{/required}}
+ this.{{name}}.add({{name}}Item);
+ return this;
+ }
+ {{/isListContainer}}
+ {{#isMapContainer}}
+
+ public {{classname}} put{{nameInCamelCase}}Item(String key, {{{items.datatypeWithEnum}}} {{name}}Item) {
+ {{^required}}
+ if (this.{{name}} == null) {
+ this.{{name}} = {{{defaultValue}}};
+ }
+ {{/required}}
+ this.{{name}}.put(key, {{name}}Item);
+ return this;
+ }
+ {{/isMapContainer}}
+
+ /**
+ {{#description}}
+ * {{{description}}}
+ {{/description}}
+ {{^description}}
+ * Get {{name}}
+ {{/description}}
+ {{#minimum}}
+ * minimum: {{minimum}}
+ {{/minimum}}
+ {{#maximum}}
+ * maximum: {{maximum}}
+ {{/maximum}}
+ * @return {{name}}
+ **/
+ {{#vendorExtensions.extraAnnotation}}
+ {{{vendorExtensions.extraAnnotation}}}
+ {{/vendorExtensions.extraAnnotation}}
+ {{#useBeanValidation}}{{>beanValidation}}{{/useBeanValidation}} public {{{datatypeWithEnum}}} {{getter}}() {
+ return {{name}};
+ }
+
+ public void {{setter}}({{{datatypeWithEnum}}} {{name}}) {
+ this.{{name}} = {{name}};
+ }
+
+ {{/vars}}
+
+ @Override
+ public boolean equals(java.lang.Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }{{#hasVars}}
+ {{classname}} {{classVarName}} = ({{classname}}) o;
+ return {{#vars}}Objects.equals(this.{{name}}, {{classVarName}}.{{name}}){{#hasMore}} &&
+ {{/hasMore}}{{/vars}}{{#parent}} &&
+ super.equals(o){{/parent}};{{/hasVars}}{{^hasVars}}
+ return true;{{/hasVars}}
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash({{#vars}}{{name}}{{#hasMore}}, {{/hasMore}}{{/vars}}{{#parent}}{{#hasVars}}, {{/hasVars}}super.hashCode(){{/parent}});
+ }
+
+ @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 String toIndentedString(java.lang.Object o) {
+ if (o == null) {
+ return "null";
+ }
+ return o.toString().replace("\n", "\n ");
+ }
+}
diff --git a/src/main/resources/handlebars/JavaVertXServer/pom.mustache b/src/main/resources/handlebars/JavaVertXServer/pom.mustache
new file mode 100644
index 0000000000..f685666303
--- /dev/null
+++ b/src/main/resources/handlebars/JavaVertXServer/pom.mustache
@@ -0,0 +1,154 @@
+
+ 4.0.0
+
+ {{groupId}}
+ {{artifactId}}
+ {{artifactVersion}}
+ jar
+
+ {{appName}}
+
+
+ UTF-8
+ 1.8
+ 5.8.0
+ 4.1.4
+ 3.8.1
+ 3.2.4
+ 2.12.5
+ 1.7.32
+
+
+
+
+
+ io.vertx
+ vertx-web-openapi
+ ${vertx.version}
+
+
+ io.vertx
+ vertx-web-api-service
+ ${vertx.version}
+
+ {{#rxInterface}}
+
+ io.vertx
+ vertx-rx-java2
+ ${vertx.version}
+
+ {{/rxInterface}}
+
+ org.slf4j
+ slf4j-api
+ ${slf4j-version}
+
+
+
+ io.vertx
+ vertx-codegen
+ ${vertx.version}
+ provided
+
+
+
+ io.vertx
+ vertx-codegen
+ ${vertx.version}
+ processor
+
+
+ {{#java8}}
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ ${jackson.version}
+
+ {{/java8}}
+ {{#joda}}
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-joda
+ ${jackson.version}
+
+ {{/joda}}
+ {{#threetenbp}}
+
+ com.github.joschi.jackson
+ jackson-datatype-threetenbp
+ ${jackson.version}
+
+ {{/threetenbp}}
+
+ {{#useBeanValidation}}
+
+
+ javax.validation
+ validation-api
+
+ {{/useBeanValidation}}
+ {{#notNullJacksonAnnotation}}
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ 2.10.1
+
+ {{/notNullJacksonAnnotation}}
+
+
+ io.vertx
+ vertx-junit5
+ ${vertx.version}
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ ${jupiter.version}
+ test
+
+
+
+
+
+
+
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+
+ ${java.version}
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ ${maven-shade-plugin.version}
+
+
+ package
+
+ shade
+
+
+
+
+
+ io.vertx.core.Launcher
+ {{rootPackage}}.MainApiVerticle
+
+
+
+
+ ${project.build.directory}/${project.artifactId}-${project.version}-fat.jar
+
+
+
+
+
+
+
diff --git a/src/main/resources/handlebars/JavaVertXServer/typeInfoAnnotation.mustache b/src/main/resources/handlebars/JavaVertXServer/typeInfoAnnotation.mustache
new file mode 100644
index 0000000000..f3ed515976
--- /dev/null
+++ b/src/main/resources/handlebars/JavaVertXServer/typeInfoAnnotation.mustache
@@ -0,0 +1,14 @@
+{{#jackson}}
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "{{discriminator.propertyName}}", visible = true )
+@JsonSubTypes({
+ {{#if discriminator.mapping}}
+ {{#each discriminator.mapping}}
+ @JsonSubTypes.Type(value = {{this}}.class, name = "{{@key}}"),
+ {{/each}}
+ {{else}}
+ {{#children}}
+ @JsonSubTypes.Type(value = {{classname}}.class, name = "{{name}}"),
+ {{/children}}
+ {{/if}}
+})
+{{/jackson}}
\ No newline at end of file
diff --git a/src/test/java/io/swagger/codegen/v3/generators/java/JavaVertxServerGeneratorCodegenTest.java b/src/test/java/io/swagger/codegen/v3/generators/java/JavaVertxServerGeneratorCodegenTest.java
new file mode 100644
index 0000000000..5fbd853b2f
--- /dev/null
+++ b/src/test/java/io/swagger/codegen/v3/generators/java/JavaVertxServerGeneratorCodegenTest.java
@@ -0,0 +1,377 @@
+package io.swagger.codegen.v3.generators.java;
+
+import static io.swagger.codegen.v3.generators.java.JavaVertXServerCodegen.MOUNT_OPERATION_FROM_EXTENSIONS;
+import static io.swagger.codegen.v3.generators.java.JavaVertXServerCodegen.MOUNT_OPERATION_FROM_INTERFACE;
+import static io.swagger.codegen.v3.generators.java.JavaVertXServerCodegen.MOUNT_OPERATION_FROM_OPTION;
+import static io.swagger.codegen.v3.generators.java.JavaVertXServerCodegen.RX_INTERFACE_OPTION;
+import static io.swagger.codegen.v3.generators.java.JavaVertXServerCodegen.USE_DATAOBJECT_OPTION;
+
+import io.swagger.codegen.v3.ClientOptInput;
+import io.swagger.codegen.v3.CodegenArgument;
+import io.swagger.codegen.v3.CodegenConstants;
+import io.swagger.codegen.v3.DefaultGenerator;
+import io.swagger.codegen.v3.config.CodegenConfigurator;
+import io.swagger.codegen.v3.generators.AbstractCodegenTest;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.io.FileUtils;
+import org.junit.Assert;
+import org.junit.rules.TemporaryFolder;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class JavaVertxServerGeneratorCodegenTest extends AbstractCodegenTest {
+
+ static final String PACKAGE_INFO = "package-info.java";
+
+ static final List SERVICE_AS_EXTENSION_FILENAMES = Arrays.asList("GetOrderByIdAddressApi.java",
+ "MyFeedPetAddressApi.java", "MyUploadFileAddressApi.java", "DeleteOrderAddressApi.java",
+ "MyFindPetsByStatusAddressApi.java", "PlaceOrderAddressApi.java",
+ "MyPetAddressApi.java", "UserAddressApi.java", "MyPetIdAddressApi.java",
+ "GetInventoryAddressApi.java", "PetAddressApi.java", "MyFindByTagsAddressApi.java",
+ "TestAddressApi.java", "MyGetPetByIdAddressApi.java"
+ );
+
+ static final Map SERVICE_AS_INTERFACE = new HashMap() {{
+ put("DefaultApi.java", "DefaultApi");
+ put("PetApi.java", "PetApi");
+ put("StoreApi.java", "StoreApi");
+ put("UserApi.java", "UserApi");
+ }};
+
+ private TemporaryFolder folder = null;
+
+ @BeforeMethod
+ public void setUp() throws Exception {
+ folder = new TemporaryFolder();
+ folder.create();
+ }
+
+ @AfterMethod
+ public void tearDown() {
+ folder.delete();
+ }
+
+ @Test(description = "verify that main verticle, openapi verticle and service are written as expected (OAS 2.x & web-api-service)")
+ public void testUseOas2AndWebApiService() throws Exception {
+ final File output = folder.getRoot();
+
+ final CodegenConfigurator configurator = new CodegenConfigurator()
+ .setLang("java-vertx")
+ .setInputSpecURL("src/test/resources/3_0_0/petstore-vertx.yaml")
+ .setOutputDir(output.getAbsolutePath())
+ .addAdditionalProperty(USE_DATAOBJECT_OPTION, true)
+ .addAdditionalProperty(MOUNT_OPERATION_FROM_OPTION, MOUNT_OPERATION_FROM_EXTENSIONS);
+
+ configurator.setCodegenArguments(Collections.singletonList(
+ new CodegenArgument()
+ .option(CodegenConstants.USE_OAS2_OPTION)
+ .type("boolean")
+ .value(Boolean.TRUE.toString())));
+
+ final ClientOptInput clientOptInput = configurator.toClientOptInput();
+ new DefaultGenerator().opts(clientOptInput).generate();
+
+ final File petControllerFile = new File(output,
+ "/src/main/java/io/swagger/server/api/MainApiVerticle.java");
+ String content = FileUtils.readFileToString(petControllerFile);
+
+ Assert.assertTrue(content.contains(
+ "vertx.deployVerticle(\"io.swagger.server.api.verticle.SwaggerPetstoreVerticle\")"));
+ Assert.assertTrue(content.contains("startPromise.fail(error);"));
+ Assert.assertTrue(content.contains("startPromise.complete();"));
+
+ final File petVerticleFile = new File(output,
+ "/src/main/java/io/swagger/server/api/verticle/SwaggerPetstoreVerticle.java");
+ content = FileUtils.readFileToString(petVerticleFile);
+
+ Assert.assertTrue(content.contains("RouterBuilder.create(this.vertx, \"openapi.yaml\")"));
+ Assert.assertTrue(content.contains("routerBuilder.mountServicesFromExtensions();"));
+ Assert.assertTrue(content.contains("router.mountSubRouter(\"/\", openapiRouter);"));
+ Assert.assertTrue(content.contains("onSuccess(server -> startPromise.complete())"));
+ Assert.assertTrue(content.contains("onFailure(startPromise::fail);"));
+
+ final File packageInfoModelFile = new File(output,
+ "/src/main/java/io/swagger/server/api/model/" + PACKAGE_INFO);
+ content = FileUtils.readFileToString(packageInfoModelFile);
+ Assert.assertTrue(content.contains("@ModuleGen(name = \"model\", groupPackage = \"io.swagger.server.api.model\")"));
+ Assert.assertTrue(content.contains("package io.swagger.server.api.model;"));
+ Assert.assertTrue(content.contains("import io.vertx.codegen.annotations.ModuleGen;"));
+
+ final File petServiceFiles = new File(output,
+ "/src/main/java/io/swagger/server/api/service");
+ for (File file : petServiceFiles.listFiles()) {
+ Assert.assertTrue(SERVICE_AS_EXTENSION_FILENAMES.contains(file.getName()) || PACKAGE_INFO.equals(file.getName()));
+ content = FileUtils.readFileToString(file);
+
+ if (PACKAGE_INFO.equals(file.getName())) {
+ Assert.assertTrue(content.contains("@ModuleGen(name = \"service\", groupPackage = \"io.swagger.server.api.service\", useFutures = true)"));
+ Assert.assertTrue(content.contains("package io.swagger.server.api.service;"));
+ Assert.assertTrue(content.contains("import io.vertx.codegen.annotations.ModuleGen;"));
+ } else {
+ Assert.assertTrue(content.contains("@WebApiServiceGen"));
+ Assert.assertTrue(content.contains("String WEBSERVICE_ADDRESS_"));
+ Assert.assertTrue(content.contains("Future"));
+ Assert.assertTrue(content.contains("ServiceRequest request);"));
+ }
+ }
+ }
+
+ @Test(description = "verify that main verticle, openapi verticle and service are written as expected (OAS 3.x & web-api-service)")
+ public void testUseOas3AndWebApiService() throws Exception {
+ final File output = folder.getRoot();
+
+ final CodegenConfigurator configurator = new CodegenConfigurator()
+ .setLang("java-vertx")
+ .setInputSpecURL("src/test/resources/3_0_0/petstore-vertx.yaml")
+ .setOutputDir(output.getAbsolutePath())
+ .addAdditionalProperty(USE_DATAOBJECT_OPTION, true)
+ .addAdditionalProperty(MOUNT_OPERATION_FROM_OPTION, MOUNT_OPERATION_FROM_EXTENSIONS);
+
+ final ClientOptInput clientOptInput = configurator.toClientOptInput();
+ new DefaultGenerator().opts(clientOptInput).generate();
+
+ final File petControllerFile = new File(output,
+ "/src/main/java/io/swagger/server/api/MainApiVerticle.java");
+ String content = FileUtils.readFileToString(petControllerFile);
+
+ Assert.assertTrue(content.contains(
+ "vertx.deployVerticle(\"io.swagger.server.api.verticle.SwaggerPetstoreVerticle\")"));
+ Assert.assertTrue(content.contains("startPromise.fail(error);"));
+ Assert.assertTrue(content.contains("startPromise.complete();"));
+
+ final File petVerticleFile = new File(output,
+ "/src/main/java/io/swagger/server/api/verticle/SwaggerPetstoreVerticle.java");
+ content = FileUtils.readFileToString(petVerticleFile);
+
+ Assert.assertTrue(
+ content.contains("RouterBuilder.create(this.vertx, \"openapi.yaml\")"));
+ Assert.assertTrue(content.contains("routerBuilder.mountServicesFromExtensions();"));
+ Assert.assertTrue(content.contains("router.mountSubRouter(\"/\", openapiRouter);"));
+ Assert.assertTrue(content.contains("onSuccess(server -> startPromise.complete())"));
+ Assert.assertTrue(content.contains("onFailure(startPromise::fail);"));
+
+ final File packageInfoModelFile = new File(output,
+ "/src/main/java/io/swagger/server/api/model/" + PACKAGE_INFO);
+ content = FileUtils.readFileToString(packageInfoModelFile);
+ Assert.assertTrue(content.contains("@ModuleGen(name = \"model\", groupPackage = \"io.swagger.server.api.model\")"));
+ Assert.assertTrue(content.contains("package io.swagger.server.api.model;"));
+ Assert.assertTrue(content.contains("import io.vertx.codegen.annotations.ModuleGen;"));
+
+ final File petServiceFiles = new File(output,
+ "/src/main/java/io/swagger/server/api/service");
+ for (File file : petServiceFiles.listFiles()) {
+ Assert.assertTrue(SERVICE_AS_EXTENSION_FILENAMES.contains(file.getName()) || PACKAGE_INFO.equals(file.getName()));
+ content = FileUtils.readFileToString(file);
+
+ if (PACKAGE_INFO.equals(file.getName())) {
+ Assert.assertTrue(content.contains("@ModuleGen(name = \"service\", groupPackage = \"io.swagger.server.api.service\", useFutures = true)"));
+ Assert.assertTrue(content.contains("package io.swagger.server.api.service;"));
+ Assert.assertTrue(content.contains("import io.vertx.codegen.annotations.ModuleGen;"));
+ } else {
+ Assert.assertTrue(content.contains("@WebApiServiceGen"));
+ Assert.assertTrue(content.contains("String WEBSERVICE_ADDRESS_"));
+ Assert.assertTrue(content.contains("Future"));
+ Assert.assertTrue(content.contains("ServiceRequest request);"));
+ }
+ }
+ }
+
+ @Test(description = "verify that main verticle, openapi verticle and service are written as expected (OAS 3.x & web-api-service)")
+ public void testRxUseOas3AndWebApiService() throws Exception {
+ final File output = folder.getRoot();
+
+ final CodegenConfigurator configurator = new CodegenConfigurator()
+ .setLang("java-vertx")
+ .setInputSpecURL("src/test/resources/3_0_0/petstore-vertx.yaml")
+ .setOutputDir(output.getAbsolutePath())
+ .addAdditionalProperty(USE_DATAOBJECT_OPTION, true)
+ .addAdditionalProperty(MOUNT_OPERATION_FROM_OPTION, MOUNT_OPERATION_FROM_EXTENSIONS)
+ .addAdditionalProperty(RX_INTERFACE_OPTION, true);
+
+ final List arguments = new ArrayList<>();
+ arguments.add(new CodegenArgument()
+ .option(RX_INTERFACE_OPTION)
+ .type("boolean")
+ .value(Boolean.TRUE.toString()));
+ configurator.setCodegenArguments(arguments);
+
+ final ClientOptInput clientOptInput = configurator.toClientOptInput();
+ new DefaultGenerator().opts(clientOptInput).generate();
+
+ final File petControllerFile = new File(output,
+ "/src/main/java/io/swagger/server/api/MainApiVerticle.java");
+ String content = FileUtils.readFileToString(petControllerFile);
+
+ Assert.assertTrue(content.contains(
+ "vertx.rxDeployVerticle(\"io.swagger.server.api.verticle.SwaggerPetstoreVerticle\")"));
+ Assert.assertFalse(content.contains("startPromise.fail(error);"));
+ Assert.assertFalse(content.contains("startPromise.complete();"));
+ Assert.assertTrue(content.contains("ignoreElement();"));
+
+ final File petVerticleFile = new File(output,
+ "/src/main/java/io/swagger/server/api/verticle/SwaggerPetstoreVerticle.java");
+ content = FileUtils.readFileToString(petVerticleFile);
+
+ Assert.assertTrue(content.contains("return RouterBuilder.rxCreate(this.vertx, \"openapi.yaml\")"));
+ Assert.assertTrue(content.contains("routerBuilder.mountServicesFromExtensions();"));
+ Assert.assertTrue(content.contains("router.mountSubRouter(\"/\", openapiRouter);"));
+ Assert.assertFalse(content.contains("onSuccess(server -> startPromise.complete())"));
+ Assert.assertFalse(content.contains("onFailure(startPromise::fail);"));
+ Assert.assertTrue(content.contains("rxStart()"));
+ Assert.assertTrue(content.contains("rxStop()"));
+ Assert.assertTrue(content.contains("rxListen()"));
+ Assert.assertTrue(content.contains("ignoreElement();"));
+
+ final File packageInfoModelFile = new File(output,
+ "/src/main/java/io/swagger/server/api/model/" + PACKAGE_INFO);
+ content = FileUtils.readFileToString(packageInfoModelFile);
+ Assert.assertTrue(content.contains("@ModuleGen(name = \"model\", groupPackage = \"io.swagger.server.api.model\")"));
+ Assert.assertTrue(content.contains("package io.swagger.server.api.model;"));
+ Assert.assertTrue(content.contains("import io.vertx.codegen.annotations.ModuleGen;"));
+
+ final File petServiceFiles = new File(output,
+ "/src/main/java/io/swagger/server/api/service");
+ for (File file : petServiceFiles.listFiles()) {
+ Assert.assertTrue(SERVICE_AS_EXTENSION_FILENAMES.contains(file.getName()) || PACKAGE_INFO.equals(file.getName()));
+ content = FileUtils.readFileToString(file);
+
+ if (PACKAGE_INFO.equals(file.getName())) {
+ Assert.assertTrue(content.contains("@ModuleGen(name = \"service\", groupPackage = \"io.swagger.server.api.service\", useFutures = true)"));
+ Assert.assertTrue(content.contains("package io.swagger.server.api.service;"));
+ Assert.assertTrue(content.contains("import io.vertx.codegen.annotations.ModuleGen;"));
+ } else {
+ Assert.assertTrue(content.contains("@WebApiServiceGen"));
+ Assert.assertTrue(content.contains("String WEBSERVICE_ADDRESS_"));
+ Assert.assertTrue(content.contains("Future"));
+ Assert.assertTrue(content.contains("ServiceRequest request);"));
+ }
+
+ }
+
+ }
+
+ @Test(description = "verify that main verticle, openapi verticle and service are written as expected (OAS 3.x & mount from interface)")
+ public void testUseOas3AndMountFromInterface() throws Exception {
+ final File output = folder.getRoot();
+
+ final CodegenConfigurator configurator = new CodegenConfigurator()
+ .setLang("java-vertx")
+ .setInputSpecURL("src/test/resources/3_0_0/petstore-vertx.yaml")
+ .setOutputDir(output.getAbsolutePath())
+ .addAdditionalProperty(USE_DATAOBJECT_OPTION, true)
+ .addAdditionalProperty(MOUNT_OPERATION_FROM_OPTION, MOUNT_OPERATION_FROM_INTERFACE);
+
+ final ClientOptInput clientOptInput = configurator.toClientOptInput();
+ new DefaultGenerator().opts(clientOptInput).generate();
+
+ final File petControllerFile = new File(output,
+ "/src/main/java/io/swagger/server/api/MainApiVerticle.java");
+ String content = FileUtils.readFileToString(petControllerFile);
+
+ Assert.assertTrue(content.contains("vertx.deployVerticle(\"io.swagger.server.api.verticle.SwaggerPetstoreVerticle\")"));
+ Assert.assertTrue(content.contains("startPromise.fail(error);"));
+ Assert.assertTrue(content.contains("startPromise.complete();"));
+
+ final File petVerticleFile = new File(output,
+ "/src/main/java/io/swagger/server/api/verticle/SwaggerPetstoreVerticle.java");
+ content = FileUtils.readFileToString(petVerticleFile);
+
+ Assert.assertTrue(content.contains("RouterBuilder.create(this.vertx, \"openapi.yaml\")"));
+ Assert.assertFalse(content.contains("routerBuilder.mountServicesFromExtensions();"));
+ Assert.assertTrue(content.contains("routerBuilder.mountServiceInterface"));
+ Assert.assertTrue(content.contains("router.mountSubRouter(\"/\", openapiRouter);"));
+ Assert.assertTrue(content.contains("onSuccess(server -> startPromise.complete())"));
+ Assert.assertTrue(content.contains("onFailure(startPromise::fail);"));
+
+ final File packageInfoModelFile = new File(output,
+ "/src/main/java/io/swagger/server/api/model/" + PACKAGE_INFO);
+ content = FileUtils.readFileToString(packageInfoModelFile);
+ Assert.assertTrue(content.contains("@ModuleGen(name = \"model\", groupPackage = \"io.swagger.server.api.model\")"));
+ Assert.assertTrue(content.contains("package io.swagger.server.api.model;"));
+ Assert.assertTrue(content.contains("import io.vertx.codegen.annotations.ModuleGen;"));
+
+ final File petServiceFiles = new File(output,
+ "/src/main/java/io/swagger/server/api/service");
+ for (File file : petServiceFiles.listFiles()) {
+ Assert.assertTrue(SERVICE_AS_INTERFACE.containsKey(file.getName()) || PACKAGE_INFO.equals(file.getName()));
+ content = FileUtils.readFileToString(file);
+
+ if (PACKAGE_INFO.equals(file.getName())) {
+ Assert.assertTrue(content.contains("@ModuleGen(name = \"service\", groupPackage = \"io.swagger.server.api.service\", useFutures = true)"));
+ Assert.assertTrue(content.contains("package io.swagger.server.api.service;"));
+ Assert.assertTrue(content.contains("import io.vertx.codegen.annotations.ModuleGen;"));
+ } else {
+ String api = SERVICE_AS_INTERFACE.getOrDefault(file.getName(), PACKAGE_INFO);
+ Assert.assertFalse(content.contains("@WebApiServiceGen"));
+ Assert.assertTrue(content.contains("String WEBSERVICE_ADDRESS_" + api.toUpperCase()));
+ Assert.assertTrue(content.contains("Future"));
+ Assert.assertTrue(content.contains("ServiceRequest request);"));
+ }
+ }
+ }
+
+ @Test(description = "verify that main verticle, openapi verticle and service are written as expected (OAS 3.x & web-api-service & not dataobject)")
+ public void testUseOas3AndWebApiServiceAndNoDataobject() throws Exception {
+ final File output = folder.getRoot();
+
+ final CodegenConfigurator configurator = new CodegenConfigurator()
+ .setLang("java-vertx")
+ .setInputSpecURL("src/test/resources/3_0_0/petstore-vertx.yaml")
+ .setOutputDir(output.getAbsolutePath())
+ .addAdditionalProperty(USE_DATAOBJECT_OPTION, false)
+ .addAdditionalProperty(MOUNT_OPERATION_FROM_OPTION, MOUNT_OPERATION_FROM_EXTENSIONS);
+
+ final ClientOptInput clientOptInput = configurator.toClientOptInput();
+ new DefaultGenerator().opts(clientOptInput).generate();
+
+ final File petControllerFile = new File(output,
+ "/src/main/java/io/swagger/server/api/MainApiVerticle.java");
+ String content = FileUtils.readFileToString(petControllerFile);
+
+ Assert.assertTrue(content.contains(
+ "vertx.deployVerticle(\"io.swagger.server.api.verticle.SwaggerPetstoreVerticle\")"));
+ Assert.assertTrue(content.contains("startPromise.fail(error);"));
+ Assert.assertTrue(content.contains("startPromise.complete();"));
+
+ final File petVerticleFile = new File(output,
+ "/src/main/java/io/swagger/server/api/verticle/SwaggerPetstoreVerticle.java");
+ content = FileUtils.readFileToString(petVerticleFile);
+
+ Assert.assertTrue(
+ content.contains("RouterBuilder.create(this.vertx, \"openapi.yaml\")"));
+ Assert.assertTrue(content.contains("routerBuilder.mountServicesFromExtensions();"));
+ Assert.assertTrue(content.contains("router.mountSubRouter(\"/\", openapiRouter);"));
+ Assert.assertTrue(content.contains("onSuccess(server -> startPromise.complete())"));
+ Assert.assertTrue(content.contains("onFailure(startPromise::fail);"));
+
+ final File packageInfoModelFile = new File(output,
+ "/src/main/java/io/swagger/server/api/model/" + PACKAGE_INFO);
+ Assert.assertFalse(packageInfoModelFile.exists());
+
+ final File petServiceFiles = new File(output,
+ "/src/main/java/io/swagger/server/api/service");
+ for (File file : petServiceFiles.listFiles()) {
+ Assert.assertTrue(SERVICE_AS_EXTENSION_FILENAMES.contains(file.getName()) || PACKAGE_INFO.equals(file.getName()));
+ content = FileUtils.readFileToString(file);
+
+ if (PACKAGE_INFO.equals(file.getName())) {
+ Assert.assertTrue(content.contains("@ModuleGen(name = \"service\", groupPackage = \"io.swagger.server.api.service\", useFutures = true)"));
+ Assert.assertTrue(content.contains("package io.swagger.server.api.service;"));
+ Assert.assertTrue(content.contains("import io.vertx.codegen.annotations.ModuleGen;"));
+ } else {
+ Assert.assertTrue(content.contains("@WebApiServiceGen"));
+ Assert.assertTrue(content.contains("String WEBSERVICE_ADDRESS_"));
+ Assert.assertTrue(content.contains("Future"));
+ Assert.assertTrue(content.contains("RequestParameter body, ServiceRequest request);"));
+ }
+ }
+ }
+
+}
diff --git a/src/test/resources/3_0_0/petstore-vertx.yaml b/src/test/resources/3_0_0/petstore-vertx.yaml
new file mode 100644
index 0000000000..73ffa78fbc
--- /dev/null
+++ b/src/test/resources/3_0_0/petstore-vertx.yaml
@@ -0,0 +1,798 @@
+openapi: 3.0.1
+servers:
+ - url: 'http://localhost:8080'
+ - url: 'https://localhost:8080'
+info:
+ description: |
+ This is a sample Petstore server. You can find
+ out more about Swagger at
+ [http://swagger.io](http://swagger.io) or on
+ [irc.freenode.net, #swagger](http://swagger.io/irc/).
+ version: "1.0.0"
+ title: Swagger Petstore
+ termsOfService: 'http://swagger.io/terms/'
+ contact:
+ email: apiteam@swagger.io
+ license:
+ name: Apache 2.0
+ url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+tags:
+ - name: pet
+ description: Everything about your Pets
+ externalDocs:
+ description: Find out more
+ url: 'http://swagger.io'
+ - name: store
+ description: Access to Petstore orders
+ - name: user
+ description: Operations about user
+ externalDocs:
+ description: Find out more about our store
+ url: 'http://swagger.io'
+paths:
+ /pet:
+ x-vertx-event-bus: "pet.address"
+ post:
+ tags:
+ - pet
+ summary: Add a new pet to the store
+ operationId: addPet
+ responses:
+ '405':
+ description: Invalid input
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ requestBody:
+ $ref: '#/components/requestBodies/Pet'
+ x-vertx-event-bus: "myPet.address"
+ put:
+ tags:
+ - pet
+ summary: Update an existing pet
+ operationId: updatePet
+ responses:
+ '400':
+ description: Invalid ID supplied
+ '404':
+ description: Pet not found
+ '405':
+ description: Validation exception
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ requestBody:
+ $ref: '#/components/requestBodies/Pet'
+ x-vertx-event-bus:
+ method: "myUpdatePet"
+ /pet/findByStatus:
+ get:
+ tags:
+ - pet
+ summary: Finds Pets by status
+ description: Multiple status values can be provided with comma separated strings
+ operationId: findPetsByStatus
+ parameters:
+ - name: status
+ in: query
+ description: Status values that need to be considered for filter
+ required: true
+ explode: true
+ schema:
+ type: array
+ items:
+ type: string
+ enum:
+ - available
+ - pending
+ - sold
+ default: available
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Pet'
+ application/xml:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Pet'
+ '400':
+ description: Invalid status value
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ x-vertx-event-bus: "myFindPetsByStatus.address"
+ /pet/findByTags:
+ x-vertx-event-bus:
+ address: "myFindByTags.address"
+ get:
+ tags:
+ - pet
+ x-vertx-event-bus: "overrided"
+ summary: Finds Pets by tags
+ description: >-
+ Muliple tags can be provided with comma separated strings. Use\ \ tag1,
+ tag2, tag3 for testing.
+ operationId: findPetsByTags
+ parameters:
+ - name: tags
+ in: query
+ description: Tags to filter by
+ required: true
+ explode: true
+ schema:
+ type: array
+ items:
+ type: string
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Pet'
+ application/xml:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Pet'
+ '400':
+ description: Invalid tag value
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ deprecated: true
+ '/pet/{petId}':
+ x-vertx-event-bus:
+ address: "myPetId.address"
+ get:
+ tags:
+ - pet
+ x-vertx-event-bus:
+ address: "myGetPetById.address"
+ summary: Find pet by ID
+ description: Returns a single pet
+ operationId: getPetById
+ parameters:
+ - name: petId
+ in: path
+ description: ID of pet to return
+ required: true
+ schema:
+ type: integer
+ format: int64
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ application/xml:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ '400':
+ description: Invalid ID supplied
+ '404':
+ description: Pet not found
+ security:
+ - api_key: []
+ post:
+ tags:
+ - pet
+ x-vertx-event-bus: "overrided"
+ summary: Updates a pet in the store with form data
+ operationId: updatePetWithForm
+ parameters:
+ - name: petId
+ in: path
+ description: ID of pet that needs to be updated
+ required: true
+ schema:
+ type: integer
+ format: int64
+ responses:
+ '405':
+ description: Invalid input
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ name:
+ description: Updated name of the pet
+ type: string
+ status:
+ description: Updated status of the pet
+ type: string
+ delete:
+ tags:
+ - pet
+ x-vertx-event-bus: "overrided"
+ summary: Deletes a pet
+ operationId: deletePet
+ parameters:
+ - name: api_key
+ in: header
+ required: false
+ schema:
+ type: string
+ - name: petId
+ in: path
+ description: Pet id to delete
+ required: true
+ schema:
+ type: integer
+ format: int64
+ responses:
+ '400':
+ description: Invalid ID supplied
+ '404':
+ description: Pet not found
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+
+ /pet/feed/{petId}:
+ post:
+ tags:
+ - pet
+ x-vertx-event-bus: "myFeedPet.address"
+ summary: Find pet by ID
+ description: schedule pet feeding
+ operationId: feedPet
+ parameters:
+ - name: petId
+ in: path
+ description: ID of pet to return
+ required: true
+ schema:
+ type: integer
+ format: int64
+ - name: petType
+ in: query
+ description: type of food
+ required: true
+ schema:
+ type: string
+ - name: status
+ in: query
+ description: status
+ required: true
+ schema:
+ type: string
+ - name: sessionId
+ in: cookie
+ description: session id
+ required: true
+ schema:
+ type: string
+ - name: token
+ in: header
+ description: status
+ required: true
+ schema:
+ type: string
+ requestBody:
+ description: Pet object that needs to be added to the store
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ application/xml:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ required: true
+ responses:
+ 200:
+ description: successful operation
+
+ '/pet/{petId}/uploadImage':
+ post:
+ tags:
+ - pet
+ x-vertx-event-bus: "myUploadFile.address"
+ summary: uploads an image
+ operationId: uploadFile
+ parameters:
+ - name: petId
+ in: path
+ description: ID of pet to update
+ required: true
+ schema:
+ type: integer
+ format: int64
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ApiResponse'
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ requestBody:
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ format: binary
+ /store/inventory:
+ get:
+ tags:
+ - store
+ x-vertx-event-bus: "getInventory.address"
+ summary: Returns pet inventories by status
+ description: Returns a map of status codes to quantities
+ operationId: getInventory
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ type: object
+ additionalProperties:
+ type: integer
+ format: int32
+ security:
+ - api_key: []
+ /store/order:
+ post:
+ tags:
+ - store
+ x-vertx-event-bus: "placeOrder.address"
+ summary: Place an order for a pet
+ operationId: placeOrder
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Order'
+ application/xml:
+ schema:
+ $ref: '#/components/schemas/Order'
+ '400':
+ description: Invalid Order
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Order'
+ description: order placed for purchasing the pet
+ required: true
+ '/store/order/{orderId}':
+ get:
+ tags:
+ - store
+ x-vertx-event-bus: "getOrderById.address"
+ summary: Find purchase order by ID
+ description: >-
+ For valid response try integer IDs with value >= 1 and <= 10.\ \ Other
+ values will generated exceptions
+ operationId: getOrderById
+ parameters:
+ - name: orderId
+ in: path
+ description: ID of pet that needs to be fetched
+ required: true
+ schema:
+ type: integer
+ format: int64
+ minimum: 1
+ maximum: 10
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Order'
+ application/xml:
+ schema:
+ $ref: '#/components/schemas/Order'
+ '400':
+ description: Invalid ID supplied
+ '404':
+ description: Order not found
+ delete:
+ tags:
+ - store
+ x-vertx-event-bus: "deleteOrder.address"
+ summary: Delete purchase order by ID
+ description: >-
+ For valid response try integer IDs with positive integer value.\ \
+ Negative or non-integer values will generate API errors
+ operationId: deleteOrder
+ parameters:
+ - name: orderId
+ in: path
+ description: ID of the order that needs to be deleted
+ required: true
+ schema:
+ type: integer
+ format: int64
+ minimum: 1
+ responses:
+ '400':
+ description: Invalid ID supplied
+ '404':
+ description: Order not found
+ /user:
+ post:
+ tags:
+ - user
+ x-vertx-event-bus: "user.address"
+ summary: Create user
+ description: This can only be done by the logged in user.
+ operationId: createUser
+ responses:
+ default:
+ description: successful operation
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/User'
+ description: Created user object
+ required: true
+ /user/createWithArray:
+ post:
+ tags:
+ - user
+ x-vertx-event-bus: "user.address"
+ summary: Creates list of users with given input array
+ operationId: createUsersWithArrayInput
+ responses:
+ default:
+ description: successful operation
+ requestBody:
+ $ref: '#/components/requestBodies/UserArray'
+ /user/createWithList:
+ post:
+ tags:
+ - user
+ x-vertx-event-bus: "user.address"
+ summary: Creates list of users with given input array
+ operationId: createUsersWithListInput
+ responses:
+ default:
+ description: successful operation
+ requestBody:
+ $ref: '#/components/requestBodies/UserArray'
+ /user/login:
+ get:
+ tags:
+ - user
+ x-vertx-event-bus: "user.address"
+ summary: Logs user into the system
+ operationId: loginUser
+ parameters:
+ - name: username
+ in: query
+ description: The user name for login
+ required: true
+ schema:
+ type: string
+ - name: password
+ in: query
+ description: The password for login in clear text
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: successful operation
+ headers:
+ X-Rate-Limit:
+ description: calls per hour allowed by the user
+ schema:
+ type: integer
+ format: int32
+ X-Expires-After:
+ description: date in UTC when token expires
+ schema:
+ type: string
+ format: date-time
+ content:
+ application/json:
+ schema:
+ type: string
+ application/xml:
+ schema:
+ type: string
+ '400':
+ description: Invalid username/password supplied
+ /user/logout:
+ get:
+ tags:
+ - user
+ x-vertx-event-bus: "user.address"
+ summary: Logs out current logged in user session
+ operationId: logoutUser
+ responses:
+ default:
+ description: successful operation
+ '/user/{username}':
+ get:
+ tags:
+ - user
+ x-vertx-event-bus: "user.address"
+ summary: Get user by user name
+ operationId: getUserByName
+ parameters:
+ - name: username
+ in: path
+ description: The name that needs to be fetched. Use user1 for testing.
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/User'
+ application/xml:
+ schema:
+ $ref: '#/components/schemas/User'
+ '400':
+ description: Invalid username supplied
+ '404':
+ description: User not found
+ put:
+ tags:
+ - user
+ x-vertx-event-bus: "user.address"
+ summary: Updated user
+ description: This can only be done by the logged in user.
+ operationId: updateUser
+ parameters:
+ - name: username
+ in: path
+ description: name that need to be updated
+ required: true
+ schema:
+ type: string
+ responses:
+ '400':
+ description: Invalid user supplied
+ '404':
+ description: User not found
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/User'
+ description: Updated user object
+ required: true
+ delete:
+ tags:
+ - user
+ x-vertx-event-bus: "user.address"
+ summary: Delete user
+ description: This can only be done by the logged in user.
+ operationId: deleteUser
+ parameters:
+ - name: username
+ in: path
+ description: The name that needs to be deleted
+ required: true
+ schema:
+ type: string
+ responses:
+ '400':
+ description: Invalid username supplied
+ '404':
+ description: User not found
+ /test:
+ get:
+ security:
+ - bearer: []
+ operationId: testMethod
+ x-vertx-event-bus: "test.address"
+ responses:
+ 200:
+ description: peticion realizada con exito
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Test'
+externalDocs:
+ description: Find out more about Swagger
+ url: 'http://swagger.io'
+components:
+ schemas:
+ Order:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ petId:
+ type: integer
+ format: int64
+ quantity:
+ type: integer
+ format: int32
+ shipDate:
+ type: string
+ format: date-time
+ status:
+ type: string
+ description: Order Status
+ enum:
+ - placed
+ - approved
+ - delivered
+ complete:
+ type: boolean
+ default: false
+ xml:
+ name: Order
+ Category:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ name:
+ type: string
+ subcategories:
+ type: array
+ items:
+ $ref: '#/components/schemas/Category'
+ example:
+ id: 100
+ name: Mammal
+ subcategories:
+ - id: 110
+ name: Yinotheria
+ - id: 120
+ name: Theriiformes
+ xml:
+ name: Category
+ User:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ username:
+ type: string
+ firstName:
+ type: string
+ lastName:
+ type: string
+ email:
+ type: string
+ password:
+ type: string
+ phone:
+ type: string
+ userStatus:
+ type: integer
+ format: int32
+ description: User Status
+ xml:
+ name: User
+ Tag:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ name:
+ type: string
+ xml:
+ name: Tag
+ Pet:
+ type: object
+ required:
+ - name
+ - photoUrls
+ properties:
+ id:
+ type: integer
+ format: int64
+ category:
+ $ref: '#/components/schemas/Category'
+ name:
+ type: string
+ example: doggie
+ photoUrls:
+ type: array
+ xml:
+ name: photoUrl
+ wrapped: true
+ items:
+ type: string
+ tags:
+ type: array
+ xml:
+ name: tag
+ wrapped: true
+ items:
+ $ref: '#/components/schemas/Tag'
+ status:
+ type: string
+ description: pet status in the store
+ enum:
+ - available
+ - pending
+ - sold
+ xml:
+ name: Pet
+ ApiResponse:
+ type: object
+ properties:
+ code:
+ type: integer
+ format: int32
+ type:
+ type: string
+ message:
+ type: string
+ Test:
+ type: string
+ example: ""
+ requestBodies:
+ Pet:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ application/xml:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ description: Pet object that needs to be added to the store
+ required: true
+ UserArray:
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/User'
+ description: List of user object
+ required: true
+ securitySchemes:
+ bearer:
+ type: http
+ scheme: bearer
+ petstore_auth:
+ type: oauth2
+ flows:
+ implicit:
+ authorizationUrl: 'http://petstore.swagger.io/oauth/dialog'
+ scopes:
+ 'write:pets': modify pets in your account
+ 'read:pets': read your pets
+ api_key:
+ type: apiKey
+ name: api_key
+ in: header