Skip to content

Commit

Permalink
support nullable check for OAS 3.1 (#15698)
Browse files Browse the repository at this point in the history
  • Loading branch information
karzang committed Jun 5, 2023
1 parent d244601 commit bc7bdca
Show file tree
Hide file tree
Showing 35 changed files with 191 additions and 178 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,35 @@ private Schema processSimplifyAnyOfStringAndEnumString(Schema schema) {
}

/**
* If the schema is oneOf and the sub-schemas is null, set `nullable: true` instead.
* Check if the schema is of type 'null'
*
* Return true if the schema's type is 'null' or not specified
*
* @param schema Schema
*/
private boolean isNullTypeSchema(Schema schema) {
if (schema == null) {
return true;
}

if ((schema.getType() == null || schema.getType().equals("null")) && schema.get$ref() == null) {
return true;
}

// convert referenced enum of null only to `nullable:true`
Schema referencedSchema = ModelUtils.getReferencedSchema(openAPI, schema);
if (referencedSchema.getEnum() != null && referencedSchema.getEnum().size() == 1) {
if ("null".equals(String.valueOf(referencedSchema.getEnum().get(0)))) {
return true;
}
}

return false;
}

/**
* If the schema is oneOf and the sub-schemas is null, set `nullable: true`
* instead.
* If there's only one sub-schema, simply return the sub-schema directly.
*
* @param schema Schema
Expand All @@ -680,35 +708,19 @@ private Schema processSimplifyOneOf(Schema schema) {
return schema;
}

if (schema.getOneOf() != null && !schema.getOneOf().isEmpty()) {
for (int i = 0; i < schema.getOneOf().size(); i++) {
// convert null sub-schema to `nullable: true`
if (schema.getOneOf().get(i) == null ||
(((Schema) schema.getOneOf().get(i)).getType() == null &&
((Schema) schema.getOneOf().get(i)).get$ref() == null)) {
schema.getOneOf().remove(i);
schema.setNullable(true);
continue;
}
List<Schema> oneOfSchemas = schema.getOneOf();
if (oneOfSchemas != null) {
if (oneOfSchemas.removeIf(oneOf -> isNullTypeSchema(oneOf))) {
schema.setNullable(true);

// convert enum of null only to `nullable:true`
Schema oneOfElement = ModelUtils.getReferencedSchema(openAPI, (Schema) schema.getOneOf().get(i));
if (oneOfElement.getEnum() != null && oneOfElement.getEnum().size() == 1) {
if ("null".equals(String.valueOf(oneOfElement.getEnum().get(0)))) {
schema.setNullable(true);
schema.getOneOf().remove(i);
continue;
// if only one element left, simplify to just the element (schema)
if (oneOfSchemas.size() == 1) {
if (Boolean.TRUE.equals(schema.getNullable())) { // retain nullable setting
((Schema) oneOfSchemas.get(0)).setNullable(true);
}
return (Schema) oneOfSchemas.get(0);
}
}

// if only one element left, simplify to just the element (schema)
if (schema.getOneOf().size() == 1) {
if (Boolean.TRUE.equals(schema.getNullable())) { // retain nullable setting
((Schema) schema.getOneOf().get(0)).setNullable(true);
}
return (Schema) schema.getOneOf().get(0);
}
}

return schema;
Expand All @@ -726,34 +738,18 @@ private Schema processSimplifyAnyOf(Schema schema) {
return schema;
}

if (schema.getAnyOf() != null && !schema.getAnyOf().isEmpty()) {
for (int i = 0; i < schema.getAnyOf().size(); i++) {
// convert null sub-schema to `nullable: true`
if (schema.getAnyOf().get(i) == null ||
(((Schema) schema.getAnyOf().get(i)).getType() == null &&
((Schema) schema.getAnyOf().get(i)).get$ref() == null)) {
schema.getAnyOf().remove(i);
schema.setNullable(true);
continue;
}

// convert enum of null only to `nullable:true`
Schema anyOfElement = ModelUtils.getReferencedSchema(openAPI, (Schema) schema.getAnyOf().get(i));
if (anyOfElement.getEnum() != null && anyOfElement.getEnum().size() == 1) {
if ("null".equals(String.valueOf(anyOfElement.getEnum().get(0)))) {
schema.setNullable(true);
schema.getAnyOf().remove(i);
continue;
}
}
List<Schema> anyOfSchemas = schema.getAnyOf();
if (anyOfSchemas != null) {
if (anyOfSchemas.removeIf(anyOf -> isNullTypeSchema(anyOf))) {
schema.setNullable(true);
}

// if only one element left, simplify to just the element (schema)
if (schema.getAnyOf().size() == 1) {
if (anyOfSchemas.size() == 1) {
if (Boolean.TRUE.equals(schema.getNullable())) { // retain nullable setting
((Schema) schema.getAnyOf().get(0)).setNullable(true);
((Schema) anyOfSchemas.get(0)).setNullable(true);
}
return (Schema) schema.getAnyOf().get(0);
return (Schema) anyOfSchemas.get(0);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,11 @@ public void testOpenAPINormalizerSimplifyOneOfAnyOf() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/simplifyOneOfAnyOf_test.yaml");

Schema schema = openAPI.getComponents().getSchemas().get("AnyOfTest");
assertEquals(schema.getAnyOf().size(), 2);
assertEquals(schema.getAnyOf().size(), 4);
assertNull(schema.getNullable());

Schema schema2 = openAPI.getComponents().getSchemas().get("OneOfTest");
assertEquals(schema2.getOneOf().size(), 2);
assertEquals(schema2.getOneOf().size(), 4);
assertNull(schema2.getNullable());

Schema schema5 = openAPI.getComponents().getSchemas().get("OneOfNullableTest");
Expand All @@ -168,6 +168,7 @@ public void testOpenAPINormalizerSimplifyOneOfAnyOf() {
Schema schema4 = openAPI.getComponents().getSchemas().get("OneOfTest");
assertNull(schema4.getOneOf());
assertTrue(schema4 instanceof IntegerSchema);
assertTrue(schema4.getNullable());

Schema schema6 = openAPI.getComponents().getSchemas().get("OneOfNullableTest");
assertEquals(schema6.getOneOf().size(), 2);
Expand Down Expand Up @@ -273,7 +274,7 @@ public void testOpenAPINormalizerConvertEnumNullToNullable() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/convertEnumNullToNullable_test.yaml");

Schema schema = openAPI.getComponents().getSchemas().get("AnyOfTest");
assertEquals(schema.getAnyOf().size(), 3);
assertEquals(schema.getAnyOf().size(), 4);
assertNull(schema.getNullable());

Map<String, String> options = new HashMap<>();
Expand All @@ -292,7 +293,7 @@ public void testOpenAPINormalizerDefaultRules() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/convertEnumNullToNullable_test.yaml");

Schema schema = openAPI.getComponents().getSchemas().get("AnyOfTest");
assertEquals(schema.getAnyOf().size(), 3);
assertEquals(schema.getAnyOf().size(), 4);
assertNull(schema.getNullable());

Map<String, String> options = new HashMap<>();
Expand All @@ -313,7 +314,7 @@ public void testOpenAPINormalizerDisableAll() {

// before test
Schema schema = openAPI.getComponents().getSchemas().get("AnyOfTest");
assertEquals(schema.getAnyOf().size(), 3);
assertEquals(schema.getAnyOf().size(), 4);
assertNull(schema.getNullable());

Map<String, String> options = new HashMap<>();
Expand All @@ -323,7 +324,7 @@ public void testOpenAPINormalizerDisableAll() {

// checks should be the same after test
Schema schema3 = openAPI.getComponents().getSchemas().get("AnyOfTest");
assertEquals(schema3.getAnyOf().size(), 3);
assertEquals(schema3.getAnyOf().size(), 4);
assertNull(schema3.getNullable());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,17 @@ components:
anyOf:
- type: string
- $ref: '#/components/schemas/EnumString'
- $ref: '#/components/schemas/EnumNullString'
- $ref: '#/components/schemas/EnumNull'
EnumString:
type: string
enum:
- A
- B
EnumNullString:
type: string
enum:
- 'null'
EnumNull:
type: string
enum:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,15 @@ components:
description: to test anyOf
anyOf:
- type: string
- type: 'null'
- type: null
- $ref: null
OneOfTest:
description: to test oneOf
oneOf:
- type: integer
- type: 'null'
- type: null
- $ref: null
OneOfNullableTest:
description: to test oneOf nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1971,8 +1971,8 @@ components:
type: string
fruitReq:
additionalProperties: false
nullable: true
oneOf:
- type: "null"
- $ref: '#/components/schemas/appleReq'
- $ref: '#/components/schemas/bananaReq'
appleReq:
Expand Down Expand Up @@ -2021,8 +2021,8 @@ components:
in OAS schema >= 3.1.
discriminator:
propertyName: shapeType
nullable: true
oneOf:
- type: "null"
- $ref: '#/components/schemas/Triangle'
- $ref: '#/components/schemas/Quadrilateral'
NullableShape:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public bool ShouldSerializeMainShape()
/// <summary>
/// Gets or Sets ShapeOrNull
/// </summary>
[DataMember(Name = "shapeOrNull", EmitDefaultValue = false)]
[DataMember(Name = "shapeOrNull", EmitDefaultValue = true)]
public ShapeOrNull ShapeOrNull
{
get{ return _ShapeOrNull;}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1971,8 +1971,8 @@ components:
type: string
fruitReq:
additionalProperties: false
nullable: true
oneOf:
- type: "null"
- $ref: '#/components/schemas/appleReq'
- $ref: '#/components/schemas/bananaReq'
appleReq:
Expand Down Expand Up @@ -2021,8 +2021,8 @@ components:
in OAS schema >= 3.1.
discriminator:
propertyName: shapeType
nullable: true
oneOf:
- type: "null"
- $ref: '#/components/schemas/Triangle'
- $ref: '#/components/schemas/Quadrilateral'
NullableShape:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**MainShape** | [**Shape**](Shape.md) | | [optional]
**ShapeOrNull** | [**ShapeOrNull**](ShapeOrNull.md) | | [optional]
**Shapes** | [**List&lt;Shape&gt;**](Shape.md) | | [optional]
**NullableShape** | [**NullableShape**](NullableShape.md) | | [optional]
**ShapeOrNull** | [**ShapeOrNull**](ShapeOrNull.md) | | [optional]

[[Back to Model list]](../../README.md#documentation-for-models) [[Back to API list]](../../README.md#documentation-for-api-endpoints) [[Back to README]](../../README.md)

Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@ public partial class Drawing : Dictionary<String, Fruit>, IValidatableObject
/// Initializes a new instance of the <see cref="Drawing" /> class.
/// </summary>
/// <param name="mainShape">mainShape</param>
/// <param name="shapeOrNull">shapeOrNull</param>
/// <param name="shapes">shapes</param>
/// <param name="nullableShape">nullableShape</param>
/// <param name="shapeOrNull">shapeOrNull</param>
[JsonConstructor]
public Drawing(Shape mainShape, ShapeOrNull shapeOrNull, List<Shape> shapes, NullableShape? nullableShape = default) : base()
public Drawing(Shape mainShape, List<Shape> shapes, NullableShape? nullableShape = default, ShapeOrNull? shapeOrNull = default) : base()
{
MainShape = mainShape;
ShapeOrNull = shapeOrNull;
Shapes = shapes;
NullableShape = nullableShape;
ShapeOrNull = shapeOrNull;
OnCreated();
}

Expand All @@ -55,12 +55,6 @@ public Drawing(Shape mainShape, ShapeOrNull shapeOrNull, List<Shape> shapes, Nul
[JsonPropertyName("mainShape")]
public Shape MainShape { get; set; }

/// <summary>
/// Gets or Sets ShapeOrNull
/// </summary>
[JsonPropertyName("shapeOrNull")]
public ShapeOrNull ShapeOrNull { get; set; }

/// <summary>
/// Gets or Sets Shapes
/// </summary>
Expand All @@ -73,6 +67,12 @@ public Drawing(Shape mainShape, ShapeOrNull shapeOrNull, List<Shape> shapes, Nul
[JsonPropertyName("nullableShape")]
public NullableShape? NullableShape { get; set; }

/// <summary>
/// Gets or Sets ShapeOrNull
/// </summary>
[JsonPropertyName("shapeOrNull")]
public ShapeOrNull? ShapeOrNull { get; set; }

/// <summary>
/// Returns the string presentation of the object
/// </summary>
Expand All @@ -83,9 +83,9 @@ public override string ToString()
sb.Append("class Drawing {\n");
sb.Append(" ").Append(base.ToString()?.Replace("\n", "\n ")).Append("\n");
sb.Append(" MainShape: ").Append(MainShape).Append("\n");
sb.Append(" ShapeOrNull: ").Append(ShapeOrNull).Append("\n");
sb.Append(" Shapes: ").Append(Shapes).Append("\n");
sb.Append(" NullableShape: ").Append(NullableShape).Append("\n");
sb.Append(" ShapeOrNull: ").Append(ShapeOrNull).Append("\n");
sb.Append("}\n");
return sb.ToString();
}
Expand Down Expand Up @@ -124,9 +124,9 @@ public override Drawing Read(ref Utf8JsonReader utf8JsonReader, Type typeToConve
JsonTokenType startingTokenType = utf8JsonReader.TokenType;

Shape? mainShape = default;
ShapeOrNull? shapeOrNull = default;
List<Shape>? shapes = default;
NullableShape? nullableShape = default;
ShapeOrNull? shapeOrNull = default;

while (utf8JsonReader.Read())
{
Expand All @@ -147,10 +147,6 @@ public override Drawing Read(ref Utf8JsonReader utf8JsonReader, Type typeToConve
if (utf8JsonReader.TokenType != JsonTokenType.Null)
mainShape = JsonSerializer.Deserialize<Shape>(ref utf8JsonReader, jsonSerializerOptions);
break;
case "shapeOrNull":
if (utf8JsonReader.TokenType != JsonTokenType.Null)
shapeOrNull = JsonSerializer.Deserialize<ShapeOrNull>(ref utf8JsonReader, jsonSerializerOptions);
break;
case "shapes":
if (utf8JsonReader.TokenType != JsonTokenType.Null)
shapes = JsonSerializer.Deserialize<List<Shape>>(ref utf8JsonReader, jsonSerializerOptions);
Expand All @@ -159,6 +155,10 @@ public override Drawing Read(ref Utf8JsonReader utf8JsonReader, Type typeToConve
if (utf8JsonReader.TokenType != JsonTokenType.Null)
nullableShape = JsonSerializer.Deserialize<NullableShape>(ref utf8JsonReader, jsonSerializerOptions);
break;
case "shapeOrNull":
if (utf8JsonReader.TokenType != JsonTokenType.Null)
shapeOrNull = JsonSerializer.Deserialize<ShapeOrNull>(ref utf8JsonReader, jsonSerializerOptions);
break;
default:
break;
}
Expand All @@ -168,13 +168,10 @@ public override Drawing Read(ref Utf8JsonReader utf8JsonReader, Type typeToConve
if (mainShape == null)
throw new ArgumentNullException(nameof(mainShape), "Property is required for class Drawing.");

if (shapeOrNull == null)
throw new ArgumentNullException(nameof(shapeOrNull), "Property is required for class Drawing.");

if (shapes == null)
throw new ArgumentNullException(nameof(shapes), "Property is required for class Drawing.");

return new Drawing(mainShape, shapeOrNull, shapes, nullableShape);
return new Drawing(mainShape, shapes, nullableShape, shapeOrNull);
}

/// <summary>
Expand All @@ -190,12 +187,12 @@ public override void Write(Utf8JsonWriter writer, Drawing drawing, JsonSerialize

writer.WritePropertyName("mainShape");
JsonSerializer.Serialize(writer, drawing.MainShape, jsonSerializerOptions);
writer.WritePropertyName("shapeOrNull");
JsonSerializer.Serialize(writer, drawing.ShapeOrNull, jsonSerializerOptions);
writer.WritePropertyName("shapes");
JsonSerializer.Serialize(writer, drawing.Shapes, jsonSerializerOptions);
writer.WritePropertyName("nullableShape");
JsonSerializer.Serialize(writer, drawing.NullableShape, jsonSerializerOptions);
writer.WritePropertyName("shapeOrNull");
JsonSerializer.Serialize(writer, drawing.ShapeOrNull, jsonSerializerOptions);

writer.WriteEndObject();
}
Expand Down
Loading

0 comments on commit bc7bdca

Please sign in to comment.