1. Modify to use ODL api for the vocabulary. 2. Change the Schema for entity & complex type. 3. Add unit test for schema on structured type.

This commit is contained in:
Sam Xu 2017-11-30 14:30:44 -08:00
parent 5ee41ab4df
commit 2a98c209a6
7 changed files with 611 additions and 138 deletions

View file

@ -1,63 +0,0 @@
//---------------------------------------------------------------------
// <copyright file="EdmHelper.cs" company="Microsoft">
// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
// </copyright>
//---------------------------------------------------------------------
using System.Linq;
using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Vocabularies;
using Microsoft.OData.Edm.Vocabularies.V1;
namespace Microsoft.OpenApi.OData
{
/// <summary>
/// Extension methods for <see cref="IEdmModel"/>.
/// </summary>
internal static class EdmModelExtensions
{
/// <summary>
/// Get the Core.Description annotation.
/// </summary>
/// <param name="model">The Edm model.</param>
/// <param name="element">The vocabulary annotatable element.</param>
/// <returns>null or the description annotation.</returns>
public static string GetDescription(this IEdmModel model, IEdmVocabularyAnnotatable element)
{
return model.GetAnnotationValue(element, CoreVocabularyModel.DescriptionTerm);
}
/// <summary>
/// Get the Core.LongDescription annotation.
/// </summary>
/// <param name="model">The Edm model.</param>
/// <param name="element">The vocabulary annotatable element.</param>
/// <returns>null or the description annotation.</returns>
public static string GetLongDescription(this IEdmModel model, IEdmVocabularyAnnotatable element)
{
return model.GetAnnotationValue(element, CoreVocabularyModel.LongDescriptionTerm);
}
private static string GetAnnotationValue(this IEdmModel model, IEdmVocabularyAnnotatable element,
IEdmTerm term)
{
if (model == null || element == null)
{
return null;
}
IEdmVocabularyAnnotation annotation =
model.FindVocabularyAnnotations<IEdmVocabularyAnnotation>(element, term).FirstOrDefault();
if (annotation != null)
{
IEdmStringConstantExpression stringConstant = annotation.Value as IEdmStringConstantExpression;
if (stringConstant != null)
{
return stringConstant.Value;
}
}
return null;
}
}
}

View file

