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 : + * + */ + 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} + ${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