Add model reference support to library and command line utility. (#81)

* Support many conversion options in the command-line tool.
Support local reference files in file-based csdl in the library and command-line tool.

* Added tests for references.

* Updates from github review.
This commit is contained in:
Gareth Jones 2021-01-14 16:56:26 -08:00 committed by GitHub
parent f6f26df6f2
commit c1aa5a6f94
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 498 additions and 42 deletions

View file

@ -162,7 +162,7 @@ namespace Microsoft.OpenApi.OData.Edm
}
/// <summary>
/// Check whether the operaiton is overload in the model.
/// Check whether the operation is overload in the model.
/// </summary>
/// <param name="model">The Edm model.</param>
/// <param name="operation">The test operations.</param>
@ -172,7 +172,7 @@ namespace Microsoft.OpenApi.OData.Edm
Utils.CheckArgumentNull(model, nameof(model));
Utils.CheckArgumentNull(operation, nameof(operation));
return model.SchemaElements.OfType<IEdmOperation>()
return model.GetAllElements().OfType<IEdmOperation>()
.Where(o => o.IsBound == operation.IsBound && o.FullName() == operation.FullName() &&
o.Parameters.First().Type.Definition == operation.Parameters.First().Type.Definition
).Count() > 1;
@ -197,5 +197,28 @@ namespace Microsoft.OpenApi.OData.Edm
return model.EntityContainer.OperationImports()
.Where(o => o.Operation.IsBound == operationImport.Operation.IsBound && o.Name == operationImport.Name).Count() > 1;
}
/// <summary>
/// Get all of the elements in the model and its referenced models.
/// </summary>
/// <returns>All the elements.</returns>
public static IEnumerable<IEdmSchemaElement> GetAllElements(this IEdmModel model)
{
foreach (var element in model.SchemaElements.Where(el =>
!ODataConstants.StandardNamespaces.Any(std => el.Namespace.StartsWith(std))))
{
yield return element;
}
foreach (var refModel in model.ReferencedModels)
{
foreach (var element in refModel.SchemaElements.Where(el =>
!ODataConstants.StandardNamespaces.Any(std => el.Namespace.StartsWith(std))))
{
yield return element;
}
}
}
}
}

View file