@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.OData.Properties;
@ -89,58 +90,6 @@ namespace Microsoft.OpenApi.OData.Generator
}
}
/// <summary>
/// Create a <see cref="OpenApiSchema"/> for a <see cref="IEdmStructuredType"/>.
/// </summary>
/// <param name="context">The OData context.</param>
/// <param name="structuredType">The Edm structured type.</param>
/// <param name="processBase">The process the based.</param>
/// <returns>The created <see cref="OpenApiSchema"/>.</returns>
public static OpenApiSchema CreateStructuredTypeSchema(this ODataContext context, IEdmStructuredType structuredType, bool processBase)
{
if (processBase && structuredType.BaseType != null)
{
// A structured type with a base type is represented as a Schema Object
// that contains the keyword allOf whose value is an array with two items:
return new OpenApiSchema
{
AllOf = new List<OpenApiSchema>
{
// 1. a JSON Reference to the Schema Object of the base type
new OpenApiSchema
{
Reference = new OpenApiReference
{
Type = ReferenceType.Schema,
Id = structuredType.BaseType.FullTypeName()
}
},
// 2. a Schema Object describing the derived type
context.CreateStructuredTypeSchema(structuredType, false)
}
};
}
else
{
// A structured type without a base type is represented as a Schema Object of type object
return new OpenApiSchema
{
Title = (structuredType as IEdmSchemaElement).Name,
Type = "object",
// Each structural property and navigation property is represented
// as a name/value pair of the standard OpenAPI properties object.
Properties = context.CreateStructuredTypePropertiesSchema(structuredType),
// It optionally can contain the field description,
// whose value is the value of the unqualified annotation Core.Description of the structured type.
// However, ODL doesn't support the Core.Description on structure type.
};
}
}
/// <summary>
/// Create a <see cref="OpenApiSchema"/> for a <see cref="IEdmEnumType"/>.
/// An enumeration type is represented as a Schema Object of type string containing the OpenAPI Specification enum keyword.
@ -184,6 +133,27 @@ namespace Microsoft.OpenApi.OData.Generator
return schema;
}
/// <summary>
/// Create a <see cref="OpenApiSchema"/> for a <see cref="IEdmStructuredType"/>.
/// </summary>
/// <param name="context">The OData context.</param>
/// <param name="structuredType">The Edm structured type.</param>
/// <returns>The created <see cref="OpenApiSchema"/>.</returns>
public static OpenApiSchema CreateStructuredTypeSchema(this ODataContext context, IEdmStructuredType structuredType)
{
if (context == null)
{
throw Error.ArgumentNull(nameof(context));
}
if (structuredType == null)
{
throw Error.ArgumentNull(nameof(structuredType));
}
return context.CreateStructuredTypeSchema(structuredType, true);
}
// 4.6.1.1 Properties
public static IDictionary<string, OpenApiSchema> CreateStructuredTypePropertiesSchema(this ODataContext context, IEdmStructuredType structuredType)
{
@ -212,5 +182,73 @@ namespace Microsoft.OpenApi.OData.Generator
{
return typeDefinition?.UnderlyingType?.CreateSchema();
}
private static OpenApiSchema CreateStructuredTypeSchema(this ODataContext context, IEdmStructuredType structuredType, bool processBase)
{
Debug.Assert(context != null);
Debug.Assert(structuredType != null);
if (processBase && structuredType.BaseType != null)
{
// A structured type with a base type is represented as a Schema Object
// that contains the keyword allOf whose value is an array with two items:
return new OpenApiSchema
{
AllOf = new List<OpenApiSchema>
{
// 1. a JSON Reference to the Schema Object of the base type
new OpenApiSchema
{
Reference = new OpenApiReference
{
Type = ReferenceType.Schema,
Id = structuredType.BaseType.FullTypeName()
}
},
// 2. a Schema Object describing the derived type
context.CreateStructuredTypeSchema(structuredType, false)
},
AnyOf = null,
OneOf = null,
Properties = null
};
}
else
{
// A structured type without a base type is represented as a Schema Object of type object
OpenApiSchema schema = new OpenApiSchema
{
Title = (structuredType as IEdmSchemaElement)?.Name,
Type = "object",
// Each structural property and navigation property is represented
// as a name/value pair of the standard OpenAPI properties object.
Properties = context.CreateStructuredTypePropertiesSchema(structuredType),
// make others null
AllOf = null,
OneOf = null,
AnyOf = null
};
// It optionally can contain the field description,
// whose value is the value of the unqualified annotation Core.Description of the structured type.
if (structuredType.TypeKind == EdmTypeKind.Complex)
{
IEdmComplexType complex = (IEdmComplexType)structuredType;
schema.Description = context.Model.GetDescriptionAnnotation(complex);
}
else if (structuredType.TypeKind == EdmTypeKind.Entity)
{
IEdmEntityType entity = (IEdmEntityType)structuredType;
schema.Description = context.Model.GetDescriptionAnnotation(entity);
}
return schema;
}
}
}
}

View file

@ -46,7 +46,7 @@ namespace Microsoft.OpenApi.OData.Generator
tags.Add(new OpenApiTag
{
Name = entitySet.Name,
Description = context.Model.GetDescription(entitySet)
Description = context.Model.GetDescriptionAnnotation(entitySet)
});
break;
@ -55,7 +55,7 @@ namespace Microsoft.OpenApi.OData.Generator
tags.Add(new OpenApiTag
{
Name = singleton.Name,
Description = context.Model.GetDescription(singleton)
Description = context.Model.GetDescriptionAnnotation(singleton)
});
break;
@ -95,7 +95,7 @@ namespace Microsoft.OpenApi.OData.Generator
return new OpenApiTag
{
Name = operationImport.Name,
Description = context.Model.GetDescription(operationImport)
Description = context.Model.GetDescriptionAnnotation(operationImport)
};
}
}

