Skip to content

Commit 328757a

Browse files
Fix #1185 - Add additionalProperties when type object has default value and no props (#1193)
* Fix #1185 - Add additionalProperties when type object has default value and no props Signed-off-by: Ricardo Zanini <[email protected]> * Fix Windows path Signed-off-by: Ricardo Zanini <[email protected]> * Adding Gonzalo's comment Co-authored-by: Gonzalo Muñoz <[email protected]> --------- Signed-off-by: Ricardo Zanini <[email protected]> Co-authored-by: Gonzalo Muñoz <[email protected]>
1 parent c10fd40 commit 328757a

File tree

7 files changed

+159
-16
lines changed

7 files changed

+159
-16
lines changed

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

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -473,23 +473,18 @@ private <T> Optional<T> getConfigKeyValues(final Config config, final Path openA
473473
Class<T> propertyType) {
474474

475475
Optional<String> possibleConfigKey = getConfigKeyValue(config, openApiFilePath);
476-
if (possibleConfigKey.isPresent()) {
477-
return getValuesByConfigKey(config, CodegenConfig.getSpecConfigNameByConfigKey(possibleConfigKey.get(), configName),
478-
propertyType, configName);
479-
}
476+
return possibleConfigKey
477+
.flatMap(s -> getValuesByConfigKey(config, CodegenConfig.getSpecConfigNameByConfigKey(s, configName),
478+
propertyType, configName));
480479

481-
return Optional.empty();
482480
}
483481

