Skip to content

Commit a63e07e

Browse files
committed
Defining Input Stream providers
Signed-off-by: Ricardo Zanini <[email protected]>
1 parent f17cb56 commit a63e07e

File tree

8 files changed

+157
-11
lines changed

8 files changed

+157
-11
lines changed

deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/codegen/OpenApiGeneratorCodeGenBase.java

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package io.quarkiverse.openapi.generator.deployment.codegen;
22

3-
import static io.quarkiverse.openapi.generator.deployment.SpecConfig.getResolvedBasePackageProperty;
4-
53
import java.io.IOException;
64
import java.nio.file.Files;
75
import java.nio.file.Path;
@@ -13,6 +11,8 @@
1311
import io.quarkus.deployment.CodeGenContext;
1412
import io.quarkus.deployment.CodeGenProvider;
1513

14+
import static io.quarkiverse.openapi.generator.deployment.SpecConfig.getResolvedBasePackageProperty;
15+
1616
/**
1717
* Code generation for OpenApi Client. Generates Java classes from OpenApi spec files located in src/main/openapi or
1818
* src/test/openapi
@@ -42,15 +42,7 @@ public boolean trigger(CodeGenContext context) throws CodeGenException {
4242
.map(Path::toString)
4343
.filter(s -> s.endsWith(this.inputExtension()))
4444
.map(Path::of).forEach(openApiFilePath -> {
45-
final OpenApiClientGeneratorWrapper generator = new OpenApiClientGeneratorWrapper(
46-
openApiFilePath.normalize(), outDir)
47-
.withCircuitBreakerConfiguration(CircuitBreakerConfigurationParser.parse(
48-
context.config()));
49-
50-
context.config()
51-
.getOptionalValue(getResolvedBasePackageProperty(openApiFilePath), String.class)
52-
.ifPresent(generator::withBasePackage);
53-
generator.generate();
45+
this.generate(context, openApiFilePath, outDir);
5446
});
5547
} catch (IOException e) {
5648
throw new CodeGenException("Failed to generate java files from OpenApi files in " + openApiDir.toAbsolutePath(),
@@ -60,4 +52,15 @@ public boolean trigger(CodeGenContext context) throws CodeGenException {
6052
}
6153
return false;
6254
}
55+
56+
protected void generate(CodeGenContext context, final Path openApiFilePath, final Path outDir) {
57+
final OpenApiClientGeneratorWrapper generator = new OpenApiClientGeneratorWrapper(
58+
openApiFilePath.normalize(), outDir)
59+
.withCircuitBreakerConfiguration(CircuitBreakerConfigurationParser.parse(
60+
context.config()));
61+
context.config()
62+
.getOptionalValue(getResolvedBasePackageProperty(openApiFilePath), String.class)
63+
.ifPresent(generator::withBasePackage);
64+
generator.generate();
65+
}
6366
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package io.quarkiverse.openapi.generator.deployment.codegen;
2+
3+
import java.io.FileOutputStream;
4+
import java.io.IOException;
5+
import java.io.InputStream;
6+
import java.io.UncheckedIOException;
7+
import java.nio.channels.Channels;
8+
import java.nio.channels.ReadableByteChannel;
9+
import java.nio.file.Path;
10+
import java.nio.file.Paths;
11+
import java.util.List;
12+
import java.util.ServiceLoader;
13+
import java.util.stream.Collectors;
14+
import java.util.zip.CRC32;
15+
import java.util.zip.CheckedInputStream;
16+
17+
import org.eclipse.microprofile.config.Config;
18+
import org.slf4j.Logger;
19+
import org.slf4j.LoggerFactory;
20+
21+
import io.quarkiverse.openapi.generator.OpenApiSpecInputProvider;
22+
import io.quarkus.bootstrap.prebuild.CodeGenException;
23+
import io.quarkus.deployment.CodeGenContext;
24+
25+
public class OpenApiGeneratorStreamCodeGen extends OpenApiGeneratorCodeGenBase {
26+
27+
private static final Logger LOGGER = LoggerFactory.getLogger(OpenApiGeneratorStreamCodeGen.class);
28+
29+
private List<OpenApiSpecInputProvider> providers;
30+
31+
public OpenApiGeneratorStreamCodeGen() {
32+
33+
}
34+
35+
private void loadServices() {
36+
final ServiceLoader<OpenApiSpecInputProvider> loader = ServiceLoader.load(OpenApiSpecInputProvider.class);
37+
providers = loader.stream().map(ServiceLoader.Provider::get).collect(Collectors.toList());
38+
}
39+
40+
@Override
41+
public String providerId() {
42+
return "open-api-stream";
43+
}
44+
45+
// unused by this CodeGenProvider since we rely on the input coming from ServiceLoaders
46+
@Override
47+
public String inputExtension() {
48+
return ".yaml";
49+
}
50+
51+
// unused by this CodeGenProvider since we rely on the input coming from ServiceLoaders
52+
@Override
53+
public String inputDirectory() {
54+
return "openapi";
55+
}
56+
57+
@Override
58+
public boolean trigger(CodeGenContext context) throws CodeGenException {
59+
final Path outDir = context.outDir();
60+
boolean generated = false;
61+
62+
for (final OpenApiSpecInputProvider provider : this.providers) {
63+
for (final InputStream is : provider.read()) {
64+
// TODO: in the future, we can use the checksum to not generate the stub files again
65+
final String checksum = generateChecksumCRC32(is);
66+
final Path openApiFilePath = Paths.get(outDir.toString(), checksum + ".yaml");
67+
68+
try (ReadableByteChannel channel = Channels.newChannel(is);
69+
FileOutputStream output = new FileOutputStream(openApiFilePath.toString())) {
70+
output.getChannel().transferFrom(channel, 0, Integer.MAX_VALUE);
71+
this.generate(context, openApiFilePath, outDir);
72+
generated = true;
73+
} catch (IOException e) {
74+
throw new UncheckedIOException("Fail to save openapi file", e);
75+
}
76+
}
77+
}
78+
return generated;
79+
}
80+
81+
private String generateChecksumCRC32(final InputStream is) {
82+
CheckedInputStream checkedInputStream = new CheckedInputStream(is, new CRC32());
83+
byte[] buffer = new byte[2048];
84+
while (true) {
85+
try {
86+
if (checkedInputStream.read(buffer, 0, buffer.length) < 0) {
87+
break;
88+
}
89+
} catch (IOException e) {
90+
throw new UncheckedIOException("Fail to calculate checksum for InputStream", e);
91+
}
92+
}
93+
return String.valueOf(checkedInputStream.getChecksum().getValue());
94+
}
95+
96+
@Override
97+
public boolean shouldRun(Path sourceDir, Config config) {
98+
this.loadServices();
99+
return !this.providers.isEmpty();
100+
}
101+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
io.quarkiverse.openapi.generator.deployment.codegen.OpenApiGeneratorJsonCodeGen
22
io.quarkiverse.openapi.generator.deployment.codegen.OpenApiGeneratorYamlCodeGen
33
io.quarkiverse.openapi.generator.deployment.codegen.OpenApiGeneratorYmlCodeGen
4+
io.quarkiverse.openapi.generator.deployment.codegen.OpenApiGeneratorStreamCodeGen
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package io.quarkiverse.openapi.generator.it.providers;
2+
3+
import java.io.InputStream;
4+
import java.util.Collections;
5+
import java.util.List;
6+
7+
import io.quarkiverse.openapi.generator.OpenApiSpecInputProvider;
8+
9+
/**
10+
* Class used during tests to read the spec file from an alternative input.
11+
*/
12+
public class ClassPathOpenApiSpecInputProvider implements OpenApiSpecInputProvider {
13+
@Override
14+
public List<InputStream> read() {
15+
return Collections.singletonList(this.getClass().getResourceAsStream("petstore.json"));
16+
}
17+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
io.quarkiverse.openapi.generator.it.providers.ClassPathOpenApiSpecInputProvider
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Providing OpenAPI spec files via InputStream
2+
3+
This directory is just meant to be used in the integration test cases. Instead of reading from the `src/main/openapi` directory, a spec file can also be read via a given `InputStream`.
4+
5+
For this to work, clients must implement the `OpenApiSpecInputProvider` interface. The implementation read the files from any other source (an HTTP server, for example), and provide the input stream.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package io.quarkiverse.openapi.generator;
2+
3+
import java.io.InputStream;
4+
import java.util.List;
5+
6+
/**
7+
* Provider interface for clients to dynamically provide their own OpenAPI specification files.
8+
*/
9+
public interface OpenApiSpecInputProvider {
10+
11+
/**
12+
* Fetch OpenAPI specification files from a given source.
13+
*
14+
* @return a list of spec files in {@link InputStream} format.
15+
*/
16+
List<InputStream> read();
17+
18+
}

0 commit comments

Comments
 (0)