View file

@ -129,7 +129,12 @@ namespace Microsoft.OpenApi.OData.Generator
}
// Spec has different configure for double, AnyOf or OneOf?
OpenApiSchema schema = new OpenApiSchema();
OpenApiSchema schema = new OpenApiSchema
{
AllOf = null,
OneOf = null,
AnyOf = null
};
switch (primitiveType.PrimitiveKind)
{
case EdmPrimitiveTypeKind.Binary: // binary

View file

@ -8,16 +8,24 @@ using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Csdl;
using Microsoft.OData.Edm.Vocabularies;
using Microsoft.OData.Edm.Vocabularies.V1;
using Xunit;
using System.IO;
using System.Xml;
using System.Collections.Generic;
using Microsoft.OData.Edm.Validation;
using Xunit.Abstractions;
namespace Microsoft.OpenApi.OData.Tests
{
/// <summary>
/// Edm model helpers
/// </summary>
public static class EdmModelHelper
public class EdmModelHelper
{
public static IEdmModel EmptyModel { get; } = new EdmModel();
public static IEdmModel MultipleInheritanceEdmModel { get; }
public static IEdmModel BasicEdmModel { get; }
public static IEdmModel CompositeKeyModel { get; }
@ -26,6 +34,7 @@ namespace Microsoft.OpenApi.OData.Tests
static EdmModelHelper()
{
MultipleInheritanceEdmModel = CreateMultipleInheritanceEdmModel();
BasicEdmModel = CreateEdmModel();
CompositeKeyModel = CreateCompositeKeyModel();
TripServiceModel = LoadTripServiceModel();
@ -37,6 +46,112 @@ namespace Microsoft.OpenApi.OData.Tests
return CsdlReader.Parse(XElement.Parse(csdl).CreateReader());
}
private static IEdmModel CreateMultipleInheritanceEdmModel()
{
var model = new EdmModel();
// enum type
var colorEnumType = new EdmEnumType("NS", "Color");
colorEnumType.AddMember("Blue", new EdmEnumMemberValue(0));
colorEnumType.AddMember("While", new EdmEnumMemberValue(1));
colorEnumType.AddMember("Red", new EdmEnumMemberValue(2));
colorEnumType.AddMember("Yellow", new EdmEnumMemberValue(3));
model.AddElement(colorEnumType);
var oceanEnumType = new EdmEnumType("NS", "Ocean");
oceanEnumType.AddMember("Atlantic", new EdmEnumMemberValue(0));
oceanEnumType.AddMember("Pacific", new EdmEnumMemberValue(1));
oceanEnumType.AddMember("India", new EdmEnumMemberValue(2));
oceanEnumType.AddMember("Arctic", new EdmEnumMemberValue(3));
model.AddElement(oceanEnumType);
var continentEnumType = new EdmEnumType("NS", "Continent");
continentEnumType.AddMember("Asia", new EdmEnumMemberValue(0));
continentEnumType.AddMember("Europe", new EdmEnumMemberValue(1));
continentEnumType.AddMember("Antarctica", new EdmEnumMemberValue(2));
model.AddElement(continentEnumType);
// top level entity type
var zoo = new EdmEntityType("NS", "Zoo");
var zooId = zoo.AddStructuralProperty("Id", EdmCoreModel.Instance.GetInt32(false));
zoo.AddKeys(zooId);
model.AddElement(zoo);
// abstract entity type "Creature"
var creature = new EdmEntityType("NS", "Creature", null, true, true);
creature.AddKeys(creature.AddStructuralProperty("Id", EdmCoreModel.Instance.GetInt32(false)));
model.AddElement(creature);
var animal = new EdmEntityType("NS", "Animal", creature, true, true);
animal.AddStructuralProperty("Age", EdmCoreModel.Instance.GetInt32(false));
model.AddElement(animal);
var human = new EdmEntityType("NS", "Human", animal, false, true);
human.AddStructuralProperty("Name", EdmCoreModel.Instance.GetString(false));
model.AddElement(human);
var horse = new EdmEntityType("NS", "Horse", animal, false, true);
horse.AddStructuralProperty("Height", EdmCoreModel.Instance.GetDecimal(false));
model.AddElement(horse);
EdmNavigationPropertyInfo navInfo = new EdmNavigationPropertyInfo
{
Name = "Creatures",
Target = creature,
TargetMultiplicity = EdmMultiplicity.Many,
};
zoo.AddUnidirectionalNavigation(navInfo);
// complex type
var plant = new EdmComplexType("NS", "Plant", null, true, true);
plant.AddStructuralProperty("Color", new EdmEnumTypeReference(colorEnumType, isNullable: false));
model.AddElement(plant);
// ocean plant
var oceanPlant = new EdmComplexType("NS", "OceanPlant", plant, true, true);
oceanPlant.AddStructuralProperty("Ocean", new EdmEnumTypeReference(oceanEnumType, isNullable: false));
model.AddElement(oceanPlant);
var kelp = new EdmComplexType("NS", "Kelp", oceanPlant, false, true);
kelp.AddStructuralProperty("Length", EdmCoreModel.Instance.GetDouble(false));
model.AddElement(kelp);
// land plant
var landPlant = new EdmComplexType("NS", "LandPlant", plant, true, true);
landPlant.AddStructuralProperty("Continent", new EdmEnumTypeReference(continentEnumType, isNullable: false));
landPlant.AddStructuralProperty("Name", EdmCoreModel.Instance.GetString(false));
model.AddElement(landPlant);
var tree = new EdmComplexType("NS", "Tree", landPlant, false, true);
tree.AddStructuralProperty("Price", EdmCoreModel.Instance.GetDecimal(false));
model.AddElement(tree);
var flower = new EdmComplexType("NS", "Flower", landPlant, false, true);
flower.AddStructuralProperty("Height", EdmCoreModel.Instance.GetDouble(false));
model.AddElement(flower);
// address
var address = new EdmComplexType("NS", "Address");
address.AddStructuralProperty("Street", EdmCoreModel.Instance.GetString(true));
address.AddStructuralProperty("City", EdmCoreModel.Instance.GetString(true));
model.AddElement(address);
var coreDescription = CoreVocabularyModel.DescriptionTerm;
var annotation = new EdmVocabularyAnnotation(address, coreDescription, new EdmStringConstant("Complex type 'Address' description."));
model.AddVocabularyAnnotation(annotation);
annotation = new EdmVocabularyAnnotation(tree, coreDescription, new EdmStringConstant("Complex type 'Tree' description."));
model.AddVocabularyAnnotation(annotation);
annotation = new EdmVocabularyAnnotation(zoo, coreDescription, new EdmStringConstant("Entity type 'Zoo' description."));
model.AddVocabularyAnnotation(annotation);
annotation = new EdmVocabularyAnnotation(human, coreDescription, new EdmStringConstant("Entity type 'Human' description."));
model.AddVocabularyAnnotation(annotation);
return model;
}
private static IEdmModel CreateEdmModel()
{
var model = new EdmModel();
@ -133,5 +248,128 @@ namespace Microsoft.OpenApi.OData.Tests
model.AddElement(container);
return model;
}
private ITestOutputHelper _output;
public EdmModelHelper(ITestOutputHelper output)
{
_output = output;
}
[Fact]
public void MultipleInheritanceEdmModelMetadataDocumentTest()
{
// Arrange
string expected = @"<?xml version=""1.0"" encoding=""utf-16""?>
<edmx:Edmx Version=""4.0"" xmlns:edmx=""http://docs.oasis-open.org/odata/ns/edmx"">
<edmx:DataServices>
<Schema Namespace=""NS"" xmlns=""http://docs.oasis-open.org/odata/ns/edm"">
<EnumType Name=""Color"">
<Member Name=""Blue"" Value=""0"" />
<Member Name=""While"" Value=""1"" />
<Member Name=""Red"" Value=""2"" />
<Member Name=""Yellow"" Value=""3"" />
</EnumType>
<EnumType Name=""Ocean"">
<Member Name=""Atlantic"" Value=""0"" />
<Member Name=""Pacific"" Value=""1"" />
<Member Name=""India"" Value=""2"" />
<Member Name=""Arctic"" Value=""3"" />
</EnumType>
<EnumType Name=""Continent"">
<Member Name=""Asia"" Value=""0"" />
<Member Name=""Europe"" Value=""1"" />
<Member Name=""Antarctica"" Value=""2"" />
</EnumType>
<EntityType Name=""Zoo"">
<Key>
<PropertyRef Name=""Id"" />
</Key>
<Property Name=""Id"" Type=""Edm.Int32"" Nullable=""false"" />
<NavigationProperty Name=""Creatures"" Type=""Collection(NS.Creature)"" />
</EntityType>
<EntityType Name=""Creature"" Abstract=""true"" OpenType=""true"">
<Key>
<PropertyRef Name=""Id"" />
</Key>
<Property Name=""Id"" Type=""Edm.Int32"" Nullable=""false"" />
</EntityType>
<EntityType Name=""Animal"" BaseType=""NS.Creature"" Abstract=""true"" OpenType=""true"">
<Property Name=""Age"" Type=""Edm.Int32"" Nullable=""false"" />
</EntityType>
<EntityType Name=""Human"" BaseType=""NS.Animal"" OpenType=""true"">
<Property Name=""Name"" Type=""Edm.String"" Nullable=""false"" />
</EntityType>
<EntityType Name=""Horse"" BaseType=""NS.Animal"" OpenType=""true"">
<Property Name=""Height"" Type=""Edm.Decimal"" Nullable=""false"" />
</EntityType>
<ComplexType Name=""Plant"" Abstract=""true"" OpenType=""true"">
<Property Name=""Color"" Type=""NS.Color"" Nullable=""false"" />
</ComplexType>
<ComplexType Name=""OceanPlant"" BaseType=""NS.Plant"" Abstract=""true"" OpenType=""true"">
<Property Name=""Ocean"" Type=""NS.Ocean"" Nullable=""false"" />
</ComplexType>
<ComplexType Name=""Kelp"" BaseType=""NS.OceanPlant"" OpenType=""true"">
<Property Name=""Length"" Type=""Edm.Double"" Nullable=""false"" />
</ComplexType>
<ComplexType Name=""LandPlant"" BaseType=""NS.Plant"" Abstract=""true"" OpenType=""true"">
<Property Name=""Continent"" Type=""NS.Continent"" Nullable=""false"" />
<Property Name=""Name"" Type=""Edm.String"" Nullable=""false"" />
</ComplexType>
<ComplexType Name=""Tree"" BaseType=""NS.LandPlant"" OpenType=""true"">
<Property Name=""Price"" Type=""Edm.Decimal"" Nullable=""false"" />
</ComplexType>
<ComplexType Name=""Flower"" BaseType=""NS.LandPlant"" OpenType=""true"">
<Property Name=""Height"" Type=""Edm.Double"" Nullable=""false"" />
</ComplexType>
<ComplexType Name=""Address"">
<Property Name=""Street"" Type=""Edm.String"" />
<Property Name=""City"" Type=""Edm.String"" />
</ComplexType>
<Annotations Target=""NS.Address"">
<Annotation Term=""Org.OData.Core.V1.Description"" String=""Complex type 'Address' description."" />
</Annotations>
<Annotations Target=""NS.Tree"">
<Annotation Term=""Org.OData.Core.V1.Description"" String=""Complex type 'Tree' description."" />
</Annotations>
<Annotations Target=""NS.Zoo"">
<Annotation Term=""Org.OData.Core.V1.Description"" String=""Entity type 'Zoo' description."" />
</Annotations>
<Annotations Target=""NS.Human"">
<Annotation Term=""Org.OData.Core.V1.Description"" String=""Entity type 'Human' description."" />
</Annotations>
</Schema>
</edmx:DataServices>
</edmx:Edmx>";
// Act
string actual = GetCsdl(MultipleInheritanceEdmModel);
_output.WriteLine(actual);
// Assert
Assert.Equal(expected, actual);
}
private string GetCsdl(IEdmModel model)
{
string edmx = string.Empty;
using (StringWriter sw = new StringWriter())
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = System.Text.Encoding.UTF8;
settings.Indent = true;
using (XmlWriter xw = XmlWriter.Create(sw, settings))
{
IEnumerable<EdmError> errors;
CsdlWriter.TryWriteCsdl(model, xw, CsdlTarget.OData, out errors);
xw.Flush();
}
edmx = sw.ToString();
}
return edmx;
}
}
}

