Skip to content

Commit 22ebf93

Browse files
Merge branch 'main' into feature/add-schema-mappings-to-generation
2 parents 7519124 + e230b96 commit 22ebf93

File tree

61 files changed

+6489
-170
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+6489
-170
lines changed

.all-contributorsrc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,15 @@
378378
"contributions": [
379379
"doc"
380380
]
381+
},
382+
{
383+
"login": "marko-bekhta",
384+
"name": "Marko Bekhta",
385+
"avatar_url": "https://avatars.githubusercontent.com/u/4004823?v=4",
386+
"profile": "https://github.com/marko-bekhta",
387+
"contributions": [
388+
"code"
389+
]
381390
}
382391
],
383392
"contributorsPerLine": 7,

.github/project.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
release:
2-
current-version: 2.5.0
2+
current-version: 2.6.0
33
next-version: 3.0.0-SNAPSHOT

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
2+
<div align="center">
3+
4+
<img src="https://github.com/quarkiverse/.github/blob/main/assets/images/quarkus.svg" width="67" height="70" ><img src="https://github.com/quarkiverse/.github/blob/main/assets/images/plus-sign.svg" height="70" ><img src="https://github.com/quarkiverse/quarkus-openapi-generator/blob/main/docs/modules/ROOT/assets/images/openapi.svg" height="70" >
5+
16
# Quarkus - OpenAPI Generator
7+
</div>
8+
<br>
29

