Skip to content

Commit e710dd6

Browse files
#693 - Allow YAML for Server Generation (#701)
* Fixes #693, update documentation and tests * Reformated * Update documentaiton * Removed integration-tests again
1 parent 6305876 commit e710dd6

File tree

10 files changed

+937
-43
lines changed

10 files changed

+937
-43
lines changed

docs/modules/ROOT/pages/includes/server-getting-started.adoc

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,24 @@ WARNING: You probably already have this configuration if you created your applic
3232
</plugin>
3333
----
3434

35-
Now, create the directory `openapi` under your `src/main/resources` path and add the OpenAPI spec files there. We support JSON, YAML and YML extensions.
35+
Now, create the directory `openapi` under your `src/main/resources` path and add the OpenAPI spec files there. We support JSON, YAML and YML extensions. You have to define the specification used for code generation with the following property:
3636

37-
If a base package name is not provided, it will be used the default `io.apicurio.api`.
37+
[source,properties]
38+
----
39+
quarkus.openapi.generator.spec=petstore-openapi.json
40+
----
3841

39-
You can customize the specification used for code generation with the following property:
42+
43+
If you want to change the directory where OpenAPI files must be found, use the property `quarkus.openapi.input-base-dir`.
44+
IMPORTANT: it is relative to the project base directory. For example, if you have a project called `MyJavaProject` and decide to place them in `MyJavaProject/openapi-definitions`, use the following property:
4045

4146
[source,properties]
4247
----
43-
quarkus.openapi.generator.spec=petstore-openapi.json
48+
quarkus.openapi.input-base-dir=openapi-definitions
4449
----
4550

51+
If a base package name is not provided, it will be used the default `io.apicurio.api`. You can customize it with the following property:
52+
4653
[source,properties]
4754
----
4855
quarkus.openapi.generator.base-package=io.petstore