View file

@ -3,23 +3,13 @@
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System;
using System.IO;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.Models;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.OpenApi.OData.Tests
{
public class EdmModelExtensionsTests
public class EdmModelExtensionsTest
{
private ITestOutputHelper _output;
public EdmModelExtensionsTests(ITestOutputHelper output)
{
_output = output;
}
[Fact]
public void GetDescriptionReturnsNullForElementWithoutCoreDescription()
{
@ -29,7 +19,7 @@ namespace Microsoft.OpenApi.OData.Tests
Assert.NotNull(me); // Guard
// Act
string description = model.GetDescription(me);
string description = model.GetDescriptionAnnotation(me);
// Assert
Assert.Null(description);
@ -44,7 +34,7 @@ namespace Microsoft.OpenApi.OData.Tests
Assert.NotNull(people); // Guard
// Act
string description = model.GetDescription(people);
string description = model.GetDescriptionAnnotation(people);
// Assert
Assert.Equal("People's description.", description);
@ -59,7 +49,7 @@ namespace Microsoft.OpenApi.OData.Tests
Assert.NotNull(people); // Guard
// Act
string description = model.GetLongDescription(people);
string description = model.GetLongDescriptionAnnotation(people);
// Assert
Assert.Equal("People's Long description.", description);

View file

@ -8,6 +8,7 @@ using System.Linq;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.OData.Generator;
using Xunit;
using Xunit.Abstractions;
@ -32,9 +33,273 @@ namespace Microsoft.OpenApi.OData.Tests
Assert.Throws<ArgumentNullException>("context", () => context.CreateSchemas());
}
#region StructuredTypeSchema
[Fact]
public void CreateStructuredTypeSchemaThrowArgumentNullContext()
{
// Arrange
ODataContext context = null;
// Act & Assert
Assert.Throws<ArgumentNullException>("context", () => context.CreateStructuredTypeSchema(structuredType: null));
}
[Fact]
public void CreateStructuredTypeSchemaThrowArgumentNullEnumType()
{
// Arrange
ODataContext context = new ODataContext(EdmCoreModel.Instance);
// Act & Assert
Assert.Throws<ArgumentNullException>("structuredType", () => context.CreateStructuredTypeSchema(structuredType: null));
}
[Fact]
public void CreateComplexTypeWithoutBaseSchemaReturnCorrectSchema()
{
// Arrange
IEdmModel model = EdmModelHelper.MultipleInheritanceEdmModel;
ODataContext context = new ODataContext(model);
IEdmComplexType complex = model.SchemaElements.OfType<IEdmComplexType>().First(t => t.Name == "Address");
Assert.NotNull(complex); // Guard
// Act
var schema = context.CreateStructuredTypeSchema(complex);
// Assert
Assert.NotNull(schema);
Assert.Equal("object", schema.Type);
Assert.Null(schema.AllOf);
Assert.NotNull(schema.Properties);
Assert.Equal(2, schema.Properties.Count);
Assert.Equal(new string[] { "Street", "City" }, schema.Properties.Select(e => e.Key));
Assert.Equal("Complex type 'Address' description.", schema.Description);
Assert.Equal("Address", schema.Title);
// Act
string json = schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0_0);
// Assert
Assert.NotNull(json);
Assert.Equal(@"{
""title"": ""Address"",
""type"": ""object"",
""properties"": {
""Street"": {
""type"": ""string"",
""nullable"": true
},
""City"": {
""type"": ""string"",
""nullable"": true
}
},
""description"": ""Complex type 'Address' description.""
}"
.Replace(), json);
}
[Fact]
public void CreateComplexTypeWithBaseSchemaReturnCorrectSchema()
{
// Arrange
IEdmModel model = EdmModelHelper.MultipleInheritanceEdmModel;
ODataContext context = new ODataContext(model);
IEdmComplexType complex = model.SchemaElements.OfType<IEdmComplexType>().First(t => t.Name == "Tree");
Assert.NotNull(complex); // Guard
// Act
var schema = context.CreateStructuredTypeSchema(complex);
// Assert
Assert.NotNull(schema);
Assert.True(String.IsNullOrEmpty(schema.Type));
Assert.NotNull(schema.AllOf);
Assert.Null(schema.AnyOf);
Assert.Null(schema.OneOf);
Assert.Null(schema.Properties);
Assert.Equal(2, schema.AllOf.Count);
var baseSchema = schema.AllOf.First();
Assert.NotNull(baseSchema.Reference);
Assert.Equal(ReferenceType.Schema, baseSchema.Reference.Type);
Assert.Equal("NS.LandPlant", baseSchema.Reference.Id);
var declaredSchema = schema.AllOf.Last();
Assert.Equal("object", declaredSchema.Type);
Assert.Null(declaredSchema.AllOf);
Assert.Null(declaredSchema.AnyOf);
Assert.Null(declaredSchema.OneOf);
Assert.NotNull(declaredSchema.Properties);
Assert.Equal(1, declaredSchema.Properties.Count);
var property = Assert.Single(declaredSchema.Properties);
Assert.Equal("Price", property.Key);
Assert.Equal("decimal", property.Value.Format);
Assert.NotNull(property.Value.OneOf);
Assert.Equal(new string[] { "number", "string" }, property.Value.OneOf.Select(e => e.Type));
Assert.Equal("Complex type 'Tree' description.", declaredSchema.Description);
Assert.Equal("Tree", declaredSchema.Title);
// Act
string json = schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0_0);
// Assert
Assert.NotNull(json);
Assert.Equal(@"{
""allOf"": [
{
""$ref"": ""#/components/schemas/NS.LandPlant""
},
{
""title"": ""Tree"",
""type"": ""object"",
""properties"": {
""Price"": {
""multipleOf"": 1,
""oneOf"": [
{
""type"": ""number""
},
{
""type"": ""string""
}
],
""format"": ""decimal""
}
},
""description"": ""Complex type 'Tree' description.""
}
]
}"
.Replace(), json);
}
[Fact]
public void CreateEntityTypeWithoutBaseSchemaReturnCorrectSchema()
{
// Arrange
IEdmModel model = EdmModelHelper.MultipleInheritanceEdmModel;
ODataContext context = new ODataContext(model);
IEdmEntityType entity = model.SchemaElements.OfType<IEdmEntityType>().First(t => t.Name == "Zoo");
Assert.NotNull(entity); // Guard
// Act
var schema = context.CreateStructuredTypeSchema(entity);
// Assert
Assert.NotNull(schema);
Assert.Equal("object", schema.Type);
Assert.Null(schema.AllOf);
Assert.NotNull(schema.Properties);
Assert.Equal(2, schema.Properties.Count);
Assert.Equal(new string[] { "Id", "Creatures" }, schema.Properties.Select(e => e.Key));
Assert.Equal("Entity type 'Zoo' description.", schema.Description);
Assert.Equal("Zoo", schema.Title);
// Act
string json = schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0_0);
// Assert
Assert.NotNull(json);
Assert.Equal(@"{
""title"": ""Zoo"",
""type"": ""object"",
""properties"": {
""Id"": {
""maximum"": 2147483647,
""minimum"": -2147483648,
""type"": ""integer"",
""format"": ""int32""
},
""Creatures"": {
""type"": ""array"",
""items"": {
""$ref"": ""#/components/schemas/NS.Creature""
}
}
},
""description"": ""Entity type 'Zoo' description.""
}"
.Replace(), json);
}
[Fact]
public void CreateEntityTypeWithBaseSchemaReturnCorrectSchema()
{
// Arrange
IEdmModel model = EdmModelHelper.MultipleInheritanceEdmModel;
ODataContext context = new ODataContext(model);
IEdmEntityType entity = model.SchemaElements.OfType<IEdmEntityType>().First(t => t.Name == "Human");
Assert.NotNull(entity); // Guard
// Act
var schema = context.CreateStructuredTypeSchema(entity);
// Assert
Assert.NotNull(schema);
Assert.True(String.IsNullOrEmpty(schema.Type));
Assert.NotNull(schema.AllOf);
Assert.Null(schema.AnyOf);
Assert.Null(schema.OneOf);
Assert.Null(schema.Properties);
Assert.Equal(2, schema.AllOf.Count);
var baseSchema = schema.AllOf.First();
Assert.NotNull(baseSchema.Reference);
Assert.Equal(ReferenceType.Schema, baseSchema.Reference.Type);
Assert.Equal("NS.Animal", baseSchema.Reference.Id);
var declaredSchema = schema.AllOf.Last();
Assert.Equal("object", declaredSchema.Type);
Assert.Null(declaredSchema.AllOf);
Assert.Null(declaredSchema.AnyOf);
Assert.Null(declaredSchema.OneOf);
Assert.NotNull(declaredSchema.Properties);
Assert.Equal(1, declaredSchema.Properties.Count);
var property = Assert.Single(declaredSchema.Properties);
Assert.Equal("Name", property.Key);
Assert.Equal("string", property.Value.Type);
Assert.Null(property.Value.OneOf);
Assert.Equal("Entity type 'Human' description.", declaredSchema.Description);
Assert.Equal("Human", declaredSchema.Title);
// Act
string json = schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0_0);
_output.WriteLine(json);
// Assert
Assert.NotNull(json);
Assert.Equal(@"{
""allOf"": [
{
""$ref"": ""#/components/schemas/NS.Animal""
},
{
""title"": ""Human"",
""type"": ""object"",
""properties"": {
""Name"": {
""type"": ""string""
}
},
""description"": ""Entity type 'Human' description.""
}
]
}"
.Replace(), json);
}
#endregion
#region EnumTypeSchema
[Fact]
public void CreateEnumTypeThrowArgumentNullContext()
public void CreateEnumTypeSchemaThrowArgumentNullContext()
{
// Arrange
ODataContext context = null;
@ -44,7 +309,7 @@ namespace Microsoft.OpenApi.OData.Tests
}
[Fact]
public void CreateEnumTypeThrowArgumentNullEnumType()
public void CreateEnumTypeSchemaThrowArgumentNullEnumType()
{
// Arrange
ODataContext context = new ODataContext(EdmCoreModel.Instance);
@ -54,7 +319,7 @@ namespace Microsoft.OpenApi.OData.Tests
}
[Fact]
public void CreateEnumTypeReturnCorrectSchema()
public void CreateEnumTypeSchemaReturnCorrectSchema()
{
// Arrange
IEdmModel model = EdmModelHelper.BasicEdmModel;
@ -78,7 +343,7 @@ namespace Microsoft.OpenApi.OData.Tests
// Act
string json = schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0_0);
// Act
// Assert
Assert.NotNull(json);
Assert.Equal(@"{
""title"": ""Color"",