310
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
4-
[![All Contributors](https://img.shields.io/badge/all_contributors-40-orange.svg?style=flat-square)](#contributors-)
11+
[![All Contributors](https://img.shields.io/badge/all_contributors-41-orange.svg?style=flat-square)](#contributors-)
512
<!-- ALL-CONTRIBUTORS-BADGE:END -->
613
[![Build](<https://img.shields.io/github/actions/workflow/status/quarkiverse/quarkus-openapi-generator/build.yml?branch=main&logo=GitHub&style=flat-square>)](https://github.com/quarkiverse/quarkus-openapi-generator/actions?query=workflow%3ABuild)
714
[![Maven Central](https://img.shields.io/maven-central/v/io.quarkiverse.openapi.generator/quarkus-openapi-generator.svg?label=Maven%20Central&style=flat-square)](https://search.maven.org/artifact/io.quarkiverse.openapi.generator/quarkus-openapi-generator)
@@ -90,6 +97,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
9097
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kfebert"><img src="https://avatars.githubusercontent.com/u/848796?v=4?s=100" width="100px;" alt="Karl Ferdinand Ebert"/><br /><sub><b>Karl Ferdinand Ebert</b></sub></a><br /><a href="https://github.com/quarkiverse/quarkus-openapi-generator/commits?author=kfebert" title="Code">💻</a> <a href="https://github.com/quarkiverse/quarkus-openapi-generator/commits?author=kfebert" title="Documentation">📖</a></td>
9198
<td align="center" valign="top" width="14.28%"><a href="https://github.com/michalkolenda"><img src="https://avatars.githubusercontent.com/u/29705783?v=4?s=100" width="100px;" alt="Michał Kolenda"/><br /><sub><b>Michał Kolenda</b></sub></a><br /><a href="https://github.com/quarkiverse/quarkus-openapi-generator/commits?author=michalkolenda" title="Code">💻</a></td>
9299
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rednalyn"><img src="https://avatars.githubusercontent.com/u/31593992?v=4?s=100" width="100px;" alt="rednalyn"/><br /><sub><b>rednalyn</b></sub></a><br /><a href="https://github.com/quarkiverse/quarkus-openapi-generator/commits?author=rednalyn" title="Documentation">📖</a></td>
100+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/marko-bekhta"><img src="https://avatars.githubusercontent.com/u/4004823?v=4?s=100" width="100px;" alt="Marko Bekhta"/><br /><sub><b>Marko Bekhta</b></sub></a><br /><a href="https://github.com/quarkiverse/quarkus-openapi-generator/commits?author=marko-bekhta" title="Code">💻</a></td>
93101
</tr>
94102
</tbody>
95103
</table>

client/deployment/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
<name>Quarkus - Openapi Generator - Client - Deployment</name>
1212

1313
<properties>
14-
<version.org.openapitools>7.8.0</version.org.openapitools>
14+
<version.org.openapitools>7.10.0</version.org.openapitools>
1515
<version.org.slf4j>2.0.16</version.org.slf4j>
1616
<version.com.github.jknack>4.3.1</version.com.github.jknack>
17-
<version.io.swagger.parser>2.1.23</version.io.swagger.parser>
17+
<version.io.swagger.parser>2.1.24</version.io.swagger.parser>
1818
</properties>
1919

2020
<dependencies>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package io.quarkiverse.openapi.generator.deployment.template;
2+
3+
import java.util.List;
4+
import java.util.concurrent.ExecutionException;
5+
6+
import io.quarkus.qute.EvalContext;
7+
import io.quarkus.qute.Expression;
8+
9+
final class ExprEvaluator {
10+
11+
private ExprEvaluator() {
12+
}
13+
14+
@SuppressWarnings("unchecked")
15+
public static <T> T evaluate(EvalContext context, Expression expression) throws ExecutionException, InterruptedException {
16+
return (T) context.evaluate(expression).toCompletableFuture().get();
17+
}
18+
19+
@SuppressWarnings("unchecked")
20+
public static <T> T[] evaluate(EvalContext context, List<Expression> expressions, Class<T> type)
21+
throws ExecutionException, InterruptedException {
22+
T[] results = (T[]) java.lang.reflect.Array.newInstance(type, expressions.size());
23+
24+
for (int i = 0; i < expressions.size(); i++) {
25+
Expression expression = expressions.get(i);
26+
T result = type.cast(context.evaluate(expression).toCompletableFuture().get());
27+
results[i] = result;
28+
}
29+
30+
return results;
31+
}
32+
33+
}

client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/OpenApiNamespaceResolver.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import java.util.concurrent.CompletionStage;
88
import java.util.concurrent.ExecutionException;
99

10+
import org.openapitools.codegen.model.OperationMap;
11+
1012
import io.quarkiverse.openapi.generator.deployment.codegen.OpenApiGeneratorOutputPaths;
1113
import io.quarkus.qute.EvalContext;
1214
import io.quarkus.qute.Expression;
@@ -18,9 +20,8 @@
1820
* implement and use them.
1921
*/
2022
public class OpenApiNamespaceResolver implements NamespaceResolver {
21-
private static final String GENERATE_DEPRECATED_PROP = "generateDeprecated";
22-
2323
static final OpenApiNamespaceResolver INSTANCE = new OpenApiNamespaceResolver();
24+
private static final String GENERATE_DEPRECATED_PROP = "generateDeprecated";
2425

2526
private OpenApiNamespaceResolver() {
2627
}
@@ -53,6 +54,10 @@ public String parseUri(String uri) {
5354
return OpenApiGeneratorOutputPaths.getRelativePath(Path.of(uri)).toString().replace(File.separatorChar, '/');
5455
}
5556

57+
public boolean hasAuthMethods(OperationMap operations) {
58+
return operations != null && operations.getOperation().stream().anyMatch(operation -> operation.hasAuthMethods);
59+
}
60+
5661
@Override
5762
public CompletionStage<Object> resolve(EvalContext context) {
5863
try {

client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/template/QuteTemplatingEngineAdapter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ public QuteTemplatingEngineAdapter() {
4242
.addDefaults()
4343
.addValueResolver(new ReflectionValueResolver())
4444
.addNamespaceResolver(OpenApiNamespaceResolver.INSTANCE)
45+
.addNamespaceResolver(StrNamespaceResolver.INSTANCE)
4546
.removeStandaloneLines(true)
46-
.strictRendering(false)
47+
.strictRendering(true)
4748
.build();
4849
}
4950

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package io.quarkiverse.openapi.generator.deployment.template;
2+
3+
import java.util.concurrent.CompletableFuture;
4+
import java.util.concurrent.CompletionStage;
5+
6+
import io.quarkus.qute.EvalContext;
7+
import io.quarkus.qute.NamespaceResolver;
8+
9+
/**
10+
* Namespace resolver to mimic the function of io.quarkus.qute.runtime.extensions.StringTemplateExtensions.
11+
* This extension is built-in with Qute when used in a context with Quarkus DI.
12+
* Since these extensions are built in build time by Qute we can't use them because our process also runs in build time without
13+
* a CDI context.
14+
* So any namespace resolver auto-generated by Quarkus won't be added to our engine.
15+
*
16+
* @see <a href="https://quarkus.io/guides/qute-reference#template_extension_methods">Template Extension Methods</a>
17+
*/
18+
public class StrNamespaceResolver implements NamespaceResolver {
19+
20+
static final StrNamespaceResolver INSTANCE = new StrNamespaceResolver();
21+
22+
private StrNamespaceResolver() {
23+
}
24+
25+
@Override
26+
public String getNamespace() {
27+
return "str";
28+
}
29+
30+
/**
31+
* @see io.quarkus.qute.runtime.extensions.StringTemplateExtensions#fmt(String, String, Object...)
32+
*/
33+
public String fmt(String format, Object... args) {
34+
return String.format(format, args);
35+
}
36+
37+
@Override
38+
public CompletionStage<Object> resolve(EvalContext context) {
39+
switch (context.getName()) {
40+
case "fmt":
41+
if (context.getParams().size() < 2) {
42+
throw new IllegalArgumentException(
43+
"Missing required parameter for 'fmt'. Make sure that the function has at least two parameters");
44+
}
45+
try {
46+
return CompletableFuture.completedFuture(
47+
fmt(ExprEvaluator.evaluate(context, context.getParams().get(0)),
48+
ExprEvaluator.evaluate(context, context.getParams().subList(1, context.getParams().size()),
49+
Object.class)));
50+
} catch (Exception e) {
51+
throw new RuntimeException(e);
52+
}
53+
default:
54+
throw new IllegalArgumentException("There's no method named '" + context.getName() + "'");
55+
}
56+
}
57+
58+
}

client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapper.java

Lines changed: 62 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,14 @@
3333
public abstract class OpenApiClientGeneratorWrapper {
3434

3535
public static final String VERBOSE = "verbose";
36-
private static final String ONCE_LOGGER = "org.openapitools.codegen.utils.oncelogger.enabled";
3736
/**
3837
* Security scheme for which to apply security constraints even if the OpenAPI definition has no security definition
3938
*/
4039
public static final String DEFAULT_SECURITY_SCHEME = "defaultSecurityScheme";
4140
public static final String SUPPORTS_ADDITIONAL_PROPERTIES_AS_ATTRIBUTE = "supportsAdditionalPropertiesWithComposedSchema";
42-
private static final Map<String, String> defaultTypeMappings = Map.of(
43-
"date", "LocalDate",
44-
"DateTime", "OffsetDateTime");
45-
private static final Map<String, String> defaultImportMappings = Map.of(
46-
"LocalDate", "java.time.LocalDate",
41+
private static final String ONCE_LOGGER = "org.openapitools.codegen.utils.oncelogger.enabled";
42+
private static final Map<String, String> defaultTypeMappings = Map.of("date", "LocalDate", "DateTime", "OffsetDateTime");
43+
private static final Map<String, String> defaultImportMappings = Map.of("LocalDate", "java.time.LocalDate",
4744
"OffsetDateTime", "java.time.OffsetDateTime");
4845
private final QuarkusCodegenConfigurator configurator;
4946
private final DefaultGenerator generator;
@@ -53,8 +50,8 @@ public abstract class OpenApiClientGeneratorWrapper {
5350
private String modelPackage = "";
5451

5552
OpenApiClientGeneratorWrapper(final QuarkusCodegenConfigurator configurator, final Path specFilePath, final Path outputDir,
56-
final boolean verbose,
57-
final boolean validateSpec) {
53+
final boolean verbose, final boolean validateSpec) {
54+
5855
// do not generate docs nor tests
5956
GlobalSettings.setProperty(CodegenConstants.API_DOCS, FALSE.toString());
6057
GlobalSettings.setProperty(CodegenConstants.API_TESTS, FALSE.toString());
@@ -78,17 +75,44 @@ public abstract class OpenApiClientGeneratorWrapper {
7875
defaultTypeMappings.forEach(this.configurator::addTypeMapping);
7976
defaultImportMappings.forEach(this.configurator::addImportMapping);
8077

81-
this.generator = new DefaultGenerator();
82-
}
78+
this.setDefaults();
8379

84-
public OpenApiClientGeneratorWrapper withApiPackage(final String pkg) {
85-
this.apiPackage = pkg;
86-
return this;
80+
this.generator = new DefaultGenerator();
8781
}
8882

89-
public OpenApiClientGeneratorWrapper withModelPackage(final String pkg) {
90-
this.modelPackage = pkg;
91-
return this;
83+
/**
84+
* A few properties from the "with*" methods must be injected in the Qute context by default since we turned strict model
85+
* rendering.
86+
* This way we avoid side effects in the model such as "NOT_FOUND" strings printed everywhere.
87+
*
88+
* @see <a href="https://quarkus.io/guides/qute-reference#configuration-reference">Qute - Configuration Reference</a>
89+
*/
90+
private void setDefaults() {
91+
// Set default values directly here
92+
this.configurator.addAdditionalProperty("additionalApiTypeAnnotations", new String[0]);
93+
this.configurator.addAdditionalProperty("additionalPropertiesAsAttribute", FALSE);
94+
this.configurator.addAdditionalProperty("additionalEnumTypeUnexpectedMember", FALSE);
95+
this.configurator.addAdditionalProperty("additionalEnumTypeUnexpectedMemberName", "");
96+
this.configurator.addAdditionalProperty("additionalEnumTypeUnexpectedMemberStringValue", "");
97+
this.configurator.addAdditionalProperty("additionalRequestArgs", new String[0]);
98+
this.configurator.addAdditionalProperty("classes-codegen", new HashMap<>());
99+
this.configurator.addAdditionalProperty("circuit-breaker", new HashMap<>());
100+
this.configurator.addAdditionalProperty("configKey", "");
101+
this.configurator.addAdditionalProperty("datatypeWithEnum", "");
102+
this.configurator.addAdditionalProperty("enable-security-generation", TRUE);
103+
this.configurator.addAdditionalProperty("generate-part-filename", FALSE);
104+
this.configurator.addAdditionalProperty("mutiny", FALSE);
105+
this.configurator.addAdditionalProperty("mutiny-operation-ids", new HashMap<>());
106+
this.configurator.addAdditionalProperty("mutiny-return-response", FALSE);
107+
this.configurator.addAdditionalProperty("part-filename-value", "");
108+
this.configurator.addAdditionalProperty("return-response", FALSE);
109+
this.configurator.addAdditionalProperty("skipFormModel", TRUE);
110+
this.configurator.addAdditionalProperty("templateDir", "");
111+
this.configurator.addAdditionalProperty("use-bean-validation", FALSE);
112+
this.configurator.addAdditionalProperty("use-field-name-in-part-filename", FALSE);
113+
this.configurator.addAdditionalProperty("verbose", FALSE);
114+
// TODO: expose as properties https://github.com/quarkiverse/quarkus-openapi-generator/issues/869
115+
this.configurator.addAdditionalProperty(CodegenConstants.SERIALIZABLE_MODEL, FALSE);
92116
}
93117

94118
/**
@@ -98,30 +122,30 @@ public OpenApiClientGeneratorWrapper withModelPackage(final String pkg) {
98122
* @return this wrapper
99123
*/
100124
public OpenApiClientGeneratorWrapper withCircuitBreakerConfig(final Map<String, List<String>> config) {
101-
if (config != null) {
102-
configurator.addAdditionalProperty("circuit-breaker", config);
103-
}
125+
Optional.ofNullable(config).ifPresent(cfg -> {
126+
this.configurator.addAdditionalProperty("circuit-breaker", config);
127+
});
104128
return this;
105129
}
106130

107131
public OpenApiClientGeneratorWrapper withClassesCodeGenConfig(final Map<String, Object> config) {
108-
if (config != null) {
109-
configurator.addAdditionalProperty("classes-codegen", config);
110-
}
132+
Optional.ofNullable(config).ifPresent(cfg -> {
133+
this.configurator.addAdditionalProperty("classes-codegen", cfg);
134+
});
111135
return this;
112136
}
113137

114138
public OpenApiClientGeneratorWrapper withMutiny(final Boolean config) {
115-
if (config != null) {
116-
configurator.addAdditionalProperty("mutiny", config);
117-
}
139+
Optional.ofNullable(config).ifPresent(cfg -> {
140+
this.configurator.addAdditionalProperty("mutiny", cfg);
141+
});
118142
return this;
119143
}
120144

121145
public OpenApiClientGeneratorWrapper withMutinyReturnResponse(final Boolean config) {
122-
if (config != null) {
123-
configurator.addAdditionalProperty("mutiny-return-response", config);
124-
}
146+
Optional.ofNullable(config).ifPresent(cfg -> {
147+
this.configurator.addAdditionalProperty("mutiny-return-response", cfg);
148+
});
125149
return this;
126150
}
127151

@@ -214,16 +238,16 @@ public OpenApiClientGeneratorWrapper withAdditionalEnumTypeUnexpectedMemberStrin
214238
* @return this wrapper
215239
*/
216240
public OpenApiClientGeneratorWrapper withAdditionalApiTypeAnnotationsConfig(final String additionalApiTypeAnnotations) {
217-
if (additionalApiTypeAnnotations != null) {
241+
Optional.ofNullable(additionalApiTypeAnnotations).ifPresent(cfg -> {
218242
this.configurator.addAdditionalProperty("additionalApiTypeAnnotations", additionalApiTypeAnnotations.split(";"));
219-
}
243+
});
220244
return this;
221245
}
222246

223247
public OpenApiClientGeneratorWrapper withAdditionalRequestArgs(final String additionalRequestArgs) {
224-
if (additionalRequestArgs != null) {
248+
Optional.ofNullable(additionalRequestArgs).ifPresent(cfg -> {
225249
this.configurator.addAdditionalProperty("additionalRequestArgs", additionalRequestArgs.split(";"));
226-
}
250+
});
227251
return this;
228252
}
229253

@@ -266,6 +290,12 @@ public OpenApiClientGeneratorWrapper withModelNamePrefix(final String modelNameP
266290
return this;
267291
}
268292

293+
/**
294+
* Main entrypoint, or where to generate the files based on the given base package.
295+
*
296+
* @param basePackage Java package name, e.g. org.acme
297+
* @return a list of generated files
298+
*/
269299
public List<File> generate(final String basePackage) {
270300
this.basePackage = basePackage;
271301
this.consolidatePackageNames();

client/deployment/src/main/resources/templates/libraries/microprofile/additionalEnumTypeUnexpectedMember.qute

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
* Special value if the API response contains some new value not declared in this enum.
33
* You should react accordingly.
44
*/
5-
{additionalEnumTypeUnexpectedMemberName}({#if e.isContainer}{e.items.dataType}{#else}{e.dataType}{/if}.valueOf("{additionalEnumTypeUnexpectedMemberStringValue}")){#if e.allowableValues},{/if}
5+
{additionalEnumTypeUnexpectedMemberName}({#if e.isContainer.or(false)}{e.items.dataType}{#else}{e.dataType}{/if}.valueOf("{additionalEnumTypeUnexpectedMemberStringValue}")){#if e.allowableValues},{/if}

0 commit comments

Comments
 (0)