Fix paths when operation is bound to a type derived from the type of a
navigation property. (#88)
This commit is contained in:
parent
1643ba5ea1
commit
e1763727f5
4
.editorconfig
Normal file
4
.editorconfig
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[*.{cs,vb}]
|
||||||
|
|
||||||
|
# IDE0009: Member access should be qualified.
|
||||||
|
dotnet_diagnostic.IDE0009.severity = none
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) ||
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue