Skip to content

Commit ae8bbff

Browse files
Add configuration option for a custom template directory (quarkiverse#801)
1 parent 91eddd5 commit ae8bbff

File tree

10 files changed

+546
-3
lines changed

10 files changed

+546
-3
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public enum ConfigName {
3434
//global configs
3535
VERBOSE("verbose"),
3636
INPUT_BASE_DIR("input-base-dir"),
37+
TEMPLATE_BASE_DIR("template-base-dir"),
3738
INCLUDE("include"),
3839
EXCLUDE("exclude"),
3940
VALIDATE_SPEC("validateSpec"),

client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/GlobalCodegenConfig.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ public class GlobalCodegenConfig extends CommonItemConfig {
2727
@ConfigItem(name = "input-base-dir")
2828
public Optional<String> inputBaseDir;
2929

30+
/**
31+
* Option to change the directory where template files must be found.
32+
*/
33+
@ConfigItem(name = "template-base-dir")
34+
public Optional<String> templateBaseDir;
35+
3036
/**
3137
* Whether or not to skip validating the input spec prior to generation. By default, invalid specifications will result in
3238
* an error.

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import static io.quarkiverse.openapi.generator.deployment.CodegenConfig.ConfigName.INPUT_BASE_DIR;
1414
import static io.quarkiverse.openapi.generator.deployment.CodegenConfig.ConfigName.MODEL_NAME_PREFIX;
1515
import static io.quarkiverse.openapi.generator.deployment.CodegenConfig.ConfigName.MODEL_NAME_SUFFIX;
16+
import static io.quarkiverse.openapi.generator.deployment.CodegenConfig.ConfigName.TEMPLATE_BASE_DIR;
1617
import static io.quarkiverse.openapi.generator.deployment.CodegenConfig.ConfigName.VALIDATE_SPEC;
1718

1819
import java.io.IOException;
@@ -119,7 +120,9 @@ public boolean trigger(CodeGenContext context) throws CodeGenException {
119120
}
120121

121122
try (Stream<Path> openApiFilesPaths = Files.walk(openApiDir)) {
122-
Path templateDir = context.workDir().resolve("classes").resolve("templates");
123+
Optional<String> templateBaseDir = getTemplateBaseDirRelativeToSourceRoot(context.inputDir(), context.config());
124+
Path templateDir = templateBaseDir.map(Path::of)
125+
.orElseGet(() -> context.workDir().resolve("classes").resolve("templates"));
123126
openApiFilesPaths
124127
.filter(Files::isRegularFile)
125128
.filter(path -> {
@@ -318,9 +321,16 @@ private Optional<String> getModelNamePrefix(final Config config, final Path open
318321
}
319322

320323
private Optional<String> getInputBaseDirRelativeToModule(final Path sourceDir, final Config config) {
321-
return config.getOptionalValue(getGlobalConfigName(INPUT_BASE_DIR), String.class).map(inputBaseDir -> {
324+
return config.getOptionalValue(getGlobalConfigName(INPUT_BASE_DIR), String.class).map(baseDir -> {
322325
int srcIndex = sourceDir.toString().lastIndexOf("src");
323-
return srcIndex < 0 ? null : sourceDir.toString().substring(0, srcIndex) + inputBaseDir;
326+
return srcIndex < 0 ? null : sourceDir.toString().substring(0, srcIndex) + baseDir;
327+
});
328+
}
329+
330+
private Optional<String> getTemplateBaseDirRelativeToSourceRoot(final Path sourceDir, final Config config) {
331+
return config.getOptionalValue(getGlobalConfigName(TEMPLATE_BASE_DIR), String.class).map(baseDir -> {
332+
int srcIndex = sourceDir.toString().lastIndexOf(inputDirectory());
333+
return srcIndex < 0 ? null : sourceDir.toString().substring(0, srcIndex) + baseDir;
324334
});
325335
}
326336

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<modelVersion>4.0.0</modelVersion>
4+
<parent>
5+
<artifactId>quarkus-openapi-generator-integration-tests</artifactId>
6+
<groupId>io.quarkiverse.openapi.generator</groupId>
7+
<version>3.0.0-SNAPSHOT</version>
8+
</parent>
9+
<artifactId>quarkus-openapi-generator-it-change-custom-template-directory</artifactId>
10+
<name>Quarkus - Openapi Generator - Integration Tests - Client - Change custom template directory</name>
11+
12+
<dependencies>
13+
<dependency>
14+
<groupId>io.quarkiverse.openapi.generator</groupId>
15+
<artifactId>quarkus-openapi-generator</artifactId>
16+
</dependency>
17+
<dependency>
18+
<groupId>io.quarkus</groupId>
19+
<artifactId>quarkus-junit5</artifactId>
20+
<scope>test</scope>
21+
</dependency>
22+
<dependency>
23+
<groupId>com.github.tomakehurst</groupId>
24+
<artifactId>wiremock-jre8</artifactId>
25+
<scope>test</scope>
26+
</dependency>
27+
<dependency>
28+
<groupId>io.quarkiverse.openapi.generator</groupId>
29+
<artifactId>quarkus-openapi-generator-test-utils</artifactId>
30+
<scope>test</scope>
31+
</dependency>
32+
</dependencies>
33+
34+
<build>
35+
<plugins>
36+
<plugin>
37+
<groupId>io.quarkus</groupId>
38+
<artifactId>quarkus-maven-plugin</artifactId>
39+
<extensions>true</extensions>
40+
<executions>
41+
<execution>
42+
<goals>
43+
<goal>build</goal>
44+
<goal>generate-code</goal>
45+
<goal>generate-code-tests</goal>
46+
</goals>
47+
</execution>
48+
</executions>
49+
</plugin>
50+
</plugins>
51+
</build>
52+
<profiles>
53+
<profile>
54+
<id>native-image</id>
55+
<activation>
56+
<property>
57+
<name>native</name>
58+
</property>
59+
</activation>
60+
<build>
61+
<plugins>
62+
<plugin>
63+
<artifactId>maven-surefire-plugin</artifactId>
64+
<configuration>
65+
<skipTests>${native.surefire.skip}</skipTests>
66+
</configuration>
67+
</plugin>
68+
<plugin>
69+
<artifactId>maven-failsafe-plugin</artifactId>
70+
<executions>
71+
<execution>
72+
<goals>
73+
<goal>integration-test</goal>
74+
<goal>verify</goal>
75+
</goals>
76+
<configuration>
77+
<systemPropertyVariables>
78+
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
79+
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
80+
<maven.home>${maven.home}</maven.home>
81+
</systemPropertyVariables>
82+
</configuration>
83+
</execution>
84+
</executions>
85+
</plugin>
86+
</plugins>
87+
</build>
88+
<properties>
89+
<quarkus.package.type>native</quarkus.package.type>
90+
</properties>
91+
</profile>
92+
</profiles>
93+
94+
</project>
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package {package};
2+
3+
{#for imp in imports}
4+
import {imp.import};
5+
{/for}
6+
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
7+
{#if hasAuthMethods || custom-register-providers.orEmpty.size > 0}
8+
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
9+
{/if}
10+
{#if hasAuthMethods}
11+
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
12+
import {package}.auth.CompositeAuthenticationProvider;
13+
{#if client-headers-factory is 'default'}
14+
import {package}.auth.AuthenticationPropagationHeadersFactory;
15+
{/if}
16+
{/if}
17+
18+
import java.io.InputStream;
19+
import java.io.OutputStream;
20+
import java.util.List;
21+
import java.util.Map;
22+
import jakarta.ws.rs.*;
23+
import jakarta.ws.rs.core.MediaType;
24+
25+
import jakarta.enterprise.context.ApplicationScoped;
26+
27+
import io.quarkiverse.openapi.generator.annotations.GeneratedClass;
28+
import io.quarkiverse.openapi.generator.annotations.GeneratedMethod;
29+
import io.quarkiverse.openapi.generator.annotations.GeneratedParam;
30+
31+
{#if appName}
32+
/**
33+
* {appName}
34+
* {#if appDescription}<p>{appDescription}</p>{/if}
35+
*/
36+
{/if}
37+
@Path("{#if useAnnotatedBasePath}{contextPath}{/if}{commonPath}")
38+
@RegisterRestClient({#if defaultServerUrl}baseUri="{defaultServerUrl}",{/if}configKey="{configKey}")
39+
@GeneratedClass(value="{openapi:parseUri(inputSpec)}", tag = "{baseName}")
40+
{#if hasAuthMethods}
41+
@RegisterProvider(CompositeAuthenticationProvider.class)
42+
{#when client-headers-factory}
43+
{#is 'default'}
44+
@RegisterClientHeaders(AuthenticationPropagationHeadersFactory.class)
45+
{#is 'none'}
46+
@RegisterClientHeaders
47+
{#else}
48+
@RegisterClientHeaders({client-headers-factory}.class)
49+
{/when}
50+
{/if}
51+
{#for crpClassConfig in custom-register-providers.orEmpty}
52+
@RegisterProvider({crpClassConfig}.class)
53+
{/for}
54+
@ApplicationScoped
55+
public interface {classname} {
56+
57+
public default void myCustomMethod() {
58+
}
59+
60+
{#for op in operations.operation}
61+
{#if !op.isDeprecated || openapi:genDeprecatedApiAttr(package, classname, classes-codegen)}
62+
{#if op.summary}
63+
/**
64+
* {op.summary}
65+
*
66+
{#if op.notes}
67+
* {op.notes}
68+
*
69+
{/if}
70+
*/
71+
{/if}
72+
@{op.httpMethod}
73+
{#if op.subresourceOperation}
74+
@Path("{op.path}")
75+
{/if}
76+
{#if op.hasConsumes}
77+
@Consumes(\{{#for consume in op.consumes}"{consume.mediaType}"{#if consume_hasNext}, {/if}{/for}\})
78+
{/if}
79+
{#if op.hasProduces}
80+
@Produces(\{{#for produce in op.produces}"{produce.mediaType}"{#if produce_hasNext}, {/if}{/for}\})
81+
{/if}
82+
@GeneratedMethod ("{op.operationIdOriginal}")
83+
{#for cbClassConfig in circuit-breaker.orEmpty}{#if cbClassConfig.key == package + classname}
84+
{#for cbMethod in cbClassConfig.value.orEmpty}{#if cbMethod == op.nickname}
85+
@org.eclipse.microprofile.faulttolerance.CircuitBreaker
86+
{/if}{/for}
87+
{/if}{/for}
88+
{#if mutiny}
89+
{#if return-response}
90+
public io.smallrye.mutiny.Uni<jakarta.ws.rs.core.Response> {op.nickname}(
91+
{#else}
92+
{#if op.returnType == "void"}
93+
public {#if op.returnType}io.smallrye.mutiny.Uni<Void>{#else}io.smallrye.mutiny.Uni<jakarta.ws.rs.core.Response>{/if} {op.nickname}(
94+
{#else}
95+
public {#if op.returnType}io.smallrye.mutiny.Uni<{op.returnType}>{#else}io.smallrye.mutiny.Uni<jakarta.ws.rs.core.Response>{/if} {op.nickname}(
96+
{/if}
97+
{/if}
98+
{#else}
99+
{#if return-response}
100+
public jakarta.ws.rs.core.Response {op.nickname}(
101+
{#else}
102+
public {#if op.returnType}{op.returnType}{#else}jakarta.ws.rs.core.Response{/if} {op.nickname}(
103+
{/if}
104+
{/if}
105+
{#if op.hasFormParams}
106+
{#if is-resteasy-reactive}
107+
@jakarta.ws.rs.BeanParam {op.operationIdCamelCase}MultipartForm multipartForm{#if op.hasPathParams},{/if}{!
108+
!}{#for p in op.pathParams}{#include templates.path_params param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasQueryParams},{/if}{!
109+
!}{#for p in op.queryParams}{#include templates.query_params param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParams},{/if}{!
110+
!}{#for p in op.bodyParams}{#include bodyParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasHeaderParams},{/if}{!
111+
!}{#for p in op.headerParams}{#include headerParams.qute param=p/}{#if p_hasNext}, {/if}{/for}
112+
{#else}
113+
@org.jboss.resteasy.annotations.providers.multipart.MultipartForm {op.operationIdCamelCase}MultipartForm multipartForm{#if op.hasPathParams},{/if}{!
114+
!}{#for p in op.pathParams}{#include pathParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasQueryParams},{/if}{!
115+
!}{#for p in op.queryParams}{#include queryParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasBodyParams},{/if}{!
116+
!}{#for p in op.bodyParams}{#include bodyParams.qute param=p/}{#if p_hasNext}, {/if}{/for}{#if op.hasHeaderParams},{/if}{!
117+
!}{#for p in op.headerParams}{#include headerParams.qute param=p/}{#if p_hasNext}, {/if}{/for}
118+
{/if}
119+
{#else}
120+
{#for p in op.allParams}
121+
{#if p.hasValidation}@jakarta.validation.Valid {/if}{#include pathParams.qute param=p/}{#include queryParams.qute param=p/}{#include bodyParams.qute param=p/}{#include headerParams.qute param=p/}{#if p_hasNext}, {/if}
122+
{/for}
123+
{/if}
124+
);
125+
{#if op.hasFormParams}
126+
127+
{#include multipartFormdataPojo.qute param=op/}
128+
{/if}
129+
130+
{/if} {! check deprecated !}
131+
{/for}
132+
}

0 commit comments

Comments
 (0)