484482
private <K, V> Optional<Map<K, V>> getConfigKeyValues(final SmallRyeConfig config, final Path openApiFilePath,
485483
CodegenConfig.ConfigName configName,
486484
Class<K> kClass, Class<V> vClass) {
487485

488486
Optional<String> possibleConfigKey = getConfigKeyValue(config, openApiFilePath);
489-
if (possibleConfigKey.isPresent()) {
490-
return getValuesByConfigKey(config, configName, kClass, vClass, possibleConfigKey.get());
491-
}
487+
return possibleConfigKey.flatMap(s -> getValuesByConfigKey(config, configName, kClass, vClass, s));
492488

493-
return Optional.empty();
494489
}
495490
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import java.nio.file.Path;
44
import java.util.ArrayList;
5-
import java.util.Arrays;
65
import java.util.Collection;
76
import java.util.Iterator;
87
import java.util.List;
@@ -12,7 +11,7 @@ public class OpenApiGeneratorOutputPaths {
1211
public static final String OPENAPI_PATH = "open-api";
1312
public static final String STREAM_PATH = "open-api-stream";
1413

15-
private static final Collection<String> rootPaths = Arrays.asList(STREAM_PATH);
14+
private static final Collection<String> rootPaths = List.of(STREAM_PATH);
1615

1716
public static Path getRelativePath(Path path) {
1817
List<String> paths = new ArrayList<>();

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,20 @@ public CodegenModel fromModel(String name, Schema model) {
148148
return codegenModel;
149149
}
150150

151+
@Override
152+
public CodegenProperty fromProperty(String name, Schema p, boolean required, boolean schemaIsFromAdditionalProperties) {
153+
if (p != null && p.getType() != null) {
154+
// Property is a `type: object` without `additionalProperties: true`, without `properties`, but has `default` values set!
155+
// In this peculiar situation, the template will try to initialize a Java Object with such values, and it will fail to compile.
156+
// See https://github.com/quarkiverse/quarkus-openapi-generator/issues/1185 for more context.
157+
if ("object".equals(p.getType()) && p.getDefault() != null && p.getAdditionalProperties() == null
158+
&& p.getItems() == null) {
159+
p.setAdditionalProperties(true);
160+
}
161+
}
162+
return super.fromProperty(name, p, required, schemaIsFromAdditionalProperties);
163+
}
164+
151165
private void warnIfDuplicated(CodegenModel m) {
152166
Set<String> propertyNames = new TreeSet<>();
153167
for (CodegenProperty element : m.allVars) {

client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/wrapper/OpenApiClientGeneratorWrapperTest.java

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99

1010
import java.io.File;
1111
import java.io.FileNotFoundException;
12+
import java.net.URI;
1213
import java.net.URISyntaxException;
14+
import java.net.URL;
1315
import java.nio.file.Path;
1416
import java.nio.file.Paths;
1517
import java.util.List;
1618
import java.util.Map;
19+
import java.util.Objects;
1720
import java.util.Optional;
1821
import java.util.stream.Collectors;
1922

@@ -718,6 +721,30 @@ void verifyDynamicUrlAnnotation() throws Exception {
718721
}
719722
}
720723

724+
@Test
725+
void verifyAllowAdditionalPropertiesIfNotPresent() throws Exception {
726+
List<File> generatedFiles = createGeneratorWrapperReactive("issue-1185.json")
727+
.generate("org.issue1185")
728+
.stream()
729+
.filter(file -> file.getPath().endsWith("PictureDescriptionLocal.java")).toList();
730+
731+
assertThat(generatedFiles).isNotEmpty();
732+
// generationConfig
733+
for (File file : generatedFiles) {
734+
try {
735+
Optional<FieldDeclaration> var = StaticJavaParser.parse(file)
736+
.findAll(FieldDeclaration.class).stream()
737+
.filter(c -> c.getVariable(0)
738+
.getNameAsString().equals("generationConfig"))
739+
.findFirst();
740+
assertThat(var).isPresent();
741+
assertThat(var.get().getElementType().asString()).contains("Map<String,Object>");
742+
} catch (FileNotFoundException e) {
743+
throw new RuntimeException(e.getMessage());
744+
}
745+
}
746+
}
747+
721748
private List<File> generateRestClientFiles() throws URISyntaxException {
722749
OpenApiClientGeneratorWrapper generatorWrapper = createGeneratorWrapper("simple-openapi.json").withCircuitBreakerConfig(
723750
Map.of("org.openapitools.client.api.DefaultApi", List.of("opThatDoesNotExist", "byeMethodGet")));
@@ -736,15 +763,34 @@ private OpenApiClientGeneratorWrapper createGeneratorWrapper(String specFileName
736763
}
737764

738765
private Path getOpenApiSpecPath(String specFileName) throws URISyntaxException {
739-
return Path.of(requireNonNull(this.getClass().getResource(String.format("/openapi/%s", specFileName))).toURI());
766+
URL url = this.getClass().getResource("/openapi/" + specFileName);
767+
Objects.requireNonNull(url, "Could not find /openapi/" + specFileName);
768+
769+
URI uri;
770+
try {
771+
uri = url.toURI();
772+
} catch (URISyntaxException e) {
773+
// this should never happen for a well-formed file URL
774+
throw new RuntimeException("Invalid URI for " + url, e);
775+
}
776+
777+
return Paths.get(uri);
740778
}
741779

742780
private Path getOpenApiTargetPath(Path openApiSpec) throws URISyntaxException {
743781
return Paths.get(getTargetDir(), "openapi-gen");
744782
}
745783

746-
private String getTargetDir() throws URISyntaxException {
747-
return Paths.get(requireNonNull(getClass().getResource("/")).toURI()).getParent().toString();
784+
private String getTargetDir() {
785+
URL url = requireNonNull(getClass().getResource("/"),
786+
"Could not locate classpath root");
787+
try {
788+
return Paths.get(url.toURI())
789+
.getParent()
790+
.toString();
791+
} catch (URISyntaxException e) {
792+
throw new RuntimeException("Invalid URI for " + url, e);
793+
}
748794
}
749795

750796
private Optional<VariableDeclarator> findVariableByName(List<FieldDeclaration> fields, String name) {
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
{
2+
"openapi": "3.1.0",
3+
"info": {
4+
"title": "Docling Serve – PictureDescriptionLocal",
5+
"version": "0.13.0"
6+
},
7+
"paths": {
8+
"/v1alpha/convert/file": {
9+
"post": {
10+
"summary": "Process File with Local Picture Description",
11+
"operationId": "processFileWithPictureDescriptionLocal",
12+
"tags": ["Docling"],
13+
"requestBody": {
14+
"required": true,
15+
"content": {
16+
"multipart/form-data": {
17+
"schema": {
18+
"type": "object",
19+
"properties": {
20+
"files": {
21+
"type": "array",
22+
"items": {
23+
"type": "string",
24+
"format": "binary"
25+
}
26+
},
27+
"picture_description_local": {
28+
"$ref": "#/components/schemas/PictureDescriptionLocal"
29+
}
30+
},
31+
"required": ["files", "picture_description_local"]
32+
}
33+
}
34+
}
35+
},
36+
"responses": {
37+
"200": {
38+
"description": "Successful Response",
39+
"content": {
40+
"application/json": {
41+
"schema": {
42+
"type": "object"
43+
}
44+
}
45+
}
46+
}
47+
}
48+
}
49+
}
50+
},
51+
"components": {
52+
"schemas": {
53+
"PictureDescriptionLocal": {
54+
"type": "object",
55+
"title": "PictureDescriptionLocal",
56+
"description": "Options for running a local vision-language model in the picture description. Parameters refer to a model hosted on Hugging Face.",
57+
"properties": {
58+
"repo_id": {
59+
"type": "string",
60+
"title": "Repo Id",
61+
"description": "Repository ID on the Hugging Face Hub."
62+
},
63+
"prompt": {
64+
"type": "string",
65+
"title": "Prompt",
66+
"description": "Prompt used when calling the vision-language model.",
67+
"default": "Describe this image in a few sentences."
68+
},
69+
"generation_config": {
70+
"type": "object",
71+
"title": "Generation Config",
72+
"description": "Config from Transformers’ GenerationConfig (e.g. max_new_tokens, do_sample).",
73+
"default": {
74+
"max_new_tokens": 200,
75+
"do_sample": false
76+
},
77+
"examples": [
78+
{
79+
"do_sample": false,
80+
"max_new_tokens": 200
81+
}
82+
]
83+
}
84+
},
85+
"required": ["repo_id"]
86+
}
87+
}
88+
}
89+
}

docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ endif::add-copy-button-to-config-props[]
1515

1616
[.description]
1717
--
18-
Path to the Moqu (relative to the project).
18+
Path to the Moqu OpenAPI files, relative to the `src/main/resources` directory.
1919

2020

2121
ifdef::add-copy-button-to-env-var[]

docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock_quarkus.openapi-generator.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ endif::add-copy-button-to-config-props[]
1515

1616
[.description]
1717
--
18-
Path to the Moqu (relative to the project).
18+
Path to the Moqu OpenAPI files, relative to the `src/main/resources` directory.
1919

2020

2121
ifdef::add-copy-button-to-env-var[]

0 commit comments

Comments
 (0)