@ -71,7 +71,7 @@ namespace Microsoft.OpenApi.OData.Edm
{
IDictionary<string, IList<IEdmOperation>> edmOperationDict = new Dictionary<string, IList<IEdmOperation>>();
foreach (var edmOperation in Model.SchemaElements.OfType<IEdmOperation>().Where(e => e.IsBound))
foreach (var edmOperation in Model.GetAllElements().OfType<IEdmOperation>().Where(e => e.IsBound))
{
IEdmOperationParameter bindingParameter = edmOperation.Parameters.First();

View file

@ -294,7 +294,7 @@ namespace Microsoft.OpenApi.OData.Edm
/// </summary>
private void RetrieveBoundOperationPaths()
{
foreach (var edmOperation in _model.SchemaElements.OfType<IEdmOperation>().Where(e => e.IsBound))
foreach (var edmOperation in _model.GetAllElements().OfType<IEdmOperation>().Where(e => e.IsBound))
{
if (!CanFilter(edmOperation))
{

View file

@ -4,6 +4,7 @@
// ------------------------------------------------------------
using Microsoft.OData.Edm;
using Microsoft.OpenApi.OData.Edm;
namespace Microsoft.OpenApi.OData.Generator
{
@ -30,7 +31,7 @@ namespace Microsoft.OpenApi.OData.Generator
return;
}
foreach (var element in model.SchemaElements)
foreach (var element in model.GetAllElements())
{
switch (element.SchemaElementKind)
{

View file

@ -34,7 +34,11 @@ namespace Microsoft.OpenApi.OData.Generator
// Each entity type, complex type, enumeration type, and type definition directly
// or indirectly used in the paths field is represented as a name / value pair of the schemas map.
foreach (var element in context.Model.SchemaElements.Where(c => !c.Namespace.StartsWith("Org.OData.")))
// Ideally this would be driven off the types used in the paths, but in practice, it is simply
// all of the types present in the model.
IEnumerable<IEdmSchemaElement> elements = context.Model.GetAllElements();
foreach (var element in elements)
{
switch (element.SchemaElementKind)
{

View file

@ -38,7 +38,11 @@ namespace Microsoft.OpenApi.OData.Generator
// Each entity type, complex type, enumeration type, and type definition directly
// or indirectly used in the paths field is represented as a name / value pair of the schemas map.
foreach (var element in context.Model.SchemaElements.Where(c => !c.Namespace.StartsWith("Org.OData.")))
// Ideally this would be driven off the types used in the paths, but in practice, it is simply
// all of the types present in the model.
IEnumerable<IEdmSchemaElement> elements = context.Model.GetAllElements();
foreach (var element in elements)
{
switch (element.SchemaElementKind)
{

View file

@ -0,0 +1,27 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.OpenApi.OData
{
internal static class ODataConstants
{
/// <summary>
/// Namespaces used in standard included models.
/// </summary>
public static IList<string> StandardNamespaces = new List<string>
{
"Org.OData.",
"Edm",
"OData.Community.",
};
}
}

View file

@ -45,6 +45,11 @@ namespace OoasUtil
/// </summary>
public OpenApiFormat? Format { get; private set; }
/// <summary>
/// Whether KeyAsSegment is used.
/// </summary>
public bool? KeyAsSegment { get; private set; }
/// <summary>
/// Output OpenApi Specification Version.
/// </summary>
@ -60,6 +65,31 @@ namespace OoasUtil
/// </summary>
public bool IsLocalFile { get; private set; }
/// <summary>
/// Set the output to produce all derived types in responses.
/// </summary>
public bool? DerivedTypesReferencesForResponses { get; private set; }
/// <summary>
/// Set the output to expect all derived types in request bodies.
/// </summary>
public bool? DerivedTypesReferencesForRequestBody { get; private set; }
/// <summary>
/// Set the output to expose pagination for collections.
/// </summary>
public bool? EnablePagination { get; private set; }
/// <summary>
/// tSet the output to use unqualified calls for bound operations.
/// </summary>
public bool? EnableUnqualifiedCall { get; private set; }
/// <summary>
/// tDisable examples in the schema.
/// </summary>
public bool? DisableSchemaExamples { get; private set; }
/// <summary>
/// Process the arguments.
/// </summary>
@ -98,6 +128,14 @@ namespace OoasUtil
i++;
break;
case "--keyassegment":
case "-k":
if (!ProcessKeyAsSegment(true))
{
return false;
}
break;
case "--yaml":
case "-y":
if (!ProcessTarget(OpenApiFormat.Yaml))
@ -132,6 +170,46 @@ namespace OoasUtil
i++;
break;
case "--derivedtypesreferencesforresponses":
case "-drs":
if (!ProcessDerivedTypesReferencesForResponses(true))
{
return false;
}
break;
case "--derivedtypesreferencesforrequestbody":
case "-drq":
if (!ProcessDerivedTypesReferencesForRequestBody(true))
{
return false;
}
break;
case "--enablepagination":
case "-p":
if (!ProcessEnablePagination(true))
{
return false;
}
break;
case "--enableunqualifiedcall":
case "-u":
if (!ProcessEnableUnqualifiedCall(true))
{
return false;
}
break;
case "--disableschemaexamples":
case "-x":
if (!ProcessDisableSchemaExamples(true))
{
return false;
}
break;
default:
PrintUsage();
return false;
@ -155,6 +233,36 @@ namespace OoasUtil
Version = OpenApiSpecVersion.OpenApi3_0;
}
if (KeyAsSegment == null)
{
KeyAsSegment = false;
}
if (DerivedTypesReferencesForResponses == null)
{
DerivedTypesReferencesForResponses = false;
}
if (DerivedTypesReferencesForRequestBody == null)
{
DerivedTypesReferencesForRequestBody = false;
}
if (EnablePagination == null)
{
EnablePagination = false;
}
if (EnableUnqualifiedCall == null)
{
EnableUnqualifiedCall = false;
}
if (DisableSchemaExamples == null)
{
DisableSchemaExamples = false;
}
_continue = ValidateArguments();
return _continue;
}
@ -198,6 +306,84 @@ namespace OoasUtil
return true;
}
private bool ProcessKeyAsSegment(bool keyAsSegment)
{
if (KeyAsSegment != null)
{
Console.WriteLine("[Error:] Multiple [--keyassegment|-k] are not allowed.\n");
PrintUsage();
return false;
}
KeyAsSegment = keyAsSegment;
return true;
}
private bool ProcessDerivedTypesReferencesForResponses(bool derivedTypesReferencesForResponses)
{
if (DerivedTypesReferencesForResponses != null)
{
Console.WriteLine("[Error:] Multiple [--derivedtypesreferencesforresponses|-drs] are not allowed.\n");
PrintUsage();
return false;
}
DerivedTypesReferencesForResponses = derivedTypesReferencesForResponses;
return true;
}
private bool ProcessDerivedTypesReferencesForRequestBody(bool derivedTypesReferencesForRequestBody)
{
if (DerivedTypesReferencesForRequestBody != null)
{
Console.WriteLine("[Error:] Multiple [--derivedtypesreferencesforrequestbody|-drq] are not allowed.\n");
PrintUsage();
return false;
}
DerivedTypesReferencesForRequestBody = derivedTypesReferencesForRequestBody;
return true;
}
private bool ProcessEnablePagination(bool enablePagination)
{
if (EnablePagination != null)
{
Console.WriteLine("[Error:] Multiple [--enablepagination|-p] are not allowed.\n");
PrintUsage();
return false;
}
EnablePagination = enablePagination;
return true;
}
private bool ProcessEnableUnqualifiedCall(bool enableUnqualifiedCall)
{
if (EnableUnqualifiedCall != null)
{
Console.WriteLine("[Error:] Multiple [--enableunqualifiedcall|-u] are not allowed.\n");
PrintUsage();
return false;
}
EnableUnqualifiedCall = enableUnqualifiedCall;
return true;
}
private bool ProcessDisableSchemaExamples(bool disableSchemaExamples)
{
if (DisableSchemaExamples != null)
{
Console.WriteLine("[Error:] Multiple [--disableschemaexamples|-x] are not allowed.\n");
PrintUsage();
return false;
}
DisableSchemaExamples = disableSchemaExamples;
return true;
}
private bool ProcessTarget(int version)
{
if (Version != null)
@ -256,6 +442,12 @@ namespace OoasUtil
sb.Append(" --version|-v\t\t\tDisplay version.\n");
sb.Append(" --input|-i CsdlFileOrUrl\tSet the CSDL file name or the OData Service Url.\n");
sb.Append(" --output|-o OutputFile\tSet the output file name.\n");
sb.Append(" --keyassegment|-k\t\t\tSet the output to use key-as-segment style URLs.\n");
sb.Append(" --derivedtypesreferencesforresponses|-drs\t\t\tSet the output to produce all derived types in responses.\n");
sb.Append(" --derivedtypesreferencesforrequestbody|-drq\t\t\tSet the output to expect all derived types in request bodies.\n");
sb.Append(" --enablepagination|-p\t\t\tSet the output to expose pagination for collections.\n");
sb.Append(" --enableunqualifiedcall|-u\t\t\tSet the output to use unqualified calls for bound operations.\n");
sb.Append(" --disableschemaexamples|-x\t\t\tDisable examples in the schema.\n");
sb.Append(" --json|-j\t\t\tSet the output format as JSON.\n");
sb.Append(" --yaml|-y\t\t\tSet the output format as YAML.\n");
sb.Append(" --specversion|-s IntVersion\tSet the OpenApi Specification version of the output. Only 2 or 3 are supported.\n");

View file

@ -6,10 +6,13 @@
using System;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Csdl;
using Microsoft.OpenApi;
using Microsoft.OpenApi.OData;
namespace OoasUtil
{
@ -28,9 +31,10 @@ namespace OoasUtil
/// </summary>
/// <param name="input">The input.</param>
/// <param name="output">The output.</param>
/// <param name="target">The output target.</param>
public FileOpenApiGenerator(string input, string output, OpenApiFormat format, OpenApiSpecVersion version)
: base(output, format, version)
/// <param name="format">The format.</param>
/// <param name="settings">Conversion settings.</param>
public FileOpenApiGenerator(string input, string output, OpenApiFormat format, OpenApiConvertSettings settings)
: base(output, format, settings)
{
Input = input;
}
@ -43,7 +47,32 @@ namespace OoasUtil
try
{
string csdl = File.ReadAllText(Input);
return CsdlReader.Parse(XElement.Parse(csdl).CreateReader());
var directory = Path.GetDirectoryName(Input);
var parsed = XElement.Parse(csdl);
using (XmlReader mainReader = parsed.CreateReader())
{
return CsdlReader.Parse(mainReader, u =>
{
// Currently only support relative paths
if (u.IsAbsoluteUri)
{
Console.WriteLine($"Referenced model must use relative paths to the main model.");
return null;
}
var file = Path.Combine(directory, u.OriginalString);
string referenceText = File.ReadAllText(file);
var referenceParsed = XElement.Parse(referenceText);
XmlReader referenceReader = referenceParsed.CreateReader();
return referenceReader;
});
}
}
catch (EdmParseException parseException)
{
Console.WriteLine("Failed to parse the CSDL file.");
Console.WriteLine(string.Join(Environment.NewLine, parseException.Errors.Select(e => e.ToString())));
return null;
}
catch (Exception e)
{

View file

@ -24,27 +24,27 @@ namespace OoasUtil
/// </summary>
public OpenApiFormat Format { get; }
/// <summary>
/// Output specification version.
/// </summary>
public OpenApiSpecVersion Version { get; }
/// <summary>
/// Output file.
/// </summary>
public string Output { get; }
/// <summary>
/// Settings to use for conversion.
/// </summary>
public OpenApiConvertSettings Settings { get; }
/// <summary>
/// Initializes a new instance of the <see cref="OpenApiGenerator"/> class.
/// </summary>
/// <param name="input">The input.</param>
/// <param name="output">The output.</param>
/// <param name="target">The output target.</param>
public OpenApiGenerator(string output, OpenApiFormat format, OpenApiSpecVersion version)
/// <param name="format">The output format.</param>
/// <param name="settings">Conversion settings.</param>
public OpenApiGenerator(string output, OpenApiFormat format, OpenApiConvertSettings settings)
{
Output = output;
Format = format;
Version = version;
Settings = settings;
}
/// <summary>
@ -56,14 +56,12 @@ namespace OoasUtil
{
IEdmModel edmModel = GetEdmModel();
OpenApiConvertSettings settings = GetSettings();
settings.OpenApiSpecVersion = Version;
this.ModifySettings();
using (FileStream fs = File.Create(Output))
{
OpenApiDocument document = edmModel.ConvertToOpenApi(settings);
document.Serialize(fs, settings.OpenApiSpecVersion, Format);
OpenApiDocument document = edmModel.ConvertToOpenApi(Settings);
document.Serialize(fs, Settings.OpenApiSpecVersion, Format);
fs.Flush();
}
}
@ -76,9 +74,8 @@ namespace OoasUtil
return true;
}
protected virtual OpenApiConvertSettings GetSettings()
protected virtual void ModifySettings()
{
return new OpenApiConvertSettings();
}
protected abstract IEdmModel GetEdmModel();

View file

@ -5,6 +5,8 @@
//---------------------------------------------------------------------
using System;
using System.Reflection;
using Microsoft.OpenApi.OData;
namespace OoasUtil
{
@ -29,13 +31,25 @@ namespace OoasUtil
}
OpenApiGenerator generator;
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
EnableKeyAsSegment = processer.KeyAsSegment,
EnableDerivedTypesReferencesForResponses = processer.DerivedTypesReferencesForResponses.Value,
EnableDerivedTypesReferencesForRequestBody = processer.DerivedTypesReferencesForRequestBody.Value,
EnablePagination = processer.EnablePagination.Value,
EnableUnqualifiedCall = processer.EnableUnqualifiedCall.Value,
ShowSchemaExamples = !processer.DisableSchemaExamples.Value,
OpenApiSpecVersion = processer.Version.Value,
};
if (processer.IsLocalFile)
{
generator = new FileOpenApiGenerator(processer.Input, processer.Output, processer.Format.Value, processer.Version.Value);
generator = new FileOpenApiGenerator(processer.Input, processer.Output, processer.Format.Value, settings);
}
else
{
generator = new UrlOpenApiGenerator(new Uri(processer.Input), processer.Output, processer.Format.Value, processer.Version.Value);
generator = new UrlOpenApiGenerator(new Uri(processer.Input), processer.Output, processer.Format.Value, settings);
}
if (generator.Generate())

View file

@ -25,14 +25,38 @@ Output the "JSON" format Open API document;
Output the "YAML" format Open API document;
### [--specversion|-s int]
### [--keyassegment|-k]
Indicate which version, either 2 or 3, of the OpenApi specification to output. Only 2 or 3 are supported;
Output the document using key-as-segment style URLs.;
### [--derivedtypesreferencesforresponses|-drs]
Output the document to expect all derived types in responses.;
### [--derivedtypesreferencesforrequestbody|-drq]
Output the document to expect all derived types in request bodies.;
### [--enablepagination|-p]
Output the document to expose pagination for collections.;
### [--enableunqualifiedcall|-u]
Output the document to use unqualified calls for bound operations.;
### [--disableschemaexamples|-x]
Output the document without examples in the schema.;
### [--yaml|-y]
Output the "YAML" format Open API document;
### [--specversion|-s int]
Indicate which version, either 2 or 3, of the OpenApi specification to output. Only 2 or 3 are supported;
### [--input|-i file]
Indicate to where to get CSDL, from file or from Uri.
@ -44,7 +68,7 @@ Indicate to output file name.
## Examples
`OoasUtil.exe -j -s 3 -i http://services.odata.org/TrippinRESTierService -o trip.json`
`OoasUtil.exe -j -k -drs -drq -p -u -s 3 -i http://services.odata.org/TrippinRESTierService -o trip.json`
The content of `trip.json` is similar at https://github.com/xuzhg/OData.OpenAPI/blob/master/Microsoft.OData.OpenAPI/Microsoft.OData.OpenAPI.Tests/Resources/TripService.OpenApi.json

View file

@ -31,9 +31,10 @@ namespace OoasUtil
/// </summary>
/// <param name="input">The input.</param>
/// <param name="output">The output.</param>
/// <param name="target">The output target.</param>
public UrlOpenApiGenerator(Uri input, string output, OpenApiFormat format, OpenApiSpecVersion version)
: base(output, format, version)
/// <param name="format">The format.</param>
/// <param name="settings">Conversion settings.</param>
public UrlOpenApiGenerator(Uri input, string output, OpenApiFormat format, OpenApiConvertSettings settings)
: base(output, format, settings)
{
Input = input;
}
@ -58,12 +59,10 @@ namespace OoasUtil
return CsdlReader.Parse(XElement.Parse(csdl).CreateReader());
}
protected override OpenApiConvertSettings GetSettings()
protected override void ModifySettings()
{
return new OpenApiConvertSettings
{
ServiceRoot = Input
};
base.ModifySettings();
Settings.ServiceRoot = Input;
}
}
}

View file

@ -3,6 +3,7 @@
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System;
using System.Xml.Linq;
using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Csdl;
@ -12,6 +13,8 @@ using Xunit;
using System.IO;
using System.Xml;
using System.Collections.Generic;
using System.Net;
using System.Text;
using Microsoft.OData.Edm.Validation;
using Xunit.Abstractions;
@ -26,6 +29,8 @@ namespace Microsoft.OpenApi.OData.Tests
public static IEdmModel MultipleInheritanceEdmModel { get; }
public static IEdmModel InheritanceEdmModelAcrossReferences { get; }
public static IEdmModel BasicEdmModel { get; }
public static IEdmModel MultipleSchemasEdmModel { get; }
@ -46,6 +51,7 @@ namespace Microsoft.OpenApi.OData.Tests
ContractServiceModel = LoadEdmModel("Contract.OData.xml");
GraphBetaModel = LoadEdmModel("Graph.Beta.OData.xml");
MultipleSchemasEdmModel = LoadEdmModel("Multiple.Schema.OData.xml");
InheritanceEdmModelAcrossReferences = CreateInheritanceEdmModelAcrossReferences();
}
private static IEdmModel LoadEdmModel(string source)
@ -160,6 +166,50 @@ namespace Microsoft.OpenApi.OData.Tests
return model;
}
public static IEdmModel CreateInheritanceEdmModelAcrossReferences()
{
const string modelText =
@"<edmx:Edmx Version=""4.0"" xmlns:edmx=""http://docs.oasis-open.org/odata/ns/edmx"">
<edmx:Reference Uri=""subModel.csdl"">
<edmx:Include Alias=""subModel"" Namespace=""SubNS"" />
</edmx:Reference>
<edmx:DataServices>
<Schema Namespace=""NS"" xmlns=""http://docs.oasis-open.org/odata/ns/edm"">
<EntityType Name=""Customer"" BaseType=""SubNS.CustomerBase"">
<Property Name=""Extra"" Type=""Edm.Int32"" Nullable=""false"" />
</EntityType>
<EntityContainer Name =""Default"">
<EntitySet Name=""Customers"" EntityType=""NS.Customer"" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>";
const string subModelText =
@"<edmx:Edmx Version=""4.0"" xmlns:edmx=""http://docs.oasis-open.org/odata/ns/edmx"">
<edmx:DataServices>
<Schema Namespace=""SubNS"" xmlns=""http://docs.oasis-open.org/odata/ns/edm"">
<EntityType Name=""CustomerBase"">
<Key>
<PropertyRef Name=""ID"" />
</Key>
<Property Name=""ID"" Type=""Edm.Int32"" Nullable=""false"" />
</EntityType>
</Schema>
</edmx:DataServices>
</edmx:Edmx>";
IEdmModel model;
IEnumerable<EdmError> errors;
XElement parsed = XElement.Parse(modelText);
bool result = CsdlReader.TryParse(parsed.CreateReader(),
uri => XElement.Parse(subModelText).CreateReader(),
out model,
out errors);
Assert.True(result);
return model;
}
private static IEdmModel CreateEdmModel()
{
var model = new EdmModel();
@ -359,12 +409,12 @@ namespace Microsoft.OpenApi.OData.Tests
public static string GetCsdl(IEdmModel model)
{
string edmx = string.Empty;
string edmx = String.Empty;
using (StringWriter sw = new StringWriter())
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = System.Text.Encoding.UTF8;
settings.Encoding = Encoding.UTF8;
settings.Indent = true;
using (XmlWriter xw = XmlWriter.Create(sw, settings))

View file

@ -3,8 +3,12 @@
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Csdl;
using Microsoft.OData.Edm.Validation;
using Microsoft.OpenApi.OData.Tests;
using Xunit;
@ -55,5 +59,21 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
Assert.Equal(2, baseTypes.Count());
Assert.Equal(new[] { subEntityType, baseEntityType }, baseTypes);
}
[Fact]
public void GetAllElementsReturnsElementsFromAllModels()
{
// Arrange
IEdmModel model = EdmModelHelper.InheritanceEdmModelAcrossReferences;
// Act
var elements = model.GetAllElements();
// Assert
Assert.Collection(elements,
e => Assert.Equal("Customer", e.Name),
e => Assert.Equal("Default", e.Name),
e => Assert.Equal("CustomerBase", e.Name));
}
}
}

View file

@ -335,6 +335,52 @@ namespace Microsoft.OpenApi.OData.Tests
}"
.ChangeLineBreaks(), json);
}
[Fact]
public void CreateEntityTypeWithCrossReferenceBaseSchemaReturnCorrectSchema()
{
// Arrange
IEdmModel model = EdmModelHelper.InheritanceEdmModelAcrossReferences;
ODataContext context = new ODataContext(model, new OpenApiConvertSettings
{
ShowSchemaExamples = true
});
IEdmEntityType entity = model.SchemaElements.OfType<IEdmEntityType>().First(t => t.Name == "Customer");
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("SubNS.CustomerBase", 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("Extra", property.Key);
Assert.Equal("integer", property.Value.Type);
Assert.Null(property.Value.OneOf);
Assert.Equal("Customer", declaredSchema.Title);
}
#endregion
#region EnumTypeSchema

View file

@ -9,6 +9,7 @@ using Microsoft.OData.Edm;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Properties;
using Microsoft.OpenApi.OData.Tests;
using Xunit;
namespace Microsoft.OpenApi.OData.PathItem.Tests
@ -75,6 +76,31 @@ namespace Microsoft.OpenApi.OData.PathItem.Tests
pathItem.Operations.Select(o => o.Key));
}
[Fact]
public void CreateEntityPathItemReturnsCorrectPathItemWithReferences()
{
// Test that references don't disturb the paths.
// Arrange
IEdmModel model = EdmModelHelper.InheritanceEdmModelAcrossReferences;
ODataContext context = new ODataContext(model);
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
Assert.NotNull(entitySet); // guard
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet), new ODataKeySegment(entitySet.EntityType()));
// Act
var pathItem = _pathItemHandler.CreatePathItem(context, path);
// Assert
Assert.NotNull(pathItem);
Assert.NotNull(pathItem.Operations);
Assert.NotEmpty(pathItem.Operations);
Assert.Equal(3, pathItem.Operations.Count);
Assert.Equal(new OperationType[] { OperationType.Get, OperationType.Patch, OperationType.Delete },
pathItem.Operations.Select(o => o.Key));
}
[Theory]
[InlineData(true, new OperationType[] { OperationType.Get, OperationType.Patch, OperationType.Delete })]
[InlineData(false, new OperationType[] { OperationType.Patch, OperationType.Delete })]