Skip to content

Commit 745ca82

Browse files
persistent openapi specification
1 parent afd4742 commit 745ca82

3 files changed

Lines changed: 139 additions & 3 deletions

File tree

docs/asciidoc/modules/openapi.adoc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,14 @@ To avoid this behaviour you can specify maven build phase which suits your needs
128128
|`openapi.yaml`
129129
|Set openAPI template file path.
130130

131-
|===
131+
|`copyOpenApiSpecTo`
132+
|
133+
|Copy the generated OpenAPI spec to the given file in the project repository. The format is
134+
determined by the file extension: `.yaml`, `.yml` or `.json`. Example:
135+
136+
<copyOpenApiSpecTo>${project.basedir}/docs/openapi.yaml</copyOpenApiSpecTo>
137+
138+
|`===
132139

133140
=== Usage
134141

modules/jooby-maven-plugin/src/main/java/io/jooby/maven/OpenAPIMojo.java

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,14 @@
1010
import static org.apache.maven.plugins.annotations.ResolutionScope.COMPILE_PLUS_RUNTIME;
1111

1212
import java.io.File;
13+
import java.io.IOException;
14+
import java.nio.file.Files;
1315
import java.nio.file.Path;
1416
import java.nio.file.Paths;
17+
import java.nio.file.StandardCopyOption;
18+
import java.util.ArrayList;
1519
import java.util.List;
20+
import java.util.Locale;
1621
import java.util.Map;
1722
import java.util.Optional;
1823

@@ -53,6 +58,9 @@ public class OpenAPIMojo extends BaseMojo {
5358

5459
@Parameter private List<File> adoc;
5560

61+
@Parameter(property = "openAPI.copyOpenApiSpecTo")
62+
private File copyOpenApiSpecTo;
63+
5664
@Override
5765
protected void doExecute(List<MavenProject> projects, String mainClass) throws Exception {
5866
ClassLoader classLoader = createClassLoader(projects);
@@ -84,10 +92,48 @@ protected void doExecute(List<MavenProject> projects, String mainClass) throws E
8492
var result = tool.generate(mainClass);
8593

8694
var adocPath = ofNullable(adoc).orElse(List.of()).stream().map(File::toPath).toList();
95+
var written = new ArrayList<Path>();
8796
for (var format : OpenAPIGenerator.Format.values()) {
88-
tool.export(result, format, Map.of("adoc", adocPath))
89-
.forEach(output -> getLog().info(" writing: " + output));
97+
written.addAll(tool.export(result, format, Map.of("adoc", adocPath)));
98+
}
99+
written.forEach(output -> getLog().info(" writing: " + output));
100+
101+
if (copyOpenApiSpecTo != null) {
102+
var destination = copyOpenApiSpecTo.toPath();
103+
copySpec(written, destination);
104+
getLog().info(" copying: " + destination);
105+
}
106+
}
107+
108+
public static void copySpec(List<Path> written, Path destination) throws IOException {
109+
var format = specFormat(destination);
110+
var source =
111+
written.stream()
112+
.filter(path -> path.getFileName().toString().endsWith("." + format.extension()))
113+
.findFirst()
114+
.orElseThrow(
115+
() ->
116+
new IOException(
117+
String.format(
118+
"OpenAPI %s output not found for copyOpenApiSpecTo: %s",
119+
format.name(), destination)));
120+
var parent = destination.getParent();
121+
if (parent != null) {
122+
Files.createDirectories(parent);
90123
}
124+
Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING);
125+
}
126+
127+
public static OpenAPIGenerator.Format specFormat(Path destination) {
128+
var name = destination.getFileName().toString().toLowerCase(Locale.ROOT);
129+
if (name.endsWith(".json")) {
130+
return OpenAPIGenerator.Format.JSON;
131+
}
132+
if (name.endsWith(".yaml") || name.endsWith(".yml")) {
133+
return OpenAPIGenerator.Format.YAML;
134+
}
135+
throw new IllegalArgumentException(
136+
"copyOpenApiSpecTo must end with .yaml, .yml or .json: " + destination);
91137
}
92138

93139
private Optional<String> trim(String value) {
@@ -186,4 +232,30 @@ public void setJavadoc(String javadoc) {
186232
public String getJavadoc() {
187233
return javadoc;
188234
}
235+
236+
/**
237+
* Copy the generated OpenAPI spec to the given file. The format is determined by the file
238+
* extension: <code>.yaml</code>, <code>.yml</code> or <code>.json</code>.
239+
*
240+
* @return Destination file.
241+
*/
242+
public @Nullable File getCopyOpenApiSpecTo() {
243+
return copyOpenApiSpecTo;
244+
}
245+
246+
/**
247+
* Copy the generated OpenAPI spec to the given file. The format is determined by the file
248+
* extension: <code>.yaml</code>, <code>.yml</code> or <code>.json</code>.
249+
*
250+
* <p>Example:
251+
*
252+
* <pre>{@code
253+
* <copyOpenApiSpecTo>${project.basedir}/docs/openapi.yaml</copyOpenApiSpecTo>
254+
* }</pre>
255+
*
256+
* @param copyOpenApiSpecTo Destination file.
257+
*/
258+
public void setCopyOpenApiSpecTo(@Nullable File copyOpenApiSpecTo) {
259+
this.copyOpenApiSpecTo = copyOpenApiSpecTo;
260+
}
189261
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package io.jooby.maven;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
7+
import java.nio.file.Files;
8+
import java.nio.file.Path;
9+
import java.util.List;
10+
11+
import org.junit.jupiter.api.Test;
12+
import org.junit.jupiter.api.io.TempDir;
13+
14+
import io.jooby.openapi.OpenAPIGenerator;
15+
16+
public class OpenAPIMojoTest {
17+
18+
@Test
19+
public void specFormat() {
20+
assertEquals(
21+
OpenAPIGenerator.Format.YAML, OpenAPIMojo.specFormat(Path.of("docs/openapi.yaml")));
22+
assertEquals(
23+
OpenAPIGenerator.Format.YAML, OpenAPIMojo.specFormat(Path.of("docs/openapi.yml")));
24+
assertEquals(
25+
OpenAPIGenerator.Format.JSON, OpenAPIMojo.specFormat(Path.of("docs/openapi.json")));
26+
assertThrows(
27+
IllegalArgumentException.class, () -> OpenAPIMojo.specFormat(Path.of("docs/openapi.txt")));
28+
}
29+
30+
@Test
31+
public void copyYamlSpec(@TempDir Path tempDir) throws Exception {
32+
var outputDir = tempDir.resolve("classes/myapp");
33+
Files.createDirectories(outputDir);
34+
var source = outputDir.resolve("App.yaml");
35+
Files.writeString(source, "openapi: 3.0.1");
36+
37+
var destination = tempDir.resolve("docs/openapi.yml");
38+
OpenAPIMojo.copySpec(List.of(source), destination);
39+
40+
assertTrue(Files.isRegularFile(destination));
41+
assertEquals(Files.readString(source), Files.readString(destination));
42+
}
43+
44+
@Test
45+
public void copyJsonSpec(@TempDir Path tempDir) throws Exception {
46+
var outputDir = tempDir.resolve("classes/myapp");
47+
Files.createDirectories(outputDir);
48+
var source = outputDir.resolve("App.json");
49+
Files.writeString(source, "{\"openapi\":\"3.0.1\"}");
50+
51+
var destination = tempDir.resolve("docs/openapi.json");
52+
OpenAPIMojo.copySpec(List.of(source), destination);
53+
54+
assertTrue(Files.isRegularFile(destination));
55+
assertEquals(Files.readString(source), Files.readString(destination));
56+
}
57+
}

0 commit comments

Comments
 (0)