server/deployment/src/main/java/io/quarkiverse/openapi/server/generator/deployment/CodegenConfig.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class CodegenConfig {
99
static final String CODEGEN_TIME_CONFIG_PREFIX = "quarkus.openapi.generator";
1010
private static final String CODEGEN_BASE_PACKAGE = CODEGEN_TIME_CONFIG_PREFIX + ".base-package";
1111
private static final String CODEGEN_SPEC = CODEGEN_TIME_CONFIG_PREFIX + ".spec";
12+
private static final String INPUT_BASE_DIR = CODEGEN_TIME_CONFIG_PREFIX + ".input-base-dir";
1213

1314
public static String getBasePackagePropertyName() {
1415
return CODEGEN_BASE_PACKAGE;
@@ -17,4 +18,8 @@ public static String getBasePackagePropertyName() {
1718
public static String getSpecPropertyName() {
1819
return CODEGEN_SPEC;
1920
}
21+
22+
public static String getInputBaseDirPropertyName() {
23+
return INPUT_BASE_DIR;
24+
}
2025
}
Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package io.quarkiverse.openapi.server.generator.deployment.codegen;
22

3-
import java.io.IOException;
3+
import java.io.File;
44
import java.nio.file.Files;
55
import java.nio.file.Path;
6-
import java.util.stream.Stream;
6+
import java.util.Arrays;
77

88
import org.eclipse.microprofile.config.Config;
99
import org.slf4j.Logger;
1010
import org.slf4j.LoggerFactory;
1111

12+
import com.fasterxml.jackson.databind.ObjectMapper;
13+
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
14+
1215
import io.quarkiverse.openapi.server.generator.deployment.CodegenConfig;
1316
import io.quarkus.bootstrap.prebuild.CodeGenException;
1417
import io.quarkus.deployment.CodeGenContext;
@@ -24,52 +27,82 @@ public String providerId() {
2427
}
2528

2629
@Override
27-
public String inputExtension() {
28-
return "json";
30+
public String[] inputExtensions() {
31+
return new String[] { "json", "yaml", "yml" };
2932
}
3033

3134
@Override
3235
public String inputDirectory() {
3336
return "resources";
3437
}
3538

39+
private Path getInputBaseDir(final Path sourceDir, final Config config) {
40+
return config.getOptionalValue(CodegenConfig.getInputBaseDirPropertyName(), String.class)
41+
.map(inputBaseDir -> {
42+
int srcIndex = sourceDir.toString().lastIndexOf("src");
43+
return Path.of(sourceDir.toString().substring(0, srcIndex), inputBaseDir);
44+
}).orElse(Path.of(sourceDir.toString(), "openapi"));
45+
}
46+
3647
@Override
3748
public boolean shouldRun(Path sourceDir, Config config) {
38-
return sourceDir != null && config.getOptionalValue(CodegenConfig.getSpecPropertyName(), String.class)
39-
.isPresent();
49+
if (config.getOptionalValue(CodegenConfig.getSpecPropertyName(), String.class).isEmpty()) {
50+
return false;
51+
}
52+
Path path = getInputBaseDir(sourceDir, config);
53+
return Files.isDirectory(path);
4054
}
4155

4256
@Override
4357
public boolean trigger(CodeGenContext context) throws CodeGenException {
44-
final Path openApiDir = context.inputDir();
58+
final Path openApiDir = getInputBaseDir(context.inputDir(), context.config());
4559
final Path outDir = context.outDir();
46-
final ApicurioCodegenWrapper apicurioCodegenWrapper = new ApicurioCodegenWrapper(context.config(), outDir.toFile());
47-
48-
if (Files.isDirectory(openApiDir)) {
49-
50-
try (Stream<Path> openApiFilesPaths = Files.walk(openApiDir)) {
51-
openApiFilesPaths
52-
.filter(Files::isRegularFile)
53-
.map(Path::toString)
54-
.filter(s -> s.endsWith(this.inputExtension()))
55-
.map(Path::of).forEach(openApiResource -> {
56-
if (openApiResource.toFile().getName().equals(context.config()
57-
.getOptionalValue(CodegenConfig.getSpecPropertyName(), String.class).get())) {
58-
try {
59-
apicurioCodegenWrapper.generate(openApiResource);
60-
} catch (CodeGenException e) {
61-
log.warn("Exception found processing specification with name: {}",
62-
openApiResource.getFileName());
63-
}
64-
}
65-
});
60+
final ApicurioCodegenWrapper apicurioCodegenWrapper = new ApicurioCodegenWrapper(
61+
context.config(), outDir.toFile());
62+
final String specPropertyName = context.config()
63+
.getOptionalValue(CodegenConfig.getSpecPropertyName(), String.class)
64+
.orElseThrow();
65+
final File openApiResource = new File(openApiDir.toFile(), specPropertyName);
66+
if (!openApiResource.exists()) {
67+
throw new CodeGenException(
68+
"Specification file not found: " + openApiResource.getAbsolutePath());
69+
}
70+
if (!openApiResource.isFile()) {
71+
throw new CodeGenException(
72+
"Specification file is not a file: " + openApiResource.getAbsolutePath());
73+
}
74+
if (!openApiResource.canRead()) {
75+
throw new CodeGenException(
76+
"Specification file is not readable: " + openApiResource.getAbsolutePath());
77+
}
78+
if (Arrays.stream(this.inputExtensions()).noneMatch(specPropertyName::endsWith)) {
79+
throw new CodeGenException(
80+
"Specification file must have one of the following extensions: " + Arrays.toString(
81+
this.inputExtensions()));
82+
}
83+
// Apicurio only supports JSON => convert yaml to JSON
84+
final File jsonSpec = specPropertyName.endsWith("json") ? openApiResource
85+
: convertToJSON(openApiResource.toPath());
86+
try {
87+
apicurioCodegenWrapper.generate(jsonSpec.toPath());
88+
} catch (CodeGenException e) {
89+
log.warn("Exception found processing specification with name: {}",
90+
openApiResource.getAbsolutePath());
91+
}
92+
return true;
93+
}
6694

67-
} catch (IOException e) {
68-
throw new CodeGenException("Failed to generate java files from OpenApi file in " + openApiDir.toAbsolutePath(),
69-
e);
70-
}
71-
return true;
95+
private File convertToJSON(Path yamlPath) throws CodeGenException {
96+
try {
97+
ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
98+
Object obj = yamlReader.readValue(yamlPath.toFile(), Object.class);
99+
ObjectMapper jsonWriter = new ObjectMapper();
100+
File jsonFile = File.createTempFile(yamlPath.toFile().getName(), ".json");
101+
jsonFile.deleteOnExit();
102+
jsonWriter.writeValue(jsonFile, obj);
103+
return jsonFile;
104+
} catch (Exception e) {
105+
throw new CodeGenException("Error converting YAML to JSON", e);
72106
}
73-
return false;
74107
}
75108
}
Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
package io.quarkiverse.openapi.server.generator.deployment;
22

3-
import java.net.URISyntaxException;
3+
import static org.junit.jupiter.api.Assertions.assertTrue;
4+
5+
import java.io.File;
6+
import java.io.IOException;
7+
import java.nio.file.Files;
48
import java.nio.file.Path;
59

10+
import org.apache.commons.io.FileUtils;
611
import org.eclipse.microprofile.config.Config;
12+
import org.junit.jupiter.api.BeforeAll;
713
import org.junit.jupiter.api.Test;
814

915
import io.quarkiverse.openapi.server.generator.deployment.codegen.ApicurioOpenApiServerCodegen;
@@ -12,14 +18,46 @@
1218

1319
public class CodegenTest {
1420

21+
private final static Path WORK_DIR = Path.of("target/generated-test-sources");
22+
private final static Path INPUT_DIR = Path.of("src/test/resources");
23+
private final static String OUT_DIR = "target/generated-test-sources";
24+
25+
@BeforeAll
26+
public static void setup() throws IOException {
27+
FileUtils.deleteDirectory(new File("target/generated-test-sources"));
28+
}
29+
1530
@Test
16-
public void testGeneration() throws CodeGenException, URISyntaxException {
17-
Config config = MockConfigUtils.getTestConfig("application.properties");
31+
public void testJSON() throws CodeGenException {
32+
Config config = MockConfigUtils.getTestConfig("json.application.properties");
33+
CodeGenContext codeGenContext = new CodeGenContext(null, Path.of(OUT_DIR, "json"), WORK_DIR,
34+
INPUT_DIR, false, config, true);
35+
ApicurioOpenApiServerCodegen apicurioOpenApiServerCodegen = new ApicurioOpenApiServerCodegen();
36+
apicurioOpenApiServerCodegen.trigger(codeGenContext);
37+
assertTrue(
38+
Files.exists(Path.of("target/generated-test-sources/json/io/petstore/PetResource.java")));
39+
}
1840

19-
CodeGenContext codeGenContext = new CodeGenContext(null, Path.of("target/generated-test-sources"),
20-
Path.of("target/generated-test-sources"), Path.of("generated-test-classes"), false, config, true);
41+
@Test
42+
public void testYaml() throws CodeGenException {
43+
Config config = MockConfigUtils.getTestConfig("yaml.application.properties");
44+
CodeGenContext codeGenContext = new CodeGenContext(null, Path.of(OUT_DIR, "yaml"), WORK_DIR,
45+
INPUT_DIR, false, config, true);
46+
ApicurioOpenApiServerCodegen apicurioOpenApiServerCodegen = new ApicurioOpenApiServerCodegen();
47+
apicurioOpenApiServerCodegen.trigger(codeGenContext);
48+
assertTrue(
49+
Files.exists(Path.of("target/generated-test-sources/yaml/io/petstore/PetResource.java")));
50+
}
2151

52+
@Test
53+
public void testInputDir() throws CodeGenException {
54+
Config config = MockConfigUtils.getTestConfig("inputDir.application.properties");
55+
CodeGenContext codeGenContext = new CodeGenContext(null, Path.of(OUT_DIR, "inputDir"), WORK_DIR,
56+
INPUT_DIR, false, config, true);
2257
ApicurioOpenApiServerCodegen apicurioOpenApiServerCodegen = new ApicurioOpenApiServerCodegen();
2358
apicurioOpenApiServerCodegen.trigger(codeGenContext);
59+
assertTrue(Files.exists(
60+
Path.of("target/generated-test-sources/inputDir/io/petstore/PetResource.java")));
2461
}
62+
2563
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
quarkus.openapi.generator.spec=petstore-openapi-2.json
2+
quarkus.openapi.generator.input-base-dir=src/test/resources2
3+
quarkus.openapi.generator.base-package=io.petstore
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
quarkus.openapi.generator.spec=petstore-openapi.yaml
2+
quarkus.openapi.generator.base-package=io.petstore

0 commit comments

Comments
 (0)