Add rule to set container (array, set, map) to true (#18128)

* add rule to set containter to null

* update doc
This commit is contained in:
William Cheng 2024-03-17 16:37:46 +08:00 committed by GitHub
parent 0b4cf0a027
commit c5ab78575a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 224 additions and 1 deletions

View File

@ -598,3 +598,10 @@ Example:
```
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"
```
- `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.
Example:
```
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"
```

View File

@ -101,10 +101,17 @@ public class OpenAPINormalizer {
final String X_INTERNAL = "x-internal";
boolean removeXInternal;
// when set (e.g. operationId:getPetById, addPet), filter out (or remove) everything else
// when set (e.g. operationId:getPetById|addPet), filter out (or remove) everything else
final String FILTER = "FILTER";
HashSet<String> operationIdFilters = new HashSet<>();
// when set (e.g. operationId:getPetById|addPet), filter out (or remove) everything else
final String SET_CONTAINER_TO_NULLABLE = "SET_CONTAINER_TO_NULLABLE";
HashSet<String> setContainerToNullable = new HashSet<>();
boolean updateArrayToNullable;
boolean updateSetToNullable;
boolean updateMapToNullable;
// ============= end of rules =============
/**
@ -199,6 +206,23 @@ public class OpenAPINormalizer {
}
}
}
if (inputRules.get(SET_CONTAINER_TO_NULLABLE) != null) {
rules.put(SET_CONTAINER_TO_NULLABLE, true);
setContainerToNullable = new HashSet<>(Arrays.asList(inputRules.get(SET_CONTAINER_TO_NULLABLE).split("[|]")));
if (setContainerToNullable.contains("array")) {
updateArrayToNullable = true;
}
if (setContainerToNullable.contains("set")) {
updateSetToNullable = true;
}
if (setContainerToNullable.contains("map")) {
updateMapToNullable = true;
}
if (!updateArrayToNullable && !updateSetToNullable && !updateMapToNullable) {
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));
}
}
}
/**
@ -445,8 +469,10 @@ public class OpenAPINormalizer {
}
if (schema instanceof ArraySchema) { // array
normalizeArraySchema(schema);
normalizeSchema(schema.getItems(), visitedSchemas);
} else if (schema.getAdditionalProperties() instanceof Schema) { // map
normalizeMapSchema(schema);
normalizeSchema((Schema) schema.getAdditionalProperties(), visitedSchemas);
} else if (ModelUtils.isOneOf(schema)) { // oneOf
return normalizeOneOf(schema, visitedSchemas);
@ -498,6 +524,14 @@ public class OpenAPINormalizer {
return schema;
}
private Schema normalizeArraySchema(Schema schema) {
return processSetArraytoNullable(schema);
}
private Schema normalizeMapSchema(Schema schema) {
return processSetMapToNullable(schema);
}
private Schema normalizeSimpleSchema(Schema schema, Set<Schema> visitedSchemas) {
return processNormalize31Spec(schema, visitedSchemas);
}
@ -864,6 +898,60 @@ public class OpenAPINormalizer {
return schema;
}
/**
* Set nullable to true in array/set if needed.
*
* @param schema Schema
* @return Schema
*/
private Schema processSetArraytoNullable(Schema schema) {
if (!getRule(SET_CONTAINER_TO_NULLABLE)) {
return schema;
}
if (Boolean.TRUE.equals(schema.getUniqueItems())) { // a set
if (updateSetToNullable) {
if (schema.getNullable() != null || (schema.getExtensions() != null && schema.getExtensions().containsKey("x-nullable"))) {
// already set, don't overwrite
return schema;
}
schema.setNullable(true);
}
} else { // array
if (updateArrayToNullable) {
if (schema.getNullable() != null || (schema.getExtensions() != null && schema.getExtensions().containsKey("x-nullable"))) {
// already set, don't overwrite
return schema;
}
schema.setNullable(true);
}
}
return schema;
}
/**
* Set nullable to true in map if needed.
*
* @param schema Schema
* @return Schema
*/
private Schema processSetMapToNullable(Schema schema) {
if (!getRule(SET_CONTAINER_TO_NULLABLE)) {
return schema;
}
if (updateMapToNullable) {
if (schema.getNullable() != null || (schema.getExtensions() != null && schema.getExtensions().containsKey("x-nullable"))) {
// already set, don't override
return schema;
}
schema.setNullable(true);
}
return schema;
}
/**
* If the schema is anyOf and the sub-schemas is null, set `nullable: true` instead.
* If there's only one sub-schema, simply return the sub-schema directly.

View File

@ -475,4 +475,42 @@ public class OpenAPINormalizerTest {
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, Collections.emptyMap());
openAPINormalizer.normalize();
}
@Test
public void testSetContainerToNullable() {
// test `array|map`
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/setContainerToNullable_test.yaml");
Schema schema = openAPI.getComponents().getSchemas().get("Person");
assertEquals(((Schema) schema.getProperties().get("array_property")).getNullable(), null);
assertEquals(((Schema) schema.getProperties().get("set_property")).getNullable(), null);
assertEquals(((Schema) schema.getProperties().get("map_property")).getNullable(), null);
Map<String, String> options = new HashMap<>();
options.put("SET_CONTAINER_TO_NULLABLE", "array|map");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
openAPINormalizer.normalize();
Schema schema2 = openAPI.getComponents().getSchemas().get("Person");
assertEquals(((Schema) schema2.getProperties().get("array_property")).getNullable(), true);
assertEquals(((Schema) schema2.getProperties().get("set_property")).getNullable(), null);
assertEquals(((Schema) schema2.getProperties().get("map_property")).getNullable(), true);
// test `set`
OpenAPI openAPI2 = TestUtils.parseSpec("src/test/resources/3_0/setContainerToNullable_test.yaml");
Schema schema3 = openAPI2.getComponents().getSchemas().get("Person");
assertEquals(((Schema) schema3.getProperties().get("array_property")).getNullable(), null);
assertEquals(((Schema) schema3.getProperties().get("set_property")).getNullable(), null);
assertEquals(((Schema) schema3.getProperties().get("map_property")).getNullable(), null);
options.put("SET_CONTAINER_TO_NULLABLE", "set");
OpenAPINormalizer openAPINormalizer2 = new OpenAPINormalizer(openAPI2, options);
openAPINormalizer2.normalize();
Schema schema4 = openAPI2.getComponents().getSchemas().get("Person");
assertEquals(((Schema) schema4.getProperties().get("array_property")).getNullable(), null);
assertEquals(((Schema) schema4.getProperties().get("set_property")).getNullable(), true);
assertEquals(((Schema) schema4.getProperties().get("map_property")).getNullable(), null);
}
}

View File

@ -0,0 +1,90 @@
openapi: 3.0.1
info:
version: 1.0.0
title: Example
license:
name: MIT
servers:
- url: http://api.example.xyz/v1
paths:
/person/display/{personId}:
get:
tags:
- person
- basic
parameters:
- name: personId
in: path
required: true
description: The id of the person to retrieve
schema:
type: string
operationId: list
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/Person"
delete:
tags:
- person
x-internal: true
parameters:
- name: personId
in: path
required: true
description: The id of the person to retrieve
schema:
type: string
operationId: delete
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/Person"
put:
tags:
- person
parameters:
- name: personId
in: path
required: true
description: The id of the person to retrieve
schema:
type: string
operationId: put
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/Person"
components:
schemas:
Person:
description: person
type: object
properties:
lastName:
type: string
firstName:
type: string
array_property:
type: array
items:
type: string
set_property:
type: array
uniqueItems: true
items:
type: string
map_property:
type: object
additionalProperties:
type: string