Skip to content

Commit

Permalink
Represent nullable references within anyOf correctly (#247)
Browse files Browse the repository at this point in the history
* Represent nullbale references within anyOf correctly

* Use anyOf in schema if ref is nullable
  • Loading branch information
millicentachieng authored Jul 8, 2022
1 parent f7f2689 commit 43ad8f9
Show file tree
Hide file tree
Showing 9 changed files with 414 additions and 200 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -420,10 +420,9 @@ private static OpenApiSchema CreateEnumTypeSchema(this ODataContext context, IEd
Debug.Assert(typeReference != null);

OpenApiSchema schema = new OpenApiSchema();
schema.Nullable = typeReference.IsNullable;
schema.Reference = null;

if (context.Settings.OpenApiSpecVersion >= OpenApiSpecVersion.OpenApi3_0)
if (typeReference.IsNullable && context.Settings.OpenApiSpecVersion >= OpenApiSpecVersion.OpenApi3_0)
{
schema.AnyOf = new List<OpenApiSchema>
{
Expand All @@ -435,6 +434,11 @@ private static OpenApiSchema CreateEnumTypeSchema(this ODataContext context, IEd
Type = ReferenceType.Schema,
Id = typeReference.Definition.FullTypeName()
}
},
new OpenApiSchema
{
Type = "object",
Nullable = true
}
};
}
Expand All @@ -448,6 +452,7 @@ private static OpenApiSchema CreateEnumTypeSchema(this ODataContext context, IEd
Id = typeReference.Definition.FullTypeName()
};
schema.UnresolvedReference = true;
schema.Nullable = typeReference.IsNullable;
}

return schema;
Expand All @@ -459,7 +464,6 @@ private static OpenApiSchema CreateStructuredTypeSchema(this ODataContext contex
Debug.Assert(typeReference != null);

OpenApiSchema schema = new OpenApiSchema();
schema.Nullable = typeReference.IsNullable;

// AnyOf will only be valid openApi for version 3
// otherwise the reference should be set directly
Expand All @@ -478,6 +482,11 @@ private static OpenApiSchema CreateStructuredTypeSchema(this ODataContext contex
Type = ReferenceType.Schema,
Id = typeReference.Definition.FullTypeName()
}
},
new OpenApiSchema
{
Type = "object",
Nullable = true
}
};
}
Expand All @@ -490,7 +499,8 @@ private static OpenApiSchema CreateStructuredTypeSchema(this ODataContext contex
Type = ReferenceType.Schema,
Id = typeReference.Definition.FullTypeName()
};
schema.UnresolvedReference = true;
schema.UnresolvedReference = true;
schema.Nullable = typeReference.IsNullable;
}

