Skip to content

Commit b20329a

Browse files
authored
Merge pull request #1003 from Backbase/feature/Add-flatten-output-parameter-on-bundle
Add 'flattenOutput' parameter for 'bundle' execution.
2 parents 34116c6 + 74f05a1 commit b20329a

File tree

4 files changed

+141
-20
lines changed

4 files changed

+141
-20
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ It currently consists of
1414

1515
# Release Notes
1616
BOAT is still under development and subject to change.
17+
## 0.17.56
18+
* Added new `flattenOutput` parameter into `bundle` execution to generate the bundled APIs directly into `output` directory, even though the respective `input` files are located within a subdirectory in the `input`.
1719
## 0.17.55
1820
* Enhanced `boat-swift5` generator to cater for nested freeformObjects. This issue was identified in `ContentServicesApi`
1921
## 0.17.54 and later

boat-maven-plugin/README.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,36 @@ Bundles a spec by resolving external references.
202202
Configuration can point to a single in- and output file, or to in- and output directories. When directories are
203203
specified, all files specified by the `includes` parameter are bundled.
204204

205+
Available parameters:
206+
207+
input (Default: ${project.basedir}/src/main/resources)
208+
Required: true
209+
Path to a directory or a file to indicate the input Open API files to bundle.
210+
211+
includes (Default: **/openapi.yaml, **/*api*.yaml)
212+
Required: false
213+
List of file patterns to include to bundle.
214+
215+
output (Default: ${project.build.directory}/openapi)
216+
Required: true
217+
Output directory for the bundled OpenAPI specs.
218+
219+
flattenOutput (Default: false)
220+
Required: false
221+
Flatten the output directory structure, i.e. the bundle API specs are directly put into the output folder, even if the respective `input` files are located within a subdirectory in the `input`.
222+
223+
version
224+
Required: false
225+
Optional parameter, to override the bundled spec version.
226+
227+
versionFileName (Default: false)
228+
Required: false
229+
Optional parameter to include the version in the bundled spec file name.
230+
231+
removeExtensions (Default: "")
232+
Required: false
233+
Optional parameter to remove extensions from the bundled spec.
234+
205235
Examples in `json` files are parsed to objects.
206236

207237
<configuration>
@@ -486,4 +516,4 @@ This parameter is available as a replacement for the inputSpec parameter in goal
486516
It downloads a copy of the artifact if it is not already present, and uses a specified spec (or directory of specs)
487517
from the artifact as the inputSpec for the goal.
488518

489-
More examples can be found in integration tests.
519+
More examples can be found in integration tests.

boat-maven-plugin/src/main/java/com/backbase/oss/boat/BundleMojo.java

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
import static java.util.Collections.singletonMap;
55
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
66
import static org.apache.commons.lang3.StringUtils.isNotBlank;
7-
import static org.apache.commons.lang3.StringUtils.isNumeric;
87

98
import com.backbase.oss.boat.loader.OpenAPILoader;
109
import com.backbase.oss.boat.loader.OpenAPILoaderException;
1110
import com.backbase.oss.boat.serializer.SerializerUtils;
1211
import com.backbase.oss.boat.transformers.Bundler;
1312
import com.backbase.oss.boat.transformers.SetVersion;
1413
import com.backbase.oss.boat.transformers.ExtensionFilter;
14+
15+
import com.google.common.annotations.VisibleForTesting;
1516
import io.swagger.v3.oas.models.OpenAPI;
1617
import java.io.File;
1718
import java.io.IOException;
@@ -20,8 +21,10 @@
2021
import java.nio.file.Paths;
2122
import java.nio.file.StandardOpenOption;
2223
import java.util.List;
24+
import java.util.Optional;
2325
import java.util.stream.Collectors;
2426

27+
import io.swagger.v3.oas.models.info.Info;
2528
import lombok.Getter;
2629
import lombok.Setter;
2730
import lombok.extern.slf4j.Slf4j;
@@ -51,6 +54,9 @@ public class BundleMojo extends AbstractMojo {
5154
@Parameter(name = "output", required = true, defaultValue = "${project.build.directory}/openapi")
5255
private File output;
5356

57+
@Parameter(name = "flattenOutput", required = false)
58+
private boolean flattenOutput = false;
59+
5460
@Parameter(name = "version", required = false)
5561
private String version;
5662

@@ -82,17 +88,20 @@ public void execute() throws MojoExecutionException, MojoFailureException {
8288
final File[] inputFiles;
8389
final File[] outputFiles;
8490
if (input.isDirectory()) {
85-
8691
DirectoryScanner directoryScanner = new DirectoryScanner();
8792
directoryScanner.setBasedir(input);
8893
directoryScanner.setIncludes(includes);
8994
directoryScanner.scan();
95+
9096
String[] includedFiles = directoryScanner.getIncludedFiles();
91-
inputFiles = stream(includedFiles).map(f -> new File(input, f)).collect(Collectors.toList()).toArray(File[]::new);
97+
inputFiles = stream(includedFiles)
98+
.map(file -> new File(input, file))
99+
.collect(Collectors.toList())
100+
.toArray(File[]::new);
92101
outputFiles = stream(includedFiles)
102+
.map(this::normalizeOutputFileName)
93103
.map(file -> new File(output, file))
94104
.toArray(File[]::new);
95-
96105
log.info("Found " + inputFiles.length + " specs to bundle.");
97106
} else {
98107
inputFiles = new File[] {input};
@@ -102,7 +111,6 @@ public void execute() throws MojoExecutionException, MojoFailureException {
102111
for (int i = 0; i < inputFiles.length; i++) {
103112
bundleOpenAPI(inputFiles[i], outputFiles[i]);
104113
}
105-
106114
}
107115

108116
private void bundleOpenAPI(File inputFile, File outputFile) throws MojoExecutionException {
@@ -130,21 +138,21 @@ private void bundleOpenAPI(File inputFile, File outputFile) throws MojoExecution
130138
if (versionFileName) {
131139
String versionedFileName = versionFileName(outputFile.getAbsolutePath(), openAPI);
132140
outputFile = Paths.get(versionedFileName).toFile();
133-
134141
}
142+
135143
Files.write(outputFile.toPath(), SerializerUtils.toYamlString(openAPI).getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
136144
log.info("Bundled: {} into: {}", inputFile, outputFile);
137145
} catch (OpenAPILoaderException | IOException e) {
138146
throw new MojoExecutionException("Error transforming OpenAPI: {}" + inputFile, e);
139147
}
140148
}
141149

142-
150+
@VisibleForTesting
143151
String versionFileName(String originalFileName, OpenAPI openAPI) throws MojoExecutionException {
144-
String openApiVersion = openAPI.getInfo() != null ? openAPI.getInfo().getVersion() : null;
145-
if (openApiVersion == null) {
146-
throw new MojoExecutionException("Configured to use version in filename, but no version set.");
147-
}
152+
String openApiVersion = Optional.ofNullable(openAPI.getInfo())
153+
.map(Info::getVersion)
154+
.orElseThrow(() -> new MojoExecutionException("Configured to use version in filename, but no version set."));
155+
148156
if (!openApiVersion.matches("^\\d\\..*")) {
149157
throw new MojoExecutionException(
150158
"Version should be semver (or at least have a recognisable major version), but found '" + openApiVersion
@@ -159,4 +167,8 @@ String versionFileName(String originalFileName, OpenAPI openAPI) throws MojoExec
159167
return originalFileName.replaceAll("^(.*api-v)([0-9]+)(\\.yaml$)", "$1" + openApiVersion + "$3");
160168
}
161169

170+
@VisibleForTesting
171+
String normalizeOutputFileName(String outputFileName) {
172+
return flattenOutput ? new File(outputFileName).getName() : outputFileName;
173+
}
162174
}

boat-maven-plugin/src/test/java/com/backbase/oss/boat/BundleMojoTest.java

Lines changed: 85 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import org.apache.maven.plugin.MojoFailureException;
1414
import org.junit.jupiter.api.Test;
1515
import org.junit.jupiter.api.function.Executable;
16+
import org.junit.jupiter.params.ParameterizedTest;
17+
import org.junit.jupiter.params.provider.ValueSource;
1618

1719
import static org.junit.jupiter.api.Assertions.assertEquals;
1820
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -49,18 +51,59 @@ void testSkip() {
4951
@Test
5052
@SneakyThrows
5153
void testBundleFolder() {
54+
var outputFolder = "target/test-bundle-folder";
55+
5256
BundleMojo mojo = new BundleMojo();
5357

54-
mojo.setInput(new File(getClass().getResource("/bundler/folder/one-client-api-v1.yaml").getFile())
55-
.getParentFile());
56-
mojo.setOutput(new File("target/test-bundle-folder"));
58+
mojo.setInput(new File(getClass().getResource("/bundler/folder").getFile()));
59+
mojo.setOutput(new File(outputFolder));
5760
mojo.setIncludes(new String[]{"*-api-v*.yaml"});
5861
mojo.setVersionFileName(true);
5962
mojo.execute();
6063

61-
assertTrue(new File("target/test-bundle-folder/one-client-api-v1.3.5.yaml").exists());
62-
assertTrue(new File("target/test-bundle-folder/one-client-api-v2.0.0.yaml").exists());
63-
assertTrue(new File("target/test-bundle-folder/another-client-api-v1.7.9.yaml").exists());
64+
assertTrue(new File(outputFolder, "one-client-api-v1.3.5.yaml").exists());
65+
assertTrue(new File(outputFolder, "one-client-api-v2.0.0.yaml").exists());
66+
assertTrue(new File(outputFolder, "another-client-api-v1.7.9.yaml").exists());
67+
}
68+
69+
@Test
70+
@SneakyThrows
71+
void testBundleFolder_whenNotToBeFlattened_inSubDirStructure() {
72+
var outputFolder = "target/test-bundle-folder-with-subdir";
73+
74+
BundleMojo mojo = new BundleMojo();
75+
76+
mojo.setInput(new File(getClass().getResource("/bundler").getFile()));
77+
mojo.setOutput(new File(outputFolder));
78+
mojo.setIncludes(new String[]{"**/*api-v*.yaml"}); // Includes sub-directories by '**/'
79+
mojo.setVersionFileName(true);
80+
mojo.setFlattenOutput(false);
81+
82+
mojo.execute();
83+
84+
assertTrue(new File(outputFolder, "/folder/one-client-api-v1.3.5.yaml").exists());
85+
assertTrue(new File(outputFolder, "/folder/one-client-api-v2.0.0.yaml").exists());
86+
assertTrue(new File(outputFolder, "/folder/another-client-api-v1.7.9.yaml").exists());
87+
}
88+
89+
@Test
90+
@SneakyThrows
91+
void testBundleFolder_whenToBeFlattened_inSubDirStructure() {
92+
var outputFolder = "target/test-bundle-folder-flattened";
93+
94+
BundleMojo mojo = new BundleMojo();
95+
96+
mojo.setInput(new File(getClass().getResource("/bundler").getFile()));
97+
mojo.setOutput(new File(outputFolder));
98+
mojo.setIncludes(new String[]{"**/*api-v*.yaml"}); // Includes sub-directories by '**/'
99+
mojo.setVersionFileName(true);
100+
mojo.setFlattenOutput(true);
101+
102+
mojo.execute();
103+
104+
assertTrue(new File(outputFolder, "one-client-api-v1.3.5.yaml").exists());
105+
assertTrue(new File(outputFolder, "one-client-api-v2.0.0.yaml").exists());
106+
assertTrue(new File(outputFolder, "another-client-api-v1.7.9.yaml").exists());
64107
}
65108

