Skip to content

Commit

Permalink
Add server extension
Browse files Browse the repository at this point in the history
  • Loading branch information
carlesarnal committed Oct 9, 2023
1 parent 28a2cc1 commit cbe4f2b
Show file tree
Hide file tree
Showing 19 changed files with 4,298 additions and 0 deletions.
12 changes: 12 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<module>deployment</module>
<module>runtime</module>
<module>test-utils</module>
<module>server</module>
</modules>
<scm>
<connection>:git:[email protected]:quarkiverse/quarkus-openapi-generator.git</connection>
Expand All @@ -28,6 +29,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.version>3.4.2</quarkus.version>
<apicurio.version>1.1.0.Final</apicurio.version>
<version.com.github.javaparser>3.25.5</version.com.github.javaparser>
<version.org.assertj>3.24.2</version.org.assertj>
<version.org.eclipse.microprofile.fault-tolerance>4.0.2</version.org.eclipse.microprofile.fault-tolerance>
Expand All @@ -52,6 +54,16 @@
<artifactId>quarkus-openapi-generator</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkiverse.openapi.generator</groupId>
<artifactId>quarkus-openapi-generator-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.apicurio</groupId>
<artifactId>apicurio-codegen</artifactId>
<version>${apicurio.version}</version>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
Expand Down
44 changes: 44 additions & 0 deletions server/deployment/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>quarkus-openapi-generator-server-parent</artifactId>
<groupId>io.quarkiverse.openapi.generator</groupId>
<version>3.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>quarkus-openapi-generator-server-deployment</artifactId>
<name>Quarkus - Openapi Generator - Server - Deployment</name>

