Fix paths when operation is bound to a type derived from the type of a

navigation property. (#88)
This commit is contained in:
Gareth Jones 2021-02-10 16:05:26 -08:00 committed by Sam Xu
parent 1643ba5ea1
commit e1763727f5
10 changed files with 405 additions and 28 deletions

4
.editorconfig Normal file
View file

@ -0,0 +1,4 @@
[*.{cs,vb}]
# IDE0009: Member access should be qualified.
dotnet_diagnostic.IDE0009.severity = none

View file

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio Version 16
VisualStudioVersion = 15.0.26730.3 VisualStudioVersion = 16.0.30907.101
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.OData.Reader", "src\Microsoft.OpenApi.OData.Reader\Microsoft.OpenApi.OData.Reader.csproj", "{FF3ACD93-19E0-486C-9C0F-FA1C2E7FC8C2}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.OData.Reader", "src\Microsoft.OpenApi.OData.Reader\Microsoft.OpenApi.OData.Reader.csproj", "{FF3ACD93-19E0-486C-9C0F-FA1C2E7FC8C2}"
EndProject EndProject
@ -11,6 +11,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OoasUtil", "src\OoasUtil\Oo
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OoasGui", "src\OoasGui\OoasGui.csproj", "{79B190E8-EDB0-4C03-8FD8-EB48E4807CFB}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OoasGui", "src\OoasGui\OoasGui.csproj", "{79B190E8-EDB0-4C03-8FD8-EB48E4807CFB}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{99C5C9A7-63FD-4E78-96E8-69C402868C3E}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU

View file

@ -24,7 +24,8 @@ namespace Microsoft.OpenApi.OData.Edm
/// Generate the list of <see cref="ODataPath"/> based on the given <see cref="IEdmModel"/>. /// Generate the list of <see cref="ODataPath"/> based on the given <see cref="IEdmModel"/>.
/// </summary> /// </summary>
/// <param name="model">The Edm model.</param> /// <param name="model">The Edm model.</param>
/// <param name="settings">The conversion settings.</param>
/// <returns>The collection of built <see cref="ODataPath"/>.</returns> /// <returns>The collection of built <see cref="ODataPath"/>.</returns>
IEnumerable<ODataPath> GetPaths(IEdmModel model); IEnumerable<ODataPath> GetPaths(IEdmModel model, OpenApiConvertSettings settings);
} }
} }

View file