66109
@Test
@@ -123,6 +166,42 @@ void testInvalidVersionInApi() {
123166

124167
}
125168

169+
@ParameterizedTest
170+
@ValueSource(strings = { "outputFile.yaml", "/client-api/v1/outputFile.yaml"})
171+
void testOutputFileName_DefaultBehaviour(String outputFileName) {
172+
BundleMojo mojo = new BundleMojo();
173+
174+
var actual = mojo.normalizeOutputFileName(outputFileName);
175+
176+
assertEquals(outputFileName, actual);
177+
}
178+
179+
@Test
180+
void testOutputFileName_WhenToBeFlattened_SimpleStructure() {
181+
BundleMojo mojo = new BundleMojo();
182+
mojo.setFlattenOutput(true);
183+
184+
var fileName = "outputFile.yaml";
185+
186+
var expected = fileName;
187+
var actual = mojo.normalizeOutputFileName(fileName);
188+
189+
assertEquals(expected, actual);
190+
}
191+
192+
@Test
193+
void testOutputFileName_WhenToBeFlattened_NestedStructure() {
194+
BundleMojo mojo = new BundleMojo();
195+
mojo.setFlattenOutput(true);
196+
197+
var fileName = "/client-api/v1/outputFile.yaml";
198+
199+
var expected = "outputFile.yaml";
200+
var actual = mojo.normalizeOutputFileName(fileName);
201+
202+
assertEquals(expected, actual);
203+
}
204+
126205
private void assertThrowsMojoExecutionExceptionWithMessage(Executable executable, String message) {
127206
MojoExecutionException thrown = assertThrows(MojoExecutionException.class, executable);
128207
assertTrue(thrown.getMessage().startsWith(message), "Expected message '" + message + "' but got '"
@@ -140,6 +219,4 @@ protected File getFile(String name) {
140219
assert resource != null;
141220
return new File(resource.getFile());
142221
}
143-
144-
145222
}

0 commit comments

Comments
 (0)