return schema;
Expand All @@ -502,10 +512,9 @@ private static OpenApiSchema CreateTypeDefinitionSchema(this ODataContext contex
Debug.Assert(reference != null);

OpenApiSchema schema = new OpenApiSchema();
schema.Nullable = reference.IsNullable;
schema.Reference = null;

if (context.Settings.OpenApiSpecVersion >= OpenApiSpecVersion.OpenApi3_0)
if (reference.IsNullable && context.Settings.OpenApiSpecVersion >= OpenApiSpecVersion.OpenApi3_0)
{
schema.AnyOf = new List<OpenApiSchema>
{
Expand All @@ -517,6 +526,11 @@ private static OpenApiSchema CreateTypeDefinitionSchema(this ODataContext contex
Type = ReferenceType.Schema,
Id = reference.Definition.FullTypeName()
}
},
new OpenApiSchema
{
Type = "object",
Nullable = true
}
};
}
Expand All @@ -530,6 +544,7 @@ private static OpenApiSchema CreateTypeDefinitionSchema(this ODataContext contex
Id = reference.Definition.FullTypeName()
};
schema.UnresolvedReference = true;
schema.Nullable = reference.IsNullable;
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
- Fixes request body and response representation for ref POST and PUT operations #228
- Adds discriminator object to complex types which have derived types #233
- Adds @odata.type property and makes this a required property in schemas that have discriminator objects #243
- Represent nullable references within anyOf correctly #190
</PackageReleaseNotes>
<AssemblyName>Microsoft.OpenApi.OData.Reader</AssemblyName>
<AssemblyOriginatorKeyFile>..\..\tool\Microsoft.OpenApi.OData.snk</AssemblyOriginatorKeyFile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,12 @@ public void CreateEdmTypeSchemaReturnSchemaForNullableCollectionComplexType(Open
""anyOf"": [
{
""$ref"": ""#/components/schemas/Microsoft.OData.Service.Sample.TrippinInMemory.Models.AirportLocation""
},
{
""type"": ""object"",
""nullable"": true
}
],
""nullable"": true
]
}
}".ChangeLineBreaks(), json);
}
Expand Down Expand Up @@ -185,26 +188,40 @@ public void CreateEdmTypeSchemaReturnSchemaForEnumType(bool isNullable, OpenApiS
// & Assert
Assert.NotNull(schema);

// Although the schema will be set
// for openApiV2 nullable will not be serialized
Assert.Equal(isNullable, schema.Nullable);


if (specVersion == OpenApiSpecVersion.OpenApi2_0)
{
Assert.NotNull(schema.Reference);
Assert.Null(schema.AnyOf);
Assert.Equal(ReferenceType.Schema, schema.Reference.Type);
Assert.Equal(enumType.FullTypeName(), schema.Reference.Id);
Assert.Equal(isNullable, schema.Nullable);
}
else
{
Assert.Null(schema.Reference);
Assert.NotNull(schema.AnyOf);
Assert.NotEmpty(schema.AnyOf);
var anyOf = Assert.Single(schema.AnyOf);
Assert.NotNull(anyOf.Reference);
Assert.Equal(ReferenceType.Schema, anyOf.Reference.Type);
Assert.Equal(enumType.FullTypeName(), anyOf.Reference.Id);

if (isNullable)
{
Assert.NotNull(schema.AnyOf);
Assert.NotEmpty(schema.AnyOf);
Assert.Null(schema.Reference);
Assert.Equal(2, schema.AnyOf.Count);
var anyOfRef = schema.AnyOf.FirstOrDefault();
Assert.NotNull(anyOfRef.Reference);
Assert.Equal(ReferenceType.Schema, anyOfRef.Reference.Type);
Assert.Equal(enumType.FullTypeName(), anyOfRef.Reference.Id);
var anyOfNull = schema.AnyOf.Skip(1).FirstOrDefault();
Assert.NotNull(anyOfNull.Type);
Assert.Equal("object", anyOfNull.Type);
Assert.True(anyOfNull.Nullable);
}
else
{
Assert.Null(schema.AnyOf);
Assert.NotNull(schema.Reference);
Assert.Equal(ReferenceType.Schema, schema.Reference.Type);
Assert.Equal(enumType.FullTypeName(), schema.Reference.Id);
}
}
}