<dependencies>
<!-- Quarkus -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkiverse.openapi.generator</groupId>
<artifactId>quarkus-openapi-generator-server</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-extension-processor</artifactId>
<version>${quarkus.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.quarkiverse.openapi.server.generator.deployment;

import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;

@ConfigRoot(name = CodegenConfig.CODEGEN_TIME_CONFIG_PREFIX, phase = ConfigPhase.BUILD_TIME)
public class CodegenConfig {

static final String CODEGEN_TIME_CONFIG_PREFIX = "quarkus.openapi.generator";
private static final String CODEGEN_BASE_PACKAGE = CODEGEN_TIME_CONFIG_PREFIX + ".base-package";
private static final String CODEGEN_SPEC = CODEGEN_TIME_CONFIG_PREFIX + ".spec";

public static String getBasePackagePropertyName() {
return CODEGEN_BASE_PACKAGE;
}

public static String getSpecPropertyName() {
return CODEGEN_SPEC;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package io.quarkiverse.openapi.server.generator.deployment.codegen;

import static io.quarkiverse.openapi.server.generator.deployment.CodegenConfig.getBasePackagePropertyName;

import java.io.*;
import java.nio.file.Path;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.commons.io.IOUtils;
import org.eclipse.microprofile.config.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.apicurio.hub.api.codegen.JaxRsProjectSettings;
import io.apicurio.hub.api.codegen.OpenApi2JaxRs;
import io.quarkus.bootstrap.prebuild.CodeGenException;

public class ApicurioCodegenWrapper {

private static final Logger log = LoggerFactory.getLogger(ApicurioOpenApiServerCodegen.class);
private static final String DEFAULT_PACKAGE = "io.apicurio.api";

private final Config config;
private final File outdir;
private final JaxRsProjectSettings projectSettings;

public ApicurioCodegenWrapper(Config config, File outdir) {
this(config, outdir, defaultProjectSettings());
}

public ApicurioCodegenWrapper(Config config, File outdir, JaxRsProjectSettings projectSettings) {
this.config = config;
this.outdir = outdir;
this.projectSettings = projectSettings;
this.projectSettings.setJavaPackage(getBasePackage(config));
}

public void generate(Path openApiResource) throws CodeGenException {
final File openApiFile = openApiResource.toFile();

log.info("Generating JAX-RS interfaces and beans from: " + openApiResource);

if (outdir.isFile()) {
throw new CodeGenException(
"Output directory is unexpectedly a file (should be a directory or non-existent).");
}

if (!outdir.exists()) {
outdir.mkdirs();
}

// Generate code - output a ZIP file.
File zipFile = new File(outdir, "generated-code.zip");
try (FileOutputStream fos = new FileOutputStream(zipFile)) {
OpenApi2JaxRs generator = new OpenApi2JaxRs();
generator.setSettings(projectSettings);
generator.setUpdateOnly(true);
generator.setOpenApiDocument(new FileInputStream(openApiFile));
log.info("Generating code...");
generator.generate(fos);
} catch (Exception e) {
log.error("Error generating code from openapi spec", e);
throw new CodeGenException(e);
}

// Unpack the temporary ZIP file
log.info("Code generated, unpacking the output ZIP.");
try {
unzip(zipFile, outdir);
} catch (IOException e) {
log.error("Error generating code from openapi spec", e);
throw new CodeGenException(e);
} finally {
// Delete the temporary ZIP file
zipFile.delete();
}

log.info("Code successfully generated.");
}

private void unzip(File fromZipFile, File toOutputDir) throws IOException {
try (java.util.zip.ZipFile zipFile = new ZipFile(fromZipFile)) {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
File entryDestination = new File(toOutputDir, entry.getName());
if (entry.isDirectory()) {
entryDestination.mkdirs();
} else {
entryDestination.getParentFile().mkdirs();
try (InputStream in = zipFile.getInputStream(entry);
OutputStream out = new FileOutputStream(entryDestination)) {
IOUtils.copy(in, out);
}
}
}
}
}

private String getBasePackage(final Config config) {
return config
.getOptionalValue(getBasePackagePropertyName(), String.class)
.orElse(DEFAULT_PACKAGE);
}

private static JaxRsProjectSettings defaultProjectSettings() {
JaxRsProjectSettings projectSettings = new JaxRsProjectSettings();
projectSettings.setJavaPackage(DEFAULT_PACKAGE);

projectSettings.setReactive(false);
projectSettings.setCodeOnly(true);
projectSettings.setCliGenCI(false);
projectSettings.setMavenFileStructure(false);
projectSettings.setIncludeSpec(false);
projectSettings.setCliGenCI(false);
return projectSettings;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package io.quarkiverse.openapi.server.generator.deployment.codegen;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;

import org.eclipse.microprofile.config.Config;

import io.quarkiverse.openapi.server.generator.deployment.CodegenConfig;
import io.quarkus.bootstrap.prebuild.CodeGenException;
import io.quarkus.deployment.CodeGenContext;
import io.quarkus.deployment.CodeGenProvider;

public class ApicurioOpenApiServerCodegen implements CodeGenProvider {

@Override
public String providerId() {
return "jaxrs";
}

@Override
public String inputExtension() {
return "json";
}

@Override
public String inputDirectory() {
return "resources";
}

@Override
public boolean shouldRun(Path sourceDir, Config config) {
return sourceDir != null && config.getOptionalValue(CodegenConfig.getSpecPropertyName(), String.class)
.isPresent();
}

@Override
public boolean trigger(CodeGenContext context) throws CodeGenException {
final Path openApiDir = context.inputDir();
final Path outDir = context.outDir();
final ApicurioCodegenWrapper apicurioCodegenWrapper = new ApicurioCodegenWrapper(context.config(), outDir.toFile());

if (Files.isDirectory(openApiDir)) {

try (Stream<Path> openApiFilesPaths = Files.walk(openApiDir)) {
openApiFilesPaths
.filter(Files::isRegularFile)
.map(Path::toString)
.filter(s -> s.endsWith(this.inputExtension()))
.map(Path::of).forEach(openApiResource -> {
if (openApiResource.toFile().getName().equals(context.config()
.getOptionalValue(CodegenConfig.getSpecPropertyName(), String.class).get())) {
try {
apicurioCodegenWrapper.generate(openApiResource);
} catch (CodeGenException e) {
e.printStackTrace();
}
}
});

} catch (IOException e) {
throw new CodeGenException("Failed to generate java files from OpenApi file in " + openApiDir.toAbsolutePath(),
e);
}
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.quarkiverse.openapi.server.generator.deployment.codegen.ApicurioOpenApiServerCodegen
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.quarkiverse.openapi.server.generator.deployment;

import java.net.URISyntaxException;
import java.nio.file.Path;

import org.eclipse.microprofile.config.Config;
import org.junit.jupiter.api.Test;

import io.quarkiverse.openapi.server.generator.deployment.codegen.ApicurioOpenApiServerCodegen;
import io.quarkus.bootstrap.prebuild.CodeGenException;
import io.quarkus.deployment.CodeGenContext;

public class CodegenTest {

@Test
public void testGeneration() throws CodeGenException, URISyntaxException {
Config config = MockConfigUtils.getTestConfig("application.properties");

CodeGenContext codeGenContext = new CodeGenContext(null, Path.of("target/generated-test-sources"),
Path.of("target/generated-test-sources"), Path.of("generated-test-classes"), false, config, true);

ApicurioOpenApiServerCodegen apicurioOpenApiServerCodegen = new ApicurioOpenApiServerCodegen();
apicurioOpenApiServerCodegen.trigger(codeGenContext);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.quarkiverse.openapi.server.generator.deployment;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URL;
import java.util.Objects;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;

import io.smallrye.config.PropertiesConfigSource;

public final class MockConfigUtils {

private MockConfigUtils() {
}

public static Config getTestConfig(String propertiesFile) {
PropertiesConfigSource configSource;
try {
configSource = new PropertiesConfigSource(getResource(propertiesFile));
} catch (IOException e) {
throw new UncheckedIOException(e);
}

return ConfigProviderResolver
.instance()
.getBuilder()
.withSources(configSource)
.build();
}

private static URL getResource(String resourcePath) {
return Objects.requireNonNull(MockConfigUtils.class.getResource(resourcePath));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
quarkus.openapi.generator.spec=petstore-openapi.json
quarkus.openapi.generator.base-package=io.petstore
Loading

0 comments on commit cbe4f2b

Please sign in to comment.