Skip to content

Commit c5ab785

Browse files
authored
Add rule to set container (array, set, map) to true (#18128)
* add rule to set containter to null * update doc
1 parent 0b4cf0a commit c5ab785

4 files changed

Lines changed: 224 additions & 1 deletion

File tree

docs/customization.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,3 +598,10 @@ Example:
598598
```
599599
java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java -i modules/openapi-generator/src/test/resources/3_0/petstore.yaml -o /tmp/java-okhttp/ --openapi-normalizer FILTER="operationId:addPet|getPetById"
600600
```
601+
602+
- `SET_CONTAINER_TO_NULLABLE`: When set to `array|set|map` (or just `array`) for example, it will set `nullable` in array, set and map to true.
603+
604+
Example:
605+
```
606+
java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java -i modules/openapi-generator/src/test/resources/3_0/petstore.yaml -o /tmp/java-okhttp/ --openapi-normalizer SET_CONTAINER_TO_NULLABLE="array|map"
607+
```

modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,17 @@ public class OpenAPINormalizer {
101101
final String X_INTERNAL = "x-internal";
102102
boolean removeXInternal;
103103

104-
// when set (e.g. operationId:getPetById, addPet), filter out (or remove) everything else
104+
// when set (e.g. operationId:getPetById|addPet), filter out (or remove) everything else
105105
final String FILTER = "FILTER";
106106
HashSet<String> operationIdFilters = new HashSet<>();
107107

108+
// when set (e.g. operationId:getPetById|addPet), filter out (or remove) everything else
109+
final String SET_CONTAINER_TO_NULLABLE = "SET_CONTAINER_TO_NULLABLE";
110+
HashSet<String> setContainerToNullable = new HashSet<>();
111+
boolean updateArrayToNullable;
112+
boolean updateSetToNullable;
113+
boolean updateMapToNullable;
114+
108115
// ============= end of rules =============
109116

110117
/**
@@ -199,6 +206,23 @@ public void processRules(Map<String, String> inputRules) {
199206
}
200207
}
201208
}
209+
210+
if (inputRules.get(SET_CONTAINER_TO_NULLABLE) != null) {
211+
rules.put(SET_CONTAINER_TO_NULLABLE, true);
212+
setContainerToNullable = new HashSet<>(Arrays.asList(inputRules.get(SET_CONTAINER_TO_NULLABLE).split("[|]")));
213+
if (setContainerToNullable.contains("array")) {
214+
updateArrayToNullable = true;
215+
}
216+
if (setContainerToNullable.contains("set")) {
217+
updateSetToNullable = true;
218+
}
219+
if (setContainerToNullable.contains("map")) {
220+
updateMapToNullable = true;
221+
}
222+
if (!updateArrayToNullable && !updateSetToNullable && !updateMapToNullable) {
223+
LOGGER.error("SET_CONTAINER_TO_NULLABLE rule must be in the form of `array|set|map`, e.g. `set`, `array|map`: {}", inputRules.get(SET_CONTAINER_TO_NULLABLE));
224+
}
225+
}
202226
}
203227

204228
/**
@@ -445,8 +469,10 @@ public Schema normalizeSchema(Schema schema, Set<Schema> visitedSchemas) {
445469
}
446470

447471
if (schema instanceof ArraySchema) { // array
472+
normalizeArraySchema(schema);
448473
normalizeSchema(schema.getItems(), visitedSchemas);
449474
} else if (schema.getAdditionalProperties() instanceof Schema) { // map
475+
normalizeMapSchema(schema);
450476
normalizeSchema((Schema) schema.getAdditionalProperties(), visitedSchemas);
451477
} else if (ModelUtils.isOneOf(schema)) { // oneOf
452478
return normalizeOneOf(schema, visitedSchemas);
@@ -498,6 +524,14 @@ public Schema normalizeSchema(Schema schema, Set<Schema> visitedSchemas) {
498524
return schema;
499525
}
500526

527+
private Schema normalizeArraySchema(Schema schema) {
528+
return processSetArraytoNullable(schema);
529+
}
530+
531+
private Schema normalizeMapSchema(Schema schema) {
532+
return processSetMapToNullable(schema);
533+
}
534+
501535
private Schema normalizeSimpleSchema(Schema schema, Set<Schema> visitedSchemas) {
502536
return processNormalize31Spec(schema, visitedSchemas);
503537
}
@@ -864,6 +898,60 @@ private Schema processSimplifyOneOf(Schema schema) {
864898
return schema;
865899
}
866900

901+
/**
902+
* Set nullable to true in array/set if needed.
903+
*
904+
* @param schema Schema
905+
* @return Schema
906+
*/
907+
private Schema processSetArraytoNullable(Schema schema) {
908+
if (!getRule(SET_CONTAINER_TO_NULLABLE)) {
909+
return schema;
910+
}
911+
912+
if (Boolean.TRUE.equals(schema.getUniqueItems())) { // a set
913+
if (updateSetToNullable) {
914+
if (schema.getNullable() != null || (schema.getExtensions() != null && schema.getExtensions().containsKey("x-nullable"))) {
915+
// already set, don't overwrite
916+
return schema;
917+
}
918+
schema.setNullable(true);
919+
}
920+
} else { // array
921+
if (updateArrayToNullable) {
922+
if (schema.getNullable() != null || (schema.getExtensions() != null && schema.getExtensions().containsKey("x-nullable"))) {
923+
// already set, don't overwrite
924+
return schema;
925+
}
926+
schema.setNullable(true);
927+
}
928+
}
929+
930+
return schema;
931+
}
932+
933+
/**
934+
* Set nullable to true in map if needed.
935+
*
936+
* @param schema Schema
937+
* @return Schema
938+
*/
939+
private Schema processSetMapToNullable(Schema schema) {
940+
if (!getRule(SET_CONTAINER_TO_NULLABLE)) {
941+
return schema;
942+
}
943+
944+
if (updateMapToNullable) {
945+
if (schema.getNullable() != null || (schema.getExtensions() != null && schema.getExtensions().containsKey("x-nullable"))) {
946+
// already set, don't override
947+
return schema;
948+
}
949+
schema.setNullable(true);
950+
}
951+
952+
return schema;
953+
}
954+
867955
/**
868956
* If the schema is anyOf and the sub-schemas is null, set `nullable: true` instead.
869957
* If there's only one sub-schema, simply return the sub-schema directly.

modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,4 +475,42 @@ public void testComposedSchemaDoesNotThrow() {
475475
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, Collections.emptyMap());
476476
openAPINormalizer.normalize();
477477
}
478+
479+
@Test
480+
public void testSetContainerToNullable() {
481+
// test `array|map`
482+
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/setContainerToNullable_test.yaml");
483+
484+
Schema schema = openAPI.getComponents().getSchemas().get("Person");
485+
assertEquals(((Schema) schema.getProperties().get("array_property")).getNullable(), null);
486+
assertEquals(((Schema) schema.getProperties().get("set_property")).getNullable(), null);
487+
assertEquals(((Schema) schema.getProperties().get("map_property")).getNullable(), null);
488+
489+
Map<String, String> options = new HashMap<>();
490+
options.put("SET_CONTAINER_TO_NULLABLE", "array|map");
491+
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
492+
openAPINormalizer.normalize();
493+
494+
Schema schema2 = openAPI.getComponents().getSchemas().get("Person");
495+
assertEquals(((Schema) schema2.getProperties().get("array_property")).getNullable(), true);
496+
assertEquals(((Schema) schema2.getProperties().get("set_property")).getNullable(), null);
497+
assertEquals(((Schema) schema2.getProperties().get("map_property")).getNullable(), true);
498+
499+
// test `set`
500+
OpenAPI openAPI2 = TestUtils.parseSpec("src/test/resources/3_0/setContainerToNullable_test.yaml");
501+
502+
Schema schema3 = openAPI2.getComponents().getSchemas().get("Person");
503+
assertEquals(((Schema) schema3.getProperties().get("array_property")).getNullable(), null);
504+
assertEquals(((Schema) schema3.getProperties().get("set_property")).getNullable(), null);
505+
assertEquals(((Schema) schema3.getProperties().get("map_property")).getNullable(), null);
506+
507+
options.put("SET_CONTAINER_TO_NULLABLE", "set");
508+
OpenAPINormalizer openAPINormalizer2 = new OpenAPINormalizer(openAPI2, options);
509+
openAPINormalizer2.normalize();
510+
511+
Schema schema4 = openAPI2.getComponents().getSchemas().get("Person");
512+
assertEquals(((Schema) schema4.getProperties().get("array_property")).getNullable(), null);
513+
assertEquals(((Schema) schema4.getProperties().get("set_property")).getNullable(), true);
514+
assertEquals(((Schema) schema4.getProperties().get("map_property")).getNullable(), null);
515+
}
478516
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
openapi: 3.0.1
2+
info:
3+
version: 1.0.0
4+
title: Example
5+
license:
6+
name: MIT
7+
servers:
8+
- url: http://api.example.xyz/v1
9+
paths:
10+
/person/display/{personId}:
11+
get:
12+
tags:
13+
- person
14+
- basic
15+
parameters:
16+
- name: personId
17+
in: path
18+
required: true
19+
description: The id of the person to retrieve
20+
schema:
21+
type: string
22+
operationId: list
23+
responses:
24+
'200':
25+
description: OK
26+
content:
27+
application/json:
28+
schema:
29+
$ref: "#/components/schemas/Person"
30+
delete:
31+
tags:
32+
- person
33+
x-internal: true
34+
parameters:
35+
- name: personId
36+
in: path
37+
required: true
38+
description: The id of the person to retrieve
39+
schema:
40+
type: string
41+
operationId: delete
42+
responses:
43+
'200':
44+
description: OK
45+
content:
46+
application/json:
47+
schema:
48+
$ref: "#/components/schemas/Person"
49+
put:
50+
tags:
51+
- person
52+
parameters:
53+
- name: personId
54+
in: path
55+
required: true
56+
description: The id of the person to retrieve
57+
schema:
58+
type: string
59+
operationId: put
60+
responses:
61+
'200':
62+
description: OK
63+
content:
64+
application/json:
65+
schema:
66+
$ref: "#/components/schemas/Person"
67+
68+
components:
69+
schemas:
70+
Person:
71+
description: person
72+
type: object
73+
properties:
74+
lastName:
75+
type: string
76+
firstName:
77+
type: string
78+
array_property:
79+
type: array
80+
items:
81+
type: string
82+
set_property:
83+
type: array
84+
uniqueItems: true
85+
items:
86+
type: string
87+
map_property:
88+
type: object
89+
additionalProperties:
90+
type: string

0 commit comments

Comments
 (0)