@@ -140,6 +145,83 @@
test
+
+ io.swagger
+ swagger-annotations
+ ${swagger-annotations-version}
+
+
+
+
+ com.google.code.findbugs
+ jsr305
+ 3.0.2
+
+
+
+
+ io.github.openfeign
+ feign-core
+ ${feign-version}
+
+
+ io.github.openfeign
+ feign-jackson
+ ${feign-version}
+
+
+ io.github.openfeign
+ feign-slf4j
+ ${feign-version}
+
+
+ io.github.openfeign.form
+ feign-form
+ ${feign-form-version}
+
+
+ io.github.openfeign
+ feign-okhttp
+ ${feign-version}
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ org.openapitools
+ jackson-databind-nullable
+
+
+ com.github.joschi.jackson
+ jackson-datatype-threetenbp
+ ${jackson-threetenbp-version}
+
+
+ com.github.scribejava
+ scribejava-core
+ ${scribejava-version}
+
+
+
+
+ com.squareup.okhttp3
+ mockwebserver
+ 4.9.1
+ test
+
+
+
@@ -254,6 +336,34 @@
+
+ org.openapitools
+ openapi-generator-maven-plugin
+ 5.0.0
+
+
+
+ generate
+
+
+ ${project.basedir}/src/main/resources/boat-maven-plugin-api.yaml
+ java
+ false
+ false
+ false
+ false
+
+ src/gen/java/main
+ com.backbase.oss.boat.bay.client.model
+ com.backbase.oss.boat.bay.client.api
+ feign
+ java8-localdatetime
+ @lombok.AllArgsConstructor @lombok.Builder @lombok.NoArgsConstructor
+
+
+
+
+
\ No newline at end of file
diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/radio/RadioMojo.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/radio/RadioMojo.java
new file mode 100644
index 000000000..3ec98cea8
--- /dev/null
+++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/radio/RadioMojo.java
@@ -0,0 +1,294 @@
+package com.backbase.oss.boat.radio;
+
+import com.backbase.oss.boat.Utils;
+import com.backbase.oss.boat.bay.client.ApiClient;
+import com.backbase.oss.boat.bay.client.api.BoatMavenPluginApi;
+import com.backbase.oss.boat.bay.client.model.*;
+import com.backbase.oss.boat.loader.OpenAPILoader;
+import com.backbase.oss.boat.loader.OpenAPILoaderException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import feign.auth.BasicAuthRequestInterceptor;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+
+import static java.lang.String.format;
+
+/**
+ * Upload specs (one of more) to Boat-Bay.
+ */
+@Mojo(name = "radio", requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true)
+@Slf4j
+@Getter
+@Setter
+public class RadioMojo extends AbstractMojo {
+
+ /**
+ * Project GroupId in Boat-Bay. Defaults to {@code ${project.groupId}}
+ */
+ @Parameter(property = "groupId", defaultValue = "${project.groupId}")
+ private String groupId;
+
+ /**
+ * Project ArtifactId in Boat-Bay. Defaults to {@code ${project.artifactId}}
+ */
+ @Parameter(property = "artifactId", defaultValue = "${project.artifactId}")
+ private String artifactId;
+
+ /**
+ * Project Version in Boat-Bay. Defaults to {@code ${project.version}}
+ */
+ @Parameter(property = "version", defaultValue = "${project.version}")
+ private String version;
+
+ /**
+ * Boat-Bay domain. eg. https://boatbay.mycompany.eu
+ */
+ @Parameter(property = "boatBayUrl", required = true)
+ private String boatBayUrl;
+
+ /**
+ * Fail the build for breaking changes in specs
+ */
+ @Parameter(property = "failOnBreakingChange", defaultValue="false")
+ private boolean failOnBreakingChange;
+
+ /**
+ * Fail the build if the spec has lint violation (Violation with Severity.MUST)
+ */
+ @Parameter(property = "failOnLintViolation", defaultValue="false")
+ private boolean failOnLintViolation;
+
+ /**
+ * Fail the build if boatbay server returns an error
+ */
+ @Parameter(property = "failOnBoatBayErrorResponse", defaultValue="true")
+ private boolean failOnBoatBayErrorResponse =true;
+
+ /**
+ * Project portal Identifier in Boat-Bay.
+ */
+ @Parameter(property = "portalKey", required = true)
+ private String portalKey;
+
+ /**
+ * Project source identifier in Boat-Bay.
+ */
+ @Parameter(property = "sourceKey", required = true)
+ private String sourceKey;
+
+ /**
+ * Defines the username which can access Boat-Bay upload API. Required if boat-bay APIs are protected.
+ */
+ @Parameter(property = "username")
+ private String username;
+
+ /**
+ * Defines the password of the username which can access the Boat-Bay upload API. Required if boat-bay APIs are protected.
+ */
+ @Parameter(property = "password")
+ private String password;
+
+ /**
+ *
+ * Array of spec to be uploaded. Spec fields:
+ *
+ *
+ * {@code key} :
+ * Spec Key in Boat-Bay. Defaults to {@code filename.lastIndexOf("-")}.
+ * For example - By default {@code my-service-api-v3.1.4.yaml} would be evaluated to {@code my-service-api}
+ *
+ *
+ * {@code name} :
+ * Spec Name in Boat-Bay. Defaults to filename.
+ *
+ *
+ * {@code inputSpec} :
+ * 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(property = "specs", required = true)
+ private SpecConfig[] specs;
+
+ /**
+ * Output directory for boat-radio report.
+ */
+ @Parameter(name = "radioOutput", defaultValue = "${project.build.directory}/target/boat-radio-report")
+ private File radioOutput;
+
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+
+ BasicAuthRequestInterceptor basicAuthRequestInterceptor = null;
+
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ if (username != null && !username.isEmpty() && password != null && !password.isEmpty()) {
+ getLog().info("Basic Authentication set for username " + username);
+ basicAuthRequestInterceptor = new BasicAuthRequestInterceptor(username, password);
+ } else {
+ getLog().info("No Authentication set");
+ }
+
+ List allSpecs = new ArrayList<>();
+
+ for (SpecConfig spec : specs) {
+ allSpecs.add(mapToUploadSpec(spec));
+ }
+
+ ApiClient apiClient = new ApiClient().setBasePath(boatBayUrl);
+ if (basicAuthRequestInterceptor != null) {
+ apiClient.addAuthorization("Basic Auth", basicAuthRequestInterceptor);
+ }
+
+ BoatMavenPluginApi api = apiClient.buildClient(BoatMavenPluginApi.class);
+
+ UploadRequestBody uploadRequestBody = UploadRequestBody.builder()
+ .groupId(groupId).artifactId(artifactId).version(version).specs(allSpecs).build();
+
+ List reports =null;
+ try {
+ reports = api.uploadSpec(portalKey, sourceKey, uploadRequestBody);
+ }catch (Exception e){
+ getLog().error("BoatBay error :: " + e.getMessage());
+ if(failOnBoatBayErrorResponse)
+ throw new MojoFailureException("BoatBay error", e);
+ }
+
+ // Process Result
+ if(reports!=null) {
+ try {
+ File outputFile = new File(getOutput(), "radioOutput.json");
+ objectMapper.writerWithDefaultPrettyPrinter().writeValue(outputFile, reports);
+ // Log summary of report
+ reports.forEach(report -> {
+ getLog().info(format("Spec %s summary :", report.getSpec().getKey()));
+ getLog().info(format("Changes are %s ", report.getSpec().getChanges()));
+ getLog().info("Number of Violations:" + report.getViolations().size());
+ });
+ // Log link to reports
+ getLog().info("UPLOAD TO BOAT-BAY SUCCESSFUL, check the full report: " + outputFile.getCanonicalPath());
+
+ if (failOnBreakingChange) {
+ boolean doesSpecsHaveBreakingChanges = reports.stream()
+ .anyMatch(report -> report.getSpec().getChanges().equals(Changes.BREAKING));
+ if (doesSpecsHaveBreakingChanges)
+ throw new MojoFailureException("Specs have Breaking Changes. Check full report.");
+ }
+
+ if (failOnLintViolation) {
+ boolean doesSpecsHaveMustViolations = reports.stream()
+ .anyMatch(report -> report.getViolations().stream()
+ .anyMatch(violation -> violation.getSeverity().equals(Severity.MUST)));
+ if (doesSpecsHaveMustViolations)
+ throw new MojoFailureException("Specs have Must Violations. Check full report.");
+ }
+ } catch (IOException e) {
+ throw new MojoFailureException("Failed to write output", e);
+ }
+ }
+
+ }
+
+ private UploadSpec mapToUploadSpec(SpecConfig spec) throws MojoExecutionException {
+
+ //Validate if the spec file path is valid and unique.
+ File inputSpecFile = new File(spec.getInputSpec());
+ File inputParent = inputSpecFile.getParentFile();
+
+ if (inputParent.isDirectory()) {
+ try {
+ String[] files = Utils.selectInputs(inputParent.toPath(), inputSpecFile.getName());
+
+ switch (files.length) {
+ case 0:
+ String noFileMessage = format("Input spec %s doesn't match any local file", spec.getInputSpec());
+ getLog().error(noFileMessage);
+ throw new MojoExecutionException(noFileMessage);
+
+ case 1:
+ inputSpecFile = new File(inputParent, files[0]);
+ spec.setInputSpec(inputSpecFile.getAbsolutePath());
+ break;
+
+ default:
+ String message = format("Input spec %s matches more than one single file", spec.getInputSpec());
+ getLog().error(message);
+ Stream.of(files).forEach(f -> getLog().error(format(" %s", f)));
+ throw new MojoExecutionException(message);
+ }
+ } catch (IOException e) {
+ throw new MojoExecutionException("Cannot find input " + spec.getInputSpec());
+ }
+ } else {
+ String message = format("Invalid parent spec folder %s ", spec.getInputSpec());
+ getLog().error(message);
+ throw new MojoExecutionException(message);
+ }
+
+ //Validate if the spec file is valid open-api spec
+ String contents;
+ try {
+ contents = IOUtils.toString(inputSpecFile.toURI(), Charset.defaultCharset());
+ OpenAPILoader.parse(contents);
+ } catch (IOException e) {
+ String msg = "Invalid File Path: " + inputSpecFile.getName();
+ getLog().error(msg);
+ throw new MojoExecutionException(msg, e);
+ } catch (OpenAPILoaderException e) {
+ String msg = "Invalid Open Api file: " + inputSpecFile.getName();
+ getLog().error(msg);
+ throw new MojoExecutionException(msg, e);
+ }
+
+ String key = spec.getKey();
+ if (key == null || key.isEmpty()) {
+ key = inputSpecFile.getName().substring(0, inputSpecFile.getName().lastIndexOf("-"));
+ }
+
+ String name = spec.getName();
+ if (name == null || name.isEmpty()) {
+ name = inputSpecFile.getName();
+ }
+
+ //Validation Complete. Prepare UploadSpec.
+ UploadSpec uploadSpec = UploadSpec.builder()
+ .fileName(inputSpecFile.getName()).key(key).name(name).openApi(contents).build();
+
+ return uploadSpec;
+
+ }
+
+ @SneakyThrows
+ private File getOutput() {
+ if (radioOutput == null) {
+ radioOutput = new File("./target/boat-radio-report");
+ }
+ if (!radioOutput.exists()) {
+ radioOutput.mkdirs();
+ }
+ return radioOutput;
+ }
+
+
+}
+
diff --git a/boat-maven-plugin/src/main/java/com/backbase/oss/boat/radio/SpecConfig.java b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/radio/SpecConfig.java
new file mode 100644
index 000000000..fdf490548
--- /dev/null
+++ b/boat-maven-plugin/src/main/java/com/backbase/oss/boat/radio/SpecConfig.java
@@ -0,0 +1,36 @@
+package com.backbase.oss.boat.radio;
+
+import lombok.Data;
+
+@Data
+/**
+ * Spec to be uploaded.
+ */
+public class SpecConfig {
+
+ /**
+ * Spec Key in Boat-Bay. Defaults to {@code inputSpecFile.getName().lastIndexOf("-")}.
+ * For example - By default {@code my-service-api-v3.1.4.yaml} would be evaluated to {@code my-service-api}
+ */
+ private String key;
+
+ /**
+ * Spec Name in Boat-Bay. Defaults to filename.
+ */
+ private String name;
+
+ /**
+ * 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}.
+ *
+ */
+ private String inputSpec;
+
+}
diff --git a/boat-maven-plugin/src/main/resources/boat-maven-plugin-api.yaml b/boat-maven-plugin/src/main/resources/boat-maven-plugin-api.yaml
new file mode 100644
index 000000000..f0cdcbd0a
--- /dev/null
+++ b/boat-maven-plugin/src/main/resources/boat-maven-plugin-api.yaml
@@ -0,0 +1,44 @@
+openapi: 3.0.0
+info:
+ title: Boat Bay Upload Server
+ description: Endpoints for uploading Specs to boat bay
+ license:
+ name: Backbase
+ version: 1.0.0
+servers:
+- url: http://localhost:8080/
+tags:
+- name: boat-maven-plugin
+ description: Endpoints used by the Boat Maven Plugin
+paths:
+ /api/boat/portals/{portalKey}/boat-maven-plugin/{sourceKey}/upload:
+ post:
+ tags:
+ - boat-maven-plugin
+ summary: upload and lint specs
+ operationId: uploadSpec
+ parameters:
+ - $ref: 'schemas/definitions.yaml#/components/parameters/PortalKey'
+ - name: sourceKey
+ in: path
+ description: source identifier
+ required: true
+ style: simple
+ explode: false
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: 'schemas/definitions.yaml#/components/schemas/UploadRequestBody'
+ required: true
+ responses:
+ "200":
+ description: list of lint reports for specs
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: 'schemas/definitions.yaml#/components/schemas/BoatLintReport'
diff --git a/boat-maven-plugin/src/main/resources/schemas/definitions.yaml b/boat-maven-plugin/src/main/resources/schemas/definitions.yaml
new file mode 100644
index 000000000..d09915508
--- /dev/null
+++ b/boat-maven-plugin/src/main/resources/schemas/definitions.yaml
@@ -0,0 +1,458 @@
+openapi: 3.0.3
+info:
+ title: Shared Components BOAT BAY
+ version: 1.0.0
+paths: {}
+components:
+ schemas:
+ BoatPortal:
+ type: object
+ properties:
+ id:
+ type: number
+ key:
+ type: string
+ name:
+ type: string
+ content:
+ type: string
+ createdOn:
+ type: string
+ format: date-time
+ createdBy:
+ type: string
+ numberOfServices:
+ type: integer
+ numberOfCapabilities:
+ type: integer
+ productDescription:
+ type: string
+ lastLintReport:
+ $ref: '#/components/schemas/BoatLintReport'
+ statistics:
+ $ref: '#/components/schemas/BoatStatistics'
+ required:
+ - id
+ - key
+ - name
+ BoatProduct:
+ type: object
+ properties:
+ portalKey:
+ type: string
+ portalName:
+ type: string
+ id:
+ type: number
+ key:
+ type: string
+ name:
+ type: string
+ content:
+ type: string
+ createdOn:
+ type: string
+ format: date-time
+ createdBy:
+ type: string
+ lastLintReport:
+ $ref: '#/components/schemas/BoatLintReport'
+ statistics:
+ $ref: '#/components/schemas/BoatStatistics'
+ jiraProjectId:
+ type: string
+ required:
+ - portalKey
+ - portalName
+ - id
+ - key
+ - name
+ BoatCapability:
+ type: object
+ properties:
+ id:
+ type: number
+ key:
+ type: string
+ name:
+ type: string
+ content:
+ type: string
+ createdOn:
+ type: string
+ format: date-time
+ createdBy:
+ type: string
+ services:
+ type: array
+ items:
+ $ref: '#/components/schemas/BoatService'
+ lastLintReport:
+ $ref: '#/components/schemas/BoatLintReport'
+ statistics:
+ $ref: '#/components/schemas/BoatStatistics'
+ required:
+ - id
+ - key
+ - name
+ BoatService:
+ type: object
+ properties:
+ id:
+ type: number
+ key:
+ type: string
+ name:
+ type: string
+ description:
+ type: string
+ icon:
+ type: string
+ color:
+ type: string
+ createdOn:
+ type: string
+ format: date-time
+ createdBy:
+ type: string
+ statistics:
+ $ref: '#/components/schemas/BoatStatistics'
+ capability:
+ $ref: '#/components/schemas/BoatCapability'
+ required:
+ - id
+ - key
+ - name
+ - capability
+ BoatSpec:
+ type: object
+ properties:
+ id:
+ type: number
+ key:
+ type: string
+ name:
+ type: string
+ title:
+ type: string
+ description:
+ type: string
+ icon:
+ type: string
+ version:
+ type: string
+ grade:
+ type: string
+ createdOn:
+ type: string
+ format: date-time
+ createdBy:
+ type: string
+ statistics:
+ $ref: '#/components/schemas/BoatStatistics'
+ backwardsCompatible:
+ type: boolean
+ changes:
+ $ref: '#/components/schemas/Changes'
+ capability:
+ $ref: '#/components/schemas/BoatCapability'
+ serviceDefinition:
+ $ref: '#/components/schemas/BoatService'
+ openApi:
+ type: string
+ required:
+ - id
+ - key
+ - name
+ - version
+ - capability
+ - serviceDefinition
+ BoatLintReport:
+ type: object
+ properties:
+ id:
+ type: number
+ spec:
+ $ref: '#/components/schemas/BoatSpec'
+ name:
+ type: string
+ passed:
+ type: boolean
+ lintedOn:
+ type: string
+ format: date-time
+ openApi:
+ type: string
+ version:
+ type: string
+ grade:
+ type: string
+ violations:
+ type: array
+ items:
+ $ref: '#/components/schemas/BoatViolation'
+ required:
+ - id
+ - sec
+ - name
+ - passed
+ - lintedOn
+ - openApi
+ - version
+ - grade
+ - spec
+ - violations
+ BoatLintRule:
+ type: object
+ properties:
+ id:
+ type: number
+ ruleId:
+ type: string
+ enabled:
+ type: boolean
+ title:
+ type: string
+ ruleSet:
+ type: string
+ severity:
+ $ref: '#/components/schemas/Severity'
+ url:
+ type: string
+ format: uri
+ required:
+ - id
+ - ruleId
+ - enabled
+ - title
+ - ruleSet
+ - severity
+ - url
+ BoatProductRelease:
+ type: object
+ properties:
+ id:
+ type: number
+ key:
+ type: string
+ name:
+ type: string
+ version:
+ type: string
+ releaseDate:
+ type: string
+ format: date-time
+ required:
+ - id
+ - key
+ - name
+ - version
+ - releaseDate
+ BoatTag:
+ type: object
+ properties:
+ name:
+ type: string
+ description:
+ type: string
+ hide:
+ type: boolean
+ color:
+ type: string
+ numberOfOccurrences:
+ type: integer
+ required:
+ - name
+ Resource:
+ type: array
+ items:
+ type: string
+ format: byte
+ BoatStatistics:
+ type: object
+ properties:
+ updatedOn:
+ type: string
+ format: date-time
+ mustViolationsCount:
+ type: integer
+ format: int64
+ shouldViolationsCount:
+ type: integer
+ format: int64
+ mayViolationsCount:
+ type: integer
+ format: int64
+ hintViolationsCount:
+ type: integer
+ format: int64
+ required:
+ - updatedOn
+ - mustViolationsCount
+ - shouldViolationsCount
+ - mayViolationsCount
+ - hintViolationsCount
+ Changes:
+ type: string
+ enum:
+ - INVALID_VERSION
+ - NOT_APPLICABLE
+ - ERROR_COMPARING
+ - UNCHANGED
+ - COMPATIBLE
+ - BREAKING
+ BoatViolation:
+ type: object
+ properties:
+ rule:
+ $ref: '#/components/schemas/BoatLintRule'
+ description:
+ type: string
+ severity:
+ $ref: '#/components/schemas/Severity'
+ lines:
+ $ref: '#/components/schemas/IntRange'
+ pointer:
+ type: string
+ required:
+ - rule
+ - description
+ - lines
+ - pointer
+ - severity
+ Severity:
+ type: string
+ enum:
+ - MUST
+ - SHOULD
+ - MAY
+ - HINT
+ UploadRequestBody:
+ type: object
+ properties:
+ specs:
+ type: array
+ items:
+ $ref: '#/components/schemas/UploadSpec'
+ groupId:
+ type: string
+ artifactId:
+ type: string
+ version:
+ type: string
+ required:
+ - specs
+ - artifactId
+ - groupId
+ - version
+ UploadSpec:
+ type: object
+ properties:
+ fileName:
+ type: string
+ openApi:
+ type: string
+ key:
+ type: string
+ name:
+ type: string
+ required:
+ - fileName
+ - openApi
+ - name
+ IntRange:
+ type: object
+ properties:
+ start:
+ type: integer
+ endInclusive:
+ type: integer
+ required:
+ - start
+ - endInclusive
+ parameters:
+ PortalKey:
+ name: portalKey
+ in: path
+ description: Portal Identifier
+ required: true
+ schema:
+ type: string
+ ProductKey:
+ name: productKey
+ in: path
+ description: Product Identifier
+ required: true
+ schema:
+ type: string
+ CapabilityKey:
+ name: capabilityKey
+ in: path
+ description: Capability Identifier
+ required: true
+ schema:
+ type: string
+ ServiceKey:
+ name: serviceKey
+ in: path
+ description: Service Identifier
+ required: true
+ schema:
+ type: string
+ SpecKey:
+ name: specKey
+ in: path
+ description: Spec Identifier
+ required: true
+ schema:
+ type: string
+ ReleaseKey:
+ name: releaseKey
+ in: path
+ description: Product Release Identifier
+ required: true
+ schema:
+ type: string
+ SpecId:
+ name: specId
+ in: path
+ description: Unique Spec Identifier
+ required: true
+ schema:
+ type: number
+ Version:
+ name: version
+ in: path
+ description: Spec Version
+ required: true
+ schema:
+ type: string
+ Page:
+ name: page
+ in: query
+ required: true
+ schema:
+ type: integer
+ Size:
+ name: size
+ in: query
+ required: true
+ schema:
+ type: integer
+ Sort:
+ name: sort
+ in: query
+ required: true
+ schema:
+ type: array
+ items:
+ type: string
+ headers:
+ X-Total-Count:
+ description: "Total amount of items"
+ schema:
+ type: integer
+ Link:
+ description: "Link"
+ schema:
+ type: string
diff --git a/boat-maven-plugin/src/test/java/com/backbase/oss/boat/radio/RadioMojoTests.java b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/radio/RadioMojoTests.java
new file mode 100644
index 000000000..ba1f95291
--- /dev/null
+++ b/boat-maven-plugin/src/test/java/com/backbase/oss/boat/radio/RadioMojoTests.java
@@ -0,0 +1,596 @@
+package com.backbase.oss.boat.radio;
+
+import com.backbase.oss.boat.bay.client.model.*;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import feign.auth.BasicAuthRequestInterceptor;
+import java.io.File;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.mockwebserver.Dispatcher;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@Slf4j
+class RadioMojoTests {
+
+ static MockWebServer mockBackEnd;
+
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ @BeforeAll
+ @SneakyThrows
+ static void setUp() {
+ mockBackEnd = new MockWebServer();
+ mockBackEnd.start();
+ }
+
+ @Test
+ @SneakyThrows
+ void test_all_valid_inputs() {
+
+ final String portalKey = "example";
+ final String sourceKey = "pet-store-bom";
+ final String specKey = "spec-key";
+ final String specValue = "spec-name";
+ final String groupId = "com.backbase.boat.samples";
+ final String artifactId = "pet-store-bom";
+ final String version = "2021.09";
+ final String fileNameRequest = "one-client-*-v1.yaml";
+ final String fileNameResolved = "one-client-api-v1.yaml";
+
+ final BigDecimal reportId = BigDecimal.valueOf(10);
+ final String reportGrade = "A";
+
+ SpecConfig specConfig = new SpecConfig();
+ specConfig.setKey(specKey);
+ specConfig.setName(specValue);
+ specConfig.setInputSpec(getFile("/bundler/folder/" + fileNameRequest));
+
+ RadioMojo mojo = new RadioMojo();
+ mojo.setGroupId(groupId);
+ mojo.setArtifactId(artifactId);
+ mojo.setVersion(version);
+ mojo.setPortalKey(portalKey);
+ mojo.setSourceKey(sourceKey);
+ mojo.setSpecs(new SpecConfig[]{specConfig});
+ mojo.setBoatBayUrl(String.format("http://localhost:%s", mockBackEnd.getPort()));
+
+ final Dispatcher dispatcher = new Dispatcher() {
+ @SneakyThrows
+ @Override
+ public MockResponse dispatch(RecordedRequest request) {
+ switch (request.getPath()) {
+ case "/api/boat/portals/" + portalKey + "/boat-maven-plugin/" + sourceKey + "/upload":
+
+ UploadRequestBody requestBody = objectMapper.readValue(request.getBody().readUtf8(), UploadRequestBody.class);
+ UploadSpec uploadSpec = requestBody.getSpecs().get(0);
+
+ if (requestBody.getGroupId().equals(groupId) &&
+ requestBody.getArtifactId().equals(artifactId) &&
+ requestBody.getVersion().equals(version) &&
+ uploadSpec.getKey().equals(specKey) &&
+ uploadSpec.getName().equals(specValue) &&
+ uploadSpec.getFileName().equals(fileNameResolved) &&
+ uploadSpec.getOpenApi().length() > 0
+ ) {
+ log.info(uploadSpec.getOpenApi());
+ List result = getSampleBoatLintReports(specKey, reportId, reportGrade,Changes.COMPATIBLE,Severity.HINT);
+ return new MockResponse().setResponseCode(200).setBody(objectMapper.writeValueAsString(result));
+ } else {
+ return new MockResponse().setResponseCode(400);
+ }
+ }
+ return new MockResponse().setResponseCode(404);
+ }
+ };
+
+ mockBackEnd.setDispatcher(dispatcher);
+
+ mojo.execute();
+
+ File output = new File(mojo.getRadioOutput(), "radioOutput.json");
+
+ assertTrue(output.exists());
+
+ List result = Arrays.asList(objectMapper.readValue(output, BoatLintReport[].class));
+
+ assertEquals(1, result.size());
+ assertEquals(reportId, result.get(0).getId());
+ assertEquals(reportGrade, result.get(0).getGrade());
+
+ }
+
+
+ @Test
+ @SneakyThrows
+ void test_empty_specKeyAndName() {
+
+ final String portalKey = "example";
+ final String sourceKey = "pet-store-bom";
+ final String groupId = "com.backbase.boat.samples";
+ final String artifactId = "pet-store-bom";
+ final String version = "2021.09";
+ final String fileNameRequest = "one-client-*-v2.yaml";
+ final String fileNameResolved = "one-client-api-v2.yaml";
+
+ final BigDecimal reportId = BigDecimal.valueOf(20);
+ final String reportGrade = "B";
+
+ SpecConfig specConfig = new SpecConfig();
+ specConfig.setInputSpec(getFile("/bundler/folder/" + fileNameRequest));
+
+ RadioMojo mojo = new RadioMojo();
+ mojo.setGroupId(groupId);
+ mojo.setArtifactId(artifactId);
+ mojo.setVersion(version);
+ mojo.setPortalKey(portalKey);
+ mojo.setSourceKey(sourceKey);
+ mojo.setSpecs(new SpecConfig[]{specConfig});
+ mojo.setBoatBayUrl(String.format("http://localhost:%s", mockBackEnd.getPort()));
+
+ final Dispatcher dispatcher = new Dispatcher() {
+ @SneakyThrows
+ @Override
+ public MockResponse dispatch(RecordedRequest request) {
+ switch (request.getPath()) {
+ case "/api/boat/portals/" + portalKey + "/boat-maven-plugin/" + sourceKey + "/upload":
+
+ UploadRequestBody requestBody = objectMapper.readValue(request.getBody().readUtf8(), UploadRequestBody.class);
+ UploadSpec uploadSpec = requestBody.getSpecs().get(0);
+
+ String expectedDefaultKey = fileNameResolved.substring(0, fileNameResolved.lastIndexOf("-"));
+
+ if (requestBody.getGroupId().equals(groupId) &&
+ requestBody.getArtifactId().equals(artifactId) &&
+ requestBody.getVersion().equals(version) &&
+ uploadSpec.getKey().equals(expectedDefaultKey) &&
+ uploadSpec.getName().equals(fileNameResolved) &&
+ uploadSpec.getFileName().equals(fileNameResolved) &&
+ uploadSpec.getOpenApi().length() > 0
+ ) {
+ log.info(uploadSpec.getOpenApi());
+ List result = getSampleBoatLintReports(expectedDefaultKey, reportId, reportGrade,Changes.COMPATIBLE,Severity.HINT);
+ return new MockResponse().setResponseCode(200).setBody(objectMapper.writeValueAsString(result));
+ } else {
+ return new MockResponse().setResponseCode(400);
+ }
+ }
+ return new MockResponse().setResponseCode(404);
+ }
+ };
+
+ mockBackEnd.setDispatcher(dispatcher);
+
+ mojo.execute();
+
+ File output = new File(mojo.getRadioOutput(), "radioOutput.json");
+
+ assertTrue(output.exists());
+
+ List result = Arrays.asList(objectMapper.readValue(output, BoatLintReport[].class));
+
+ assertEquals(1, result.size());
+ assertEquals(reportId, result.get(0).getId());
+ assertEquals(reportGrade, result.get(0).getGrade());
+
+ }
+
+ @Test
+ void test_multiple_file_found_error() {
+
+ final String portalKey = "example";
+ final String sourceKey = "pet-store-bom";
+ final String groupId = "com.backbase.boat.samples";
+ final String artifactId = "pet-store-bom";
+ final String version = "2021.09";
+ final String fileNameRequest = "one-client-api-*.yaml";
+
+ SpecConfig specConfig = new SpecConfig();
+ specConfig.setInputSpec(getFile("/bundler/folder/" + fileNameRequest));
+
+ RadioMojo mojo = new RadioMojo();
+ mojo.setGroupId(groupId);
+ mojo.setArtifactId(artifactId);
+ mojo.setVersion(version);
+ mojo.setPortalKey(portalKey);
+ mojo.setSourceKey(sourceKey);
+ mojo.setSpecs(new SpecConfig[]{specConfig});
+ mojo.setBoatBayUrl(String.format("http://localhost:%s", mockBackEnd.getPort()));
+
+ final Dispatcher dispatcher = new Dispatcher() {
+ @SneakyThrows
+ @Override
+ public MockResponse dispatch(RecordedRequest request) {
+ switch (request.getPath()) {
+ case "/api/boat/portals/" + portalKey + "/boat-maven-plugin/" + sourceKey + "/upload":
+
+ return new MockResponse().setResponseCode(200);
+ }
+ return new MockResponse().setResponseCode(404);
+ }
+ };
+
+ mockBackEnd.setDispatcher(dispatcher);
+
+ assertThrows(MojoExecutionException.class, () -> mojo.execute());
+
+ }
+
+ @Test
+ void test_no_file_found_error() {
+
+ final String portalKey = "example";
+ final String sourceKey = "pet-store-bom";
+ final String groupId = "com.backbase.boat.samples";
+ final String artifactId = "pet-store-bom";
+ final String version = "2021.09";
+ final String invalidFileName = "invalid-client-*.yaml";
+
+ SpecConfig specConfig = new SpecConfig();
+ specConfig.setInputSpec(getFile("/bundler/folder/" + invalidFileName));
+
+ RadioMojo mojo = new RadioMojo();
+ mojo.setGroupId(groupId);
+ mojo.setArtifactId(artifactId);
+ mojo.setVersion(version);
+ mojo.setPortalKey(portalKey);
+ mojo.setSourceKey(sourceKey);
+ mojo.setSpecs(new SpecConfig[]{specConfig});
+ mojo.setBoatBayUrl(String.format("http://localhost:%s", mockBackEnd.getPort()));
+
+ final Dispatcher dispatcher = new Dispatcher() {
+ @SneakyThrows
+ @Override
+ public MockResponse dispatch(RecordedRequest request) {
+ switch (request.getPath()) {
+ case "/api/boat/portals/" + portalKey + "/boat-maven-plugin/" + sourceKey + "/upload":
+
+ return new MockResponse().setResponseCode(200);
+ }
+ return new MockResponse().setResponseCode(404);
+ }
+ };
+
+ mockBackEnd.setDispatcher(dispatcher);
+
+ assertThrows(MojoExecutionException.class, () -> mojo.execute());
+
+ }
+
+ @Test
+ void test_invalid_open_api_file() {
+
+ final String portalKey = "example";
+ final String sourceKey = "pet-store-bom";
+ final String groupId = "com.backbase.boat.samples";
+ final String artifactId = "pet-store-bom";
+ final String version = "2021.09";
+ final String invalidFile = "logback.xml";
+
+ SpecConfig specConfig = new SpecConfig();
+ specConfig.setInputSpec(getFile("/" + invalidFile));
+
+ RadioMojo mojo = new RadioMojo();
+ mojo.setGroupId(groupId);
+ mojo.setArtifactId(artifactId);
+ mojo.setVersion(version);
+ mojo.setPortalKey(portalKey);
+ mojo.setSourceKey(sourceKey);
+ mojo.setSpecs(new SpecConfig[]{specConfig});
+ mojo.setBoatBayUrl(String.format("http://localhost:%s", mockBackEnd.getPort()));
+
+ final Dispatcher dispatcher = new Dispatcher() {
+ @SneakyThrows
+ @Override
+ public MockResponse dispatch(RecordedRequest request) {
+ switch (request.getPath()) {
+ case "/api/boat/portals/" + portalKey + "/boat-maven-plugin/" + sourceKey + "/upload":
+
+ return new MockResponse().setResponseCode(200);
+ }
+ return new MockResponse().setResponseCode(404);
+ }
+ };
+
+ mockBackEnd.setDispatcher(dispatcher);
+
+ assertThrows(MojoExecutionException.class, () -> mojo.execute());
+
+ }
+
+ @Test
+ void test_invalid_parent_folder() {
+
+ final String portalKey = "example";
+ final String sourceKey = "pet-store-bom";
+ final String groupId = "com.backbase.boat.samples";
+ final String artifactId = "pet-store-bom";
+ final String version = "2021.09";
+ final String validName = "one-client-api-v1.yaml";
+
+ SpecConfig specConfig = new SpecConfig();
+ specConfig.setInputSpec(getFile("/invalid-parent/" + validName));
+
+ RadioMojo mojo = new RadioMojo();
+ mojo.setGroupId(groupId);
+ mojo.setArtifactId(artifactId);
+ mojo.setVersion(version);
+ mojo.setPortalKey(portalKey);
+ mojo.setSourceKey(sourceKey);
+ mojo.setSpecs(new SpecConfig[]{specConfig});
+ mojo.setBoatBayUrl(String.format("http://localhost:%s", mockBackEnd.getPort()));
+
+ final Dispatcher dispatcher = new Dispatcher() {
+ @SneakyThrows
+ @Override
+ public MockResponse dispatch(RecordedRequest request) {
+ switch (request.getPath()) {
+ case "/api/boat/portals/" + portalKey + "/boat-maven-plugin/" + sourceKey + "/upload":
+
+ return new MockResponse().setResponseCode(200);
+ }
+ return new MockResponse().setResponseCode(404);
+ }
+ };
+
+ mockBackEnd.setDispatcher(dispatcher);
+
+ Exception exception = assertThrows(MojoExecutionException.class, () -> mojo.execute());
+
+ assertTrue(exception.getMessage().startsWith("Invalid parent spec folder"));
+
+ }
+
+ @SneakyThrows
+ @Test
+ void test_auth() {
+
+ final String portalKey = "example";
+ final String sourceKey = "pet-store-bom";
+ final String specKey = "spec-key";
+ final String groupId = "com.backbase.boat.samples";
+ final String artifactId = "pet-store-bom";
+ final String version = "2021.09";
+ final String validName = "one-client-api-v1.yaml";
+ final String username = "admin";
+ final String password = "admin";
+ final BigDecimal reportId = BigDecimal.valueOf(10);
+ final String reportGrade = "A";
+
+ SpecConfig specConfig = new SpecConfig();
+ specConfig.setInputSpec(getFile("/bundler/folder/" + validName));
+
+ RadioMojo mojo = new RadioMojo();
+ mojo.setGroupId(groupId);
+ mojo.setArtifactId(artifactId);
+ mojo.setVersion(version);
+ mojo.setPortalKey(portalKey);
+ mojo.setSourceKey(sourceKey);
+ mojo.setUsername(username);
+ mojo.setPassword(password);
+ mojo.setSpecs(new SpecConfig[]{specConfig});
+ mojo.setBoatBayUrl(String.format("http://localhost:%s", mockBackEnd.getPort()));
+
+ final Dispatcher dispatcher = new Dispatcher() {
+ @SneakyThrows
+ @Override
+ public MockResponse dispatch(RecordedRequest request) {
+ switch (request.getPath()) {
+ case "/api/boat/portals/" + portalKey + "/boat-maven-plugin/" + sourceKey + "/upload":
+
+ if(request.getHeader("Authorization")!=null && request.getHeader("Authorization").length()>0){
+ List result = getSampleBoatLintReports(specKey, reportId, reportGrade,Changes.COMPATIBLE,Severity.HINT);
+ return new MockResponse().setResponseCode(200).setBody(objectMapper.writeValueAsString(result));
+ }
+ return new MockResponse().setResponseCode(401);
+ }
+ return new MockResponse().setResponseCode(404);
+ }
+ };
+
+ mockBackEnd.setDispatcher(dispatcher);
+
+ mojo.execute();
+
+ File output = new File(mojo.getRadioOutput(), "radioOutput.json");
+
+ assertTrue(output.exists());
+
+ }
+
+ @SneakyThrows
+ @Test
+ void test_build_fail_on_breaking_changes() {
+
+ final String portalKey = "example";
+ final String sourceKey = "pet-store-bom";
+ final String groupId = "com.backbase.boat.samples";
+ final String artifactId = "pet-store-bom";
+ final String specKey = "spec-key";
+ final String version = "2021.09";
+ final BigDecimal reportId = BigDecimal.valueOf(10);
+ final String reportGrade = "A";
+ final String validName = "one-client-api-v1.yaml";
+
+ SpecConfig specConfig = new SpecConfig();
+ specConfig.setInputSpec(getFile("/bundler/folder/" + validName));
+
+ RadioMojo mojo = new RadioMojo();
+ mojo.setGroupId(groupId);
+ mojo.setArtifactId(artifactId);
+ mojo.setVersion(version);
+ mojo.setPortalKey(portalKey);
+ mojo.setSourceKey(sourceKey);
+ mojo.setSpecs(new SpecConfig[]{specConfig});
+ mojo.setBoatBayUrl(String.format("http://localhost:%s", mockBackEnd.getPort()));
+
+ final Dispatcher dispatcher = new Dispatcher() {
+ @SneakyThrows
+ @Override
+ public MockResponse dispatch(RecordedRequest request) {
+ switch (request.getPath()) {
+ case "/api/boat/portals/" + portalKey + "/boat-maven-plugin/" + sourceKey + "/upload":
+ List result = getSampleBoatLintReports(specKey, reportId, reportGrade,Changes.BREAKING,Severity.HINT);
+ return new MockResponse().setResponseCode(200).setBody(objectMapper.writeValueAsString(result));
+ }
+ return new MockResponse().setResponseCode(404);
+ }
+ };
+
+ mockBackEnd.setDispatcher(dispatcher);
+
+
+ // Build will not fail if failOnBreakingChange is false which is default.
+ mojo.execute();
+ File output = new File(mojo.getRadioOutput(), "radioOutput.json");
+ assertTrue(output.exists());
+
+ // Build will fail if failOnBreakingChange is true
+ mojo.setFailOnBreakingChange(true);
+ Exception exception = assertThrows(MojoFailureException.class, () -> mojo.execute());
+ assertTrue(exception.getMessage().startsWith("Specs have Breaking Changes"));
+
+ }
+
+ @SneakyThrows
+ @Test
+ void test_build_fail_on_must_violation() {
+
+ final String portalKey = "example";
+ final String sourceKey = "pet-store-bom";
+ final String groupId = "com.backbase.boat.samples";
+ final String artifactId = "pet-store-bom";
+ final String specKey = "spec-key";
+ final String version = "2021.09";
+ final BigDecimal reportId = BigDecimal.valueOf(10);
+ final String reportGrade = "A";
+ final String validName = "one-client-api-v1.yaml";
+
+ SpecConfig specConfig = new SpecConfig();
+ specConfig.setInputSpec(getFile("/bundler/folder/" + validName));
+
+ RadioMojo mojo = new RadioMojo();
+ mojo.setGroupId(groupId);
+ mojo.setArtifactId(artifactId);
+ mojo.setVersion(version);
+ mojo.setPortalKey(portalKey);
+ mojo.setSourceKey(sourceKey);
+ mojo.setSpecs(new SpecConfig[]{specConfig});
+ mojo.setBoatBayUrl(String.format("http://localhost:%s", mockBackEnd.getPort()));
+
+ final Dispatcher dispatcher = new Dispatcher() {
+ @SneakyThrows
+ @Override
+ public MockResponse dispatch(RecordedRequest request) {
+ switch (request.getPath()) {
+ case "/api/boat/portals/" + portalKey + "/boat-maven-plugin/" + sourceKey + "/upload":
+ List result = getSampleBoatLintReports(specKey, reportId, reportGrade,Changes.COMPATIBLE,Severity.MUST);
+ return new MockResponse().setResponseCode(200).setBody(objectMapper.writeValueAsString(result));
+ }
+ return new MockResponse().setResponseCode(404);
+ }
+ };
+
+ mockBackEnd.setDispatcher(dispatcher);
+
+
+ // Build will not fail if failOnLintViolation is false which is default.
+ mojo.execute();
+ File output = new File(mojo.getRadioOutput(), "radioOutput.json");
+ assertTrue(output.exists());
+
+ // Build will fail if failOnLintViolation is true
+ mojo.setFailOnLintViolation(true);
+ Exception exception = assertThrows(MojoFailureException.class, () -> mojo.execute());
+ assertTrue(exception.getMessage().startsWith("Specs have Must Violations"));
+
+ }
+
+ @SneakyThrows
+ @Test
+ void test_when_boat_bay_is_unavailable() {
+
+ final String portalKey = "example";
+ final String sourceKey = "pet-store-bom";
+ final String groupId = "com.backbase.boat.samples";
+ final String artifactId = "pet-store-bom";
+ final String specKey = "spec-key";
+ final String version = "2021.09";
+ final BigDecimal reportId = BigDecimal.valueOf(10);
+ final String reportGrade = "A";
+ final String validName = "one-client-api-v1.yaml";
+
+ SpecConfig specConfig = new SpecConfig();
+ specConfig.setInputSpec(getFile("/bundler/folder/" + validName));
+
+ RadioMojo mojo = new RadioMojo();
+ mojo.setGroupId(groupId);
+ mojo.setArtifactId(artifactId);
+ mojo.setVersion(version);
+ mojo.setPortalKey(portalKey);
+ mojo.setSourceKey(sourceKey);
+ mojo.setSpecs(new SpecConfig[]{specConfig});
+ //Set invalid domain
+ mojo.setBoatBayUrl(String.format("http://invalid-domain:%s", mockBackEnd.getPort()));
+
+ final Dispatcher dispatcher = new Dispatcher() {
+ @SneakyThrows
+ @Override
+ public MockResponse dispatch(RecordedRequest request) {
+ switch (request.getPath()) {
+ case "/api/boat/portals/" + portalKey + "/boat-maven-plugin/" + sourceKey + "/upload":
+ return new MockResponse().setResponseCode(200);
+ }
+ return new MockResponse().setResponseCode(404);
+ }
+ };
+
+ mockBackEnd.setDispatcher(dispatcher);
+
+
+ // Build will fail, when failOnBoatBayErrorResponse is true (Defualt)
+ Exception exception = assertThrows(MojoFailureException.class, () -> mojo.execute());
+ assertTrue(exception.getMessage().startsWith("BoatBay error"));
+
+ // Build will not fail if failOnBoatBayErrorResponse is false
+ mojo.setFailOnBoatBayErrorResponse(false);
+ //No Exception is thrown
+ assertDoesNotThrow(() -> mojo.execute());
+ File output = new File(mojo.getRadioOutput(), "radioOutput.json");
+ //But No output file present
+ assertTrue(!output.exists());
+
+ }
+
+
+ private String getFile(String glob) {
+ return (new File("src/test/resources").getAbsolutePath() + glob);
+ }
+
+ @NotNull
+ private List getSampleBoatLintReports(String expectedDefaultKey, BigDecimal reportId, String reportGrade,
+ Changes typeOfChange, Severity sampleSeverityInResponse) {
+ BoatLintReport boatLintReport = new BoatLintReport();
+ boatLintReport.setId(reportId);
+ boatLintReport.setGrade(reportGrade);
+ boatLintReport.violations(List.of(BoatViolation.builder().severity(sampleSeverityInResponse).build()));
+ boatLintReport.setSpec(BoatSpec.builder().key(expectedDefaultKey).changes(typeOfChange).build());
+ List result = new ArrayList<>();
+ result.add(boatLintReport);
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/boat-quay/boat-quay-lint/pom.xml b/boat-quay/boat-quay-lint/pom.xml
index 93de07041..71eb784f4 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.13-SNAPSHOT
+ 0.15.0-SNAPSHOT
boat-quay-lint
jar
diff --git a/boat-quay/boat-quay-rules/pom.xml b/boat-quay/boat-quay-rules/pom.xml
index 6c2cc02bf..6aa1b391e 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.13-SNAPSHOT
+ 0.15.0-SNAPSHOT
boat-quay-rules
jar
diff --git a/boat-quay/pom.xml b/boat-quay/pom.xml
index 4bfbd5cac..b11807155 100644
--- a/boat-quay/pom.xml
+++ b/boat-quay/pom.xml
@@ -5,7 +5,7 @@
com.backbase.oss
backbase-openapi-tools
- 0.14.13-SNAPSHOT
+ 0.15.0-SNAPSHOT
diff --git a/boat-scaffold/pom.xml b/boat-scaffold/pom.xml
index 7f5f65d1a..746f5b2b8 100644
--- a/boat-scaffold/pom.xml
+++ b/boat-scaffold/pom.xml
@@ -5,7 +5,7 @@
com.backbase.oss
backbase-openapi-tools
- 0.14.13-SNAPSHOT
+ 0.15.0-SNAPSHOT
boat-scaffold
@@ -86,7 +86,7 @@
com.backbase.oss
boat-trail-resources
- 0.14.13-SNAPSHOT
+ 0.15.0-SNAPSHOT
test
diff --git a/boat-terminal/pom.xml b/boat-terminal/pom.xml
index eab26cbc9..6d02ee95c 100644
--- a/boat-terminal/pom.xml
+++ b/boat-terminal/pom.xml
@@ -5,7 +5,7 @@
com.backbase.oss
backbase-openapi-tools
- 0.14.13-SNAPSHOT
+ 0.15.0-SNAPSHOT
boat-terminal
diff --git a/boat-trail-resources/pom.xml b/boat-trail-resources/pom.xml
index d310079f4..7adb6cfcf 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.13-SNAPSHOT
+ 0.15.0-SNAPSHOT
boat-trail-resources
diff --git a/pom.xml b/pom.xml
index d52b36c35..2a52649dc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.backbase.oss
backbase-openapi-tools
- 0.14.13-SNAPSHOT
+ 0.15.0-SNAPSHOT
pom
Backbase Open Api Tools will help you converting RAML to OpenAPI plus many more
diff --git a/tests/pom.xml b/tests/pom.xml
index efe4e5f46..2486df527 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -5,7 +5,7 @@
com.backbase.oss
backbase-openapi-tools
- 0.14.13-SNAPSHOT
+ 0.15.0-SNAPSHOT
tests