@ -158,7 +158,7 @@ namespace Microsoft.OpenApi.OData.Edm
/// <returns>All acceptable OData path.</returns> /// <returns>All acceptable OData path.</returns>
private IEnumerable<ODataPath> LoadAllODataPaths() private IEnumerable<ODataPath> LoadAllODataPaths()
{ {
IEnumerable<ODataPath> allPaths = _pathProvider.GetPaths(Model); IEnumerable<ODataPath> allPaths = _pathProvider.GetPaths(Model, Settings);
foreach (var path in allPaths) foreach (var path in allPaths)
{ {
if ((path.Kind == ODataPathKind.Operation && !Settings.EnableOperationPath) || if ((path.Kind == ODataPathKind.Operation && !Settings.EnableOperationPath) ||

View file

@ -3,10 +3,12 @@
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. // Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------ // ------------------------------------------------------------
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using Microsoft.OData.Edm; using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Vocabularies;
namespace Microsoft.OpenApi.OData.Edm namespace Microsoft.OpenApi.OData.Edm
{ {
@ -38,8 +40,9 @@ namespace Microsoft.OpenApi.OData.Edm
/// Generate the list of <see cref="ODataPath"/> based on the given <see cref="IEdmModel"/>. /// Generate the list of <see cref="ODataPath"/> based on the given <see cref="IEdmModel"/>.
/// </summary> /// </summary>
/// <param name="model">The Edm model.</param> /// <param name="model">The Edm model.</param>
/// <param name="settings">The conversion settings.</param>
/// <returns>The collection of built <see cref="ODataPath"/>.</returns> /// <returns>The collection of built <see cref="ODataPath"/>.</returns>
public virtual IEnumerable<ODataPath> GetPaths(IEdmModel model) public virtual IEnumerable<ODataPath> GetPaths(IEdmModel model, OpenApiConvertSettings settings)
{ {
if (model == null || model.EntityContainer == null) if (model == null || model.EntityContainer == null)
{ {
@ -67,7 +70,7 @@ namespace Microsoft.OpenApi.OData.Edm
} }
// bound operations // bound operations
RetrieveBoundOperationPaths(); RetrieveBoundOperationPaths(settings);
// unbound operations // unbound operations
foreach (IEdmOperationImport import in _model.EntityContainer.OperationImports()) foreach (IEdmOperationImport import in _model.EntityContainer.OperationImports())
@ -338,7 +341,7 @@ namespace Microsoft.OpenApi.OData.Edm
/// <summary> /// <summary>
/// Retrieve all bounding <see cref="IEdmOperation"/>. /// Retrieve all bounding <see cref="IEdmOperation"/>.
/// </summary> /// </summary>
private void RetrieveBoundOperationPaths() private void RetrieveBoundOperationPaths(OpenApiConvertSettings convertSettings)
{ {
foreach (var edmOperation in _model.GetAllElements().OfType<IEdmOperation>().Where(e => e.IsBound)) foreach (var edmOperation in _model.GetAllElements().OfType<IEdmOperation>().Where(e => e.IsBound))
{ {
@ -396,10 +399,17 @@ namespace Microsoft.OpenApi.OData.Edm
} }
// 3. Search for derived // 3. Search for derived
if (AppendBoundOperationOnDerived(edmOperation, isCollection, bindingEntityType)) if (AppendBoundOperationOnDerived(edmOperation, isCollection, bindingEntityType, convertSettings))
{ {
continue; continue;
} }
// 4. Search for derived generated navigation property
if (AppendBoundOperationOnDerivedNavigationPropertyPath(edmOperation, isCollection, bindingEntityType, convertSettings))
{
continue;
}
} }
} }
} }
@ -477,7 +487,11 @@ namespace Microsoft.OpenApi.OData.Edm
return found; return found;
} }
private bool AppendBoundOperationOnDerived(IEdmOperation edmOperation, bool isCollection, IEdmEntityType bindingEntityType) private bool AppendBoundOperationOnDerived(
IEdmOperation edmOperation,
bool isCollection,
IEdmEntityType bindingEntityType,
OpenApiConvertSettings convertSettings)
{ {
bool found = false; bool found = false;
@ -488,6 +502,14 @@ namespace Microsoft.OpenApi.OData.Edm
{ {
foreach (var ns in baseNavigationSource) foreach (var ns in baseNavigationSource)
{ {
if (HasUnsatisfiedDerivedTypeConstraint(
ns as IEdmVocabularyAnnotatable,
baseType,
convertSettings))
{
continue;
}
if (isCollection) if (isCollection)
{ {
if (ns is IEdmEntitySet) if (ns is IEdmEntitySet)
@ -523,5 +545,84 @@ namespace Microsoft.OpenApi.OData.Edm
return found; return found;
} }
private bool HasUnsatisfiedDerivedTypeConstraint(
IEdmVocabularyAnnotatable annotatable,
IEdmEntityType baseType,
OpenApiConvertSettings convertSettings)
{
return convertSettings.RequireDerivedTypesConstraintForBoundOperations &&
!(_model.GetCollection(annotatable, "Org.OData.Validation.V1.DerivedTypeConstraint") ?? Enumerable.Empty<string>())
.Any(c => c.Equals(baseType.FullName(), StringComparison.OrdinalIgnoreCase));
}
private bool AppendBoundOperationOnDerivedNavigationPropertyPath(
IEdmOperation edmOperation,
bool isCollection,
IEdmEntityType bindingEntityType,
OpenApiConvertSettings convertSettings)
{
bool found = false;
bool isEscapedFunction = _model.IsUrlEscapeFunction(edmOperation);
foreach (var baseType in bindingEntityType.FindAllBaseTypes())
{
if (_allNavigationPropertyPaths.TryGetValue(baseType, out IList<ODataPath> paths))
{
foreach (var path in paths)
{
if (path.Kind == ODataPathKind.Ref)
{
continue;
}
var npSegment = path.Segments.Last(s => s is ODataNavigationPropertySegment)
as ODataNavigationPropertySegment;
if (npSegment == null)
{
continue;
}
bool isLastKeySegment = path.LastSegment is ODataKeySegment;
if (isCollection)
{
if (isLastKeySegment)
{
continue;
}
if (npSegment.NavigationProperty.TargetMultiplicity() != EdmMultiplicity.Many)
{
continue;
}
}
else
{
if (!isLastKeySegment && npSegment.NavigationProperty.TargetMultiplicity() ==
EdmMultiplicity.Many)
{
continue;
}
}
if (HasUnsatisfiedDerivedTypeConstraint(
npSegment.NavigationProperty as IEdmVocabularyAnnotatable,
baseType,
convertSettings))
{
continue;
}
ODataPath newPath = path.Clone();
newPath.Push(new ODataTypeCastSegment(bindingEntityType));
newPath.Push(new ODataOperationSegment(edmOperation, isEscapedFunction));
AppendPath(newPath);
found = true;
}
}
}
return found;
}
} }
} }

View file

@ -161,6 +161,13 @@ namespace Microsoft.OpenApi.OData
/// </summary> /// </summary>
public bool ShowSchemaExamples { get; set; } = false; public bool ShowSchemaExamples { get; set; } = false;
/// <summary>
/// Gets/Sets a value indicating whether or not to require the
/// Validation.DerivedTypeConstraint to be applied to NavigationSources
/// to bind operations of derived types to them.
/// </summary>
public bool RequireDerivedTypesConstraintForBoundOperations { get; set; } = false;
/// <summary> /// <summary>
/// Gets/sets a value indicating whether or not to show the root path of the described API. /// Gets/sets a value indicating whether or not to show the root path of the described API.
/// </summary> /// </summary>
@ -197,6 +204,7 @@ namespace Microsoft.OpenApi.OData
EnableDerivedTypesReferencesForRequestBody = this.EnableDerivedTypesReferencesForRequestBody, EnableDerivedTypesReferencesForRequestBody = this.EnableDerivedTypesReferencesForRequestBody,
RoutePathPrefixProvider = this.RoutePathPrefixProvider, RoutePathPrefixProvider = this.RoutePathPrefixProvider,
ShowLinks = this.ShowLinks, ShowLinks = this.ShowLinks,
RequireDerivedTypesConstraintForBoundOperations = this.RequireDerivedTypesConstraintForBoundOperations,
ShowSchemaExamples = this.ShowSchemaExamples, ShowSchemaExamples = this.ShowSchemaExamples,
ShowRootPath = this.ShowRootPath, ShowRootPath = this.ShowRootPath,
PathProvider = this.PathProvider PathProvider = this.PathProvider

View file

@ -71,6 +71,9 @@
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon> <DependentUpon>Resources.resx</DependentUpon>
</Compile> </Compile>
<None Include="..\..\.editorconfig">
<Link>.editorconfig</Link>
</None>
<None Include="packages.config" /> <None Include="packages.config" />
<None Include="Properties\Settings.settings"> <None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator> <Generator>SettingsSingleFileGenerator</Generator>

View file

@ -74,22 +74,29 @@ namespace OoasUtil
/// Set the output to expect all derived types in request bodies. /// Set the output to expect all derived types in request bodies.
/// </summary> /// </summary>
public bool? DerivedTypesReferencesForRequestBody { get; private set; } public bool? DerivedTypesReferencesForRequestBody { get; private set; }
/// <summary> /// <summary>
/// Set the output to expose pagination for collections. /// Set the output to expose pagination for collections.
/// </summary> /// </summary>
public bool? EnablePagination { get; private set; } public bool? EnablePagination { get; private set; }
/// <summary> /// <summary>
/// tSet the output to use unqualified calls for bound operations. /// Set the output to use unqualified calls for bound operations.
/// </summary> /// </summary>
public bool? EnableUnqualifiedCall { get; private set; } public bool? EnableUnqualifiedCall { get; private set; }
/// <summary> /// <summary>
/// tDisable examples in the schema. /// Disable examples in the schema.
/// </summary> /// </summary>
public bool? DisableSchemaExamples { get; private set; } public bool? DisableSchemaExamples { get; private set; }
/// <summary>
/// Gets/Sets a value indicating whether or not to require the
/// Validation.DerivedTypeConstraint to be applied to NavigationSources
/// to bind operations of derived types to them.
/// </summary>
public bool? RequireDerivedTypesConstraint { get; private set; }
/// <summary> /// <summary>
/// Process the arguments. /// Process the arguments.
/// </summary> /// </summary>
@ -186,6 +193,14 @@ namespace OoasUtil
} }
break; break;
case "--requireDerivedTypesConstraint":
case "-rdt":
if (!ProcessRequireDerivedTypesConstraint(true))
{
return false;
}
break;
case "--enablepagination": case "--enablepagination":
case "-p": case "-p":
if (!ProcessEnablePagination(true)) if (!ProcessEnablePagination(true))
@ -248,6 +263,11 @@ namespace OoasUtil
DerivedTypesReferencesForRequestBody = false; DerivedTypesReferencesForRequestBody = false;
} }
if (RequireDerivedTypesConstraint == null)
{
RequireDerivedTypesConstraint = false;
}
if (EnablePagination == null) if (EnablePagination == null)
{ {
EnablePagination = false; EnablePagination = false;
@ -345,6 +365,19 @@ namespace OoasUtil
return true; return true;
} }
private bool ProcessRequireDerivedTypesConstraint(bool requireDerivedTypesConstraint)
{
if (RequireDerivedTypesConstraint != null)
{
Console.WriteLine("[Error:] Multiple [--requireDerivedTypesConstraint|-rdt] are not allowed.\n");
PrintUsage();
return false;
}
RequireDerivedTypesConstraint = requireDerivedTypesConstraint;
return true;
}
private bool ProcessEnablePagination(bool enablePagination) private bool ProcessEnablePagination(bool enablePagination)
{ {
if (EnablePagination != null) if (EnablePagination != null)
@ -445,6 +478,7 @@ namespace OoasUtil
sb.Append(" --keyassegment|-k\t\t\tSet the output to use key-as-segment style URLs.\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(" --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(" --derivedtypesreferencesforrequestbody|-drq\t\t\tSet the output to expect all derived types in request bodies.\n");
sb.Append(" --requireDerivedTypesConstraint|-rdt\t\t\tSet the output to require derived type constraint to bind Operations.\n");
sb.Append(" --enablepagination|-p\t\t\tSet the output to expose pagination for collections.\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(" --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(" --disableschemaexamples|-x\t\t\tDisable examples in the schema.\n");

View file

@ -37,6 +37,7 @@ namespace OoasUtil
EnableKeyAsSegment = processer.KeyAsSegment, EnableKeyAsSegment = processer.KeyAsSegment,
EnableDerivedTypesReferencesForResponses = processer.DerivedTypesReferencesForResponses.Value, EnableDerivedTypesReferencesForResponses = processer.DerivedTypesReferencesForResponses.Value,
EnableDerivedTypesReferencesForRequestBody = processer.DerivedTypesReferencesForRequestBody.Value, EnableDerivedTypesReferencesForRequestBody = processer.DerivedTypesReferencesForRequestBody.Value,
RequireDerivedTypesConstraintForBoundOperations = processer.RequireDerivedTypesConstraint.Value,
EnablePagination = processer.EnablePagination.Value, EnablePagination = processer.EnablePagination.Value,
EnableUnqualifiedCall = processer.EnableUnqualifiedCall.Value, EnableUnqualifiedCall = processer.EnableUnqualifiedCall.Value,
ShowSchemaExamples = !processer.DisableSchemaExamples.Value, ShowSchemaExamples = !processer.DisableSchemaExamples.Value,

View file

@ -4,12 +4,14 @@
// ------------------------------------------------------------ // ------------------------------------------------------------
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Xml; using System.Xml;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.OData.Edm; using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Csdl; using Microsoft.OData.Edm.Csdl;
using Microsoft.OData.Edm.Validation;
using Microsoft.OpenApi.OData.Tests; using Microsoft.OpenApi.OData.Tests;
using Xunit; using Xunit;
@ -23,9 +25,10 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
// Arrange // Arrange
IEdmModel model = new EdmModel(); IEdmModel model = new EdmModel();
ODataPathProvider provider = new ODataPathProvider(); ODataPathProvider provider = new ODataPathProvider();
var settings = new OpenApiConvertSettings();
// Act // Act
var paths = provider.GetPaths(model); var paths = provider.GetPaths(model, settings);
// Assert // Assert
Assert.NotNull(paths); Assert.NotNull(paths);
@ -37,25 +40,174 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
{ {
// Arrange // Arrange
IEdmModel model = EdmModelHelper.GraphBetaModel; IEdmModel model = EdmModelHelper.GraphBetaModel;
var settings = new OpenApiConvertSettings();
ODataPathProvider provider = new ODataPathProvider(); ODataPathProvider provider = new ODataPathProvider();
// Act // Act
var paths = provider.GetPaths(model); var paths = provider.GetPaths(model, settings);
// Assert
Assert.NotNull(paths);
Assert.Equal(4887, paths.Count());
}
[Fact]
public void GetPathsForGraphBetaModelWithDerivedTypesConstraintReturnsAllPaths()
{
// Arrange
IEdmModel model = EdmModelHelper.GraphBetaModel;
ODataPathProvider provider = new ODataPathProvider();
var settings = new OpenApiConvertSettings
{
RequireDerivedTypesConstraintForBoundOperations = true
};
// Act
var paths = provider.GetPaths(model, settings);
// Assert // Assert
Assert.NotNull(paths); Assert.NotNull(paths);
Assert.Equal(4544, paths.Count()); Assert.Equal(4544, paths.Count());
} }
[Fact]
public void GetPathsForInheritanceModelWithoutDerivedTypesConstraintReturnsMore()
{
// Arrange
IEdmModel model = GetInheritanceModel(string.Empty);
ODataPathProvider provider = new ODataPathProvider();
var settings = new OpenApiConvertSettings();
// Act
var paths = provider.GetPaths(model, settings);
// Assert
Assert.NotNull(paths);
Assert.Equal(3, paths.Count());
}
[Fact]
public void GetPathsForInheritanceModelWithDerivedTypesConstraintNoAnnotationReturnsFewer()
{
// Arrange
IEdmModel model = GetInheritanceModel(string.Empty);
ODataPathProvider provider = new ODataPathProvider();
var settings = new OpenApiConvertSettings
{
RequireDerivedTypesConstraintForBoundOperations = true
};
// Act
var paths = provider.GetPaths(model, settings);
// Assert
Assert.NotNull(paths);
Assert.Equal(2, paths.Count());
}
[Fact]
public void GetPathsForInheritanceModelWithDerivedTypesConstraintWithAnnotationReturnsMore()
{
// Arrange
IEdmModel model = GetInheritanceModel(@"
<Annotation Term=""Org.OData.Validation.V1.DerivedTypeConstraint"">
<Collection>
<String>NS.Customer</String>
<String>NS.NiceCustomer</String>
</Collection>
</Annotation>");
ODataPathProvider provider = new ODataPathProvider();
var settings = new OpenApiConvertSettings
{
RequireDerivedTypesConstraintForBoundOperations = true
};
// Act
var paths = provider.GetPaths(model, settings);
// Assert
Assert.Equal(3, paths.Count());
}
#if DEBUG
// Super useful for debugging tests.
private string ListToString(IEnumerable<ODataPath> paths)
{
return string.Join(Environment.NewLine,
paths.Select(p => string.Join("/", p.Segments.Select(s => s.Identifier))));
}
#endif
[Fact]
public void GetPathsForNavPropModelWithoutDerivedTypesConstraintReturnsMore()
{
// Arrange
IEdmModel model = GetNavPropModel(string.Empty);
ODataPathProvider provider = new ODataPathProvider();
var settings = new OpenApiConvertSettings();
// Act
var paths = provider.GetPaths(model, settings);
// Assert
Assert.NotNull(paths);
Assert.Equal(4, paths.Count());
}
[Fact]
public void GetPathsForNavPropModelWithDerivedTypesConstraintNoAnnotationReturnsFewer()
{
// Arrange
IEdmModel model = GetNavPropModel(string.Empty);
ODataPathProvider provider = new ODataPathProvider();
var settings = new OpenApiConvertSettings
{
RequireDerivedTypesConstraintForBoundOperations = true
};
// Act
var paths = provider.GetPaths(model, settings);
// Assert
Assert.NotNull(paths);
Assert.Equal(3, paths.Count());
}
[Fact]
public void GetPathsForNavPropModelWithDerivedTypesConstraintWithAnnotationReturnsMore()
{
// Arrange
IEdmModel model = GetNavPropModel(@"
<Annotation Term=""Org.OData.Validation.V1.DerivedTypeConstraint"">
<Collection>
<String>NS.Customer</String>
<String>NS.NiceCustomer</String>
</Collection>
</Annotation>");
ODataPathProvider provider = new ODataPathProvider();
var settings = new OpenApiConvertSettings
{
RequireDerivedTypesConstraintForBoundOperations = true
};
// Act
var paths = provider.GetPaths(model, settings);
// Assert
Assert.NotNull(paths);
Assert.Equal(4, paths.Count());
}
[Fact] [Fact]
public void GetPathsForSingleEntitySetWorks() public void GetPathsForSingleEntitySetWorks()
{ {
// Arrange // Arrange
IEdmModel model = GetEdmModel("", ""); IEdmModel model = GetEdmModel("", "");
ODataPathProvider provider = new ODataPathProvider(); ODataPathProvider provider = new ODataPathProvider();
var settings = new OpenApiConvertSettings();
// Act // Act
var paths = provider.GetPaths(model); var paths = provider.GetPaths(model, settings);
// Assert // Assert
Assert.NotNull(paths); Assert.NotNull(paths);
@ -69,9 +221,10 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
// Arrange // Arrange
IEdmModel model = GetEdmModel("", @"<Singleton Name=""Me"" Type=""NS.Customer"" />"); IEdmModel model = GetEdmModel("", @"<Singleton Name=""Me"" Type=""NS.Customer"" />");
ODataPathProvider provider = new ODataPathProvider(); ODataPathProvider provider = new ODataPathProvider();
var settings = new OpenApiConvertSettings();
// Act // Act
var paths = provider.GetPaths(model); var paths = provider.GetPaths(model, settings);
// Assert // Assert
Assert.NotNull(paths); Assert.NotNull(paths);
@ -90,9 +243,10 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
</Function>"; </Function>";
IEdmModel model = GetEdmModel(boundFunction, ""); IEdmModel model = GetEdmModel(boundFunction, "");
ODataPathProvider provider = new ODataPathProvider(); ODataPathProvider provider = new ODataPathProvider();
var settings = new OpenApiConvertSettings();
// Act // Act
var paths = provider.GetPaths(model); var paths = provider.GetPaths(model, settings);
// Assert // Assert
Assert.NotNull(paths); Assert.NotNull(paths);
@ -111,9 +265,10 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
</Action>"; </Action>";
IEdmModel model = GetEdmModel(boundAction, ""); IEdmModel model = GetEdmModel(boundAction, "");
ODataPathProvider provider = new ODataPathProvider(); ODataPathProvider provider = new ODataPathProvider();
var settings = new OpenApiConvertSettings();
// Act // Act
var paths = provider.GetPaths(model); var paths = provider.GetPaths(model, settings);
// Assert // Assert
Assert.NotNull(paths); Assert.NotNull(paths);
@ -137,9 +292,10 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
IEdmModel model = GetEdmModel(boundAction, unbounds); IEdmModel model = GetEdmModel(boundAction, unbounds);
ODataPathProvider provider = new ODataPathProvider(); ODataPathProvider provider = new ODataPathProvider();
var settings = new OpenApiConvertSettings();
// Act // Act
var paths = provider.GetPaths(model); var paths = provider.GetPaths(model, settings);
// Assert // Assert
Assert.NotNull(paths); Assert.NotNull(paths);
@ -165,9 +321,10 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
IEdmModel model = GetEdmModel(entityType, entitySet); IEdmModel model = GetEdmModel(entityType, entitySet);
ODataPathProvider provider = new ODataPathProvider(); ODataPathProvider provider = new ODataPathProvider();
var settings = new OpenApiConvertSettings();
// Act // Act
var paths = provider.GetPaths(model); var paths = provider.GetPaths(model, settings);
// Assert // Assert
Assert.NotNull(paths); Assert.NotNull(paths);
@ -196,9 +353,10 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
string entitySet = @"<EntitySet Name=""Orders"" EntityType=""NS.Order"" />"; string entitySet = @"<EntitySet Name=""Orders"" EntityType=""NS.Order"" />";
IEdmModel model = GetEdmModel(entityType, entitySet); IEdmModel model = GetEdmModel(entityType, entitySet);
ODataPathProvider provider = new ODataPathProvider(); ODataPathProvider provider = new ODataPathProvider();
var settings = new OpenApiConvertSettings();
// Act // Act
var paths = provider.GetPaths(model); var paths = provider.GetPaths(model, settings);
// Assert // Assert
Assert.NotNull(paths); Assert.NotNull(paths);
@ -220,9 +378,10 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
// Arrange // Arrange
IEdmModel model = GetEdmModel(hasStream, streamPropName); IEdmModel model = GetEdmModel(hasStream, streamPropName);
ODataPathProvider provider = new ODataPathProvider(); ODataPathProvider provider = new ODataPathProvider();
var settings = new OpenApiConvertSettings();
// Act // Act
var paths = provider.GetPaths(model); var paths = provider.GetPaths(model,settings);
// Assert // Assert
Assert.NotNull(paths); Assert.NotNull(paths);
@ -250,7 +409,7 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
private static IEdmModel GetEdmModel(string schemaElement, string containerElement) private static IEdmModel GetEdmModel(string schemaElement, string containerElement)
{ {
string template = @"<?xml version=""1.0"" encoding=""utf-16""?> string template = $@"<?xml version=""1.0"" encoding=""utf-16""?>
<Schema Namespace=""NS"" xmlns=""http://docs.oasis-open.org/odata/ns/edm""> <Schema Namespace=""NS"" xmlns=""http://docs.oasis-open.org/odata/ns/edm"">
<EntityType Name=""Customer""> <EntityType Name=""Customer"">
<Key> <Key>
@ -258,15 +417,76 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
</Key> </Key>
<Property Name=""ID"" Type=""Edm.Int32"" Nullable=""false"" /> <Property Name=""ID"" Type=""Edm.Int32"" Nullable=""false"" />
</EntityType> </EntityType>
{0} {schemaElement}
<EntityContainer Name =""Default""> <EntityContainer Name =""Default"">
<EntitySet Name=""Customers"" EntityType=""NS.Customer"" /> <EntitySet Name=""Customers"" EntityType=""NS.Customer"" />
{1} {containerElement}
</EntityContainer> </EntityContainer>
</Schema>"; </Schema>";
string schema = string.Format(template, schemaElement, containerElement); return GetEdmModel(template);
bool parsed = SchemaReader.TryParse(new XmlReader[] { XmlReader.Create(new StringReader(schema)) }, out IEdmModel parsedModel, out _); }
Assert.True(parsed);
private static IEdmModel GetInheritanceModel(string annotation)
{
string template = $@"<?xml version=""1.0"" encoding=""utf-16""?>
<Schema Namespace=""NS"" xmlns=""http://docs.oasis-open.org/odata/ns/edm"">
<EntityType Name=""Customer"">
<Key>
<PropertyRef Name=""ID"" />
</Key>
<Property Name=""ID"" Type=""Edm.Int32"" Nullable=""false"" />
</EntityType>
<EntityType Name=""NiceCustomer"" BaseType=""NS.Customer"">
<Property Name=""Other"" Type=""Edm.Int32"" Nullable=""true"" />
</EntityType>
<Action Name=""Ack"" IsBound=""true"" >
<Parameter Name = ""bindingParameter"" Type=""NS.NiceCustomer"" />
</Action>
<EntityContainer Name =""Default"">
<EntitySet Name=""Customers"" EntityType=""NS.Customer"">
{annotation}
</EntitySet>
</EntityContainer>
</Schema>";
return GetEdmModel(template);
}
private static IEdmModel GetNavPropModel(string annotation)
{
string template = $@"<?xml version=""1.0"" encoding=""utf-16""?>
<Schema Namespace=""NS"" xmlns=""http://docs.oasis-open.org/odata/ns/edm"">
<EntityType Name=""Root"">
<Key>
<PropertyRef Name=""ID"" />
</Key>
<Property Name=""ID"" Type=""Edm.Int32"" Nullable=""false"" />
<NavigationProperty Name=""Customers"" Type=""Collection(NS.Customer)"" ContainsTarget=""true"">
{annotation}
</NavigationProperty>
</EntityType>
<EntityType Name=""Customer"">
<Key>
<PropertyRef Name=""ID"" />
</Key>
<Property Name=""ID"" Type=""Edm.Int32"" Nullable=""false"" />
</EntityType>
<EntityType Name=""NiceCustomer"" BaseType=""NS.Customer"">
<Property Name=""Other"" Type=""Edm.Int32"" Nullable=""true"" />
</EntityType>
<Action Name=""Ack"" IsBound=""true"" >
<Parameter Name = ""bindingParameter"" Type=""NS.NiceCustomer"" />
</Action>
<EntityContainer Name =""Default"">
<Singleton Name=""Root"" Type=""NS.Root"" />
</EntityContainer>
</Schema>";
return GetEdmModel(template);
}
private static IEdmModel GetEdmModel(string schema)
{
bool parsed = SchemaReader.TryParse(new XmlReader[] { XmlReader.Create(new StringReader(schema)) }, out IEdmModel parsedModel, out IEnumerable<EdmError> errors);
Assert.True(parsed, $"Parse failure. {string.Join(Environment.NewLine, errors.Select(e => e.ToString()))}");
return parsedModel; return parsedModel;
} }