diff --git a/README.md b/README.md index bc97a9718..b8c13a1b0 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,11 @@ The project is very much Work In Progress and will be published on maven central # Release Notes BOAT is still under development and subject to change. +## 0.14.3 + +* *Maven Plugin* + * Added new goal `boat:transform`; see the description in the [plugin documentation](boat-maven-plugin/README.md#boattransform). + ## 0.14.2 * *Angular Generator* * Added support for Angular version ranges in peer dependencies @@ -39,6 +44,7 @@ BOAT is still under development and subject to change. * Check prefix for paths should contain version. * Enabled rules. * Use Standard HTTP Status Codes. + ## 0.12.0 * *General* * Improved code quality @@ -140,13 +146,11 @@ BOAT is still under development and subject to change. * ability to resolve references like #/components/schemas/myObject/items or #/components/schemas/myObject/properties/embeddedObject * simple fix to avoid npe in StaticHtml2Generation escaping response message. - ## 0.5.0 * Add DereferenceComponentsPropertiesTransformer (that does a bit extra) * Fix recursive referencing in UnAliasTransformer - ## 0.4.0 * Added bundle skip * Changed numbering scheme @@ -626,6 +630,7 @@ For the `spring` generator, the additional configuration options are: | `useClassLevelBeanValidation` | Adds @Validated annotation to API interfaces (Default: false) | | `useLombokAnnotations` | Use Lombok annotations to generate properties accessors and `hashCode`/`equals` methods (Default: false) | | `addServletRequest` | Adds ServletRequest objects to API method definitions (Default: false) | +| `addBindingResult` | Adds BindingResult to Api method definitions' request bodies if UseBeanValidation true, for this to be effective you must configure UseBeanValidation, this is not done automatically (Default: false)| | `implicitHeaders` | Skip header parameters in the generated API methods using @ApiImplicitParams annotation. (Default: false) | | `swaggerDocketConfig` | Generate Spring OpenAPI Docket configuration class. (Default: false) | | `apiFirst` | Generate the API from the OAI spec at server compile time (API first approach) (Default: false) | diff --git a/boat-engine/pom.xml b/boat-engine/pom.xml index 6995811e2..d2db98496 100644 --- a/boat-engine/pom.xml +++ b/boat-engine/pom.xml @@ -5,7 +5,7 @@ com.backbase.oss backbase-openapi-tools - 0.14.2-SNAPSHOT + 0.14.3-SNAPSHOT boat-engine jar diff --git a/boat-engine/src/main/java/com/backbase/oss/boat/loader/OpenAPILoader.java b/boat-engine/src/main/java/com/backbase/oss/boat/loader/OpenAPILoader.java index 2b19e4773..fba68bf28 100644 --- a/boat-engine/src/main/java/com/backbase/oss/boat/loader/OpenAPILoader.java +++ b/boat-engine/src/main/java/com/backbase/oss/boat/loader/OpenAPILoader.java @@ -2,9 +2,12 @@ import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.parser.OpenAPIV3Parser; +import io.swagger.v3.parser.core.models.AuthorizationValue; import io.swagger.v3.parser.core.models.ParseOptions; import io.swagger.v3.parser.core.models.SwaggerParseResult; import java.io.File; +import java.util.List; + import lombok.extern.slf4j.Slf4j; @Slf4j @@ -55,6 +58,10 @@ public static OpenAPI load(File file, boolean resolveFully, boolean flatten) thr } public static OpenAPI load(String url, boolean resolveFully, boolean flatten) throws OpenAPILoaderException { + return load(url, resolveFully, flatten, null); + } + + public static OpenAPI load(String url, boolean resolveFully, boolean flatten, List auth) throws OpenAPILoaderException { log.debug("Reading OpenAPI from: {} resolveFully: {}", url, resolveFully); OpenAPIV3Parser openAPIParser = new OpenAPIV3Parser(); ParseOptions parseOptions = new ParseOptions(); @@ -64,7 +71,7 @@ public static OpenAPI load(String url, boolean resolveFully, boolean flatten) th parseOptions.setFlattenComposedSchemas(true); parseOptions.setResolveCombinators(true); - SwaggerParseResult swaggerParseResult = openAPIParser.readLocation(url, null, parseOptions); + SwaggerParseResult swaggerParseResult = openAPIParser.readLocation(url, auth, parseOptions); if (swaggerParseResult.getOpenAPI() == null) { log.error("Could not load OpenAPI from : {} \n{}", url, String.join("\t\n", swaggerParseResult.getMessages())); throw new OpenAPILoaderException("Could not load open api from :" + url, swaggerParseResult.getMessages()); diff --git a/boat-engine/src/main/java/com/backbase/oss/boat/transformers/VendorExtensionFilter.java b/boat-engine/src/main/java/com/backbase/oss/boat/transformers/ExtensionFilter.java similarity index 67% rename from boat-engine/src/main/java/com/backbase/oss/boat/transformers/VendorExtensionFilter.java rename to boat-engine/src/main/java/com/backbase/oss/boat/transformers/ExtensionFilter.java index ef68d453e..af3993920 100644 --- a/boat-engine/src/main/java/com/backbase/oss/boat/transformers/VendorExtensionFilter.java +++ b/boat-engine/src/main/java/com/backbase/oss/boat/transformers/ExtensionFilter.java @@ -1,31 +1,51 @@ package com.backbase.oss.boat.transformers; -import static java.util.Optional.ofNullable; -import static java.util.Spliterators.spliteratorUnknownSize; -import static java.util.stream.StreamSupport.stream; - import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ContainerNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Spliterator; import io.swagger.v3.core.util.Yaml; import io.swagger.v3.oas.models.OpenAPI; +import lombok.Getter; import lombok.NonNull; +import lombok.Setter; import lombok.SneakyThrows; +import static java.util.Collections.emptyList; +import static java.util.Optional.ofNullable; +import static java.util.Spliterators.spliteratorUnknownSize; +import static java.util.stream.Collectors.toSet; +import static java.util.stream.StreamSupport.stream; + @SuppressWarnings("java:S3740") -public class VendorExtensionFilter implements Transformer { +@Getter +@Setter +public class ExtensionFilter implements Transformer { + + private List remove = emptyList(); @Override public @NonNull OpenAPI transform(@NonNull OpenAPI openAPI, @NonNull Map options) { - return ofNullable(options.get("remove")) - .map(remove -> transform(openAPI, (Collection) remove)) - .orElse(openAPI); + List extensions = new ArrayList<>(remove); + + ofNullable(options.get("remove")) + .map(Collection.class::cast) + .ifPresent(extensions::addAll); + + extensions.addAll( + extensions.stream() + .filter(s -> !s.startsWith("x-")) + .map(s -> "x-" + s) + .collect(toSet())); + + return extensions.isEmpty() ? openAPI : transform(openAPI, extensions); } @SneakyThrows diff --git a/boat-engine/src/main/java/com/backbase/oss/boat/transformers/SpecVersionTransformer.java b/boat-engine/src/main/java/com/backbase/oss/boat/transformers/SetVersion.java similarity index 51% rename from boat-engine/src/main/java/com/backbase/oss/boat/transformers/SpecVersionTransformer.java rename to boat-engine/src/main/java/com/backbase/oss/boat/transformers/SetVersion.java index 2d7c452c4..e525a3900 100644 --- a/boat-engine/src/main/java/com/backbase/oss/boat/transformers/SpecVersionTransformer.java +++ b/boat-engine/src/main/java/com/backbase/oss/boat/transformers/SetVersion.java @@ -2,17 +2,24 @@ import java.util.Map; -import static java.util.Optional.*; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.NonNull; -import lombok.RequiredArgsConstructor; +import lombok.Setter; -@RequiredArgsConstructor -public class SpecVersionTransformer implements Transformer { +import static java.util.Optional.ofNullable; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +public class SetVersion implements Transformer { @NonNull - private final String version; + private String version; @Override public OpenAPI transform(OpenAPI openAPI, Map options) { @@ -25,6 +32,12 @@ public OpenAPI transform(OpenAPI openAPI, Map options) { return openAPI; } -} + /** + * Default setter, used at creation from POM configuration. + */ + public void set(String version) { + setVersion(version); + } +} diff --git a/boat-engine/src/test/java/com/backbase/oss/boat/transformers/VendorExtensionFilterTests.java b/boat-engine/src/test/java/com/backbase/oss/boat/transformers/ExtensionFilterTests.java similarity index 92% rename from boat-engine/src/test/java/com/backbase/oss/boat/transformers/VendorExtensionFilterTests.java rename to boat-engine/src/test/java/com/backbase/oss/boat/transformers/ExtensionFilterTests.java index 8c28e5d15..dfec5883e 100644 --- a/boat-engine/src/test/java/com/backbase/oss/boat/transformers/VendorExtensionFilterTests.java +++ b/boat-engine/src/test/java/com/backbase/oss/boat/transformers/ExtensionFilterTests.java @@ -17,11 +17,11 @@ import org.junit.jupiter.api.Test; @Slf4j -class VendorExtensionFilterTests { +class ExtensionFilterTests { @Test void run() throws Throwable { - Transformer trn = new VendorExtensionFilter(); + Transformer trn = new ExtensionFilter(); OpenAPI api1 = OpenAPILoader.load(new File("src/test/resources/openapi/extension-filter/openapi.yaml")); OpenAPI api2 = trn.transform(api1, singletonMap("remove", singleton("x-remove"))); diff --git a/boat-maven-plugin/README.md b/boat-maven-plugin/README.md index b3ab2938f..baa931737 100644 --- a/boat-maven-plugin/README.md +++ b/boat-maven-plugin/README.md @@ -3,188 +3,189 @@ The `boat` plugin has multiple goals: -- `export` +## boat:export - Generates client/server code from a OpenAPI json/yaml - definition. Finds files name `api.raml`, `client-api.raml` or `service-api.raml`. Processes these files (and the - json schemes they refer to) to produce `open-api.yaml` files in the output directory. +Generates client/server code from a OpenAPI json/yaml definition. Finds files name `api.raml`, `client-api.raml` or `service-api.raml`. +Processes these files (and the json schemes they refer to) to produce `open-api.yaml` files in the output directory. -- `export-bom` +## boat:export-bom - Converts all RAML spec dependencies to OpenAPI Specs. See integration tests for examples +Converts all RAML spec dependencies to OpenAPI Specs. See integration tests for examples -- `export-dep` +## boat:export-dep - Exports project dependencies where the ArtifactId ends with. See integration tests for examples - '-spec'. +Exports project dependencies where the ArtifactId ends with. See integration tests for examples '-spec'. + +## boat:generate + +Open API Generator based on https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator-maven-plugin. All configuration options as +defined on openapi-generator-maven-plugin can be applied here too. + +Boat maven plugin uses slightly modified templates for html, java and webclient that help generate specs and clients that work best in a Backbase projects. + +## boat:generate-spring-boot-embedded + +Same with `generate` but with opinionated defaults for Spring + + + ${project.basedir}/../api/product-service-api/src/main/resources/openapi.yaml + com.backbase.product.api.service.v2 + com.backbase.product.api.service.v2.model + + +... is the same as: + + + ${project.build.directory}/generated-sources/openapi + true + spring-boat + true + false + false + ${project.basedir}/../api/product-service-api/src/main/resources/openapi.yaml + + spring-boot + java8 + true + true + true + false + true + true + false + com.backbase.product.api.service.v2 + com.backbase.product.api.service.v2.model + + + +## boat:generate-rest-template-embedded + +Same with `generate` but with opinionated defaults for Rest Template Client + + + ${project.build.directory}/generated-sources/openapi + true + java + true + false + false + ${project.basedir}/../api/product-service-api/src/main/resources/openapi.yaml + + resttemplate + java8 + true + true + true + false + true + true + false + com.backbase.goldensample.product.api.client.v2 + com.backbase.goldensample.product.api.client.v2.model + + + +## boat:generate-webclient-embedded + +Same with `generate` but with opinionated defaults for Web Client + + + ${project.build.directory}/generated-sources/openapi + true + java + true + false + false + ${project.basedir}/../api/product-service-api/src/main/resources/openapi.yaml + + webclient + java8 + true + true + true + false + true + true + false + com.backbase.goldensample.product.api.client.v2 + com.backbase.goldensample.product.api.client.v2.model + + + +## boat:decompose + +Merges any components using allOf references. + +## boat:diff + +Calculates a Change log for APIs. + +## boat:remove-deprecated + +Removes deprecated elements in an OpenAPI spec. + +## boat:validate + +Validates OpenAPI specs. + +Configuration can point to a specific file, or a directory. When a directory is specified all files with a `.yaml` +extension are validated. `failOnWarning` specifies whether to fail the build when validation violations are found, +otherwise, warnings are written to the log for everyone to ignore. -- `generate` - - Open API Generator based on https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator-maven-plugin. All configuration options as - defined on openapi-generator-maven-plugin can be applied here too. - boat-maven-plugin uses slightly modified templates for html, java and webclient that help generate specs and clients that work best in a Backbase projects. - -- `generate-spring-boot-embedded`, `generate` but with opinionated defaults - - - ${project.basedir}/../api/product-service-api/src/main/resources/openapi.yaml - com.backbase.product.api.service.v2 - com.backbase.product.api.service.v2.model - - - Is the same as: - - - ${project.build.directory}/generated-sources/openapi - true - spring-boat - true - false - false - ${project.basedir}/../api/product-service-api/src/main/resources/openapi.yaml - - spring-boot - java8 - true - true - true - false - true - true - false - com.backbase.product.api.service.v2 - com.backbase.product.api.service.v2.model - - -- `generate-rest-template-embedded`, `generate` but with opinionated defaults - - - ${project.build.directory}/generated-sources/openapi - true - java - true - false - false - ${project.basedir}/../api/product-service-api/src/main/resources/openapi.yaml - - resttemplate - java8 - true - true - true - false - true - true - false - com.backbase.goldensample.product.api.client.v2 - com.backbase.goldensample.product.api.client.v2.model - - - -- `generate-webclient-embedded`, `generate` but with opinionated defaults - - - ${project.build.directory}/generated-sources/openapi - true - java - true - false - false - ${project.basedir}/../api/product-service-api/src/main/resources/openapi.yaml - - webclient - java8 - true - true - true - false - true - true - false - com.backbase.goldensample.product.api.client.v2 - com.backbase.goldensample.product.api.client.v2.model - - - -- `decompose` - - Merges any components using allOf references. - -- `diff` - - Calculates a Change log for APIs. - -- `remove-deprecated` - - Removes deprecated elements in an OpenAPI spec. - -- `boat:validate` - - Validates OpenAPI specs. - - Configuration can point to a specific file, or a directory. When a directory is specified all files with a `.yaml` - extension are validated. `failOnWarning` specifies whether to fail the build when validation violations are found, - otherwise, warnings are written to the log for everyone to ignore. - - ``` ${project.build.outputDirectory}/specs/ true - ``` -- `boat:lint` +## boat:lint - API lint which provides checks for compliance with many of Backbase's API standards - - Available parameters: - - failOnWarning (Default: false) - Set this to true to fail in case a warning is found. - - ignoreRules - List of rules ids which will be ignored. - - inputSpec - Required: true - Input spec directory or file. - - output (Default: - ${project.build.directory}/boat-lint-reports) - Output directory for lint reports. - - showIgnoredRules (Default: false) - Set this to true to show the list of ignored rules.. +API lint which provides checks for compliance with many of Backbase's API standards + +Available parameters: + + failOnWarning (Default: false) + Set this to true to fail in case a warning is found. + + ignoreRules + List of rules ids which will be ignored. + + inputSpec + Required: true + Input spec directory or file. + + output (Default: ${project.build.directory}/boat-lint-reports) + Output directory for lint reports. + + showIgnoredRules (Default: false) + Set this to true to show the list of ignored rules.. - writeLintReport (Default: true) - Set this to true to generate lint report. - - Example: - - ``` - - ${unversioned-filename-spec-dir}/ - ${project.build.directory}/boat-lint-reports - true - ${ignored-lint-rules} - true + writeLintReport (Default: true) + Set this to true to generate lint report. + +Example: + + + ${unversioned-filename-spec-dir}/ + ${project.build.directory}/boat-lint-reports + true + ${ignored-lint-rules} + true - ``` - - To see details about this goal: - -`mvn help:describe -DgroupId=com.backbase.oss -DartifactId=boat-maven-plugin -Dgoal=lint -Ddetail` + +To see details about this goal: + + mvn help:describe -DgroupId=com.backbase.oss -DartifactId=boat-maven-plugin -Dgoal=lint -Ddetail` -- `boat:bundle` - - Bundles a spec by resolving external references. - - Configuration can point to a single in- and output file, or to in- and output directories. When directories are - specified, all files specified by the `includes` parameter are bundled. - - Examples in `json` files are parsed to objects. - ``` +## boat:bundle + +Bundles a spec by resolving external references. + +Configuration can point to a single in- and output file, or to in- and output directories. When directories are +specified, all files specified by the `includes` parameter are bundled. + +Examples in `json` files are parsed to objects. + ${bundle.skip} ${project.basedir}/src/main/resources/ @@ -196,14 +197,11 @@ The `boat` plugin has multiple goals: - ``` - - For more information, run -`mvn help:describe -Dplugin=com.backbase.oss:boat-maven-plugin -Ddetail` + mvn help:describe -Dplugin=com.backbase.oss:boat-maven-plugin -Dgoal=bundle -Ddetail -## Configuration examples +Configuration example ```$xml @@ -243,6 +241,83 @@ For more information, run ``` Usage -```mvn boat:generate``` + + mvn boat:generate Or hook up to your build process by adding ```executions``` configuration. + +## boat:transform + +Apply transformers to an existing specification. + +Available parameters: + + inputs + Required: true + User property: boat.transform.inputs + A list of input specifications. + + mappers + File name mappers used to generate the output file name, instances of + org.codehaus.plexus.components.io.filemappers.FileMapper. + The following mappers can be used without needing to specify the FQCN of + the implementation. + + regexp: + org.codehaus.plexus.components.io.filemappers.RegExpFileMapper + merge: + org.codehaus.plexus.components.io.filemappers.MergeFileMapper + prefix: + org.codehaus.plexus.components.io.filemappers.PrefixFileMapper + suffix: + org.codehaus.plexus.components.io.filemappers.SuffixFileMapper + + The parameter defaults to + + + -transformed + + + options + Additional options passed to transformers. + + output (Default: ${project.build.directory}) + User property: boat.transform.output + Target directory of the transformed specifications. + + pipeline + Required: true + The list of transformers to be applied to each input specification. + + serverId + User property: boat.transform.serverId + Retrieves authorization from Maven's settings.xml. + + skip + Alias: codegen.skip + User property: boat.transform.skip + Whether to skip the execution of this goal. + +Configuration example + +```$xml + + + ${project.build.directory}/openapi.yaml + + + ${spec.name}-${spec.version}.yaml + + + ${spec.version} + + + abstract + implements + extra-annotation + extra-java-code + + + + +``` diff --git a/boat-maven-plugin/pom.xml b/boat-maven-plugin/pom.xml index b259d1dec..baa0b2ae5 100644 --- a/boat-maven-plugin/pom.xml +++ b/boat-maven-plugin/pom.xml @@ -1,11 +1,12 @@ - + 4.0.0 com.backbase.oss backbase-openapi-tools - 0.14.2-SNAPSHOT + 0.14.3-SNAPSHOT boat-maven-plugin @@ -81,6 +82,11 @@ plexus-build-api 0.0.7 + + org.codehaus.plexus + plexus-io + 3.2.0 + org.apache.maven @@ -111,15 +117,18 @@ org.junit.jupiter junit-jupiter-params - 5.7.0 test - org.junit.jupiter junit-jupiter test + + org.mockito + mockito-junit-jupiter + test + org.apache.maven.shared maven-invoker diff --git a/boat-maven-plugin/src/it/example/boat-artifact-input/artifact-input/pom.xml b/boat-maven-plugin/src/it/example/boat-artifact-input/artifact-input/pom.xml new file mode 100644 index 000000000..9e4491ffe --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-artifact-input/artifact-input/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + + com.backbase.oss.boat.example + boat-artifact-input + 1.0.0-SNAPSHOT + + + artifact-input + pom + + BOAT :: Input artifact + + + + + com.backbase.oss + boat-maven-plugin + + + generate-docs-from-zip + generate-sources + + doc + + + + com.backbase.oss.boat.example + openapi-zips + 1.0.0-SNAPSHOT + api + zip + presentation-client-api/openapi.yaml + + + + + + + + + + + + diff --git a/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-spec-bom/pom.xml b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-spec-bom/pom.xml new file mode 100644 index 000000000..e213c287b --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-spec-bom/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + com.backbase.oss.boat.example + openapi-spec-bom + 1.0.0-SNAPSHOT + + + 1.0.0-SNAPSHOT + + + pom + + BOAT :: OpenAPI Bill-Of-Materials + + + + + com.backbase.oss.boat.example + openapi-zips + ${openapi-spec.version} + + + + + + + + diff --git a/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/pom.xml b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/pom.xml new file mode 100644 index 000000000..8a0113446 --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + com.backbase.oss.boat.example + + + openapi-zips + 1.0.0-SNAPSHOT + + + BOAT :: OpenAPI Example + + + + + maven-assembly-plugin + 3.1.0 + + + create-archive + package + + single + + + + src/assembly/build.xml + + + + + + + + + + diff --git a/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/assembly/build.xml b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/assembly/build.xml new file mode 100644 index 000000000..8a0b62203 --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/assembly/build.xml @@ -0,0 +1,18 @@ + + api + + zip + + false + + + src/main/resources/ + ${artifactId} + + **/** + + + + \ No newline at end of file diff --git a/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-client-api/index.html b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-client-api/index.html new file mode 100644 index 000000000..80aa61995 --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-client-api/index.html @@ -0,0 +1,24 @@ + + + + Wallet Test Client API + + + + + + + + + + + + + \ No newline at end of file diff --git a/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-client-api/openapi.yaml b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-client-api/openapi.yaml new file mode 100644 index 000000000..eed41ab6d --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-client-api/openapi.yaml @@ -0,0 +1,1223 @@ +openapi: 3.0.3 +info: + title: Wallet Test Client API + description: No description available + version: 2.19.0 +servers: +- url: /artifact-service/ + description: The server +tags: +- name: wallet test client api +paths: + /client-api/v1/wallet/paymentcards: + summary: Payment Cards + description: No description available + get: + tags: + - wallet + summary: "Returns available payment card details for user, optionally filtered\ + \ by nameOnCard." + description: "Returns available payment card details for user, optionally filtered\ + \ by nameOnCard" + operationId: getPaymentCards + parameters: + - name: nameOnCard + in: query + description: "Filter by the cardholder's name (case-insensitive), can be the\ + \ first one or more characters of one of the words/names" + required: false + schema: + type: string + examples: + example: + summary: example + value: Smi + - name: dateTimeOnly + in: query + description: Creation date in datetime-only format + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 2017-10-04T14:54:36 + - name: dateTime + in: query + description: Creation date in Zoned RFC3339 Date-time format + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 2017-10-04T14:54:36Z + - name: dateTime2616 + in: query + description: Zoned RFC2616 Date-time param example + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: "Wed, 4 Jul 2001 12:08:56 PDT" + - name: date + in: query + description: Date-only param example + required: false + schema: + type: string + format: date + examples: + example: + summary: example + value: 2017-10-04 + - name: time + in: query + description: time-only param example + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 14:54:36 + - name: orderBy + in: query + description: "Order by field: nameOnCard\n" + required: false + schema: + type: string + examples: + example: + summary: example + - name: direction + in: query + description: Direction + required: false + schema: + type: string + default: DESC + enum: + - ASC + - DESC + examples: + example: + summary: example + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentCards' + examples: + example: + value: "[ {\n \"id\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\"\ + ,\n \"pan\" : \"5434111122223333\",\n \"cvc\" : \"123\",\n \ + \ \"startDate\" : \"0116\",\n \"expiryDate\" : \"1219\",\n \"\ + nameOnCard\" : \"Mr Timmy Tester\",\n \"creationDate\" : \"2011-05-30T12:13:14+03:00\"\ + ,\n \"balance\" : {\n \"amount\" : \"2001\",\n \"currencyCode\"\ + \ : \"EUR\"\n },\n \"apr\" : 12.75\n}, {\n \"id\" : \"d593c212-70ad-41a6-a547-d5d9232414cb\"\ + ,\n \"pan\" : \"5434111122224444\",\n \"cvc\" : \"101\",\n \ + \ \"startDate\" : \"0216\",\n \"expiryDate\" : \"0120\",\n \"\ + nameOnCard\" : \"Mr Timmothy Tester\",\n \"creationDate\" : \"\ + 2011-05-30T12:13:14+03:00\",\n \"balance\" : {\n \"amount\"\ + \ : \"4.4399999999999995\",\n \"currencyCode\" : \"GBP\"\n\ + \ },\n \"apr\" : 12.75\n}, {\n \"id\" : \"9635966b-28e9-4479-8121-bb7bc9beeb62\"\ + ,\n \"pan\" : \"5434121212121212\",\n \"cvc\" : \"121\",\n \ + \ \"startDate\" : \"0115\",\n \"expiryDate\" : \"1218\",\n \"\ + nameOnCard\" : \"Mr Timmy Tester\",\n \"creationDate\" : \"2011-05-30T12:13:14+03:00\"\ + ,\n \"balance\" : {\n \"amount\" : \"1981\",\n \"currencyCode\"\ + \ : \"EUR\"\n },\n \"apr\" : 12.75\n} ]" + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "406": + description: NotAcceptable + content: + application/json: + schema: + $ref: '#/components/schemas/NotAcceptableError' + examples: + example: + value: "{\n \"message\" : \"Could not find acceptable representation\"\ + ,\n \"supportedMediaTypes\" : [ \"application/json\" ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + x-bb-access-control-resource: WALLET + x-bb-access-control-function: MANAGE_PAYMENT_CARDS + x-bb-access-control-privilege: READ_PAYMENT_CARD + post: + tags: + - wallet + summary: Adds a payment card to the user's wallet. + description: Adds a payment card to the user's wallet + operationId: postPaymentCards + parameters: + - name: X-Request-Id + in: header + description: Correlates HTTP requests between a client and server. + required: false + schema: + type: string + examples: + example: + summary: example + value: f058ebd6-02f7-4d3f-942e-904344e8cde5 + requestBody: + description: Adds a payment card to the user's wallet + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentCard' + examples: + example: + value: "{\n \"id\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\",\n\ + \ \"pan\" : \"5434111122223333\",\n \"cvc\" : \"123\",\n \"startDate\"\ + \ : \"0116\",\n \"expiryDate\" : \"1219\",\n \"nameOnCard\" :\ + \ \"Mr Timmy Tester\",\n \"creationDate\" : \"2011-05-30T12:13:14+03:00\"\ + ,\n \"balance\" : {\n \"amount\" : \"1000\",\n \"currencyCode\"\ + \ : \"EUR\"\n },\n \"apr\" : 12.75\n}" + responses: + "201": + description: request to create payment card accepted + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentCardsPostResponseBody' + examples: + example: + value: "{\n \"id\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\"\n\ + }" + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + x-bb-access-control-resource: WALLET + x-bb-access-control-function: MANAGE_PAYMENT_CARDS + x-bb-access-control-privilege: WRITE_PAYMENT_CARD + /client-api/v1/wallet/paymentcards/{cardId}: + summary: Payment Card + description: No description available + get: + tags: + - wallet + summary: Returns details of a specific payment card. + description: Returns details of a specific payment card + operationId: getPaymentCard + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentCard' + examples: + example: + value: "{\n \"id\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\",\n\ + \ \"pan\" : \"5434111122223333\",\n \"cvc\" : \"123\",\n \"\ + startDate\" : \"0116\",\n \"expiryDate\" : \"1219\",\n \"nameOnCard\"\ + \ : \"Mr Timmy Tester\",\n \"creationDate\" : \"2011-05-30T12:13:14+03:00\"\ + ,\n \"balance\" : {\n \"amount\" : \"1000\",\n \"currencyCode\"\ + \ : \"EUR\"\n },\n \"apr\" : 12.75\n}" + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + x-bb-access-control-resource: WALLET + x-bb-access-control-function: MANAGE_PAYMENT_CARDS + x-bb-access-control-privilege: READ_PAYMENT_CARD + delete: + tags: + - wallet + summary: Deletes a payment card with a given id. + description: Deletes a payment card with a given id + operationId: deletePaymentCard + responses: + "204": + description: Payment card is succesfully deleted + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + x-bb-access-control-resource: WALLET + x-bb-access-control-function: MANAGE_PAYMENT_CARDS + x-bb-access-control-privilege: DELETE_PAYMENT_CARD + parameters: + - name: cardId + in: path + description: No description available + required: true + schema: + type: string + examples: + example: + summary: example + /client-api/v1/bbt/build-info: + summary: /build-info + description: No description available + get: + tags: + - bbt + summary: Build Information. + description: Build Information + operationId: getBuildinfo + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/BbtBuild-infoGetGetResponseBody' + examples: + example: + value: "{\n \"build-info\" : {\n \"build.version\" : \"1.1.111-SNAPSHOT\"\ + \n }\n}" + /client-api/v1/patch: + summary: patch + description: PATCH endpoint for test operations + patch: + tags: + - patch + summary: patch + description: Patch Test + operationId: patchpatch + parameters: + - name: X-Request-Id + in: header + description: Correlates HTTP requests between a client and server. + required: false + schema: + type: string + examples: + example: + summary: example + value: f058ebd6-02f7-4d3f-942e-904344e8cde5 + responses: + "200": + description: No description available + content: {} + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + /client-api/v1/test/required-boolean-query-param: + summary: required boolean query param + description: arbitrary tests + get: + tags: + - test + description: No description available + operationId: getrequiredBooleanQueryParam + parameters: + - name: bool + in: query + description: Required boolean parameter with no default value + required: true + schema: + type: boolean + examples: + example: + summary: example + value: false + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/RequiredbooleanqueryparamGetResponseBody' + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + /client-api/v1/test/values: + summary: Test Values + description: Test Values + get: + tags: + - test + description: No description available + operationId: getTestValues + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/TestValuesGetResponseBody' + examples: + example: + value: "{\n \"message\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\"\ + ,\n \"number\" : \"102.4\"\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + /client-api/v1/test/headers: + summary: Test header propagation + description: Test header propagation + get: + tags: + - test + description: No description available + operationId: getTestHeaderPropagation + parameters: + - name: addHops + in: query + description: number of additional hops to perform + required: false + schema: + type: integer + format: int32 + examples: + example: + summary: example + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/TestHeadersResponseBody' + examples: + example: + value: "{\n \"requests\" : [ {\n \"name\" : \"building-blocks-test-wallet-presentation-service\"\ + ,\n \"url\" : \"/client-api/v1/test/headers\",\n \"headers\"\ + \ : {\n \"correlation-id\" : [ \"2ed475b714a3945a\" ],\n\ + \ \"accept\" : [ \"application/json\" ],\n \"x-bbt-test\"\ + \ : [ \"X-BBT-contentVal2\" ],\n \"connection\" : [ \"keep-alive\"\ + \ ]\n }\n }, {\n \"name\" : \"building-blocks-test-wallet-pandp-service\"\ + ,\n \"url\" : \"/service-api/v1/test/headers\",\n \"headers\"\ + \ : {\n \"authorization\" : [ \"Bearer eyJh\" ],\n \"\ + accept\" : [ \"application/xml, text/xml, application/json, application/*+xml,\ + \ application/*+json\" ],\n \"content-type\" : [ \"application/json\"\ + \ ],\n \"x-cxt-user-token\" : [ \"Bearer ey\" ],\n \"\ + x-cxt-remote-user\" : [ \"admin\" ],\n \"x-cxt-requestuuid\"\ + \ : [ \"72002652-131a-4f28-bd00-16b8080932f5\" ],\n \"correlation-id\"\ + \ : [ \"2ed475b714a3945a\" ],\n \"x-bbt-test\" : [ \"X-BBT-contentVal2\"\ + \ ]\n }\n }, {\n \"name\" : \"building-blocks-test-wallet-pandp-service\"\ + ,\n \"url\" : \"/service-api/v1/test/headers\",\n \"headers\"\ + \ : {\n \"authorization\" : [ \"Bearer eyJh\" ],\n \"\ + accept\" : [ \"application/xml, text/xml, application/json, application/*+xml,\ + \ application/*+json\" ],\n \"content-type\" : [ \"application/json\"\ + \ ],\n \"x-cxt-user-token\" : [ \"Bearer ey\" ],\n \"\ + x-cxt-remote-user\" : [ \"admin\" ],\n \"x-cxt-requestuuid\"\ + \ : [ \"72002652-131a-4f28-bd00-16b8080932f5\" ],\n \"correlation-id\"\ + \ : [ \"2ed475b714a3945a\" ],\n \"x-bbt-test\" : [ \"X-BBT-contentVal2\"\ + \ ]\n }\n } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + /client-api/v1/test/date-query-params: + summary: dateQueryParam + description: | + Tests for date/time query parameters in service-apis. Sends the same query parameters to the equivalent endpoint + in the pandp service which echoes the given values back in the response body. Values echoed by the pandp service + are then returned in the response to this request. + get: + tags: + - test + description: No description available + operationId: getdateQueryParam + parameters: + - name: dateTimeOnly + in: query + description: Creation date in datetime-only format + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 2017-10-04T14:54:36 + - name: dateTime + in: query + description: Creation date in Zoned RFC3339 Date-time format + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 2017-10-04T14:54:36Z + - name: dateTime2616 + in: query + description: Zoned RFC2616 Date-time param example + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: "Wed, 4 Jul 2001 12:08:56 PDT" + - name: date + in: query + description: Date-only param example + required: false + schema: + type: string + format: date + examples: + example: + summary: example + value: 2017-10-04 + - name: time + in: query + description: time-only param example + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 14:54:36 + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/DateQueryParamGetResponseBody' +components: + schemas: + BadRequestError: + required: + - message + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' + BbAccessControl: + required: + - function + - privilege + - resource + type: object + properties: + resource: + type: string + description: "Resource being protected, e.g. 'User'" + function: + type: string + description: "Business function, e.g. 'Manage Users'" + privilege: + type: string + description: "The privilege required, e.g. 'view'" + BbApiDeprecation: + required: + - deprecatedFromVersion + - description + - reason + - removedFromVersion + type: object + properties: + deprecatedFromVersion: + type: string + description: Version of the product from which the endpoint has been deprecated + and should no longer be used + deprecated: true + removedFromVersion: + type: string + description: Version of the product from which the API endpoint will be + removed + reason: + type: string + description: The reason the API endpoint was deprecated + deprecated: true + description: + type: string + description: "Any further information, e.g. migration information" + BbtBuild-infoGetGetResponseBody: + type: object + properties: + build-info: + type: object + example: + build-info: + build.version: 1.1.111-SNAPSHOT + BbtbuildInfogetgetresponsebody: + type: object + properties: + build-info: + type: object + example: + build-info: + build.version: 1.1.111-SNAPSHOT + Currency: + title: Monetary Amount + required: + - amount + - currencyCode + type: object + properties: + amount: + type: string + description: The amount in the specified currency + currencyCode: + pattern: "^[A-Z]{3}$" + type: string + description: The alpha-3 code (complying with ISO 4217) of the currency + that qualifies the amount + description: Schema defining monetary amount in given currency. + DateQueryParamGetResponseBody: + type: object + properties: + dateTimeOnly: + type: string + dateTimeOnlyParsedValue: + type: string + dateTime: + type: string + dateTimeParsedValue: + type: string + dateTime2616: + type: string + dateTime2616ParsedValue: + type: string + date: + type: string + dateParsedValue: + type: string + time: + type: string + timeParsedValue: + type: string + formatDateTime: + type: string + description: "The dateTime parameter formatted as 'date-time', java.util.Date\ + \ or java.time.ZoneDateTime" + format: date-time + formatDate: + type: string + description: "The date parameter formatted as 'date', String or java.time.LocalDate" + format: date + formatTime: + type: string + description: "The time parameter formatted as 'time', String or java.time.LocalTime" + formatUtcMillisec: + type: string + description: "The dateTime parameter formatted as 'date', long" + ErrorItem: + type: object + properties: + message: + type: string + description: Default Message. Any further information. + key: + type: string + description: "{capability-name}.api.{api-key-name}. For generated validation\ + \ errors this is the path in the document the error resolves to. e.g.\ + \ object name + '.' + field" + context: + type: object + description: Context can be anything used to construct localised messages. + description: A validation error + ForbiddenError: + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' + InternalServerError: + required: + - message + type: object + properties: + message: + type: string + description: Further Information + description: Represents HTTP 500 Internal Server Error + NotAcceptableError: + type: object + properties: + message: + type: string + supportedMediaTypes: + type: array + description: List of supported media types for this endpoint + items: + type: string + NotFoundError: + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' + ObjectWrappingException: + type: object + properties: + message: + type: string + data: + type: object + PaymentCard: + required: + - cvc + - expiryDate + - id + - nameOnCard + - pan + - startDate + type: object + properties: + id: + type: string + pan: + maxLength: 19 + type: string + description: "Must be sixteen digits, optionally in blocks of 4 separated\ + \ by a dash" + cvc: + maxLength: 3 + minLength: 3 + type: string + description: Card Verification Code + startDate: + pattern: "^(0[1-9]|1[0-2])/?([0-9]{4}|[0-9]{2})$" + type: string + description: "Must be in one of these four formats: MM/YY MMYY MMYYYY MM/YYYY" + expiryDate: + pattern: "^(0[1-9]|1[0-2])/?([0-9]{4}|[0-9]{2})$" + type: string + description: "Must be in one of these four formats: MM/YY MMYY MMYYYY MM/YYYY" + nameOnCard: + type: string + creationDate: + type: string + format: date-time + balance: + $ref: '#/components/schemas/Currency' + apr: + type: number + cardtype: + type: string + enum: + - CREDIT + - DEBIT + - PREPAID + PaymentCards: + type: array + items: + $ref: '#/components/schemas/PaymentCard' + PaymentCardsPostResponseBody: + type: object + properties: + id: + type: string + example: + id: a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1 + RequiredbooleanqueryparamGetResponseBody: + type: object + properties: + message: + type: string + TestHeadersResponseBody: + type: object + properties: + requests: + type: array + items: + type: object + properties: + name: + type: string + url: + type: string + headers: + type: object + TestValuesGetResponseBody: + type: object + properties: + message: + type: string + number: + type: string + example: + message: a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1 + number: "102.4" + UnauthorizedAltError: + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' + UnauthorizedError: + required: + - message + type: object + properties: + message: + type: string + UnsupportedMediaTypeError: + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' diff --git a/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-integration-api/index.html b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-integration-api/index.html new file mode 100644 index 000000000..ef9076b62 --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-integration-api/index.html @@ -0,0 +1,24 @@ + + + + Example + + + + + + + + + + + + + \ No newline at end of file diff --git a/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-integration-api/openapi.yaml b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-integration-api/openapi.yaml new file mode 100644 index 000000000..bf69079bf --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-integration-api/openapi.yaml @@ -0,0 +1,48 @@ +openapi: 3.0.3 +info: + title: Example + description: | + # Example + Test Schema to test an integration-api + version: 2.19.0 +servers: +- url: /artifact-service/ + description: The server +tags: +- name: example +paths: + /integration-api/v1/items: + summary: items + description: Retrieve all items. + get: + tags: + - items + summary: Retrieve list of all items. + description: Retrieve list of all items. + operationId: getitems + responses: + "200": + description: Test Schema + content: + application/json: + schema: + $ref: '#/components/schemas/ItemsGetResponseBody' + examples: + example: + value: "{\n \"name\" : \"Example\",\n \"description\" : \"Example\ + \ description\"\n}" +components: + schemas: + ItemsGetResponseBody: + required: + - name + type: object + properties: + name: + type: string + description: + type: string + description: this models a simple item. + example: + name: Example + description: Example description diff --git a/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-service-api/index.html b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-service-api/index.html new file mode 100644 index 000000000..c9863ec40 --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-service-api/index.html @@ -0,0 +1,24 @@ + + + + Wallet Test Service API + + + + + + + + + + + + + \ No newline at end of file diff --git a/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-service-api/openapi.yaml b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-service-api/openapi.yaml new file mode 100644 index 000000000..5a2175c02 --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/openapi-zips/src/main/resources/presentation-service-api/openapi.yaml @@ -0,0 +1,885 @@ +openapi: 3.0.3 +info: + title: Wallet Test Service API + description: No description available + version: 2.19.0 +servers: +- url: /artifact-service/ + description: The server +tags: +- name: wallet test service api +paths: + /service-api/v1/wallet/admin/{userId}/paymentcards: + summary: Payment Cards + description: No description available + get: + tags: + - wallet + summary: "Returns available payment card details for user, optionally filtered\ + \ by nameOnCard." + description: "Returns available payment card details for user, optionally filtered\ + \ by nameOnCard" + operationId: getPaymentCards + parameters: + - name: nameOnCard + in: query + description: "Filter by the cardholder's name (case-insensitive), can be the\ + \ first one or more characters of one of the words/names" + required: false + schema: + type: string + examples: + example: + summary: example + value: Smi + - name: dateTimeOnly + in: query + description: Creation date in datetime-only format + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 2017-10-04T14:54:36 + - name: dateTime + in: query + description: Creation date in Zoned RFC3339 Date-time format + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 2017-10-04T14:54:36Z + - name: dateTime2616 + in: query + description: Zoned RFC2616 Date-time param example + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: "Wed, 4 Jul 2001 12:08:56 PDT" + - name: date + in: query + description: Date-only param example + required: false + schema: + type: string + format: date + examples: + example: + summary: example + value: 2017-10-04 + - name: time + in: query + description: time-only param example + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 14:54:36 + - name: orderBy + in: query + description: "Order by field: nameOnCard\n" + required: false + schema: + type: string + examples: + example: + summary: example + - name: direction + in: query + description: Direction + required: false + schema: + type: string + default: DESC + enum: + - ASC + - DESC + examples: + example: + summary: example + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentCards' + examples: + example: + value: "[ {\n \"id\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\"\ + ,\n \"pan\" : \"5434111122223333\",\n \"cvc\" : \"123\",\n \ + \ \"startDate\" : \"0116\",\n \"expiryDate\" : \"1219\",\n \"\ + nameOnCard\" : \"Mr Timmy Tester\",\n \"creationDate\" : \"2011-05-30T12:13:14+03:00\"\ + ,\n \"balance\" : {\n \"amount\" : \"2001\",\n \"currencyCode\"\ + \ : \"EUR\"\n },\n \"apr\" : 12.75\n}, {\n \"id\" : \"d593c212-70ad-41a6-a547-d5d9232414cb\"\ + ,\n \"pan\" : \"5434111122224444\",\n \"cvc\" : \"101\",\n \ + \ \"startDate\" : \"0216\",\n \"expiryDate\" : \"0120\",\n \"\ + nameOnCard\" : \"Mr Timmothy Tester\",\n \"creationDate\" : \"\ + 2011-05-30T12:13:14+03:00\",\n \"balance\" : {\n \"amount\"\ + \ : \"4.4399999999999995\",\n \"currencyCode\" : \"GBP\"\n\ + \ },\n \"apr\" : 12.75\n}, {\n \"id\" : \"9635966b-28e9-4479-8121-bb7bc9beeb62\"\ + ,\n \"pan\" : \"5434121212121212\",\n \"cvc\" : \"121\",\n \ + \ \"startDate\" : \"0115\",\n \"expiryDate\" : \"1218\",\n \"\ + nameOnCard\" : \"Mr Timmy Tester\",\n \"creationDate\" : \"2011-05-30T12:13:14+03:00\"\ + ,\n \"balance\" : {\n \"amount\" : \"1981\",\n \"currencyCode\"\ + \ : \"EUR\"\n },\n \"apr\" : 12.75\n} ]" + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "406": + description: NotAcceptable + content: + application/json: + schema: + $ref: '#/components/schemas/NotAcceptableError' + examples: + example: + value: "{\n \"message\" : \"Could not find acceptable representation\"\ + ,\n \"supportedMediaTypes\" : [ \"application/json\" ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + post: + tags: + - wallet + summary: Adds a payment card to the user's wallet. + description: Adds a payment card to the user's wallet + operationId: postPaymentCards + parameters: + - name: X-Request-Id + in: header + description: Correlates HTTP requests between a client and server. + required: false + schema: + type: string + examples: + example: + summary: example + value: f058ebd6-02f7-4d3f-942e-904344e8cde5 + requestBody: + description: Adds a payment card to the user's wallet + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentCard' + examples: + example: + value: "{\n \"id\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\",\n\ + \ \"pan\" : \"5434111122223333\",\n \"cvc\" : \"123\",\n \"startDate\"\ + \ : \"0116\",\n \"expiryDate\" : \"1219\",\n \"nameOnCard\" :\ + \ \"Mr Timmy Tester\",\n \"creationDate\" : \"2011-05-30T12:13:14+03:00\"\ + ,\n \"balance\" : {\n \"amount\" : \"1000\",\n \"currencyCode\"\ + \ : \"EUR\"\n },\n \"apr\" : 12.75\n}" + responses: + "201": + description: request to create payment card accepted + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentCardsPostResponseBody' + examples: + example: + value: "{\n \"id\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\"\n\ + }" + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + parameters: + - name: userId + in: path + description: No description available + required: true + schema: + type: string + examples: + example: + summary: example + /service-api/v1/wallet/admin/{userId}/paymentcards/{cardId}: + summary: Payment Card + description: No description available + get: + tags: + - wallet + summary: Returns details of a specific payment card. + description: Returns details of a specific payment card + operationId: getPaymentCard + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentCard' + examples: + example: + value: "{\n \"id\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\",\n\ + \ \"pan\" : \"5434111122223333\",\n \"cvc\" : \"123\",\n \"\ + startDate\" : \"0116\",\n \"expiryDate\" : \"1219\",\n \"nameOnCard\"\ + \ : \"Mr Timmy Tester\",\n \"creationDate\" : \"2011-05-30T12:13:14+03:00\"\ + ,\n \"balance\" : {\n \"amount\" : \"1000\",\n \"currencyCode\"\ + \ : \"EUR\"\n },\n \"apr\" : 12.75\n}" + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + delete: + tags: + - wallet + summary: Deletes a payment card with a given id. + description: Deletes a payment card with a given id + operationId: deletePaymentCard + responses: + "204": + description: Payment card is succesfully deleted + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + parameters: + - name: cardId + in: path + description: No description available + required: true + schema: + type: string + examples: + example: + summary: example + - name: userId + in: path + description: No description available + required: true + schema: + type: string + examples: + example: + summary: example + /service-api/v1/testQuery/required-boolean-query-param: + summary: required boolean query param + description: arbitrary tests + get: + tags: + - testQuery + description: No description available + operationId: getrequiredBooleanQueryParam + parameters: + - name: bool + in: query + description: Required boolean parameter with no default value + required: true + schema: + type: boolean + examples: + example: + summary: example + value: false + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/RequiredbooleanqueryparamGetResponseBody' + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" +components: + schemas: + BadRequestError: + required: + - message + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' + BbAccessControl: + required: + - function + - privilege + - resource + type: object + properties: + resource: + type: string + description: "Resource being protected, e.g. 'User'" + function: + type: string + description: "Business function, e.g. 'Manage Users'" + privilege: + type: string + description: "The privilege required, e.g. 'view'" + BbApiDeprecation: + required: + - deprecatedFromVersion + - description + - reason + - removedFromVersion + type: object + properties: + deprecatedFromVersion: + type: string + description: Version of the product from which the endpoint has been deprecated + and should no longer be used + deprecated: true + removedFromVersion: + type: string + description: Version of the product from which the API endpoint will be + removed + reason: + type: string + description: The reason the API endpoint was deprecated + deprecated: true + description: + type: string + description: "Any further information, e.g. migration information" + Currency: + title: Monetary Amount + required: + - amount + - currencyCode + type: object + properties: + amount: + type: string + description: The amount in the specified currency + currencyCode: + pattern: "^[A-Z]{3}$" + type: string + description: The alpha-3 code (complying with ISO 4217) of the currency + that qualifies the amount + description: Schema defining monetary amount in given currency. + ErrorItem: + type: object + properties: + message: + type: string + description: Default Message. Any further information. + key: + type: string + description: "{capability-name}.api.{api-key-name}. For generated validation\ + \ errors this is the path in the document the error resolves to. e.g.\ + \ object name + '.' + field" + context: + type: object + description: Context can be anything used to construct localised messages. + description: A validation error + ForbiddenError: + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' + InternalServerError: + required: + - message + type: object + properties: + message: + type: string + description: Further Information + description: Represents HTTP 500 Internal Server Error + NotAcceptableError: + type: object + properties: + message: + type: string + supportedMediaTypes: + type: array + description: List of supported media types for this endpoint + items: + type: string + NotFoundError: + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' + ObjectWrappingException: + type: object + properties: + message: + type: string + data: + type: object + PaymentCard: + required: + - cvc + - expiryDate + - id + - nameOnCard + - pan + - startDate + type: object + properties: + id: + type: string + pan: + maxLength: 19 + type: string + description: "Must be sixteen digits, optionally in blocks of 4 separated\ + \ by a dash" + cvc: + maxLength: 3 + minLength: 3 + type: string + description: Card Verification Code + startDate: + pattern: "^(0[1-9]|1[0-2])/?([0-9]{4}|[0-9]{2})$" + type: string + description: "Must be in one of these four formats: MM/YY MMYY MMYYYY MM/YYYY" + expiryDate: + pattern: "^(0[1-9]|1[0-2])/?([0-9]{4}|[0-9]{2})$" + type: string + description: "Must be in one of these four formats: MM/YY MMYY MMYYYY MM/YYYY" + nameOnCard: + type: string + creationDate: + type: string + format: date-time + balance: + $ref: '#/components/schemas/Currency' + apr: + type: number + cardtype: + type: string + enum: + - CREDIT + - DEBIT + - PREPAID + PaymentCards: + type: array + items: + $ref: '#/components/schemas/PaymentCard' + PaymentCardsPostResponseBody: + type: object + properties: + id: + type: string + example: + id: a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1 + RequiredbooleanqueryparamGetResponseBody: + type: object + properties: + message: + type: string + TestHeadersResponseBody: + type: object + properties: + requests: + type: array + items: + type: object + properties: + name: + type: string + url: + type: string + headers: + type: object + UnauthorizedAltError: + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' + UnauthorizedError: + required: + - message + type: object + properties: + message: + type: string + UnsupportedMediaTypeError: + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' diff --git a/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/pom.xml b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/pom.xml new file mode 100644 index 000000000..b9cd10ba3 --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-artifact-input/openapi-specs/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + + com.backbase.oss.boat.example + boat-artifact-input + 1.0.0-SNAPSHOT + + + openapi-specs + pom + + + + openapi-zips + openapi-spec-bom + + + + diff --git a/boat-maven-plugin/src/it/example/boat-artifact-input/pom.xml b/boat-maven-plugin/src/it/example/boat-artifact-input/pom.xml new file mode 100644 index 000000000..073f0b85d --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-artifact-input/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + + com.backbase.oss.boat.example + example + 1.0.0-SNAPSHOT + + + boat-artifact-input + pom + + BOAT :: DOCandLint + + + Example projects showing exporting RAML to OpenAPI from source files. + Specs can be in source files, or retrieved from dependencies + + + + + openapi-specs + artifact-input + + + + + diff --git a/boat-maven-plugin/src/it/example/boat-multiple-executions/pom.xml b/boat-maven-plugin/src/it/example/boat-multiple-executions/pom.xml new file mode 100644 index 000000000..97e6c0638 --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-multiple-executions/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + + com.backbase.oss.boat.example + example + 1.0.0-SNAPSHOT + + + boat-multiple-executions + pom + + BOAT :: Execute multiple + + + + + com.backbase.oss + boat-maven-plugin + + + generate-rest-template-embedded + generate-sources + + doc + + + https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v3.0/api-with-examples.yaml + + + + lint + verify + + lint + + + ${project.basedir}/src/main/resources/presentation-client-api/openapi.yaml + + + + generate-docs + generate-sources + + doc + + + ${project.basedir}/src/main/resources/presentation-integration-api/openapi.yaml + + + + + + + + + \ No newline at end of file diff --git a/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-client-api/index.html b/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-client-api/index.html new file mode 100644 index 000000000..80aa61995 --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-client-api/index.html @@ -0,0 +1,24 @@ + + + + Wallet Test Client API + + + + + + + + + + + + + \ No newline at end of file diff --git a/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-client-api/openapi.yaml b/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-client-api/openapi.yaml new file mode 100644 index 000000000..eed41ab6d --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-client-api/openapi.yaml @@ -0,0 +1,1223 @@ +openapi: 3.0.3 +info: + title: Wallet Test Client API + description: No description available + version: 2.19.0 +servers: +- url: /artifact-service/ + description: The server +tags: +- name: wallet test client api +paths: + /client-api/v1/wallet/paymentcards: + summary: Payment Cards + description: No description available + get: + tags: + - wallet + summary: "Returns available payment card details for user, optionally filtered\ + \ by nameOnCard." + description: "Returns available payment card details for user, optionally filtered\ + \ by nameOnCard" + operationId: getPaymentCards + parameters: + - name: nameOnCard + in: query + description: "Filter by the cardholder's name (case-insensitive), can be the\ + \ first one or more characters of one of the words/names" + required: false + schema: + type: string + examples: + example: + summary: example + value: Smi + - name: dateTimeOnly + in: query + description: Creation date in datetime-only format + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 2017-10-04T14:54:36 + - name: dateTime + in: query + description: Creation date in Zoned RFC3339 Date-time format + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 2017-10-04T14:54:36Z + - name: dateTime2616 + in: query + description: Zoned RFC2616 Date-time param example + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: "Wed, 4 Jul 2001 12:08:56 PDT" + - name: date + in: query + description: Date-only param example + required: false + schema: + type: string + format: date + examples: + example: + summary: example + value: 2017-10-04 + - name: time + in: query + description: time-only param example + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 14:54:36 + - name: orderBy + in: query + description: "Order by field: nameOnCard\n" + required: false + schema: + type: string + examples: + example: + summary: example + - name: direction + in: query + description: Direction + required: false + schema: + type: string + default: DESC + enum: + - ASC + - DESC + examples: + example: + summary: example + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentCards' + examples: + example: + value: "[ {\n \"id\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\"\ + ,\n \"pan\" : \"5434111122223333\",\n \"cvc\" : \"123\",\n \ + \ \"startDate\" : \"0116\",\n \"expiryDate\" : \"1219\",\n \"\ + nameOnCard\" : \"Mr Timmy Tester\",\n \"creationDate\" : \"2011-05-30T12:13:14+03:00\"\ + ,\n \"balance\" : {\n \"amount\" : \"2001\",\n \"currencyCode\"\ + \ : \"EUR\"\n },\n \"apr\" : 12.75\n}, {\n \"id\" : \"d593c212-70ad-41a6-a547-d5d9232414cb\"\ + ,\n \"pan\" : \"5434111122224444\",\n \"cvc\" : \"101\",\n \ + \ \"startDate\" : \"0216\",\n \"expiryDate\" : \"0120\",\n \"\ + nameOnCard\" : \"Mr Timmothy Tester\",\n \"creationDate\" : \"\ + 2011-05-30T12:13:14+03:00\",\n \"balance\" : {\n \"amount\"\ + \ : \"4.4399999999999995\",\n \"currencyCode\" : \"GBP\"\n\ + \ },\n \"apr\" : 12.75\n}, {\n \"id\" : \"9635966b-28e9-4479-8121-bb7bc9beeb62\"\ + ,\n \"pan\" : \"5434121212121212\",\n \"cvc\" : \"121\",\n \ + \ \"startDate\" : \"0115\",\n \"expiryDate\" : \"1218\",\n \"\ + nameOnCard\" : \"Mr Timmy Tester\",\n \"creationDate\" : \"2011-05-30T12:13:14+03:00\"\ + ,\n \"balance\" : {\n \"amount\" : \"1981\",\n \"currencyCode\"\ + \ : \"EUR\"\n },\n \"apr\" : 12.75\n} ]" + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "406": + description: NotAcceptable + content: + application/json: + schema: + $ref: '#/components/schemas/NotAcceptableError' + examples: + example: + value: "{\n \"message\" : \"Could not find acceptable representation\"\ + ,\n \"supportedMediaTypes\" : [ \"application/json\" ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + x-bb-access-control-resource: WALLET + x-bb-access-control-function: MANAGE_PAYMENT_CARDS + x-bb-access-control-privilege: READ_PAYMENT_CARD + post: + tags: + - wallet + summary: Adds a payment card to the user's wallet. + description: Adds a payment card to the user's wallet + operationId: postPaymentCards + parameters: + - name: X-Request-Id + in: header + description: Correlates HTTP requests between a client and server. + required: false + schema: + type: string + examples: + example: + summary: example + value: f058ebd6-02f7-4d3f-942e-904344e8cde5 + requestBody: + description: Adds a payment card to the user's wallet + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentCard' + examples: + example: + value: "{\n \"id\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\",\n\ + \ \"pan\" : \"5434111122223333\",\n \"cvc\" : \"123\",\n \"startDate\"\ + \ : \"0116\",\n \"expiryDate\" : \"1219\",\n \"nameOnCard\" :\ + \ \"Mr Timmy Tester\",\n \"creationDate\" : \"2011-05-30T12:13:14+03:00\"\ + ,\n \"balance\" : {\n \"amount\" : \"1000\",\n \"currencyCode\"\ + \ : \"EUR\"\n },\n \"apr\" : 12.75\n}" + responses: + "201": + description: request to create payment card accepted + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentCardsPostResponseBody' + examples: + example: + value: "{\n \"id\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\"\n\ + }" + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + x-bb-access-control-resource: WALLET + x-bb-access-control-function: MANAGE_PAYMENT_CARDS + x-bb-access-control-privilege: WRITE_PAYMENT_CARD + /client-api/v1/wallet/paymentcards/{cardId}: + summary: Payment Card + description: No description available + get: + tags: + - wallet + summary: Returns details of a specific payment card. + description: Returns details of a specific payment card + operationId: getPaymentCard + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentCard' + examples: + example: + value: "{\n \"id\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\",\n\ + \ \"pan\" : \"5434111122223333\",\n \"cvc\" : \"123\",\n \"\ + startDate\" : \"0116\",\n \"expiryDate\" : \"1219\",\n \"nameOnCard\"\ + \ : \"Mr Timmy Tester\",\n \"creationDate\" : \"2011-05-30T12:13:14+03:00\"\ + ,\n \"balance\" : {\n \"amount\" : \"1000\",\n \"currencyCode\"\ + \ : \"EUR\"\n },\n \"apr\" : 12.75\n}" + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + x-bb-access-control-resource: WALLET + x-bb-access-control-function: MANAGE_PAYMENT_CARDS + x-bb-access-control-privilege: READ_PAYMENT_CARD + delete: + tags: + - wallet + summary: Deletes a payment card with a given id. + description: Deletes a payment card with a given id + operationId: deletePaymentCard + responses: + "204": + description: Payment card is succesfully deleted + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + x-bb-access-control-resource: WALLET + x-bb-access-control-function: MANAGE_PAYMENT_CARDS + x-bb-access-control-privilege: DELETE_PAYMENT_CARD + parameters: + - name: cardId + in: path + description: No description available + required: true + schema: + type: string + examples: + example: + summary: example + /client-api/v1/bbt/build-info: + summary: /build-info + description: No description available + get: + tags: + - bbt + summary: Build Information. + description: Build Information + operationId: getBuildinfo + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/BbtBuild-infoGetGetResponseBody' + examples: + example: + value: "{\n \"build-info\" : {\n \"build.version\" : \"1.1.111-SNAPSHOT\"\ + \n }\n}" + /client-api/v1/patch: + summary: patch + description: PATCH endpoint for test operations + patch: + tags: + - patch + summary: patch + description: Patch Test + operationId: patchpatch + parameters: + - name: X-Request-Id + in: header + description: Correlates HTTP requests between a client and server. + required: false + schema: + type: string + examples: + example: + summary: example + value: f058ebd6-02f7-4d3f-942e-904344e8cde5 + responses: + "200": + description: No description available + content: {} + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + /client-api/v1/test/required-boolean-query-param: + summary: required boolean query param + description: arbitrary tests + get: + tags: + - test + description: No description available + operationId: getrequiredBooleanQueryParam + parameters: + - name: bool + in: query + description: Required boolean parameter with no default value + required: true + schema: + type: boolean + examples: + example: + summary: example + value: false + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/RequiredbooleanqueryparamGetResponseBody' + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + /client-api/v1/test/values: + summary: Test Values + description: Test Values + get: + tags: + - test + description: No description available + operationId: getTestValues + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/TestValuesGetResponseBody' + examples: + example: + value: "{\n \"message\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\"\ + ,\n \"number\" : \"102.4\"\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + /client-api/v1/test/headers: + summary: Test header propagation + description: Test header propagation + get: + tags: + - test + description: No description available + operationId: getTestHeaderPropagation + parameters: + - name: addHops + in: query + description: number of additional hops to perform + required: false + schema: + type: integer + format: int32 + examples: + example: + summary: example + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/TestHeadersResponseBody' + examples: + example: + value: "{\n \"requests\" : [ {\n \"name\" : \"building-blocks-test-wallet-presentation-service\"\ + ,\n \"url\" : \"/client-api/v1/test/headers\",\n \"headers\"\ + \ : {\n \"correlation-id\" : [ \"2ed475b714a3945a\" ],\n\ + \ \"accept\" : [ \"application/json\" ],\n \"x-bbt-test\"\ + \ : [ \"X-BBT-contentVal2\" ],\n \"connection\" : [ \"keep-alive\"\ + \ ]\n }\n }, {\n \"name\" : \"building-blocks-test-wallet-pandp-service\"\ + ,\n \"url\" : \"/service-api/v1/test/headers\",\n \"headers\"\ + \ : {\n \"authorization\" : [ \"Bearer eyJh\" ],\n \"\ + accept\" : [ \"application/xml, text/xml, application/json, application/*+xml,\ + \ application/*+json\" ],\n \"content-type\" : [ \"application/json\"\ + \ ],\n \"x-cxt-user-token\" : [ \"Bearer ey\" ],\n \"\ + x-cxt-remote-user\" : [ \"admin\" ],\n \"x-cxt-requestuuid\"\ + \ : [ \"72002652-131a-4f28-bd00-16b8080932f5\" ],\n \"correlation-id\"\ + \ : [ \"2ed475b714a3945a\" ],\n \"x-bbt-test\" : [ \"X-BBT-contentVal2\"\ + \ ]\n }\n }, {\n \"name\" : \"building-blocks-test-wallet-pandp-service\"\ + ,\n \"url\" : \"/service-api/v1/test/headers\",\n \"headers\"\ + \ : {\n \"authorization\" : [ \"Bearer eyJh\" ],\n \"\ + accept\" : [ \"application/xml, text/xml, application/json, application/*+xml,\ + \ application/*+json\" ],\n \"content-type\" : [ \"application/json\"\ + \ ],\n \"x-cxt-user-token\" : [ \"Bearer ey\" ],\n \"\ + x-cxt-remote-user\" : [ \"admin\" ],\n \"x-cxt-requestuuid\"\ + \ : [ \"72002652-131a-4f28-bd00-16b8080932f5\" ],\n \"correlation-id\"\ + \ : [ \"2ed475b714a3945a\" ],\n \"x-bbt-test\" : [ \"X-BBT-contentVal2\"\ + \ ]\n }\n } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + /client-api/v1/test/date-query-params: + summary: dateQueryParam + description: | + Tests for date/time query parameters in service-apis. Sends the same query parameters to the equivalent endpoint + in the pandp service which echoes the given values back in the response body. Values echoed by the pandp service + are then returned in the response to this request. + get: + tags: + - test + description: No description available + operationId: getdateQueryParam + parameters: + - name: dateTimeOnly + in: query + description: Creation date in datetime-only format + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 2017-10-04T14:54:36 + - name: dateTime + in: query + description: Creation date in Zoned RFC3339 Date-time format + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 2017-10-04T14:54:36Z + - name: dateTime2616 + in: query + description: Zoned RFC2616 Date-time param example + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: "Wed, 4 Jul 2001 12:08:56 PDT" + - name: date + in: query + description: Date-only param example + required: false + schema: + type: string + format: date + examples: + example: + summary: example + value: 2017-10-04 + - name: time + in: query + description: time-only param example + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 14:54:36 + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/DateQueryParamGetResponseBody' +components: + schemas: + BadRequestError: + required: + - message + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' + BbAccessControl: + required: + - function + - privilege + - resource + type: object + properties: + resource: + type: string + description: "Resource being protected, e.g. 'User'" + function: + type: string + description: "Business function, e.g. 'Manage Users'" + privilege: + type: string + description: "The privilege required, e.g. 'view'" + BbApiDeprecation: + required: + - deprecatedFromVersion + - description + - reason + - removedFromVersion + type: object + properties: + deprecatedFromVersion: + type: string + description: Version of the product from which the endpoint has been deprecated + and should no longer be used + deprecated: true + removedFromVersion: + type: string + description: Version of the product from which the API endpoint will be + removed + reason: + type: string + description: The reason the API endpoint was deprecated + deprecated: true + description: + type: string + description: "Any further information, e.g. migration information" + BbtBuild-infoGetGetResponseBody: + type: object + properties: + build-info: + type: object + example: + build-info: + build.version: 1.1.111-SNAPSHOT + BbtbuildInfogetgetresponsebody: + type: object + properties: + build-info: + type: object + example: + build-info: + build.version: 1.1.111-SNAPSHOT + Currency: + title: Monetary Amount + required: + - amount + - currencyCode + type: object + properties: + amount: + type: string + description: The amount in the specified currency + currencyCode: + pattern: "^[A-Z]{3}$" + type: string + description: The alpha-3 code (complying with ISO 4217) of the currency + that qualifies the amount + description: Schema defining monetary amount in given currency. + DateQueryParamGetResponseBody: + type: object + properties: + dateTimeOnly: + type: string + dateTimeOnlyParsedValue: + type: string + dateTime: + type: string + dateTimeParsedValue: + type: string + dateTime2616: + type: string + dateTime2616ParsedValue: + type: string + date: + type: string + dateParsedValue: + type: string + time: + type: string + timeParsedValue: + type: string + formatDateTime: + type: string + description: "The dateTime parameter formatted as 'date-time', java.util.Date\ + \ or java.time.ZoneDateTime" + format: date-time + formatDate: + type: string + description: "The date parameter formatted as 'date', String or java.time.LocalDate" + format: date + formatTime: + type: string + description: "The time parameter formatted as 'time', String or java.time.LocalTime" + formatUtcMillisec: + type: string + description: "The dateTime parameter formatted as 'date', long" + ErrorItem: + type: object + properties: + message: + type: string + description: Default Message. Any further information. + key: + type: string + description: "{capability-name}.api.{api-key-name}. For generated validation\ + \ errors this is the path in the document the error resolves to. e.g.\ + \ object name + '.' + field" + context: + type: object + description: Context can be anything used to construct localised messages. + description: A validation error + ForbiddenError: + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' + InternalServerError: + required: + - message + type: object + properties: + message: + type: string + description: Further Information + description: Represents HTTP 500 Internal Server Error + NotAcceptableError: + type: object + properties: + message: + type: string + supportedMediaTypes: + type: array + description: List of supported media types for this endpoint + items: + type: string + NotFoundError: + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' + ObjectWrappingException: + type: object + properties: + message: + type: string + data: + type: object + PaymentCard: + required: + - cvc + - expiryDate + - id + - nameOnCard + - pan + - startDate + type: object + properties: + id: + type: string + pan: + maxLength: 19 + type: string + description: "Must be sixteen digits, optionally in blocks of 4 separated\ + \ by a dash" + cvc: + maxLength: 3 + minLength: 3 + type: string + description: Card Verification Code + startDate: + pattern: "^(0[1-9]|1[0-2])/?([0-9]{4}|[0-9]{2})$" + type: string + description: "Must be in one of these four formats: MM/YY MMYY MMYYYY MM/YYYY" + expiryDate: + pattern: "^(0[1-9]|1[0-2])/?([0-9]{4}|[0-9]{2})$" + type: string + description: "Must be in one of these four formats: MM/YY MMYY MMYYYY MM/YYYY" + nameOnCard: + type: string + creationDate: + type: string + format: date-time + balance: + $ref: '#/components/schemas/Currency' + apr: + type: number + cardtype: + type: string + enum: + - CREDIT + - DEBIT + - PREPAID + PaymentCards: + type: array + items: + $ref: '#/components/schemas/PaymentCard' + PaymentCardsPostResponseBody: + type: object + properties: + id: + type: string + example: + id: a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1 + RequiredbooleanqueryparamGetResponseBody: + type: object + properties: + message: + type: string + TestHeadersResponseBody: + type: object + properties: + requests: + type: array + items: + type: object + properties: + name: + type: string + url: + type: string + headers: + type: object + TestValuesGetResponseBody: + type: object + properties: + message: + type: string + number: + type: string + example: + message: a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1 + number: "102.4" + UnauthorizedAltError: + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' + UnauthorizedError: + required: + - message + type: object + properties: + message: + type: string + UnsupportedMediaTypeError: + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' diff --git a/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-integration-api/index.html b/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-integration-api/index.html new file mode 100644 index 000000000..ef9076b62 --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-integration-api/index.html @@ -0,0 +1,24 @@ + + + + Example + + + + + + + + + + + + + \ No newline at end of file diff --git a/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-integration-api/openapi.yaml b/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-integration-api/openapi.yaml new file mode 100644 index 000000000..bf69079bf --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-integration-api/openapi.yaml @@ -0,0 +1,48 @@ +openapi: 3.0.3 +info: + title: Example + description: | + # Example + Test Schema to test an integration-api + version: 2.19.0 +servers: +- url: /artifact-service/ + description: The server +tags: +- name: example +paths: + /integration-api/v1/items: + summary: items + description: Retrieve all items. + get: + tags: + - items + summary: Retrieve list of all items. + description: Retrieve list of all items. + operationId: getitems + responses: + "200": + description: Test Schema + content: + application/json: + schema: + $ref: '#/components/schemas/ItemsGetResponseBody' + examples: + example: + value: "{\n \"name\" : \"Example\",\n \"description\" : \"Example\ + \ description\"\n}" +components: + schemas: + ItemsGetResponseBody: + required: + - name + type: object + properties: + name: + type: string + description: + type: string + description: this models a simple item. + example: + name: Example + description: Example description diff --git a/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-service-api/index.html b/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-service-api/index.html new file mode 100644 index 000000000..c9863ec40 --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-service-api/index.html @@ -0,0 +1,24 @@ + + + + Wallet Test Service API + + + + + + + + + + + + + \ No newline at end of file diff --git a/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-service-api/openapi.yaml b/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-service-api/openapi.yaml new file mode 100644 index 000000000..5a2175c02 --- /dev/null +++ b/boat-maven-plugin/src/it/example/boat-multiple-executions/src/main/resources/presentation-service-api/openapi.yaml @@ -0,0 +1,885 @@ +openapi: 3.0.3 +info: + title: Wallet Test Service API + description: No description available + version: 2.19.0 +servers: +- url: /artifact-service/ + description: The server +tags: +- name: wallet test service api +paths: + /service-api/v1/wallet/admin/{userId}/paymentcards: + summary: Payment Cards + description: No description available + get: + tags: + - wallet + summary: "Returns available payment card details for user, optionally filtered\ + \ by nameOnCard." + description: "Returns available payment card details for user, optionally filtered\ + \ by nameOnCard" + operationId: getPaymentCards + parameters: + - name: nameOnCard + in: query + description: "Filter by the cardholder's name (case-insensitive), can be the\ + \ first one or more characters of one of the words/names" + required: false + schema: + type: string + examples: + example: + summary: example + value: Smi + - name: dateTimeOnly + in: query + description: Creation date in datetime-only format + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 2017-10-04T14:54:36 + - name: dateTime + in: query + description: Creation date in Zoned RFC3339 Date-time format + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 2017-10-04T14:54:36Z + - name: dateTime2616 + in: query + description: Zoned RFC2616 Date-time param example + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: "Wed, 4 Jul 2001 12:08:56 PDT" + - name: date + in: query + description: Date-only param example + required: false + schema: + type: string + format: date + examples: + example: + summary: example + value: 2017-10-04 + - name: time + in: query + description: time-only param example + required: false + schema: + type: string + format: date-time + examples: + example: + summary: example + value: 14:54:36 + - name: orderBy + in: query + description: "Order by field: nameOnCard\n" + required: false + schema: + type: string + examples: + example: + summary: example + - name: direction + in: query + description: Direction + required: false + schema: + type: string + default: DESC + enum: + - ASC + - DESC + examples: + example: + summary: example + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentCards' + examples: + example: + value: "[ {\n \"id\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\"\ + ,\n \"pan\" : \"5434111122223333\",\n \"cvc\" : \"123\",\n \ + \ \"startDate\" : \"0116\",\n \"expiryDate\" : \"1219\",\n \"\ + nameOnCard\" : \"Mr Timmy Tester\",\n \"creationDate\" : \"2011-05-30T12:13:14+03:00\"\ + ,\n \"balance\" : {\n \"amount\" : \"2001\",\n \"currencyCode\"\ + \ : \"EUR\"\n },\n \"apr\" : 12.75\n}, {\n \"id\" : \"d593c212-70ad-41a6-a547-d5d9232414cb\"\ + ,\n \"pan\" : \"5434111122224444\",\n \"cvc\" : \"101\",\n \ + \ \"startDate\" : \"0216\",\n \"expiryDate\" : \"0120\",\n \"\ + nameOnCard\" : \"Mr Timmothy Tester\",\n \"creationDate\" : \"\ + 2011-05-30T12:13:14+03:00\",\n \"balance\" : {\n \"amount\"\ + \ : \"4.4399999999999995\",\n \"currencyCode\" : \"GBP\"\n\ + \ },\n \"apr\" : 12.75\n}, {\n \"id\" : \"9635966b-28e9-4479-8121-bb7bc9beeb62\"\ + ,\n \"pan\" : \"5434121212121212\",\n \"cvc\" : \"121\",\n \ + \ \"startDate\" : \"0115\",\n \"expiryDate\" : \"1218\",\n \"\ + nameOnCard\" : \"Mr Timmy Tester\",\n \"creationDate\" : \"2011-05-30T12:13:14+03:00\"\ + ,\n \"balance\" : {\n \"amount\" : \"1981\",\n \"currencyCode\"\ + \ : \"EUR\"\n },\n \"apr\" : 12.75\n} ]" + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "406": + description: NotAcceptable + content: + application/json: + schema: + $ref: '#/components/schemas/NotAcceptableError' + examples: + example: + value: "{\n \"message\" : \"Could not find acceptable representation\"\ + ,\n \"supportedMediaTypes\" : [ \"application/json\" ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + post: + tags: + - wallet + summary: Adds a payment card to the user's wallet. + description: Adds a payment card to the user's wallet + operationId: postPaymentCards + parameters: + - name: X-Request-Id + in: header + description: Correlates HTTP requests between a client and server. + required: false + schema: + type: string + examples: + example: + summary: example + value: f058ebd6-02f7-4d3f-942e-904344e8cde5 + requestBody: + description: Adds a payment card to the user's wallet + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentCard' + examples: + example: + value: "{\n \"id\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\",\n\ + \ \"pan\" : \"5434111122223333\",\n \"cvc\" : \"123\",\n \"startDate\"\ + \ : \"0116\",\n \"expiryDate\" : \"1219\",\n \"nameOnCard\" :\ + \ \"Mr Timmy Tester\",\n \"creationDate\" : \"2011-05-30T12:13:14+03:00\"\ + ,\n \"balance\" : {\n \"amount\" : \"1000\",\n \"currencyCode\"\ + \ : \"EUR\"\n },\n \"apr\" : 12.75\n}" + responses: + "201": + description: request to create payment card accepted + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentCardsPostResponseBody' + examples: + example: + value: "{\n \"id\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\"\n\ + }" + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + parameters: + - name: userId + in: path + description: No description available + required: true + schema: + type: string + examples: + example: + summary: example + /service-api/v1/wallet/admin/{userId}/paymentcards/{cardId}: + summary: Payment Card + description: No description available + get: + tags: + - wallet + summary: Returns details of a specific payment card. + description: Returns details of a specific payment card + operationId: getPaymentCard + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/PaymentCard' + examples: + example: + value: "{\n \"id\" : \"a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1\",\n\ + \ \"pan\" : \"5434111122223333\",\n \"cvc\" : \"123\",\n \"\ + startDate\" : \"0116\",\n \"expiryDate\" : \"1219\",\n \"nameOnCard\"\ + \ : \"Mr Timmy Tester\",\n \"creationDate\" : \"2011-05-30T12:13:14+03:00\"\ + ,\n \"balance\" : {\n \"amount\" : \"1000\",\n \"currencyCode\"\ + \ : \"EUR\"\n },\n \"apr\" : 12.75\n}" + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + delete: + tags: + - wallet + summary: Deletes a payment card with a given id. + description: Deletes a payment card with a given id + operationId: deletePaymentCard + responses: + "204": + description: Payment card is succesfully deleted + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" + parameters: + - name: cardId + in: path + description: No description available + required: true + schema: + type: string + examples: + example: + summary: example + - name: userId + in: path + description: No description available + required: true + schema: + type: string + examples: + example: + summary: example + /service-api/v1/testQuery/required-boolean-query-param: + summary: required boolean query param + description: arbitrary tests + get: + tags: + - testQuery + description: No description available + operationId: getrequiredBooleanQueryParam + parameters: + - name: bool + in: query + description: Required boolean parameter with no default value + required: true + schema: + type: boolean + examples: + example: + summary: example + value: false + responses: + "200": + description: No description available + content: + application/json: + schema: + $ref: '#/components/schemas/RequiredbooleanqueryparamGetResponseBody' + "400": + description: BadRequest + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestError' + examples: + example: + value: "{\n \"message\" : \"Bad Request\",\n \"errors\" : [ {\n\ + \ \"message\" : \"Value Exceeded. Must be between {min} and\ + \ {max}.\",\n \"key\" : \"common.api.shoesize\",\n \"context\"\ + \ : {\n \"max\" : \"50\",\n \"min\" : \"1\"\n }\n\ + \ } ]\n}" + "500": + description: InternalServerError + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + examples: + example: + value: "{\n \"message\" : \"Description of error\"\n}" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to an insufficient user quota of {quota}.\",\n \"key\"\ + \ : \"common.api.quota\",\n \"context\" : {\n \"quota\"\ + \ : \"someQuota\"\n }\n } ]\n}" + "415": + description: UnsupportedMediaType + content: + application/json: + schema: + $ref: '#/components/schemas/UnsupportedMediaTypeError' + examples: + example: + value: "{\n \"message\" : \"Unsupported media type.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"The request entity has a media type\ + \ {mediaType} which the resource does not support.\",\n \"\ + key\" : \"common.api.mediaType\",\n \"context\" : {\n \ + \ \"mediaType\" : \"application/javascript\"\n }\n } ]\n}" + "404": + description: NotFound + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + examples: + example: + value: "{\n \"message\" : \"Resource not found.\",\n \"errors\"\ + \ : [ {\n \"message\" : \"Unable to find the resource requested\ + \ resource: {resource}.\",\n \"key\" : \"common.api.resource\"\ + ,\n \"context\" : {\n \"resource\" : \"aResource\"\n \ + \ }\n } ]\n}" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedAltError' + examples: + example: + value: "{\n \"message\" : \"Access to requested resource denied.\"\ + ,\n \"errors\" : [ {\n \"message\" : \"Resource access denied\ + \ due to invalid credentials.\",\n \"key\" : \"common.api.token\"\ + ,\n \"context\" : {\n \"accessToken\" : \"expired\"\n\ + \ }\n } ]\n}" +components: + schemas: + BadRequestError: + required: + - message + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' + BbAccessControl: + required: + - function + - privilege + - resource + type: object + properties: + resource: + type: string + description: "Resource being protected, e.g. 'User'" + function: + type: string + description: "Business function, e.g. 'Manage Users'" + privilege: + type: string + description: "The privilege required, e.g. 'view'" + BbApiDeprecation: + required: + - deprecatedFromVersion + - description + - reason + - removedFromVersion + type: object + properties: + deprecatedFromVersion: + type: string + description: Version of the product from which the endpoint has been deprecated + and should no longer be used + deprecated: true + removedFromVersion: + type: string + description: Version of the product from which the API endpoint will be + removed + reason: + type: string + description: The reason the API endpoint was deprecated + deprecated: true + description: + type: string + description: "Any further information, e.g. migration information" + Currency: + title: Monetary Amount + required: + - amount + - currencyCode + type: object + properties: + amount: + type: string + description: The amount in the specified currency + currencyCode: + pattern: "^[A-Z]{3}$" + type: string + description: The alpha-3 code (complying with ISO 4217) of the currency + that qualifies the amount + description: Schema defining monetary amount in given currency. + ErrorItem: + type: object + properties: + message: + type: string + description: Default Message. Any further information. + key: + type: string + description: "{capability-name}.api.{api-key-name}. For generated validation\ + \ errors this is the path in the document the error resolves to. e.g.\ + \ object name + '.' + field" + context: + type: object + description: Context can be anything used to construct localised messages. + description: A validation error + ForbiddenError: + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' + InternalServerError: + required: + - message + type: object + properties: + message: + type: string + description: Further Information + description: Represents HTTP 500 Internal Server Error + NotAcceptableError: + type: object + properties: + message: + type: string + supportedMediaTypes: + type: array + description: List of supported media types for this endpoint + items: + type: string + NotFoundError: + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' + ObjectWrappingException: + type: object + properties: + message: + type: string + data: + type: object + PaymentCard: + required: + - cvc + - expiryDate + - id + - nameOnCard + - pan + - startDate + type: object + properties: + id: + type: string + pan: + maxLength: 19 + type: string + description: "Must be sixteen digits, optionally in blocks of 4 separated\ + \ by a dash" + cvc: + maxLength: 3 + minLength: 3 + type: string + description: Card Verification Code + startDate: + pattern: "^(0[1-9]|1[0-2])/?([0-9]{4}|[0-9]{2})$" + type: string + description: "Must be in one of these four formats: MM/YY MMYY MMYYYY MM/YYYY" + expiryDate: + pattern: "^(0[1-9]|1[0-2])/?([0-9]{4}|[0-9]{2})$" + type: string + description: "Must be in one of these four formats: MM/YY MMYY MMYYYY MM/YYYY" + nameOnCard: + type: string + creationDate: + type: string + format: date-time + balance: + $ref: '#/components/schemas/Currency' + apr: + type: number + cardtype: + type: string + enum: + - CREDIT + - DEBIT + - PREPAID + PaymentCards: + type: array + items: + $ref: '#/components/schemas/PaymentCard' + PaymentCardsPostResponseBody: + type: object + properties: + id: + type: string + example: + id: a5b0fe7d-c4dd-40a7-bd80-dfc7869327e1 + RequiredbooleanqueryparamGetResponseBody: + type: object + properties: + message: + type: string + TestHeadersResponseBody: + type: object + properties: + requests: + type: array + items: + type: object + properties: + name: + type: string + url: + type: string + headers: + type: object + UnauthorizedAltError: + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' + UnauthorizedError: + required: + - message + type: object + properties: + message: + type: string + UnsupportedMediaTypeError: + type: object + properties: + message: + type: string + description: Any further information + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/ErrorItem' diff --git a/boat-maven-plugin/src/it/example/pom.xml b/boat-maven-plugin/src/it/example/pom.xml index 4c8001a69..58f0aa68f 100644 --- a/boat-maven-plugin/src/it/example/pom.xml +++ b/boat-maven-plugin/src/it/example/pom.xml @@ -40,6 +40,8 @@ BOAT :: Examples + boat-multiple-executions + boat-artifact-input boat-doc boat-export boat-generate diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/AbstractGenerateMojo.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/AbstractGenerateMojo.java index bab59ae21..1d7385642 100644 --- a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/AbstractGenerateMojo.java +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/AbstractGenerateMojo.java @@ -3,11 +3,12 @@ import java.util.HashMap; import java.util.Map; import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; public abstract class AbstractGenerateMojo extends GenerateMojo { public void execute(String generatorName, String library, boolean isEmbedded, boolean reactive, boolean generateSupportingFiles) - throws MojoExecutionException { + throws MojoExecutionException, MojoFailureException { Map options = new HashMap<>(); options.put("library", library); options.put("java8", "true"); diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/AbstractLintMojo.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/AbstractLintMojo.java index 867000785..81cb645e2 100644 --- a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/AbstractLintMojo.java +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/AbstractLintMojo.java @@ -9,8 +9,8 @@ import java.util.Arrays; import java.util.List; import lombok.extern.slf4j.Slf4j; -import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Parameter; @SuppressWarnings("FieldMayBeFinal") @@ -18,13 +18,8 @@ /* Lint Specification */ -public abstract class AbstractLintMojo extends AbstractMojo { +public abstract class AbstractLintMojo extends InputMavenArtifactMojo { - /** - * Input spec directory or file. - */ - @Parameter(name = "inputSpec", property = "inputSpec", required = true) - protected File inputSpec; /** * Set this to true to fail in case a warning is found. @@ -47,18 +42,21 @@ public abstract class AbstractLintMojo extends AbstractMojo { "151","129","146","147","172","145","115","132","120", "134","183","154","105","104","130","118","110","153", "101","176","116","M009","H002","M010","H001","M008","S005","S006","S007","M011"}; - protected List lint() throws MojoExecutionException { + protected List lint() throws MojoExecutionException, MojoFailureException { + + super.execute(); + List boatLintReports = new ArrayList<>(); File[] inputFiles; - if (inputSpec.isDirectory()) { - inputFiles = inputSpec.listFiles(pathname -> pathname.getName().endsWith(".yaml")); + if (input.isDirectory()) { + inputFiles = input.listFiles(pathname -> pathname.getName().endsWith(".yaml")); if (inputFiles == null || inputFiles.length == 0) { throw new MojoExecutionException("No OpenAPI specs found in: " + inputSpec); } log.info("Found " + inputFiles.length + " specs to lint."); } else { - inputFiles = new File[]{inputSpec}; + inputFiles = new File[]{input}; } for (File inputFile : inputFiles) { @@ -82,8 +80,9 @@ private BoatLintReport lintOpenAPI(File inputFile) throws MojoExecutionException } } + @Override public void setInput(File input) { - this.inputSpec = input; + this.input = input; } public void setFailOnWarning(boolean failOnWarning) { diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/AbstractRamlToOpenApi.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/AbstractRamlToOpenApi.java index 522451aa9..caa0fab21 100644 --- a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/AbstractRamlToOpenApi.java +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/AbstractRamlToOpenApi.java @@ -40,9 +40,6 @@ import org.eclipse.aether.impl.ArtifactResolver; import org.eclipse.aether.impl.MetadataResolver; import org.eclipse.aether.repository.RemoteRepository; -import org.eclipse.aether.resolution.ArtifactRequest; -import org.eclipse.aether.resolution.ArtifactResolutionException; -import org.eclipse.aether.resolution.ArtifactResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -467,23 +464,6 @@ public void setServers(List servers) { this.servers = servers; } - public ArtifactResult resolveArtifactFromRepositories(org.eclipse.aether.artifact.Artifact artifact) { - ArtifactRequest artifactRequest = getArtifactRequest(artifact); - - ArtifactResult artifactResult = null; - try { - artifactResult = artifactResolver.resolveArtifact(repositorySession, artifactRequest); - } catch (ArtifactResolutionException e) { - throw new IllegalArgumentException("Cannot resolve artifact: " + artifact); - } - return artifactResult; - - } - - private ArtifactRequest getArtifactRequest(org.eclipse.aether.artifact.Artifact artifact) { - return new ArtifactRequest(artifact, remoteRepositories, null); - } - protected DefaultArtifact createNewDefaultArtifact(Dependency dependency) { return new DefaultArtifact(dependency.getGroupId() , dependency.getArtifactId() diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/ArtifactRepositoryResolver.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/ArtifactRepositoryResolver.java new file mode 100644 index 000000000..090b7010d --- /dev/null +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/ArtifactRepositoryResolver.java @@ -0,0 +1,44 @@ +package com.backbase.oss.boat; + +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.impl.ArtifactResolver; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResolutionException; +import org.eclipse.aether.resolution.ArtifactResult; + +import java.util.List; + +public class ArtifactRepositoryResolver { + + private final ArtifactResolver artifactResolver; + private final RepositorySystemSession repositorySession; + private final List remoteRepositories; + + public ArtifactRepositoryResolver(ArtifactResolver artifactResolver, RepositorySystemSession repositorySession, List remoteRepositories) { + this.artifactResolver = artifactResolver; + this.repositorySession = repositorySession; + this.remoteRepositories = remoteRepositories; + } + + + public ArtifactResult resolveArtifactFromRepositories(org.eclipse.aether.artifact.Artifact artifact) { + ArtifactRequest artifactRequest = getArtifactRequest(artifact); + + ArtifactResult artifactResult = null; + try { + artifactResult = artifactResolver.resolveArtifact(repositorySession, artifactRequest); + } catch (ArtifactResolutionException e) { + throw new IllegalArgumentException("Cannot resolve artifact: " + artifact); + } + return artifactResult; + + } + + + private ArtifactRequest getArtifactRequest(org.eclipse.aether.artifact.Artifact artifact) { + + return new ArtifactRequest(artifact, remoteRepositories, null); + } + +} diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/BundleMojo.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/BundleMojo.java index be7f74223..fd105a2ea 100644 --- a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/BundleMojo.java +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/BundleMojo.java @@ -9,8 +9,8 @@ import com.backbase.oss.boat.loader.OpenAPILoaderException; import com.backbase.oss.boat.serializer.SerializerUtils; import com.backbase.oss.boat.transformers.Bundler; -import com.backbase.oss.boat.transformers.SpecVersionTransformer; -import com.backbase.oss.boat.transformers.VendorExtensionFilter; +import com.backbase.oss.boat.transformers.SetVersion; +import com.backbase.oss.boat.transformers.ExtensionFilter; import io.swagger.v3.oas.models.OpenAPI; import java.io.File; import java.io.IOException; @@ -110,7 +110,7 @@ private void bundleOpenAPI(File inputFile, File outputFile) throws MojoExecution OpenAPI openAPI = OpenAPILoader.load(inputFile); if (isNotBlank(version)) { - openAPI = new SpecVersionTransformer(version) + openAPI = new SetVersion(version) .transform(openAPI); } @@ -118,7 +118,7 @@ private void bundleOpenAPI(File inputFile, File outputFile) throws MojoExecution .transform(openAPI); if (isNotEmpty(removeExtensions)) { - openAPI = new VendorExtensionFilter() + openAPI = new ExtensionFilter() .transform(openAPI, singletonMap("remove", removeExtensions)); } diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/ExportBomMojo.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/ExportBomMojo.java index e64536660..94a6a149c 100644 --- a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/ExportBomMojo.java +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/ExportBomMojo.java @@ -64,6 +64,8 @@ public class ExportBomMojo extends AbstractRamlToOpenApi { @Parameter(name = "addChangeLog", defaultValue = "true") private boolean addChangeLog; + public void setSpecBom(Dependency specBom){this.specBom = specBom;} + @Override public void execute() throws MojoExecutionException { @@ -113,7 +115,7 @@ public void execute() throws MojoExecutionException { .filter(versionRange::containsVersion) .distinct() .map(this::convertToArtifact) - .map(this::resolveArtifactFromRepositories) + .map(defaultArtifact -> new ArtifactRepositoryResolver(artifactResolver,repositorySession,remoteRepositories).resolveArtifactFromRepositories(defaultArtifact)) .map(this::parsePomFile) .map(this::groupArtifactsPerVersionAndCapability) .collect(Collectors.toList()); @@ -147,7 +149,7 @@ private Pair>> groupArtifactsPerVers .filter(this::isIncludedSpec) .map(this::createNewDefaultArtifact) .distinct() - .map(this::resolveArtifactFromRepositories) + .map(defaultArtifact -> new ArtifactRepositoryResolver(artifactResolver,repositorySession,remoteRepositories).resolveArtifactFromRepositories(defaultArtifact)) .collect(Collectors .groupingBy(artifactResult -> artifactResult.getArtifact().getGroupId(), TreeMap::new, Collectors.toSet())); diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateDocMojo.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateDocMojo.java index 5424e916b..1b16ae73b 100644 --- a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateDocMojo.java +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateDocMojo.java @@ -2,14 +2,15 @@ import lombok.extern.slf4j.Slf4j; import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; @Mojo(name = "doc", threadSafe = true) @Slf4j -public class GenerateDocMojo extends GenerateMojo { +public class GenerateDocMojo extends GenerateFromDirectoryDocMojo { @Override - public void execute() throws MojoExecutionException { + public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("Generating Boat Docs"); generatorName = "boat-docs"; super.execute(); diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateFromDirectoryDocMojo.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateFromDirectoryDocMojo.java new file mode 100644 index 000000000..dbb31e553 --- /dev/null +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateFromDirectoryDocMojo.java @@ -0,0 +1,61 @@ +package com.backbase.oss.boat; + +import lombok.extern.slf4j.Slf4j; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +import java.io.File; + +/** + * Allows generate::Doc to accept inputSpec as a directory + * Output docs will be placed in separate folders for each spec + */ +@Slf4j +public class GenerateFromDirectoryDocMojo extends GenerateMojo { + + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + if (inputSpec != null) { + File inputSpecFile = new File(inputSpec); + fileInputExecute(inputSpecFile); + } else { + log.info("Input read as Artifact"); + super.execute(); + } + } + + private void fileInputExecute(File inputSpecFile) throws MojoExecutionException, MojoFailureException { + + if (inputSpecFile.isDirectory()) { + log.info("inputSpec is being read as a directory"); + + File[] inputSpecs; + File outPutDirectory = output; + + inputSpecs = inputSpecFile.listFiles(pathname -> pathname.getName().endsWith(".yaml")); + + if (inputSpecs == null || inputSpecs.length == 0) { + throw new MojoExecutionException("No OpenAPI specs found in: " + inputSpec); + } + + for (File f : inputSpecs) { + inputSpec = f.getPath(); + output = new File(outPutDirectory.getPath(), f.getName().substring(0, f.getName().lastIndexOf(".")).concat("-docs")); + + if (!output.exists()) { + output.mkdir(); + } + + log.info(" Generating docs for spec {} in directory", f.getName()); + super.execute(); + } + + } else { + + log.info("inputSpec being read as a single file"); + super.execute(); + + } + } +} diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateMojo.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateMojo.java index bb7191f1d..d3193be90 100644 --- a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateMojo.java +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateMojo.java @@ -31,15 +31,15 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.Set; +import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.project.MavenProject; import org.openapitools.codegen.CliOption; import org.openapitools.codegen.ClientOptInput; import org.openapitools.codegen.CodegenConfig; @@ -77,7 +77,7 @@ @SuppressWarnings({"DefaultAnnotationParam", "java:S3776", "java:S5411"}) @Mojo(name = "generate", defaultPhase = LifecyclePhase.GENERATE_SOURCES, threadSafe = true) @Slf4j -public class GenerateMojo extends AbstractMojo { +public class GenerateMojo extends InputMavenArtifactMojo { private static String trimCSV(String text) { if (isNotEmpty(text)) { @@ -136,21 +136,6 @@ private static String trimCSV(String text) { protected File copyTo; - /** - * Location of the OpenAPI spec, as URL or local file glob pattern. - *