Expand Down Expand Up @@ -242,7 +259,8 @@ public void CreateEdmTypeSchemaReturnSchemaForComplexType(bool isNullable, OpenA
Assert.Null(schema.Reference);
Assert.NotNull(schema.AnyOf);
Assert.NotEmpty(schema.AnyOf);
var anyOf = Assert.Single(schema.AnyOf);
Assert.Equal(2, schema.AnyOf.Count);
var anyOf = schema.AnyOf.FirstOrDefault();
Assert.NotNull(anyOf.Reference);
Assert.Equal(ReferenceType.Schema, anyOf.Reference.Type);
Assert.Equal(complex.FullTypeName(), anyOf.Reference.Id);
Expand Down Expand Up @@ -283,10 +301,14 @@ public void CreateEdmTypeSchemaReturnSchemaForEntityType(bool isNullable, OpenAp
Assert.Null(schema.Reference);
Assert.NotNull(schema.AnyOf);
Assert.NotEmpty(schema.AnyOf);
var anyOf = Assert.Single(schema.AnyOf);
Assert.NotNull(anyOf.Reference);
Assert.Equal(ReferenceType.Schema, anyOf.Reference.Type);
Assert.Equal(entity.FullTypeName(), anyOf.Reference.Id);
var anyOfRef = schema.AnyOf.FirstOrDefault();
Assert.NotNull(anyOfRef.Reference);
Assert.Equal(ReferenceType.Schema, anyOfRef.Reference.Type);
Assert.Equal(entity.FullTypeName(), anyOfRef.Reference.Id);
var anyOfNull = schema.AnyOf.Skip(1).FirstOrDefault();
Assert.NotNull(anyOfNull.Type);
Assert.Equal("object", anyOfNull.Type);
Assert.True(anyOfNull.Nullable);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,25 +225,29 @@ public void CreateResponseForEdmFunctionReturnCorrectResponses(bool isFunctionIm
Assert.NotNull(response.Content);
OpenApiMediaType mediaType = response.Content["application/json"];

// For either version, nullable should be set
// and the serializer will ignore for v2
Assert.True(mediaType.Schema.Nullable);

// openApi version 2 should have not use nullable
if (specVersion == OpenApiSpecVersion.OpenApi2_0)
{
Assert.NotNull(mediaType.Schema);
Assert.Null(mediaType.Schema.AnyOf);
Assert.NotNull(mediaType.Schema.Reference);
Assert.Equal("Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person", mediaType.Schema.Reference.Id);
Assert.True(mediaType.Schema.Nullable);
}
else
{
Assert.NotNull(mediaType.Schema);
Assert.Null(mediaType.Schema.Reference);
Assert.NotNull(mediaType.Schema.AnyOf);
var anyOf = Assert.Single(mediaType.Schema.AnyOf);
Assert.Equal("Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person", anyOf.Reference.Id);
Assert.Equal(2, mediaType.Schema.AnyOf.Count);
var anyOfRef = mediaType.Schema.AnyOf.FirstOrDefault();
Assert.NotNull(anyOfRef);
Assert.Equal("Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person", anyOfRef.Reference.Id);
var anyOfNull = mediaType.Schema.AnyOf.Skip(1).FirstOrDefault();
Assert.NotNull(anyOfNull.Type);
Assert.Equal("object", anyOfNull.Type);
Assert.True(anyOfNull.Nullable);

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,26 +259,35 @@ public void CreateStructuredTypePropertiesSchemaWithCustomAttributeReturnsCorrec
""anyOf"": [
{
""$ref"": ""#/components/schemas/microsoft.graph.userInsightsSettings""
},
{
""type"": ""object"",
""nullable"": true
}
],
""nullable"": true,
""x-ms-isHidden"": ""true""
},
""regionalAndLanguageSettings"": {
""anyOf"": [
{
""$ref"": ""#/components/schemas/microsoft.graph.regionalAndLanguageSettings""
},
{
""type"": ""object"",
""nullable"": true
}
],
""nullable"": true
]
},
""shiftPreferences"": {
""anyOf"": [
{
""$ref"": ""#/components/schemas/microsoft.graph.shiftPreferences""
},
{
""type"": ""object"",
""nullable"": true
}
],
""nullable"": true
]
}
}
}
Expand Down Expand Up @@ -687,7 +696,7 @@ public void CreatePropertySchemaForNonNullableEnumPropertyReturnSchema(OpenApiSp

IEdmEnumType enumType = model.SchemaElements.OfType<IEdmEnumType>().First(e => e.Name == "Color");
EdmEntityType entitType = new EdmEntityType("NS", "Entity");
IEdmProperty property = new EdmStructuralProperty(entitType, "ColorEnumValue", new EdmEnumTypeReference(enumType, false), "yellow");
IEdmProperty property = new EdmStructuralProperty(entitType, "ColorEnumValue", new EdmEnumTypeReference(enumType, false));

// Act
var schema = context.CreatePropertySchema(property);
Expand All @@ -705,12 +714,7 @@ public void CreatePropertySchemaForNonNullableEnumPropertyReturnSchema(OpenApiSp
else
{
Assert.Equal(@"{
""anyOf"": [
{
""$ref"": ""#/components/schemas/DefaultNs.Color""
}
],
""default"": ""yellow""
""$ref"": ""#/components/schemas/DefaultNs.Color""
}".ChangeLineBreaks(), json);
}
}
Expand Down Expand Up @@ -749,10 +753,13 @@ public void CreatePropertySchemaForNullableEnumPropertyReturnSchema(OpenApiSpecV
""anyOf"": [
{
""$ref"": ""#/components/schemas/DefaultNs.Color""
},
{
""type"": ""object"",
""nullable"": true
}
],
""default"": ""yellow"",
""nullable"": true
""default"": ""yellow""
}".ChangeLineBreaks(), json);
}
}
Expand Down
Loading

0 comments on commit 43ad8f9

Please sign in to comment.