- * If the input is a local file, the value of this property is considered a glob pattern that must - * resolve to a unique file. - *

- *

- * The glob pattern allows to express the input specification in a version neutral way. For - * instance, if the actual file is {@code my-service-api-v3.1.4.yaml} the expression could be - * {@code my-service-api-v*.yaml}. - *

- */ - @Parameter(name = "inputSpec", property = "openapi.generator.maven.plugin.inputSpec", required = true) - protected String inputSpec; - /** * Git host, e.g. gitlab.com. */ @@ -486,11 +471,7 @@ private static String trimCSV(String text) { @Parameter(name = "writeDebugFiles") protected boolean writeDebugFiles = false; - /** - * The project being built. - */ - @Parameter(readonly = true, required = true, defaultValue = "${project}") - protected MavenProject project; + public void setBuildContext(BuildContext buildContext) { this.buildContext = buildContext; @@ -498,13 +479,15 @@ public void setBuildContext(BuildContext buildContext) { @Override @SuppressWarnings({"java:S3776", "java:S1874"}) - public void execute() throws MojoExecutionException { + public void execute() throws MojoExecutionException, MojoFailureException { + if (skip) { getLog().info("Code generation is skipped."); return; } + super.execute(); File inputSpecFile = new File(inputSpec); File inputParent = inputSpecFile.getParentFile(); @@ -524,6 +507,11 @@ public void execute() throws MojoExecutionException { break; default: + String message = format("Input spec %s matches more than one single file", inputSpec); + getLog().error(message); + Stream.of(files).forEach(f -> { + getLog().error(format(" %s", f)); + }); throw new MojoExecutionException( format("Input spec %s matches more than one single file", inputSpec)); } diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateRestTemplateEmbeddedMojo.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateRestTemplateEmbeddedMojo.java index f21a705a4..53eb60342 100644 --- a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateRestTemplateEmbeddedMojo.java +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateRestTemplateEmbeddedMojo.java @@ -1,6 +1,7 @@ package com.backbase.oss.boat; import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; /** @@ -10,7 +11,7 @@ public class GenerateRestTemplateEmbeddedMojo extends AbstractGenerateMojo { @Override - public void execute() throws MojoExecutionException { + public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("Generating Client using Spring Rest Template"); execute("java", "resttemplate", true, false, true); } diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateSpringBootEmbeddedMojo.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateSpringBootEmbeddedMojo.java index 15f17d5d2..f4bd2a569 100644 --- a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateSpringBootEmbeddedMojo.java +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateSpringBootEmbeddedMojo.java @@ -1,6 +1,7 @@ package com.backbase.oss.boat; import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; /** @@ -10,7 +11,7 @@ public class GenerateSpringBootEmbeddedMojo extends AbstractGenerateMojo { @Override - public void execute() throws MojoExecutionException { + public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("Generating Server Stubs using Spring Boot"); execute("spring", "spring-boot", true, false, false); } diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateWebClientEmbeddedMojo.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateWebClientEmbeddedMojo.java index 76bdc7c50..bf8ad91fc 100644 --- a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateWebClientEmbeddedMojo.java +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/GenerateWebClientEmbeddedMojo.java @@ -1,6 +1,7 @@ package com.backbase.oss.boat; import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; /** @@ -10,7 +11,7 @@ public class GenerateWebClientEmbeddedMojo extends AbstractGenerateMojo { @Override - public void execute() throws MojoExecutionException { + public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("Generating Server Stubs using Web Client Boot"); execute("java", "webclient", true, true, true); } diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/InputArtifact.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/InputArtifact.java new file mode 100644 index 000000000..da7e17a27 --- /dev/null +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/InputArtifact.java @@ -0,0 +1,42 @@ +package com.backbase.oss.boat; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + + +@Getter +@Setter +@Slf4j +public class InputArtifact { + private String groupId; + private String artifactId; + private String version; + private String type; + private String classifier; + private String fileName; + private boolean overWrite; + private boolean overWriteIfNewer; + private boolean needsProcessing; + + public boolean isNeedsProcessing(File destFile, File originFile){ + needsProcessing = overWrite || !destFile.exists() || (overWriteIfNewer && isNewer(destFile,originFile)); + return needsProcessing; + } + + private boolean isNewer( File destFile, File originFile ) { + try { + long destMod = Files.getLastModifiedTime( destFile.toPath() ).toMillis(); + long originMod = Files.getLastModifiedTime( originFile.toPath() ).toMillis(); + return originMod > destMod; + } catch (IOException e) { + log.debug("Assuming artifact was not modified since artifact was last downloaded, cannot last read modified time"); + return false; + } + + } +} diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/InputMavenArtifactMojo.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/InputMavenArtifactMojo.java new file mode 100644 index 000000000..579c1d310 --- /dev/null +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/InputMavenArtifactMojo.java @@ -0,0 +1,181 @@ +package com.backbase.oss.boat; + + +import lombok.extern.slf4j.Slf4j; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.util.Expand; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.impl.ArtifactResolver; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.resolution.ArtifactResult; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Slf4j +public class InputMavenArtifactMojo extends AbstractMojo { + + /** + * A maven artifact containing a spec, or multiple to be processed + */ + @Parameter(name = "inputMavenArtifact", property = "inputMavenArtifact", required = false) + protected InputArtifact inputMavenArtifact; + + /** + * File input used for Linting and Validating + * Can be directory or file + */ + @Parameter(name = "input", required = false) + protected File input; + + /** + * Location of the OpenAPI spec, as URL or local file glob pattern. + *

+ * If the input is a local file, the value of this property is considered a glob pattern that must + * resolve to a unique file. + *

+ *

+ * The glob pattern allows to express the input specification in a version neutral way. For + * instance, if the actual file is {@code my-service-api-v3.1.4.yaml} the expression could be + * {@code my-service-api-v*.yaml}. + *

+ */ + @Parameter(name = "inputSpec", property = "openapi.generator.maven.plugin.inputSpec", required = false) + protected String inputSpec; + + /** + * The project being built. + */ + @Parameter(readonly = true, required = true, defaultValue = "${project}") + protected MavenProject project; + + /** + * Used to set up artifact request + */ + @Parameter(defaultValue = "${repositorySystemSession}", readonly = true) + protected RepositorySystemSession repositorySession; + + /** + * Used to look up Artifacts in the remote repository. + */ + @Component + protected ArtifactResolver artifactResolver; + + /** + * List of Remote Repositories used by the resolver. + */ + @Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly = true) + protected List remoteRepositories; + + private final ReentrantLock reLock = new ReentrantLock(true); + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + + if (inputMavenArtifact != null && inputMavenArtifact.getArtifactId() != null) { + getArtifact(); + } + + if (input == null && inputSpec == null && inputMavenArtifact == null) { + throw new MojoExecutionException("Missing input from plugin, input options are: inputMavenArtifact, input, inputSpec"); + } + + if (input == null) { + input = new File(inputSpec); + } + + } + + + private void getArtifact() throws MojoExecutionException { + ArtifactResult result; + + File specUnzipDirectory = new File(project.getBuild().getDirectory() + + File.separator + "input-artifact" + File.separator + + inputMavenArtifact.getArtifactId(), inputMavenArtifact.getVersion()); + + + + + + // The artifact will be downloaded to the local repository if necessary. An artifact that is already resolved will + // be skipped and is not re-resolved. + result = new ArtifactRepositoryResolver(artifactResolver, repositorySession, remoteRepositories).resolveArtifactFromRepositories(new DefaultArtifact(inputMavenArtifact.getGroupId() + , inputMavenArtifact.getArtifactId() + , inputMavenArtifact.getClassifier() + , inputMavenArtifact.getType() + , inputMavenArtifact.getVersion())); + + if (inputMavenArtifact.isNeedsProcessing(specUnzipDirectory,result.getArtifact().getFile())) { + + unzipSpec(result.getArtifact().getFile(), specUnzipDirectory); + + } + + try (Stream walk = Files.walk(specUnzipDirectory.toPath())) { + + List paths = walk + .filter(Files::isRegularFile) + .filter(path -> path.endsWith(inputMavenArtifact.getFileName())) + .map(Path::toString) + .collect(Collectors.toList()); + + if (paths.size() > 1) { + log.info("found multiple files of matching {} in zip, using {}", inputMavenArtifact.getFileName(), paths.get(0)); + } else if (paths.isEmpty()) { + throw new MojoExecutionException("no file matching " + inputMavenArtifact.getFileName() + " was found in artifact zip"); + } + + inputSpec = paths.get(0); + input = new File(paths.get(0)); + + } catch (IOException e) { + log.debug(e.getMessage()); + throw new MojoExecutionException("Could not search unzipped artifact directory"); + } + + } + + + private void unzipSpec(File inputFile, File specUnzipDirectory) throws MojoExecutionException { + reLock.lock(); + try { + specUnzipDirectory.mkdirs(); + unzip(inputFile, specUnzipDirectory); + } catch (Exception e) { + reLock.unlock(); + throw new MojoExecutionException("Error extracting spec: " + inputFile, e); + }finally { + reLock.unlock(); + } + } + + private void unzip(File source, File out) throws Exception { + Expand expand = new Expand(); + expand.setSrc(source); + expand.setDest(out); + expand.setOverwrite(true); + expand.execute(); + } + + public void setInput(File input) { + this.input = input; + } + +} + + + + diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/transformers/Merge.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/transformers/Merge.java new file mode 100644 index 000000000..509904e9e --- /dev/null +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/transformers/Merge.java @@ -0,0 +1,22 @@ +package com.backbase.oss.boat.transformers; + +import lombok.NoArgsConstructor; +import org.codehaus.plexus.components.io.filemappers.MergeFileMapper; + +/** + * Merge mapper, easy to configure in {@code pom.xml} + */ +@NoArgsConstructor +public class Merge extends MergeFileMapper { + + public Merge(String value) { + set(value); + } + + /** + * Default setter, used at creation from POM configuration. + */ + public void set(String value) { + setTargetName(value); + } +} diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/transformers/Prefix.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/transformers/Prefix.java new file mode 100644 index 000000000..ea16544da --- /dev/null +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/transformers/Prefix.java @@ -0,0 +1,22 @@ +package com.backbase.oss.boat.transformers; + +import lombok.NoArgsConstructor; +import org.codehaus.plexus.components.io.filemappers.PrefixFileMapper; + +/** + * Prefix mapper, easy to configure in {@code pom.xml} + */ +@NoArgsConstructor +public class Prefix extends PrefixFileMapper { + + public Prefix(String value) { + set(value); + } + + /** + * Default setter, used at creation from POM configuration. + */ + public void set(String value) { + setPrefix(value); + } +} diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/transformers/Regex.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/transformers/Regex.java new file mode 100644 index 000000000..fbd67ad72 --- /dev/null +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/transformers/Regex.java @@ -0,0 +1,16 @@ +package com.backbase.oss.boat.transformers; + +import lombok.NoArgsConstructor; +import org.codehaus.plexus.components.io.filemappers.RegExpFileMapper; + +/** + * Regex mapper, easy to configure in {@code pom.xml} + */ +@NoArgsConstructor +public class Regex extends RegExpFileMapper { + + public Regex(String pattern, String replacement) { + setPattern(pattern); + setReplacement(replacement); + } +} diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/transformers/Suffix.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/transformers/Suffix.java new file mode 100644 index 000000000..60ab2a34b --- /dev/null +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/transformers/Suffix.java @@ -0,0 +1,20 @@ +package com.backbase.oss.boat.transformers; + +import org.codehaus.plexus.components.io.filemappers.SuffixFileMapper; + +/** + * Suffix mapper, easy to configure in {@code pom.xml} + */ +public class Suffix extends SuffixFileMapper { + + public Suffix(String value) { + set(value); + } + + /** + * Default setter, used at creation from POM configuration. + */ + public void set(String value) { + setSuffix(value); + } +} diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/transformers/TransformMojo.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/transformers/TransformMojo.java new file mode 100644 index 000000000..c68e55f75 --- /dev/null +++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/transformers/TransformMojo.java @@ -0,0 +1,226 @@ +package com.backbase.oss.boat.transformers; + +import com.backbase.oss.boat.loader.OpenAPILoader; +import com.backbase.oss.boat.serializer.SerializerUtils; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.parser.core.models.AuthorizationValue; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.SneakyThrows; +import org.apache.commons.io.FilenameUtils; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.settings.Server; +import org.apache.maven.settings.building.SettingsProblem; +import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest; +import org.apache.maven.settings.crypto.SettingsDecrypter; +import org.apache.maven.settings.crypto.SettingsDecryptionRequest; +import org.apache.maven.settings.crypto.SettingsDecryptionResult; +import org.codehaus.plexus.components.io.filemappers.FileMapper; + +import static java.lang.String.format; +import static java.util.Optional.ofNullable; +import static org.apache.commons.lang3.StringUtils.isEmpty; + +/** + * Apply transformers to an existing specification. + */ +@Mojo(name = "transform", defaultPhase = LifecyclePhase.GENERATE_SOURCES, threadSafe = true) +public class TransformMojo extends AbstractMojo { + + /** + * Whether to skip the execution of this goal. + */ + @Parameter(property = "boat.transform.skip", alias = "codegen.skip") + boolean skip; + + /** + * A list of input specifications. + */ + @Parameter(property = "boat.transform.inputs", required = true) + final List inputs = new ArrayList<>(); + + /** + * Target directory of the transformed specifications. + */ + @Parameter(property = "boat.transform.output", defaultValue = "${project.build.directory}") + File output; + + /** + * The list of transformers to be applied to each input specification. + */ + @Parameter(required = true) + final List pipeline = new ArrayList<>(); + + /** + * File name mappers used to generate the output file name, instances of + * {@code org.codehaus.plexus.components.io.filemappers.FileMapper}. + * + *

+ * The following mappers can be used without needing to specify the FQCN of the implementation. + *

+ *
regexp
+ *
{@code org.codehaus.plexus.components.io.filemappers.RegExpFileMapper}
+ *
merge
+ *
{@code org.codehaus.plexus.components.io.filemappers.MergeFileMapper}
+ *
prefix
+ *
{@code org.codehaus.plexus.components.io.filemappers.PrefixFileMapper}
+ *
suffix
+ *
{@code org.codehaus.plexus.components.io.filemappers.SuffixFileMapper}
+ *
+ *

+ * + * The parameter defaults to + *
+     *  <mappers>
+     *    <suffix>-transformed</suffix>
+     *  </mappers>
+     * 
+ *
+ * + * @see org.codehaus.plexus.components.io.filemappers.FileMapper + */ + @Parameter + final List mappers = new ArrayList<>(); + + /** + * Additional options passed to transformers. + */ + @Parameter + final Map options = new HashMap<>(); + + /** + * Retrieves authorization from Maven's {@code settings.xml}. + */ + @Parameter(name = "serverId", property = "boat.transform.serverId") + String serverId; + + @Parameter(defaultValue = "${session}", readonly = true) + private MavenSession session; + @Component + private SettingsDecrypter decrypter; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + if (this.skip) { + getLog().info("Skipping Transform Mojo."); + + return; + } + + if (this.mappers.isEmpty()) { + this.mappers.add(new Suffix("-transformed")); + } + + final List authz = buildAuthorization(); + + try { + this.inputs.forEach(input -> transform(input, authz)); + } catch (final RuntimeException e) { + final Throwable cause = e.getCause(); + + if (cause instanceof MojoExecutionException) { + throw (MojoExecutionException) cause; + } + if (cause instanceof MojoFailureException) { + throw (MojoFailureException) cause; + } + + throw new MojoFailureException("Transformation failed", e); + } + } + + @SneakyThrows + private void transform(String input, List authz) { + OpenAPI openAPI = OpenAPILoader.load(input, false, false, authz); + + for (final Transformer t : this.pipeline) { + openAPI = t.transform(openAPI, this.options); + } + + String destName = FilenameUtils.getName(input); + + for (final FileMapper fm : this.mappers) { + destName = fm.getMappedFileName(destName); + } + + destName = FilenameUtils.separatorsToSystem(destName); + + File destFile = new File(destName); + + if (!destFile.isAbsolute()) { + destFile = new File(this.output, destName); + } + + destFile.getParentFile().mkdirs(); + + Files.write(destFile.toPath(), + SerializerUtils + .toYamlString(openAPI) + .getBytes(StandardCharsets.UTF_8), + StandardOpenOption.CREATE); + } + + private List buildAuthorization() throws MojoExecutionException { + return ofNullable(readAuthorization()) + .map(authz -> new AuthorizationValue("Authorization", "header", authz)) + .map(authz -> { + this.options.put("authz", authz); + + return authz; + }) + .map(Arrays::asList) + .orElse(null); + } + + private String readAuthorization() throws MojoExecutionException { + if (isEmpty(this.serverId)) { + return null; + } + + final Server server = this.session.getSettings().getServer(this.serverId); + + if (server == null) { + throw new MojoExecutionException(format("Cannot find serverId \"%s\" in Maven settings", this.serverId)); + } + + final SettingsDecryptionRequest request = new DefaultSettingsDecryptionRequest(server); + final SettingsDecryptionResult result = this.decrypter.decrypt(request); + + // Un-encrypted passwords are passed through, so a problem indicates a real issue. + // If there are any ERROR or FATAL problems reported, then decryption failed. + for (final SettingsProblem problem : result.getProblems()) { + switch (problem.getSeverity()) { + case ERROR: + case FATAL: + throw new MojoExecutionException( + format("Unable to decrypt serverId \"%s\":%s ", this.serverId, problem)); + + default: + getLog().warn(format("Decrypting \"%s\": %s", this.serverId, problem)); + } + } + + final Server resultServer = result.getServer(); + final String username = resultServer.getUsername(); + final String password = resultServer.getPassword(); + final String auth = username + ":" + password; + + return "Basic " + Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8)); + } +} + diff --git a/boat-maven-plugin/src/test/java/com/backbase/oss/boat/ArtifactMojoTests.java b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/ArtifactMojoTests.java new file mode 100644 index 000000000..f3272fbb2 --- /dev/null +++ b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/ArtifactMojoTests.java @@ -0,0 +1,266 @@ +package com.backbase.oss.boat; + +import org.apache.maven.model.Build; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.logging.console.ConsoleLogger; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.impl.ArtifactResolver; +import org.eclipse.aether.resolution.ArtifactResolutionException; +import org.eclipse.aether.resolution.ArtifactResult; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.sonatype.plexus.build.incremental.DefaultBuildContext; + +import java.io.File; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class ArtifactMojoTests { + + ArtifactResolver artifactResolver; + ArtifactResult artifactResult; + + @Test + void testArtifactResolver(){ + + GenerateDocMojo mojo = new GenerateDocMojo(); + File output = new File("target/boat-docs"); + if (!output.exists()) { + output.mkdirs(); + } + + DefaultBuildContext defaultBuildContext = new DefaultBuildContext(); + defaultBuildContext.enableLogging(new ConsoleLogger()); + + mojo.getLog(); + mojo.buildContext = defaultBuildContext; + mojo.project = new MavenProject(); + mojo.output = output; + mojo.skip = false; + mojo.skipIfSpecIsUnchanged = false; + mojo.bundleSpecs = true; + mojo.dereferenceComponents = true; + + assertThrows(MojoExecutionException.class, mojo::execute); + + } + + @Test + void testArtifactInputMojo() throws ArtifactResolutionException, MojoFailureException, MojoExecutionException { + File file = getFile("/oas-examples/openapi-zips-1.0.0-SNAPSHOT-api.zip"); + artifactResolver = mock(ArtifactResolver.class); + artifactResult = mock( ArtifactResult.class); + org.eclipse.aether.artifact.Artifact artifact = mock(org.eclipse.aether.artifact.Artifact.class); + + when(artifactResolver.resolveArtifact(any(),any())).thenReturn(artifactResult); + when(artifactResult.getArtifact()).thenReturn(artifact); + when(artifact.getFile()).thenReturn(file); + + GenerateDocMojo mojo = new GenerateDocMojo(); + File output = new File("target/boat-docs"); + if (!output.exists()) { + output.mkdirs(); + } + + DefaultBuildContext defaultBuildContext = new DefaultBuildContext(); + defaultBuildContext.enableLogging(new ConsoleLogger()); + InputArtifact inputArtifact = new InputArtifact(); + inputArtifact.setVersion("1.0.0-SNAPSHOT"); + inputArtifact.setGroupId("test.groupId"); + inputArtifact.setArtifactId("openapi-zips"); + inputArtifact.setClassifier("api"); + inputArtifact.setType("zip"); + inputArtifact.setFileName("presentation-integration-api/openapi.yaml"); + + mojo.inputMavenArtifact=inputArtifact; + mojo.getLog(); + mojo.buildContext = defaultBuildContext; + mojo.artifactResolver = artifactResolver; + Build build = new Build(); + build.setDirectory("target"); + + + MavenProject project = new MavenProject(); + + project.setBuild(build); + mojo.project = project; + mojo.repositorySession = mock(RepositorySystemSession.class); + mojo.output = output; + mojo.skip = false; + mojo.skipIfSpecIsUnchanged = false; + mojo.bundleSpecs = true; + mojo.dereferenceComponents = true; + mojo.execute(); + + assertThat(output.list()).containsExactlyInAnyOrder("index.html", ".openapi-generator-ignore", ".openapi-generator"); + + } + @Test + void testArtifactInputMojoOverwrite() throws ArtifactResolutionException, MojoFailureException, MojoExecutionException { + File file = getFile("/oas-examples/openapi-zips-1.0.0-SNAPSHOT-api.zip"); + artifactResolver = mock(ArtifactResolver.class); + artifactResult = mock( ArtifactResult.class); + org.eclipse.aether.artifact.Artifact artifact = mock(org.eclipse.aether.artifact.Artifact.class); + + when(artifactResolver.resolveArtifact(any(),any())).thenReturn(artifactResult); + when(artifactResult.getArtifact()).thenReturn(artifact); + when(artifact.getFile()).thenReturn(file); + + GenerateDocMojo mojo = new GenerateDocMojo(); + File output = new File("target/boat-docs"); + if (!output.exists()) { + output.mkdirs(); + } + + DefaultBuildContext defaultBuildContext = new DefaultBuildContext(); + defaultBuildContext.enableLogging(new ConsoleLogger()); + InputArtifact inputArtifact = new InputArtifact(); + inputArtifact.setVersion("1.0.0-SNAPSHOT"); + inputArtifact.setGroupId("test.groupId"); + inputArtifact.setArtifactId("openapi-zips"); + inputArtifact.setClassifier("api"); + inputArtifact.setType("zip"); + inputArtifact.setOverWriteIfNewer(true); + inputArtifact.setFileName("presentation-integration-api/openapi.yaml"); + + mojo.inputMavenArtifact=inputArtifact; + mojo.getLog(); + mojo.buildContext = defaultBuildContext; + mojo.artifactResolver = artifactResolver; + Build build = new Build(); + build.setDirectory("target"); + + + MavenProject project = new MavenProject(); + + project.setBuild(build); + mojo.project = project; + mojo.repositorySession = mock(RepositorySystemSession.class); + mojo.output = output; + mojo.skip = false; + mojo.skipIfSpecIsUnchanged = false; + mojo.bundleSpecs = true; + mojo.dereferenceComponents = true; + mojo.execute(); + + assertThat(output.list()).containsExactlyInAnyOrder("index.html", ".openapi-generator-ignore", ".openapi-generator"); + + } + @Test + void testArtifactInputMojoDuplicateFile() throws ArtifactResolutionException, MojoFailureException, MojoExecutionException { + File file = getFile("/oas-examples/openapi-zips-1.0.0-SNAPSHOT-api.zip"); + artifactResolver = mock(ArtifactResolver.class); + artifactResult = mock( ArtifactResult.class); + org.eclipse.aether.artifact.Artifact artifact = mock(org.eclipse.aether.artifact.Artifact.class); + + when(artifactResolver.resolveArtifact(any(),any())).thenReturn(artifactResult); + when(artifactResult.getArtifact()).thenReturn(artifact); + when(artifact.getFile()).thenReturn(file); + + GenerateDocMojo mojo = new GenerateDocMojo(); + File output = new File("target/boat-docs"); + if (!output.exists()) { + output.mkdirs(); + } + + DefaultBuildContext defaultBuildContext = new DefaultBuildContext(); + defaultBuildContext.enableLogging(new ConsoleLogger()); + InputArtifact inputArtifact = new InputArtifact(); + inputArtifact.setVersion("1.0.0-SNAPSHOT"); + inputArtifact.setGroupId("test.groupId"); + inputArtifact.setArtifactId("openapi-zips"); + inputArtifact.setClassifier("api"); + inputArtifact.setType("zip"); + inputArtifact.setFileName("openapi.yaml"); + + mojo.inputMavenArtifact=inputArtifact; + mojo.getLog(); + mojo.buildContext = defaultBuildContext; + mojo.artifactResolver = artifactResolver; + Build build = new Build(); + build.setDirectory("target"); + + + MavenProject project = new MavenProject(); + + project.setBuild(build); + mojo.project = project; + mojo.repositorySession = mock(RepositorySystemSession.class); + mojo.output = output; + mojo.skip = false; + mojo.skipIfSpecIsUnchanged = false; + mojo.bundleSpecs = true; + mojo.dereferenceComponents = true; + mojo.execute(); + + assertThat(output.list()).containsExactlyInAnyOrder("index.html", ".openapi-generator-ignore", ".openapi-generator"); + + } + + @Test + void testArtifactInputMojoFail() throws ArtifactResolutionException, MojoFailureException, MojoExecutionException { + File file = getFile("/oas-examples/openapi-zips-1.0.0-SNAPSHOT-api.zip"); + artifactResolver = mock(ArtifactResolver.class); + artifactResult = mock( ArtifactResult.class); + org.eclipse.aether.artifact.Artifact artifact = mock(org.eclipse.aether.artifact.Artifact.class); + + when(artifactResolver.resolveArtifact(any(),any())).thenReturn(artifactResult); + when(artifactResult.getArtifact()).thenReturn(artifact); + when(artifact.getFile()).thenReturn(file); + + GenerateDocMojo mojo = new GenerateDocMojo(); + File output = new File("target/boat-docs"); + if (!output.exists()) { + output.mkdirs(); + } + + DefaultBuildContext defaultBuildContext = new DefaultBuildContext(); + defaultBuildContext.enableLogging(new ConsoleLogger()); + InputArtifact inputArtifact = new InputArtifact(); + inputArtifact.setVersion("1.0.0-SNAPSHOT"); + inputArtifact.setGroupId("test.groupId"); + inputArtifact.setArtifactId("openapi-zips"); + inputArtifact.setClassifier("api"); + inputArtifact.setType("zip"); + inputArtifact.setFileName("file-not-present.yaml"); + + mojo.inputMavenArtifact=inputArtifact; + mojo.getLog(); + mojo.buildContext = defaultBuildContext; + mojo.artifactResolver = artifactResolver; + Build build = new Build(); + build.setDirectory("target"); + + + MavenProject project = new MavenProject(); + + project.setBuild(build); + mojo.project = project; + mojo.repositorySession = mock(RepositorySystemSession.class); + mojo.output = output; + mojo.skip = false; + mojo.skipIfSpecIsUnchanged = false; + mojo.bundleSpecs = true; + mojo.dereferenceComponents = true; + + assertThrows(MojoExecutionException.class, mojo::execute); + + } + + + + private File getFile(String fileName) { + return new File(getClass().getResource(fileName).getFile()); + } + + + +} diff --git a/boat-maven-plugin/src/test/java/com/backbase/oss/boat/ExportBomMojoTests.java b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/ExportBomMojoTests.java new file mode 100644 index 000000000..9b800a078 --- /dev/null +++ b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/ExportBomMojoTests.java @@ -0,0 +1,155 @@ +package com.backbase.oss.boat; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.util.Collections; +import java.util.List; +import org.apache.maven.model.Build; +import org.apache.maven.model.Dependency; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.logging.console.ConsoleLogger; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.impl.ArtifactResolver; +import org.eclipse.aether.impl.MetadataResolver; +import org.eclipse.aether.metadata.Metadata; +import org.eclipse.aether.resolution.ArtifactResolutionException; +import org.eclipse.aether.resolution.ArtifactResult; +import org.eclipse.aether.resolution.MetadataResult; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.sonatype.plexus.build.incremental.DefaultBuildContext; + +@ExtendWith(MockitoExtension.class) +class ExportBomMojoTests { + @Mock + ArtifactResolver artifactResolver; + @Mock + ArtifactResult artifactResult; + @Mock + MetadataResolver metadataResolver; + + @Mock + MetadataResult metadataResult; + @Mock + Metadata metadatamock; + + @Captor + ArgumentCaptor argCaptor; + + + @Test + void testExportBomEmptyMeta() throws MojoFailureException, MojoExecutionException, ArtifactResolutionException { + artifactResolver = mock(ArtifactResolver.class); + artifactResult = mock( ArtifactResult.class); + metadataResolver = mock(MetadataResolver.class); + org.eclipse.aether.artifact.Artifact artifact = mock(org.eclipse.aether.artifact.Artifact.class); + + when(metadataResolver.resolveMetadata(any(),any())).thenReturn(Collections.singletonList(metadataResult)); + + ExportBomMojo mojo = new ExportBomMojo(); + File output = new File("target/boat-bom-export"); + if (!output.exists()) { + output.mkdirs(); + } + + DefaultBuildContext defaultBuildContext = new DefaultBuildContext(); + defaultBuildContext.enableLogging(new ConsoleLogger()); + + mojo.getLog(); + mojo.artifactResolver = artifactResolver; + mojo.metadataResolver = metadataResolver; + mojo.setSpecBom(new Dependency()); + + + Build build = new Build(); + build.setDirectory("target"); + + MavenProject project = new MavenProject(); + mojo.remoteRepositories = Collections.EMPTY_LIST; + project.setBuild(build); + mojo.project = project; + mojo.repositorySession = mock(RepositorySystemSession.class); + mojo.execute(); + + assertThat(output.list()).isEmpty(); + } + + @Test + void testExportBomUseOfArtifactResolver() throws MojoFailureException, MojoExecutionException, ArtifactResolutionException { + File versionFile = getFile("/export-bom/maven-metadata-test-example.xml"); + String groupId="test.groupId"; + String artifactId = "raml-bom"; + String type = "pom"; + String version= "[1.0.0,)"; + + File pomFile = getFile("/export-bom/raml-spec-bom/pom.xml"); + artifactResolver = mock(ArtifactResolver.class); + artifactResult = mock( ArtifactResult.class); + org.eclipse.aether.artifact.Artifact artifact; //= mock(org.eclipse.aether.artifact.Artifact.class); + artifact = new DefaultArtifact(groupId, artifactId, "", type, version,Collections.EMPTY_MAP, pomFile); + + + when(artifactResolver.resolveArtifact(any(),any())).thenReturn(artifactResult); + when(artifactResult.getArtifact()).thenReturn(artifact); + //when(artifact.getFile()).thenReturn(pomFile); + + + when(metadataResolver.resolveMetadata(any(),any())).thenReturn(Collections.singletonList(metadataResult)); + + ExportBomMojo mojo = new ExportBomMojo(); + File output = new File("target/boat-bom-export"); + if (!output.exists()) { + output.mkdirs(); + } + when(metadataResult.isResolved()).thenReturn(true); + + + doReturn(metadatamock).when(metadataResult).getMetadata(); + doReturn(versionFile).when(metadatamock).getFile(); + + DefaultBuildContext defaultBuildContext = new DefaultBuildContext(); + defaultBuildContext.enableLogging(new ConsoleLogger()); + + mojo.getLog(); + mojo.artifactResolver = artifactResolver; + mojo.metadataResolver = metadataResolver; + Dependency dependency = new Dependency(); + + dependency.setType(type); + dependency.setGroupId(groupId); + dependency.setArtifactId(artifactId); + dependency.setVersion(version); + mojo.setSpecBom(dependency); + + + Build build = new Build(); + build.setDirectory("target"); + + MavenProject project = new MavenProject(); + mojo.remoteRepositories = Collections.EMPTY_LIST; + + project.setBuild(build); + mojo.project = project; + mojo.repositorySession = mock(RepositorySystemSession.class); + mojo.execute(); + + assertThat(output.list()).isEmpty(); + + } + private File getFile(String fileName) { + return new File(getClass().getResource(fileName).getFile()); + } + +} diff --git a/boat-maven-plugin/src/test/java/com/backbase/oss/boat/ExportMojoTests.java b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/ExportMojoTests.java index b01164bc1..4a467063b 100644 --- a/boat-maven-plugin/src/test/java/com/backbase/oss/boat/ExportMojoTests.java +++ b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/ExportMojoTests.java @@ -11,7 +11,6 @@ import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.logging.console.ConsoleLogger; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.sonatype.plexus.build.incremental.DefaultBuildContext; diff --git a/boat-maven-plugin/src/test/java/com/backbase/oss/boat/GenerateMojoTests.java b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/GenerateMojoTests.java index 955725894..31457400a 100644 --- a/boat-maven-plugin/src/test/java/com/backbase/oss/boat/GenerateMojoTests.java +++ b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/GenerateMojoTests.java @@ -4,6 +4,7 @@ import com.backbase.oss.codegen.java.BoatSpringCodeGen; import java.io.File; import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.logging.console.ConsoleLogger; import org.junit.jupiter.api.BeforeEach; @@ -27,7 +28,7 @@ void setUp() { } @Test - void addTestCompileSourceRoot() throws MojoExecutionException { + void addTestCompileSourceRoot() throws MojoExecutionException, MojoFailureException { GenerateMojo mojo = configure(new GenerateMojo(), DefaultCodegen.class.getName()); mojo.addCompileSourceRoot = false; @@ -46,7 +47,7 @@ void addTestCompileSourceRoot() throws MojoExecutionException { } @Test - void useJavaBoat() throws MojoExecutionException { + void useJavaBoat() throws MojoExecutionException, MojoFailureException { GenerateMojo mojo = configure(new GenerateMojo(), "java"); mojo.execute(); @@ -55,7 +56,7 @@ void useJavaBoat() throws MojoExecutionException { } @Test - void useSpringBoat() throws MojoExecutionException { + void useSpringBoat() throws MojoExecutionException, MojoFailureException { GenerateMojo mojo = configure(new GenerateMojo(), "spring"); mojo.execute(); @@ -64,7 +65,7 @@ void useSpringBoat() throws MojoExecutionException { } @Test - void useJavaBoatForRestTemplateEmbedded() throws MojoExecutionException { + void useJavaBoatForRestTemplateEmbedded() throws MojoExecutionException, MojoFailureException { GenerateMojo mojo = configure(new GenerateRestTemplateEmbeddedMojo(), null); mojo.execute(); @@ -73,7 +74,7 @@ void useJavaBoatForRestTemplateEmbedded() throws MojoExecutionException { } @Test - void useSpringBoatForSpringBootEmbedded() throws MojoExecutionException { + void useSpringBoatForSpringBootEmbedded() throws MojoExecutionException, MojoFailureException { GenerateMojo mojo = configure(new GenerateSpringBootEmbeddedMojo(), null); mojo.execute(); @@ -82,7 +83,7 @@ void useSpringBoatForSpringBootEmbedded() throws MojoExecutionException { } @Test - void useJavaBoatForWebClientEmbedded() throws MojoExecutionException { + void useJavaBoatForWebClientEmbedded() throws MojoExecutionException, MojoFailureException { GenerateMojo mojo = configure(new GenerateWebClientEmbeddedMojo(), null); mojo.execute(); diff --git a/boat-maven-plugin/src/test/java/com/backbase/oss/boat/GeneratorTests.java b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/GeneratorTests.java index 03636495d..ed8cbd38f 100644 --- a/boat-maven-plugin/src/test/java/com/backbase/oss/boat/GeneratorTests.java +++ b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/GeneratorTests.java @@ -28,7 +28,7 @@ static void setupLocale() { } @Test - void testHtml2() throws MojoExecutionException { + void testHtml2() throws MojoExecutionException, MojoFailureException { String spec = System.getProperty("spec", getClass().getResource("/oas-examples/petstore.yaml").getFile()); GenerateMojo mojo = new GenerateMojo(); @@ -55,7 +55,7 @@ void testHtml2() throws MojoExecutionException { } @Test - void testBoatDocs() throws MojoExecutionException { + void testBoatDocs() throws MojoExecutionException, MojoFailureException { String spec = System.getProperty("spec", getClass().getResource("/oas-examples/petstore.yaml").getFile()); @@ -85,6 +85,36 @@ void testBoatDocs() throws MojoExecutionException { assertThat(output.list()).containsExactlyInAnyOrder("index.html", ".openapi-generator-ignore", ".openapi-generator"); } + @Test + void testBoatDocsWithDirectory() throws MojoExecutionException, MojoFailureException { + + String spec = System.getProperty("spec", getClass().getResource("/boat-doc-oas-examples").getFile()); + + log.info("Generating docs for: {}", spec); + + GenerateDocMojo mojo = new GenerateDocMojo(); + File input = new File(spec); + File output = new File("target/boat-docs-directory"); + if (!output.exists()) { + output.mkdirs(); + } + + DefaultBuildContext defaultBuildContext = new DefaultBuildContext(); + defaultBuildContext.enableLogging(new ConsoleLogger()); + + mojo.getLog(); + mojo.buildContext = defaultBuildContext; + mojo.project = new MavenProject(); + mojo.inputSpec = input.getAbsolutePath(); + mojo.output = output; + mojo.skip = false; + mojo.skipIfSpecIsUnchanged = false; + mojo.bundleSpecs = true; + mojo.dereferenceComponents = true; + mojo.execute(); + assertThat(output.list()).containsExactlyInAnyOrder("link-docs", "petstore-docs", "petstore-new-non-breaking-docs", "upto-docs"); + } + @Test void testBundledBoatDocs() throws MojoExecutionException, MojoFailureException { @@ -205,7 +235,7 @@ void testAngularExamplesInComponents() { } @Test - void testBeanValidation() throws MojoExecutionException { + void testBeanValidation() throws MojoExecutionException, MojoFailureException { GenerateMojo mojo = new GenerateMojo(); String inputFile = getClass().getResource("/oas-examples/petstore.yaml").getFile(); diff --git a/boat-maven-plugin/src/test/java/com/backbase/oss/boat/LintMojoTests.java b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/LintMojoTests.java index 5890d3452..86d252f93 100644 --- a/boat-maven-plugin/src/test/java/com/backbase/oss/boat/LintMojoTests.java +++ b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/LintMojoTests.java @@ -21,7 +21,7 @@ class LintMojoTests { @Test void testFailOnWarningNoWarnings() throws MojoFailureException, MojoExecutionException { LintMojo lintMojo = new LintMojo(); - lintMojo.setIgnoreRules(Arrays.array("219", "105", "104", "151", "134", "115")); + lintMojo.setIgnoreRules(Arrays.array("219", "105", "104", "151", "134", "115","M0012")); lintMojo.setInput(getFile("/oas-examples/no-lint-warnings.yaml")); lintMojo.setFailOnWarning(true); lintMojo.execute(); diff --git a/boat-maven-plugin/src/test/java/com/backbase/oss/boat/transformers/TransformMojoTest.java b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/transformers/TransformMojoTest.java new file mode 100644 index 000000000..149ee59ac --- /dev/null +++ b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/transformers/TransformMojoTest.java @@ -0,0 +1,144 @@ +package com.backbase.oss.boat.transformers; + +import java.io.File; +import java.io.IOException; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.settings.Server; +import org.apache.maven.settings.Settings; +import org.apache.maven.settings.crypto.SettingsDecrypter; +import org.apache.maven.settings.crypto.SettingsDecryptionResult; +import org.codehaus.plexus.logging.console.ConsoleLogger; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoSettings; +import org.sonatype.plexus.build.incremental.DefaultBuildContext; + +import static java.util.Collections.emptyList; +import static org.apache.commons.io.FileUtils.deleteDirectory; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasKey; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@MockitoSettings +@SuppressWarnings("java:S5979") +class TransformMojoTest { + private final DefaultBuildContext buildContext = new DefaultBuildContext(); + + @Mock + private MavenSession session; + @Mock + private SettingsDecrypter decrypter; + + @InjectMocks + private TransformMojo mojo; + + @BeforeEach + void beforeEach() throws IOException { + this.buildContext.enableLogging(new ConsoleLogger(2, "BOAT")); + + this.mojo.inputs.add("src/test/resources/oas-examples/petstore.yaml"); + this.mojo.output = new File("target", "transform-mojo"); + + deleteDirectory(this.mojo.output); + } + + @Test + void serverId() throws MojoExecutionException, MojoFailureException { + this.mojo.serverId = "server-id"; + + final Server server = mock(Server.class); + final Settings settings = mock(Settings.class); + final SettingsDecryptionResult result = mock(SettingsDecryptionResult.class); + + when(this.session.getSettings()).thenReturn(settings); + when(settings.getServer(this.mojo.serverId)).thenReturn(server); + when(this.decrypter.decrypt(any())).thenReturn(result); + when(result.getProblems()).thenReturn(emptyList()); + when(result.getServer()).thenReturn(server); + when(server.getUsername()).thenReturn("username"); + when(server.getPassword()).thenReturn("password"); + + + this.mojo.execute(); + + assertThat(this.mojo.options, hasKey("authz")); + assertThat(output("petstore-transformed").exists(), is(true)); + } + + @Test + void empty() throws MojoExecutionException, MojoFailureException { + this.mojo.execute(); + + assertThat(output("petstore-transformed").exists(), is(true)); + } + + @Test + void transform() throws MojoExecutionException, MojoFailureException { + final Transformer tran = mock(Transformer.class); + + when(tran.transform(any(), any())).then(ivc -> ivc.getArgument(0)); + + this.mojo.pipeline.add(tran); + this.mojo.execute(); + + verify(tran, times(1)).transform(any(), any()); + assertThat(output("petstore-transformed").exists(), is(true)); + } + + @Test + void mappers() throws MojoExecutionException, MojoFailureException { + this.mojo.mappers.add(new Merge("open--api")); + this.mojo.mappers.add(new Prefix("prefix-")); + this.mojo.mappers.add(new Suffix("-suffix")); + this.mojo.mappers.add(new Regex("--", "-")); + this.mojo.mappers.add(new Suffix(".yaml")); + this.mojo.execute(); + + assertThat(output("prefix-open-api-suffix").exists(), is(true)); + } + + @Test + void merge() throws MojoExecutionException, MojoFailureException { + this.mojo.mappers.add(new Merge("open--api.yaml")); + this.mojo.execute(); + + assertThat(output("open--api").exists(), is(true)); + } + + @Test + void suffix() throws MojoExecutionException, MojoFailureException { + this.mojo.mappers.add(new Suffix("-suffix")); + this.mojo.execute(); + + assertThat(output("petstore-suffix").exists(), is(true)); + } + + @Test + void prefix() throws MojoExecutionException, MojoFailureException { + this.mojo.mappers.add(new Prefix("prefix-")); + this.mojo.execute(); + + assertThat(output("prefix-petstore").exists(), is(true)); + } + + @Test + void regex() throws MojoExecutionException, MojoFailureException { + this.mojo.mappers.add(new Regex("(.+)tst(.+)", "$1te-st$2")); + this.mojo.execute(); + + assertThat(output("pete-store").exists(), is(true)); + } + + private File output(String name) { + return new File(this.mojo.output, name + ".yaml"); + } +} diff --git a/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/link.yaml b/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/link.yaml new file mode 100644 index 000000000..d1c8cd22d --- /dev/null +++ b/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/link.yaml @@ -0,0 +1,203 @@ +openapi: "3.0.0" +info: + title: Link Example + version: 1.0.0 +paths: + /2.0/users/{username}: + get: + operationId: getUserByName + parameters: + - name: username + in: path + required: true + schema: + type: string + responses: + '200': + description: The User + content: + application/json: + schema: + $ref: '#/components/schemas/user' + links: + userRepositories: + $ref: '#/components/links/UserRepositories' + /2.0/repositories/{username}: + get: + operationId: getRepositoriesByOwner + parameters: + - name: username + in: path + required: true + schema: + type: string + responses: + '200': + description: repositories owned by the supplied user + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/repository' + links: + userRepository: + $ref: '#/components/links/UserRepository' + /2.0/repositories/{username}/{slug}: + get: + operationId: getRepository + parameters: + - name: username + in: path + required: true + schema: + type: string + - name: slug + in: path + required: true + schema: + type: string + responses: + '200': + description: The repository + content: + application/json: + schema: + $ref: '#/components/schemas/repository' + links: + repositoryPullRequests: + $ref: '#/components/links/RepositoryPullRequests' + /2.0/repositories/{username}/{slug}/pullrequests: + get: + operationId: getPullRequestsByRepository + parameters: + - name: username + in: path + required: true + schema: + type: string + - name: slug + in: path + required: true + schema: + type: string + - name: state + in: query + schema: + type: string + enum: + - open + - merged + - declined + responses: + '200': + description: an array of pull request objects + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/pullrequest' + /2.0/repositories/{username}/{slug}/pullrequests/{pid}: + get: + operationId: getPullRequestsById + parameters: + - name: username + in: path + required: true + schema: + type: string + - name: slug + in: path + required: true + schema: + type: string + - name: pid + in: path + required: true + schema: + type: string + responses: + '200': + description: a pull request object + content: + application/json: + schema: + $ref: '#/components/schemas/pullrequest' + links: + pullRequestMerge: + $ref: '#/components/links/PullRequestMerge' + /2.0/repositories/{username}/{slug}/pullrequests/{pid}/merge: + post: + operationId: mergePullRequest + parameters: + - name: username + in: path + required: true + schema: + type: string + - name: slug + in: path + required: true + schema: + type: string + - name: pid + in: path + required: true + schema: + type: string + responses: + '204': + description: the PR was successfully merged +components: + links: + UserRepositories: + # returns array of '#/components/schemas/repository' + operationId: getRepositoriesByOwner + parameters: + username: $response.body#/username + UserRepository: + # returns '#/components/schemas/repository' + operationId: getRepository + parameters: + username: $response.body#/owner/username + slug: $response.body#/slug + RepositoryPullRequests: + # returns '#/components/schemas/pullrequest' + operationId: getPullRequestsByRepository + parameters: + username: $response.body#/owner/username + slug: $response.body#/slug + PullRequestMerge: + # executes /2.0/repositories/{username}/{slug}/pullrequests/{pid}/merge + operationId: mergePullRequest + parameters: + username: $response.body#/author/username + slug: $response.body#/repository/slug + pid: $response.body#/id + schemas: + user: + type: object + properties: + username: + type: string + uuid: + type: string + repository: + type: object + properties: + slug: + type: string + owner: + $ref: '#/components/schemas/user' + pullrequest: + type: object + properties: + id: + type: integer + title: + type: string + repository: + $ref: '#/components/schemas/repository' + author: + $ref: '#/components/schemas/user' \ No newline at end of file diff --git a/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/petstore-new-non-breaking.yaml b/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/petstore-new-non-breaking.yaml new file mode 100644 index 000000000..9f7ec3683 --- /dev/null +++ b/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/petstore-new-non-breaking.yaml @@ -0,0 +1,113 @@ +openapi: "3.0.0" +info: + version: 1.1.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + new-property: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/petstore.yaml b/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/petstore.yaml new file mode 100644 index 000000000..137eaff93 --- /dev/null +++ b/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/petstore.yaml @@ -0,0 +1,138 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + text/csv: + schema: + type: string + 500: + content: + application/json: + schema: + $ref: '#/components/schemas/InternalServerError' + description: InternalServerError + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /no-schema: + get: + summary: No Schema + operationId: getNoSchema + tags: + - pets + responses: + 200: + description: A response that does not specify a schema + content: + application/json: {} +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string + InternalServerError: + type: object + required: + - message + properties: + message: + type: string \ No newline at end of file diff --git a/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/upto.yaml b/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/upto.yaml new file mode 100644 index 000000000..a822e0c5a --- /dev/null +++ b/boat-maven-plugin/src/test/resources/boat-doc-oas-examples/upto.yaml @@ -0,0 +1,210 @@ +openapi: "3.0.0" +servers: + - url: '{scheme}://developer.uspto.gov/ds-api' + variables: + scheme: + description: 'The Data Set API is accessible via https and http' + enum: + - 'https' + - 'http' + default: 'https' +info: + description: >- + The Data Set API (DSAPI) allows the public users to discover and search + USPTO exported data sets. This is a generic API that allows USPTO users to + make any CSV based data files searchable through API. With the help of GET + call, it returns the list of data fields that are searchable. With the help + of POST call, data can be fetched based on the filters on the field names. + Please note that POST call is used to search the actual data. The reason for + the POST call is that it allows users to specify any complex search criteria + without worry about the GET size limitations as well as encoding of the + input parameters. + version: 1.0.0 + title: USPTO Data Set API + contact: + name: Open Data Portal + url: 'https://developer.uspto.gov' + email: developer@uspto.gov +tags: + - name: metadata + description: Find out about the data sets + - name: search + description: Search a data set +paths: + /: + get: + tags: + - metadata + operationId: list-data-sets + summary: List available data sets + responses: + '200': + description: Returns a list of data sets + content: + application/json: + schema: + $ref: '#/components/schemas/dataSetList' + example: + { + "total": 2, + "apis": [ + { + "apiKey": "oa_citations", + "apiVersionNumber": "v1", + "apiUrl": "https://developer.uspto.gov/ds-api/oa_citations/v1/fields", + "apiDocumentationUrl": "https://developer.uspto.gov/ds-api-docs/index.html?url=https://developer.uspto.gov/ds-api/swagger/docs/oa_citations.json" + }, + { + "apiKey": "cancer_moonshot", + "apiVersionNumber": "v1", + "apiUrl": "https://developer.uspto.gov/ds-api/cancer_moonshot/v1/fields", + "apiDocumentationUrl": "https://developer.uspto.gov/ds-api-docs/index.html?url=https://developer.uspto.gov/ds-api/swagger/docs/cancer_moonshot.json" + } + ] + } + /{dataset}/{version}/fields: + get: + tags: + - metadata + summary: >- + Provides the general information about the API and the list of fields + that can be used to query the dataset. + description: >- + This GET API returns the list of all the searchable field names that are + in the oa_citations. Please see the 'fields' attribute which returns an + array of field names. Each field or a combination of fields can be + searched using the syntax options shown below. + operationId: list-searchable-fields + parameters: + - name: dataset + in: path + description: 'Name of the dataset.' + required: true + example: "oa_citations" + schema: + type: string + - name: version + in: path + description: Version of the dataset. + required: true + example: "v1" + schema: + type: string + responses: + '200': + description: >- + The dataset API for the given version is found and it is accessible + to consume. + content: + application/json: + schema: + type: string + '404': + description: >- + The combination of dataset name and version is not found in the + system or it is not published yet to be consumed by public. + content: + application/json: + schema: + type: string + /{dataset}/{version}/records: + post: + tags: + - search + summary: >- + Provides search capability for the data set with the given search + criteria. + description: >- + This API is based on Solr/Lucene Search. The data is indexed using + SOLR. This GET API returns the list of all the searchable field names + that are in the Solr Index. Please see the 'fields' attribute which + returns an array of field names. Each field or a combination of fields + can be searched using the Solr/Lucene Syntax. Please refer + https://lucene.apache.org/core/3_6_2/queryparsersyntax.html#Overview for + the query syntax. List of field names that are searchable can be + determined using above GET api. + operationId: perform-search + parameters: + - name: version + in: path + description: Version of the dataset. + required: true + schema: + type: string + default: v1 + - name: dataset + in: path + description: 'Name of the dataset. In this case, the default value is oa_citations' + required: true + schema: + type: string + default: oa_citations + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + type: object + additionalProperties: + type: object + '404': + description: No matching record found for the given criteria. + requestBody: + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + criteria: + description: >- + Uses Lucene Query Syntax in the format of + propertyName:value, propertyName:[num1 TO num2] and date + range format: propertyName:[yyyyMMdd TO yyyyMMdd]. In the + response please see the 'docs' element which has the list of + record objects. Each record structure would consist of all + the fields and their corresponding values. + type: string + default: '*:*' + start: + description: Starting record number. Default value is 0. + type: integer + default: 0 + rows: + description: >- + Specify number of rows to be returned. If you run the search + with default values, in the response you will see 'numFound' + attribute which will tell the number of records available in + the dataset. + type: integer + default: 100 + required: + - criteria +components: + schemas: + dataSetList: + type: object + properties: + total: + type: integer + apis: + type: array + items: + type: object + properties: + apiKey: + type: string + description: To be used as a dataset parameter value + apiVersionNumber: + type: string + description: To be used as a version parameter value + apiUrl: + type: string + format: uriref + description: "The URL describing the dataset's fields" + apiDocumentationUrl: + type: string + format: uriref + description: A URL to the API console for each API \ No newline at end of file diff --git a/boat-maven-plugin/src/test/resources/export-bom/maven-metadata-test-example.xml b/boat-maven-plugin/src/test/resources/export-bom/maven-metadata-test-example.xml new file mode 100644 index 000000000..971aae3d4 --- /dev/null +++ b/boat-maven-plugin/src/test/resources/export-bom/maven-metadata-test-example.xml @@ -0,0 +1,15 @@ + + test.groupId + openapi-zips + [1.0.0,) + + 1.0.0 + 1.0.0 + + 1.0.0 + + 20150509185437 + + diff --git a/boat-maven-plugin/src/test/resources/export-bom/raml-spec-bom/pom.xml b/boat-maven-plugin/src/test/resources/export-bom/raml-spec-bom/pom.xml new file mode 100644 index 000000000..51b78d2fd --- /dev/null +++ b/boat-maven-plugin/src/test/resources/export-bom/raml-spec-bom/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + com.backbase.oss.boat.example + raml-spec-bom + 1.0.0-SNAPSHOT + + + 1.0.0-SNAPSHOT + + + pom + + BOAT :: RAML Bill-Of-Materials + + + + + com.backbase.oss.boat.example + raml-spec + ${raml-spec.version} + + + + + + + + diff --git a/boat-maven-plugin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/boat-maven-plugin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 000000000..ca6ee9cea --- /dev/null +++ b/boat-maven-plugin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline \ No newline at end of file diff --git a/boat-maven-plugin/src/test/resources/oas-examples/openapi-zips-1.0.0-SNAPSHOT-api.zip b/boat-maven-plugin/src/test/resources/oas-examples/openapi-zips-1.0.0-SNAPSHOT-api.zip new file mode 100644 index 000000000..c12e5d4f7 Binary files /dev/null and b/boat-maven-plugin/src/test/resources/oas-examples/openapi-zips-1.0.0-SNAPSHOT-api.zip differ diff --git a/boat-quay/boat-quay-lint/pom.xml b/boat-quay/boat-quay-lint/pom.xml index 3ab09fa1e..b18781a9d 100644 --- a/boat-quay/boat-quay-lint/pom.xml +++ b/boat-quay/boat-quay-lint/pom.xml @@ -5,7 +5,7 @@ com.backbase.oss boat-quay - 0.14.2-SNAPSHOT + 0.14.3-SNAPSHOT boat-quay-lint jar diff --git a/boat-quay/boat-quay-rules/pom.xml b/boat-quay/boat-quay-rules/pom.xml index a4c09aca5..8424d350b 100644 --- a/boat-quay/boat-quay-rules/pom.xml +++ b/boat-quay/boat-quay-rules/pom.xml @@ -5,7 +5,7 @@ com.backbase.oss boat-quay - 0.14.2-SNAPSHOT + 0.14.3-SNAPSHOT boat-quay-rules jar diff --git a/boat-quay/boat-quay-rules/src/main/resources/boat.conf b/boat-quay/boat-quay-rules/src/main/resources/boat.conf index 3d9b22014..294fd29a2 100644 --- a/boat-quay/boat-quay-rules/src/main/resources/boat.conf +++ b/boat-quay/boat-quay-rules/src/main/resources/boat.conf @@ -63,7 +63,7 @@ StringPropertyLengthBoundsRule { } OpenApiVersionRule { - openApiVersions: [ 3.0.3, 3.0.4 ] + openApiVersions: [ 3.1.0 ] } ExtraRuleAnnotations { diff --git a/boat-quay/pom.xml b/boat-quay/pom.xml index 8a50a34e2..b422ff5a5 100644 --- a/boat-quay/pom.xml +++ b/boat-quay/pom.xml @@ -5,7 +5,7 @@ com.backbase.oss backbase-openapi-tools - 0.14.2-SNAPSHOT + 0.14.3-SNAPSHOT diff --git a/boat-scaffold/README.md b/boat-scaffold/README.md new file mode 100644 index 000000000..b786d5550 --- /dev/null +++ b/boat-scaffold/README.md @@ -0,0 +1,28 @@ +# Boat OpenAPI generator + +The Boat OpenAPI generator is based on the official Open API Generator, version 4.0.3 and it provides several fixes and additional features. +The `boat` plugin has multiple goals: + +## Spring Code Generator + +| Option | Default | Description | +|-|-|-| +| `addBindingResult` | `false` | Adds BindingResult to Api method definitions' request bodies if UseBeanValidation true, for this to be effective you must configure UseBeanValidation, this is not done automatically | +| `addServletRequest` | `false` | Adds ServletRequest objects to API method definitions | +| `useClassLevelBeanValidation` | `false` | Adds @Validated annotation to API interfaces | +| `useLombokAnnotations` | `false` | Use Lombok annotations to generate properties accessors and `hashCode`/`equals`/`toString` methods | +| `useSetForUniqueItems` | `false` | Use `java.util.Set` for arrays that has the attribute `uniqueItems` to `true` | +| `openApiNullable` | `true` | Whether to use the `jackson-databind-nullable` library | +| `useWithModifiers` | `false` | Generates bean `with` modifiers for fluent style | +| `useProtectedFields` | `false` | Whether to use protected visibility for model fields | + +## Java Code Generator + +| Option | Default | Description | +|-|-|-| +| `createApiComponent` | `true` | Whether to generate the client as a Spring component (`resttemplate` only) | +| `restTemplateBeanName` | `none` | The qualifier of the `RestTemplate` used by the `ApiClient` (`resttemplate` only) | +| `useClassLevelBeanValidation` | `false` | Adds @Validated annotation to API interfaces | +| `useJacksonConversion` | `false` | Use Jackson to convert query parameters (`resttemplate` only) | +| `useSetForUniqueItems` | `false` | Use `java.util.Set` for arrays that has the attribute `uniqueItems` to `true` | +| `useProtectedFields` | `false` | "Whether to use protected visibility for model fields | diff --git a/boat-scaffold/pom.xml b/boat-scaffold/pom.xml index dfb488832..afe6339a3 100644 --- a/boat-scaffold/pom.xml +++ b/boat-scaffold/pom.xml @@ -5,7 +5,7 @@ com.backbase.oss backbase-openapi-tools - 0.14.2-SNAPSHOT + 0.14.3-SNAPSHOT boat-scaffold @@ -86,7 +86,7 @@ com.backbase.oss boat-trail-resources - 0.14.2-SNAPSHOT + 0.14.3-SNAPSHOT test diff --git a/boat-scaffold/src/main/java/com/backbase/oss/codegen/java/BoatJavaCodeGen.java b/boat-scaffold/src/main/java/com/backbase/oss/codegen/java/BoatJavaCodeGen.java index 29d2ef909..48fec14e6 100644 --- a/boat-scaffold/src/main/java/com/backbase/oss/codegen/java/BoatJavaCodeGen.java +++ b/boat-scaffold/src/main/java/com/backbase/oss/codegen/java/BoatJavaCodeGen.java @@ -22,6 +22,8 @@ public class BoatJavaCodeGen extends JavaClientCodegen { public static final String USE_DEFAULT_API_CLIENT = "useDefaultApiClient"; public static final String REST_TEMPLATE_BEAN_NAME = "restTemplateBeanName"; + public static final String CREATE_API_COMPONENT = "createApiComponent"; + public static final String USE_PROTECTED_FIELDS = "useProtectedFields"; private static final String JAVA_UTIL_SET_NEW = "new " + "java.util.LinkedHashSet<>()"; private static final String JAVA_UTIL_SET = "java.util.Set"; @@ -45,6 +47,9 @@ public class BoatJavaCodeGen extends JavaClientCodegen { @Getter @Setter protected String restTemplateBeanName; + @Getter + @Setter + protected boolean createApiComponent = true; public BoatJavaCodeGen() { this.embeddedTemplateDir = this.templateDir = NAME; @@ -61,6 +66,10 @@ public BoatJavaCodeGen() { "Whether to use a default ApiClient with a builtin template", this.useDefaultApiClient)); this.cliOptions.add(CliOption.newString(REST_TEMPLATE_BEAN_NAME, "An optional RestTemplate bean name")); + this.cliOptions.add(CliOption.newString(CREATE_API_COMPONENT, + "Whether to generate the client as a Spring component")); + this.cliOptions.add(CliOption.newString(USE_PROTECTED_FIELDS, + "Whether to use protected visibility for model fields")); } @Override @@ -113,6 +122,16 @@ public void processOpts() { writePropertyBack(USE_DEFAULT_API_CLIENT, this.useDefaultApiClient); this.restTemplateBeanName = (String) this.additionalProperties.get(REST_TEMPLATE_BEAN_NAME); + + if (this.additionalProperties.containsKey(CREATE_API_COMPONENT)) { + this.createApiComponent = convertPropertyToBoolean(CREATE_API_COMPONENT); + } + writePropertyBack(CREATE_API_COMPONENT, this.createApiComponent); + } + if (this.additionalProperties.containsKey(USE_PROTECTED_FIELDS)) { + this.additionalProperties.put("modelFieldsVisibility", "protected"); + } else { + this.additionalProperties.put("modelFieldsVisibility", "private"); } if (!getLibrary().startsWith("jersey")) { @@ -142,11 +161,11 @@ public void postProcessParameter(CodegenParameter p) { if (p.isContainer && this.useSetForUniqueItems && p.getUniqueItems()) { // XXX the model set baseType to the container type, why is this different? - p.baseType = p.dataType.replaceAll("^([^<]+)<.+>$", "$1"); - p.baseType = JAVA_UTIL_SET; - p.dataType = format(JAVA_UTIL_SET_GEN, p.items.dataType); - p.datatypeWithEnum = format(JAVA_UTIL_SET_GEN, p.items.datatypeWithEnum); - p.defaultValue = JAVA_UTIL_SET_NEW; + p.baseType = p.dataType.replaceAll("^([^<]+)<.+>$", "$1"); + p.baseType = JAVA_UTIL_SET; + p.dataType = format(JAVA_UTIL_SET_GEN, p.items.dataType); + p.datatypeWithEnum = format(JAVA_UTIL_SET_GEN, p.items.datatypeWithEnum); + p.defaultValue = JAVA_UTIL_SET_NEW; } } diff --git a/boat-scaffold/src/main/java/com/backbase/oss/codegen/java/BoatSpringCodeGen.java b/boat-scaffold/src/main/java/com/backbase/oss/codegen/java/BoatSpringCodeGen.java index bca4a1082..ff0e5cb9c 100644 --- a/boat-scaffold/src/main/java/com/backbase/oss/codegen/java/BoatSpringCodeGen.java +++ b/boat-scaffold/src/main/java/com/backbase/oss/codegen/java/BoatSpringCodeGen.java @@ -25,11 +25,13 @@ public class BoatSpringCodeGen extends SpringCodegen { public static final String USE_CLASS_LEVEL_BEAN_VALIDATION = "useClassLevelBeanValidation"; public static final String ADD_SERVLET_REQUEST = "addServletRequest"; + public static final String ADD_BINDING_RESULT = "addBindingResult"; public static final String USE_LOMBOK_ANNOTATIONS = "useLombokAnnotations"; public static final String USE_SET_FOR_UNIQUE_ITEMS = "useSetForUniqueItems"; public static final String OPENAPI_NULLABLE = "openApiNullable"; public static final String USE_WITH_MODIFIERS = "useWithModifiers"; - public static final String BASE_TYPE = "java.util.Set"; + public static final String USE_PROTECTED_FIELDS = "useProtectedFields"; + public static final String UNIQUE_BASE_TYPE = "java.util.Set"; static class NewLineIndent implements Mustache.Lambda { @@ -95,6 +97,13 @@ static int indentLevel(String text) { @Getter protected boolean addServletRequest; + /** + * Adds BindingResult to API interface method if @validate is used + */ + @Setter + @Getter + protected boolean addBindingResult; + /** * Add Lombok to class-level Api models. Defaults to false */ @@ -124,17 +133,22 @@ public BoatSpringCodeGen() { this.embeddedTemplateDir = this.templateDir = NAME; this.cliOptions.add(CliOption.newBoolean(USE_CLASS_LEVEL_BEAN_VALIDATION, - "Add @Validated to class-level Api interfaces", this.useClassLevelBeanValidation)); + "Add @Validated to class-level Api interfaces.", this.useClassLevelBeanValidation)); this.cliOptions.add(CliOption.newBoolean(ADD_SERVLET_REQUEST, "Adds a HttpServletRequest object to the API definition method.", this.addServletRequest)); + this.cliOptions.add(CliOption.newBoolean(ADD_BINDING_RESULT, + "Adds a Binding result as method perimeter. Only implemented if @validate is being used.", + this.addBindingResult)); this.cliOptions.add(CliOption.newBoolean(USE_LOMBOK_ANNOTATIONS, "Add Lombok to class-level Api models. Defaults to false.", this.useLombokAnnotations)); this.cliOptions.add(CliOption.newBoolean(USE_SET_FOR_UNIQUE_ITEMS, - "Use java.util.Set for arrays that have uniqueItems set to true", this.useSetForUniqueItems)); + "Use java.util.Set for arrays that have uniqueItems set to true.", this.useSetForUniqueItems)); this.cliOptions.add(CliOption.newBoolean(OPENAPI_NULLABLE, - "Enable OpenAPI Jackson Nullable library", this.openApiNullable)); + "Enable OpenAPI Jackson Nullable library.", this.openApiNullable)); this.cliOptions.add(CliOption.newBoolean(USE_WITH_MODIFIERS, - "Whether to use \"with\" prefix for POJO modifiers", this.useWithModifiers)); + "Whether to use \"with\" prefix for POJO modifiers.", this.useWithModifiers)); + this.cliOptions.add(CliOption.newString(USE_PROTECTED_FIELDS, + "Whether to use protected visibility for model fields")); this.apiNameSuffix = "Api"; } @@ -188,6 +202,9 @@ public void processOpts() { if (this.additionalProperties.containsKey(ADD_SERVLET_REQUEST)) { this.addServletRequest = convertPropertyToBoolean(ADD_SERVLET_REQUEST); } + if (this.additionalProperties.containsKey(ADD_BINDING_RESULT)) { + this.addBindingResult = convertPropertyToBoolean(ADD_BINDING_RESULT); + } if (this.additionalProperties.containsKey(USE_LOMBOK_ANNOTATIONS)) { this.useLombokAnnotations = convertPropertyToBoolean(USE_LOMBOK_ANNOTATIONS); } @@ -200,18 +217,24 @@ public void processOpts() { if (this.additionalProperties.containsKey(USE_WITH_MODIFIERS)) { this.useWithModifiers = convertPropertyToBoolean(USE_WITH_MODIFIERS); } + if (this.additionalProperties.containsKey(USE_PROTECTED_FIELDS)) { + this.additionalProperties.put("modelFieldsVisibility", "protected"); + } else { + this.additionalProperties.put("modelFieldsVisibility", "private"); + } writePropertyBack(USE_CLASS_LEVEL_BEAN_VALIDATION, this.useClassLevelBeanValidation); writePropertyBack(ADD_SERVLET_REQUEST, this.addServletRequest); + writePropertyBack(ADD_BINDING_RESULT, this.addBindingResult); writePropertyBack(USE_LOMBOK_ANNOTATIONS, this.useLombokAnnotations); writePropertyBack(OPENAPI_NULLABLE, this.openApiNullable); writePropertyBack(USE_SET_FOR_UNIQUE_ITEMS, this.useSetForUniqueItems); writePropertyBack(USE_WITH_MODIFIERS, this.useWithModifiers); if (this.useSetForUniqueItems) { - this.typeMapping.put("set", BASE_TYPE); + this.typeMapping.put("set", UNIQUE_BASE_TYPE); - this.importMapping.put("Set", BASE_TYPE); + this.importMapping.put("Set", UNIQUE_BASE_TYPE); this.importMapping.put("LinkedHashSet", "java.util.LinkedHashSet"); } @@ -227,9 +250,9 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty p) { if (p.isContainer && this.useSetForUniqueItems && p.getUniqueItems()) { p.containerType = "set"; - p.baseType = BASE_TYPE; - p.dataType = BASE_TYPE + "<" + p.items.dataType + ">"; - p.datatypeWithEnum = BASE_TYPE + "<" + p.items.datatypeWithEnum + ">"; + p.baseType = UNIQUE_BASE_TYPE; + p.dataType = UNIQUE_BASE_TYPE + "<" + p.items.dataType + ">"; + p.datatypeWithEnum = UNIQUE_BASE_TYPE + "<" + p.items.datatypeWithEnum + ">"; p.defaultValue = "new " + "java.util.LinkedHashSet<>()"; } } @@ -244,9 +267,9 @@ public void postProcessParameter(CodegenParameter p) { } if (this.useSetForUniqueItems && p.getUniqueItems()) { - p.baseType = BASE_TYPE; - p.dataType = BASE_TYPE + "<" + p.items.dataType + ">"; - p.datatypeWithEnum = BASE_TYPE + "<" + p.items.datatypeWithEnum + ">"; + p.baseType = UNIQUE_BASE_TYPE; + p.dataType = UNIQUE_BASE_TYPE + "<" + p.items.dataType + ">"; + p.datatypeWithEnum = UNIQUE_BASE_TYPE + "<" + p.items.datatypeWithEnum + ">"; p.defaultValue = "new " + "java.util.LinkedHashSet<>()"; } } diff --git a/boat-scaffold/src/main/templates/boat-java/libraries/resttemplate/api.mustache b/boat-scaffold/src/main/templates/boat-java/libraries/resttemplate/api.mustache index 99bb9075a..ab0b95a62 100644 --- a/boat-scaffold/src/main/templates/boat-java/libraries/resttemplate/api.mustache +++ b/boat-scaffold/src/main/templates/boat-java/libraries/resttemplate/api.mustache @@ -12,8 +12,9 @@ import java.util.List; import java.util.Locale; import java.util.Map;{{/fullJavaUtil}} -import org.springframework.beans.factory.annotation.Autowired; +{{#createApiComponent}}import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +{{/createApiComponent}} import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestClientException; @@ -33,15 +34,15 @@ import org.springframework.validation.annotation.Validated; {{/useBeanValidation}} {{>generatedAnnotation}} -@Component("{{package}}.{{classname}}") -{{#operations}} -{{#useBeanValidation}}{{! +{{#createApiComponent}}@Component("{{package}}.{{classname}}") +{{/createApiComponent}}{{! +}}{{#useBeanValidation}}{{! }}{{#useClassLevelBeanValidation}}{{! }}@Validated {{/useClassLevelBeanValidation}}{{! }}{{/useBeanValidation}} public class {{classname}} { - private {{^useDefaultApiClient}}final {{/useDefaultApiClient}}ApiClient apiClient; + private {{#useDefaultApiClient}}final {{/useDefaultApiClient}}ApiClient apiClient; {{^useDefaultApiClient}} public {{classname}}() { @@ -49,8 +50,8 @@ public class {{classname}} { } {{/useDefaultApiClient}} - @Autowired - public {{classname}}(ApiClient apiClient) { +{{#createApiComponent}} @Autowired +{{/createApiComponent}} public {{classname}}(ApiClient apiClient) { this.apiClient = apiClient; } @@ -64,6 +65,7 @@ public class {{classname}} { } {{/useDefaultApiClient}} +{{#operations}} {{#operation}} /** * {{summary}} @@ -162,5 +164,5 @@ public class {{classname}} { return apiClient.invokeAPI(localVarPath, HttpMethod.{{httpMethod}}, localVarQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAccept, localVarContentType, localVarAuthNames, localVarReturnType); } {{/operation}} -} {{/operations}} +} diff --git a/boat-scaffold/src/main/templates/boat-java/pojo.mustache b/boat-scaffold/src/main/templates/boat-java/pojo.mustache index ab2219159..5029536a7 100644 --- a/boat-scaffold/src/main/templates/boat-java/pojo.mustache +++ b/boat-scaffold/src/main/templates/boat-java/pojo.mustache @@ -10,7 +10,7 @@ }) {{/jackson}} {{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{>xmlAnnotation}} -public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorExtensions.x-implements}}{{#-first}}implements {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{#-last}} {{/-last}}{{/vendorExtensions.x-implements}}{ +public {{#vendorExtensions.x-abstract}}abstract {{/vendorExtensions.x-abstract}} class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorExtensions.x-implements}}{{#-first}}implements {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{#-last}} {{/-last}}{{/vendorExtensions.x-implements}}{ {{#serializableModel}} private static final long serialVersionUID = 1L; @@ -58,18 +58,18 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorE {{/gson}} {{#vendorExtensions.x-is-jackson-optional-nullable}} {{#isContainer}} - private JsonNullable<{{{datatypeWithEnum}}}> {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>undefined(); + {{modelFieldsVisibility}} JsonNullable<{{{datatypeWithEnum}}}> {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>undefined(); {{/isContainer}} {{^isContainer}} - private JsonNullable<{{{datatypeWithEnum}}}> {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>{{#defaultValue}}of({{{.}}}){{/defaultValue}}{{^defaultValue}}undefined(){{/defaultValue}}; + {{modelFieldsVisibility}} JsonNullable<{{{datatypeWithEnum}}}> {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>{{#defaultValue}}of({{{.}}}){{/defaultValue}}{{^defaultValue}}undefined(){{/defaultValue}}; {{/isContainer}} {{/vendorExtensions.x-is-jackson-optional-nullable}} {{^vendorExtensions.x-is-jackson-optional-nullable}} {{#isContainer}} - private {{{datatypeWithEnum}}} {{name}}{{#required}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{/required}}{{^required}} = null{{/required}}; + {{modelFieldsVisibility}} {{{datatypeWithEnum}}} {{name}}{{#required}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{/required}}{{^required}} = null{{/required}}; {{/isContainer}} {{^isContainer}} - {{#isDiscriminator}}protected{{/isDiscriminator}}{{^isDiscriminator}}private{{/isDiscriminator}} {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}; + {{#isDiscriminator}}protected{{/isDiscriminator}}{{^isDiscriminator}}{{modelFieldsVisibility}}{{/isDiscriminator}} {{{datatypeWithEnum}}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}; {{/isContainer}} {{/vendorExtensions.x-is-jackson-optional-nullable}} diff --git a/boat-scaffold/src/main/templates/boat-spring/api.mustache b/boat-scaffold/src/main/templates/boat-spring/api.mustache index 33381f026..c16548d3f 100644 --- a/boat-scaffold/src/main/templates/boat-spring/api.mustache +++ b/boat-scaffold/src/main/templates/boat-spring/api.mustache @@ -3,6 +3,7 @@ Boat Generator configuration: useBeanValidation: {{useBeanValidation}} useOptional: {{useOptional}} addServletRequest: {{addServletRequest}} + addBindingResult: {{addBindingResult}} useLombokAnnotations: {{useLombokAnnotations}} openApiNullable: {{openApiNullable}} useSetForUniqueItems: {{useSetForUniqueItems}} @@ -68,6 +69,9 @@ import java.io.IOException; {{#addServletRequest}} import javax.servlet.http.HttpServletRequest; {{/addServletRequest}} +{{#addBindingResult}} +import org.springframework.validation.BindingResult; +{{/addBindingResult}} {{#useBeanValidation}} import javax.validation.Valid; import javax.validation.constraints.*; diff --git a/boat-scaffold/src/main/templates/boat-spring/bodyParams.mustache b/boat-scaffold/src/main/templates/boat-spring/bodyParams.mustache index 52c257a3b..45caf586c 100644 --- a/boat-scaffold/src/main/templates/boat-spring/bodyParams.mustache +++ b/boat-scaffold/src/main/templates/boat-spring/bodyParams.mustache @@ -2,4 +2,4 @@ @ApiParam(value = "{{{description}}}" {{#required}},required=true{{/required}} {{^isContainer}}{{#allowableValues}}, allowableValues="{{{allowableValues}}}"{{/allowableValues}}{{#defaultValue}}, defaultValue={{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{/defaultValue}}{{/isContainer}}) {{#useBeanValidation}} @Valid{{#required}} @NotNull{{/required}}{{/useBeanValidation}} - @RequestBody{{^required}}(required = false){{/required}} {{^reactive}}{{{dataType}}}{{/reactive}}{{#reactive}}{{^isListContainer}}Mono{{/isListContainer}}{{#isListContainer}}Flux{{/isListContainer}}<{{{baseType}}}>{{/reactive}} {{paramName}}{{/isBodyParam}} \ No newline at end of file + @RequestBody{{^required}}(required = false){{/required}} {{^reactive}}{{{dataType}}}{{/reactive}}{{#reactive}}{{^isListContainer}}Mono{{/isListContainer}}{{#isListContainer}}Flux{{/isListContainer}}<{{{baseType}}}>{{/reactive}} {{paramName}}{{#useBeanValidation}}{{#addBindingResult}}, BindingResult bindingResult{{/addBindingResult}}{{/useBeanValidation}}{{/isBodyParam}} \ No newline at end of file diff --git a/boat-scaffold/src/main/templates/boat-spring/model.mustache b/boat-scaffold/src/main/templates/boat-spring/model.mustache index ed3a0c720..7f2a0f621 100644 --- a/boat-scaffold/src/main/templates/boat-spring/model.mustache +++ b/boat-scaffold/src/main/templates/boat-spring/model.mustache @@ -42,6 +42,8 @@ import javax.xml.bind.annotation.*; import org.springframework.hateoas.RepresentationModel; {{/hateoas}} {{/parent}} +{{#vendorExtensions.x-extra-java-imports}} +{{{vendorExtensions.x-extra-java-imports}}}{{/vendorExtensions.x-extra-java-imports}} {{#models}} {{#model}} diff --git a/boat-scaffold/src/main/templates/boat-spring/pojo.mustache b/boat-scaffold/src/main/templates/boat-spring/pojo.mustache index 75acf2558..63c5f882b 100644 --- a/boat-scaffold/src/main/templates/boat-spring/pojo.mustache +++ b/boat-scaffold/src/main/templates/boat-spring/pojo.mustache @@ -4,7 +4,9 @@ @ApiModel(description = "{{{description}}}"){{/description}} {{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{>xmlAnnotation}}{{>additionalModelTypeAnnotations}} {{#useLombokAnnotations}} -@lombok.EqualsAndHashCode(onlyExplicitlyIncluded = true{{#parent}}, callSuper = true{{/parent}}){{/useLombokAnnotations}} +@lombok.EqualsAndHashCode(onlyExplicitlyIncluded = true, doNotUseGetters = true{{#parent}}, callSuper = true{{/parent}}) +@lombok.ToString(onlyExplicitlyIncluded = true, doNotUseGetters = true{{#parent}}, callSuper = true{{/parent}}) +{{/useLombokAnnotations}} {{#vendorExtensions.x-extra-annotation}} {{{vendorExtensions.x-extra-annotation}}}{{/vendorExtensions.x-extra-annotation}} public {{#vendorExtensions.x-abstract}}abstract {{/vendorExtensions.x-abstract}}class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}}{{^parent}}{{#hateoas}}extends RepresentationModel<{{classname}}> {{/hateoas}}{{/parent}}{{#serializableModel}}implements Serializable{{/serializableModel}} @@ -37,16 +39,17 @@ public {{#vendorExtensions.x-abstract}}abstract {{/vendorExtensions.x-abstract}} @lombok.Getter(onMethod_ = @ApiModelProperty({{#example}}example = "{{{example}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}")) @lombok.Setter @lombok.EqualsAndHashCode.Include + @lombok.ToString.Include {{#vendorExtensions.x-extra-annotation}} {{#indent4}}{{{vendorExtensions.x-extra-annotation}}}{{/indent4}}{{/vendorExtensions.x-extra-annotation}} {{#useBeanValidation}}{{>beanValidation}}{{/useBeanValidation}} {{/useLombokAnnotations}} {{#isContainer}} {{#openApiNullable}} - private {{>nullableDataType}} {{name}} = {{#isNullable}}JsonNullable.undefined(){{/isNullable}}{{^isNullable}}{{#required}}{{{defaultValue}}}{{/required}}{{^required}}null{{/required}}{{/isNullable}}; + {{modelFieldsVisibility}} {{>nullableDataType}} {{name}} = {{#isNullable}}JsonNullable.undefined(){{/isNullable}}{{^isNullable}}{{#required}}{{{defaultValue}}}{{/required}}{{^required}}null{{/required}}{{/isNullable}}; {{/openApiNullable}} {{^openApiNullable}} - private {{>nullableDataType}} {{name}} = {{#required}}{{{defaultValue}}}{{/required}}{{^required}}null{{/required}}; + {{modelFieldsVisibility}} {{>nullableDataType}} {{name}} = {{#required}}{{{defaultValue}}}{{/required}}{{^required}}null{{/required}}; {{/openApiNullable}} {{/isContainer}} {{^isContainer}} @@ -57,10 +60,10 @@ public {{#vendorExtensions.x-abstract}}abstract {{/vendorExtensions.x-abstract}} @org.springframework.format.annotation.DateTimeFormat(iso = org.springframework.format.annotation.DateTimeFormat.ISO.DATE_TIME) {{/isDateTime}} {{#openApiNullable}} - private {{>nullableDataType}} {{name}}{{#isNullable}} = JsonNullable.undefined(){{/isNullable}}{{^isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{/isNullable}}; + {{modelFieldsVisibility}} {{>nullableDataType}} {{name}}{{#isNullable}} = JsonNullable.undefined(){{/isNullable}}{{^isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{/isNullable}}; {{/openApiNullable}} {{^openApiNullable}} - private {{>nullableDataType}} {{name}}{{#isNullable}} = null{{/isNullable}}{{^isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{/isNullable}}; + {{modelFieldsVisibility}} {{>nullableDataType}} {{name}}{{#isNullable}} = null{{/isNullable}}{{^isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{/isNullable}}; {{/openApiNullable}} {{/isContainer}} @@ -148,6 +151,9 @@ public {{#vendorExtensions.x-abstract}}abstract {{/vendorExtensions.x-abstract}} {{/vars}} + {{#vendorExtensions.x-extra-java-code}} + {{#indent4}}{{{vendorExtensions.x-extra-java-code}}}{{/indent4}}{{/vendorExtensions.x-extra-java-code}} + {{^useLombokAnnotations}} @Override public boolean equals(java.lang.Object o) { @@ -172,7 +178,6 @@ public {{#vendorExtensions.x-abstract}}abstract {{/vendorExtensions.x-abstract}} {{/hasVars}}super.hashCode(){{/parent}} ); } - {{/useLombokAnnotations}} @Override public String toString() { @@ -194,4 +199,5 @@ public {{#vendorExtensions.x-abstract}}abstract {{/vendorExtensions.x-abstract}} } return o.toString().replace("\n", "\n "); } + {{/useLombokAnnotations}} } diff --git a/boat-scaffold/src/test/java/com/backbase/oss/codegen/java/BoatJavaCodeGenTests.java b/boat-scaffold/src/test/java/com/backbase/oss/codegen/java/BoatJavaCodeGenTests.java index 90a4be757..80e4d762e 100644 --- a/boat-scaffold/src/test/java/com/backbase/oss/codegen/java/BoatJavaCodeGenTests.java +++ b/boat-scaffold/src/test/java/com/backbase/oss/codegen/java/BoatJavaCodeGenTests.java @@ -4,6 +4,7 @@ import java.util.Map; import static java.util.stream.Collectors.groupingBy; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -89,6 +90,31 @@ void processOptsWithoutRestTemplate() { assertThat(gen.restTemplateBeanName, is(nullValue())); } + @Test + void processOptsCreateApiComponent() { + final BoatJavaCodeGen gen = new BoatJavaCodeGen(); + final Map options = gen.additionalProperties(); + + options.put(CREATE_API_COMPONENT, "false"); + + gen.setLibrary("resttemplate"); + gen.processOpts(); + + assertThat(gen.createApiComponent, is(false)); + } + + @Test + void processOptsUseProtectedFields() { + final BoatJavaCodeGen gen = new BoatJavaCodeGen(); + final Map options = gen.additionalProperties(); + + options.put(USE_PROTECTED_FIELDS, "true"); + + gen.processOpts(); + + assertThat(gen.additionalProperties(), hasEntry("modelFieldsVisibility", "protected")); + } + @Test void uniquePropertyToSet() { final BoatJavaCodeGen gen = new BoatJavaCodeGen(); @@ -127,4 +153,5 @@ void uniqueParameterToSet() { assertThat(param.baseType, is("java.util.Set")); assertThat(param.dataType, is("java.util.Set")); } + } diff --git a/boat-scaffold/src/test/java/com/backbase/oss/codegen/java/BoatSpringCodeGenTests.java b/boat-scaffold/src/test/java/com/backbase/oss/codegen/java/BoatSpringCodeGenTests.java index 44e5fa31f..105234f1c 100644 --- a/boat-scaffold/src/test/java/com/backbase/oss/codegen/java/BoatSpringCodeGenTests.java +++ b/boat-scaffold/src/test/java/com/backbase/oss/codegen/java/BoatSpringCodeGenTests.java @@ -1,23 +1,26 @@ package com.backbase.oss.codegen.java; +import static com.backbase.oss.codegen.java.BoatSpringCodeGen.USE_PROTECTED_FIELDS; +import static java.util.stream.Collectors.groupingBy; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import com.backbase.oss.codegen.java.BoatSpringCodeGen.NewLineIndent; import com.samskivert.mustache.Template.Fragment; import java.io.IOException; import java.io.StringWriter; +import java.util.Map; import org.junit.jupiter.api.Test; import org.openapitools.codegen.CliOption; import org.openapitools.codegen.CodegenModel; import org.openapitools.codegen.CodegenParameter; import org.openapitools.codegen.CodegenProperty; -import static java.util.stream.Collectors.groupingBy; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - class BoatSpringCodeGenTests { @@ -27,7 +30,7 @@ void clientOptsUnicity() { gen.cliOptions() .stream() .collect(groupingBy(CliOption::getOpt)) - .forEach((k, v) -> assertEquals( 1,v.size(), k + " is described multiple times")); + .forEach((k, v) -> assertEquals(1, v.size(), k + " is described multiple times")); } @Test @@ -69,6 +72,19 @@ void uniqueParameterToSet() { assertThat(param.dataType, is("java.util.Set")); } + @Test + void processOptsUseProtectedFields() { + final BoatJavaCodeGen gen = new BoatJavaCodeGen(); + final Map options = gen.additionalProperties(); + + options.put(USE_PROTECTED_FIELDS, "true"); + + gen.processOpts(); + + assertThat(gen.additionalProperties(), hasEntry("modelFieldsVisibility", "protected")); + } + + @Test void newLineIndent() throws IOException { final NewLineIndent indent = new BoatSpringCodeGen.NewLineIndent(2, "_"); diff --git a/boat-scaffold/src/test/java/com/backbase/oss/codegen/java/BoatSpringTemplatesTests.java b/boat-scaffold/src/test/java/com/backbase/oss/codegen/java/BoatSpringTemplatesTests.java index db1b9b0ae..b39601179 100644 --- a/boat-scaffold/src/test/java/com/backbase/oss/codegen/java/BoatSpringTemplatesTests.java +++ b/boat-scaffold/src/test/java/com/backbase/oss/codegen/java/BoatSpringTemplatesTests.java @@ -70,7 +70,7 @@ static public void setUpClass() throws IOException { } static class Combination { - static final List CASES = asList("flx", "unq", "val", "opt", "req", "lmb", "nbl", "wth", "utl"); + static final List CASES = asList("flx", "unq", "val", "opt", "req", "bin", "lmb", "nbl", "wth", "utl"); final String name; @@ -78,6 +78,7 @@ static class Combination { final boolean useOptional; final boolean addServletRequest; + final boolean addBindingResult; final boolean useLombokAnnotations; final boolean openApiNullable; final boolean useSetForUniqueItems; @@ -95,6 +96,7 @@ static class Combination { .collect(joining("-", "boat-", "")); this.useBeanValidation = (mask & 1 << CASES.indexOf("val")) != 0; + this.addBindingResult = (mask & 1 << CASES.indexOf("bin")) != 0; this.useOptional = (mask & 1 << CASES.indexOf("opt")) != 0; this.addServletRequest = (mask & 1 << CASES.indexOf("req")) != 0; this.useLombokAnnotations = (mask & 1 << CASES.indexOf("lmb")) != 0; @@ -176,9 +178,9 @@ void generate(Combination param) { @Check void useBeanValidation() { assertThat(findPattern("/api/.+\\.java$", "@Valid"), - equalTo(this.param.useBeanValidation)); + equalTo(this.param.useBeanValidation||this.param.addBindingResult)); assertThat(findPattern("/model/.+\\.java$", "@Valid"), - equalTo(this.param.useBeanValidation)); + equalTo(this.param.useBeanValidation||this.param.addBindingResult)); } @Check @@ -197,6 +199,14 @@ void addServletRequest() { is(false)); } + @Check + void addBindingResult(){ + assertThat(findPattern("/api/.+\\.java$", "BindingResult\\s+bindingResult"), + equalTo(this.param.addBindingResult)); + assertThat(findPattern("/model/.+\\.java$", "HttpServletRequest\\s+httpServletRequest"), + is(false)); + } + @Check void useLombokAnnotations() { assertThat(findPattern("/api/.+\\.java$", "@lombok\\.Getter"), @@ -277,11 +287,17 @@ private List generateFrom(String templates) { gcf.setApiNameSuffix("-api"); gcf.setModelNameSuffix(this.param.name); - gcf.addAdditionalProperty(BeanValidationFeatures.USE_BEANVALIDATION, this.param.useBeanValidation); + gcf.addAdditionalProperty(OptionalFeatures.USE_OPTIONAL, this.param.useOptional); gcf.addAdditionalProperty(BoatSpringCodeGen.USE_CLASS_LEVEL_BEAN_VALIDATION, true); gcf.addAdditionalProperty(BoatSpringCodeGen.ADD_SERVLET_REQUEST, this.param.addServletRequest); + gcf.addAdditionalProperty(BoatSpringCodeGen.ADD_BINDING_RESULT,this.param.addBindingResult); + if(this.param.addBindingResult){ + gcf.addAdditionalProperty(BeanValidationFeatures.USE_BEANVALIDATION, true); + }else { + gcf.addAdditionalProperty(BeanValidationFeatures.USE_BEANVALIDATION, this.param.useBeanValidation); + } gcf.addAdditionalProperty(BoatSpringCodeGen.USE_LOMBOK_ANNOTATIONS, this.param.useLombokAnnotations); gcf.addAdditionalProperty(BoatSpringCodeGen.USE_SET_FOR_UNIQUE_ITEMS, this.param.useSetForUniqueItems); gcf.addAdditionalProperty(BoatSpringCodeGen.OPENAPI_NULLABLE, this.param.openApiNullable); diff --git a/boat-terminal/pom.xml b/boat-terminal/pom.xml index f371b2659..edc035544 100644 --- a/boat-terminal/pom.xml +++ b/boat-terminal/pom.xml @@ -5,7 +5,7 @@ com.backbase.oss backbase-openapi-tools - 0.14.2-SNAPSHOT + 0.14.3-SNAPSHOT boat-terminal diff --git a/boat-trail-resources/pom.xml b/boat-trail-resources/pom.xml index 68510c608..88527418d 100644 --- a/boat-trail-resources/pom.xml +++ b/boat-trail-resources/pom.xml @@ -5,7 +5,7 @@ com.backbase.oss backbase-openapi-tools - 0.14.2-SNAPSHOT + 0.14.3-SNAPSHOT boat-trail-resources diff --git a/pom.xml b/pom.xml index 9e1ded3e7..ebc174223 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,11 @@ - + 4.0.0 com.backbase.oss backbase-openapi-tools - 0.14.2-SNAPSHOT + 0.14.3-SNAPSHOT pom Backbase Open Api Tools will help you converting RAML to OpenAPI plus many more @@ -58,10 +59,8 @@ ${aggregate.report.dir} 1.18.16 - 4.13.1 - 2.11.3 - 30.0-jre + 3.8.0 1.7.30 2.1.5 @@ -71,7 +70,7 @@ - + boat-trail-resources boat-engine boat-scaffold @@ -302,11 +301,13 @@ jackson-databind-nullable 0.2.1
+ - org.junit.jupiter - junit-jupiter - 5.7.0 - test + org.junit + junit-bom + 5.7.1 + pom + import @@ -315,13 +316,19 @@ 2.2 test + org.mockito mockito-core - 3.3.3 + ${mockito.version} + test + + + org.mockito + mockito-junit-jupiter + ${mockito.version} test - diff --git a/tests/pom.xml b/tests/pom.xml index d3142ef3a..9f52738f6 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -5,7 +5,7 @@ com.backbase.oss backbase-openapi-tools - 0.14.2-SNAPSHOT + 0.14.3-SNAPSHOT tests