Modify the ODataPathProvider

This commit is contained in:
Sam Xu 2018-09-24 11:05:50 -07:00
parent b5b18ae565
commit 9e3306e76d
5 changed files with 423 additions and 201 deletions

View file

@ -0,0 +1,30 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using Microsoft.OData.Edm;
using System.Collections.Generic;
namespace Microsoft.OpenApi.OData.Edm
{
/// <summary>
/// The interface for <see cref="ODataPath"/> provider.
/// </summary>
public interface IODataPathProvider
{
/// <summary>
/// Can filter the <see cref="IEdmElement"/> or not.
/// </summary>
/// <param name="element">The Edm element.</param>
/// <returns>True/false.</returns>
bool CanFilter(IEdmElement element);
/// <summary>
/// Generate the list of <see cref="ODataPath"/> based on the given <see cref="IEdmModel"/>.
/// </summary>
/// <param name="model">The Edm model.</param>
/// <returns>The collection of built <see cref="ODataPath"/>.</returns>
IEnumerable<ODataPath> GetPaths(IEdmModel model);
}
}

View file

@ -24,7 +24,7 @@ namespace Microsoft.OpenApi.OData.Edm
internal class ODataContext
{
private IEnumerable<ODataPath> _allPaths;
private ODataPathProvider _pathProvider;
private IODataPathProvider _pathProvider;
private HttpRequestProvider _httpRequestProvider;
private AuthorizationProvider _authorizationProvider;
@ -56,7 +56,7 @@ namespace Microsoft.OpenApi.OData.Edm
_authorizationProvider = new AuthorizationProvider(model);
_httpRequestProvider = new HttpRequestProvider(model);
_pathProvider = new ODataPathProvider(Model);
_pathProvider = new ODataPathProvider();
if (settings.EnableKeyAsSegment != null)
{
@ -112,7 +112,7 @@ namespace Microsoft.OpenApi.OData.Edm
{
if (_allPaths == null)
{
_allPaths = LoadAllODataPaths();
_allPaths = LoadAllODataPaths().ToList();
}
return _allPaths;
@ -185,7 +185,7 @@ namespace Microsoft.OpenApi.OData.Edm
/// <returns>All acceptable OData path.</returns>
private IEnumerable<ODataPath> LoadAllODataPaths()
{
IEnumerable<ODataPath> allPaths = _pathProvider.CreatePaths();
IEnumerable<ODataPath> allPaths = _pathProvider.GetPaths(Model);
foreach (var path in allPaths)
{
if ((path.Kind == ODataPathKind.Operation && !Settings.EnableOperationPath) ||

View file

@ -3,7 +3,6 @@
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@ -14,71 +13,150 @@ namespace Microsoft.OpenApi.OData.Edm
/// <summary>
/// Provide class for <see cref="ODataPath"/> generating.
/// </summary>
public class ODataPathProvider
public class ODataPathProvider : IODataPathProvider
{
private List<ODataPath> _allODataPaths;
private Lazy<IDictionary<IEdmEntityType, IList<IEdmNavigationSource>>> _allNavigationSource;
private IDictionary<IEdmEntityType, IList<IEdmNavigationSource>> _allNavigationSources;
private IDictionary<IEdmEntityType, IList<ODataPath>> _allNavigationSourcePaths =
new Dictionary<IEdmEntityType, IList<ODataPath>>();
private IDictionary<IEdmEntityType, IList<ODataPath>> _allNavigationPropertyPaths =
new Dictionary<IEdmEntityType, IList<ODataPath>>();
private IList<ODataPath> _allOperationPaths = new List<ODataPath>();
private IEdmModel _model;
/// <summary>
/// Gets the Edm model.
/// Can filter the <see cref="IEdmElement"/> or not.
/// </summary>
public IEdmModel Model { get; }
/// <param name="element">The Edm element.</param>
/// <returns>True/false.</returns>
public virtual bool CanFilter(IEdmElement element) => true;
/// <summary>
/// Initializes a new instance of <see cref="ODataPathProvider"/> class.
/// Generate the list of <see cref="ODataPath"/> based on the given <see cref="IEdmModel"/>.
/// </summary>
/// <param name="model">The Edm model.</param>
public ODataPathProvider(IEdmModel model)
{
Model = model ?? throw new ArgumentNullException(nameof(model));
/// <returns>The collection of built <see cref="ODataPath"/>.</returns>
public virtual IEnumerable<ODataPath> GetPaths(IEdmModel model)
{
if (model == null || model.EntityContainer == null)
{
return Enumerable.Empty<ODataPath>();
}
// if the entity container is null or empty. assign the path as empty.
if (model.EntityContainer == null)
{
_allODataPaths = new List<ODataPath>();
}
Initialize(model);
_allNavigationSource = new Lazy<IDictionary<IEdmEntityType, IList<IEdmNavigationSource>>>(
() => model.LoadAllNavigationSources(), false);
// entity set
foreach (IEdmEntitySet entitySet in _model.EntityContainer.EntitySets())
{
if (CanFilter(entitySet))
{
RetrieveNavigationSourcePaths(entitySet);
}
}
// singleton
foreach (IEdmSingleton singleton in _model.EntityContainer.Singletons())
{
if (CanFilter(singleton))
{
RetrieveNavigationSourcePaths(singleton);
}
}
// bound operations
RetrieveBoundOperationPaths();
// unbound operations
foreach (IEdmOperationImport import in _model.EntityContainer.OperationImports())
{
if (CanFilter(import))
{
AppendPath(new ODataPath(new ODataOperationImportSegment(import)));
}
}
return MergePaths();
}
/// <summary>
/// Generate the list of <see cref="ODataPath"/>.
/// Initialize the provider.
/// </summary>
/// <returns>The collection of build <see cref="ODataPath"/>.</returns>
public virtual IEnumerable<ODataPath> CreatePaths()
/// <param name="model">The Edm model.</param>
protected virtual void Initialize(IEdmModel model)
{
if (_allODataPaths != null)
Debug.Assert(model != null);
_model = model;
_allNavigationSources = model.LoadAllNavigationSources();
_allNavigationSourcePaths.Clear();
_allNavigationPropertyPaths.Clear();
_allOperationPaths.Clear();
}
private IEnumerable<ODataPath> MergePaths()
{
List<ODataPath> allODataPaths = new List<ODataPath>();
foreach (var item in _allNavigationSourcePaths.Values)
{
allODataPaths.AddRange(item);
}
foreach (var item in _allNavigationPropertyPaths.Values)
{
allODataPaths.AddRange(item);
}
allODataPaths.AddRange(_allOperationPaths);
allODataPaths.Sort();
return allODataPaths;
}
private void AppendPath(ODataPath path)
{
Debug.Assert(path != null);
ODataPathKind kind = path.Kind;
switch(kind)
{
return _allODataPaths;
case ODataPathKind.Entity:
case ODataPathKind.EntitySet:
case ODataPathKind.Singleton:
ODataNavigationSourceSegment navigationSourceSegment = (ODataNavigationSourceSegment)path.FirstSegment;
if (!_allNavigationSourcePaths.TryGetValue(navigationSourceSegment.EntityType, out IList<ODataPath> nsList))
{
nsList = new List<ODataPath>();
_allNavigationSourcePaths[navigationSourceSegment.EntityType] = nsList;
}
nsList.Add(path);
break;
case ODataPathKind.NavigationProperty:
ODataNavigationPropertySegment navigationPropertySegment = path.Last(p => p is ODataNavigationPropertySegment)
as ODataNavigationPropertySegment;
if (!_allNavigationPropertyPaths.TryGetValue(navigationPropertySegment.EntityType, out IList<ODataPath> npList))
{
npList = new List<ODataPath>();
_allNavigationPropertyPaths[navigationPropertySegment.EntityType] = npList;
}
npList.Add(path);
break;
case ODataPathKind.Operation:
case ODataPathKind.OperationImport:
_allOperationPaths.Add(path);
break;
default:
return;
}
_allODataPaths = new List<ODataPath>();
// entity set
foreach (IEdmEntitySet entitySet in Model.EntityContainer.EntitySets())
{
RetrieveNavigationSourcePaths(entitySet);
}
// singleton
foreach (IEdmSingleton singleton in Model.EntityContainer.Singletons())
{
RetrieveNavigationSourcePaths(singleton);
}
// bound operations
RetrieveBoundOperationPaths();
// unbound operations
foreach (IEdmOperationImport import in Model.EntityContainer.OperationImports())
{
_allODataPaths.Add(new ODataPath(new ODataOperationImportSegment(import)));
}
_allODataPaths.Sort();
return _allODataPaths;
}
/// <summary>
@ -91,7 +169,7 @@ namespace Microsoft.OpenApi.OData.Edm
// navigation source itself
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(navigationSource));
_allODataPaths.Add(path.Clone());
AppendPath(path.Clone());
IEdmEntitySet entitySet = navigationSource as IEdmEntitySet;
IEdmEntityType entityType = navigationSource.EntityType();
@ -100,13 +178,16 @@ namespace Microsoft.OpenApi.OData.Edm
if (entitySet != null)
{
path.Push(new ODataKeySegment(entityType));
_allODataPaths.Add(path.Clone());
AppendPath(path.Clone());
}
// navigation property
foreach (IEdmNavigationProperty np in entityType.DeclaredNavigationProperties())
{
RetrieveNavigationPropertyPaths(np, path);
if (CanFilter(np))
{
RetrieveNavigationPropertyPaths(np, path);
}
}
if (entitySet != null)
@ -128,19 +209,19 @@ namespace Microsoft.OpenApi.OData.Edm
Debug.Assert(navigationProperty != null);
Debug.Assert(currentPath != null);
// test ahead for the navigation expandable.
// test the expandable for the navigation property.
bool shouldExpand = ShouldExpandNavigationProperty(navigationProperty, currentPath);
// append a navigation property.
currentPath.Push(new ODataNavigationPropertySegment(navigationProperty));
_allODataPaths.Add(currentPath.Clone());
AppendPath(currentPath.Clone());
// append a navigation property key.
IEdmEntityType navEntityType = navigationProperty.ToEntityType();
if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
{
currentPath.Push(new ODataKeySegment(navEntityType));
_allODataPaths.Add(currentPath.Clone());
AppendPath(currentPath.Clone());
}
if (shouldExpand)
@ -148,7 +229,10 @@ namespace Microsoft.OpenApi.OData.Edm
// expand to sub navigation properties
foreach (IEdmNavigationProperty subNavProperty in navEntityType.DeclaredNavigationProperties())
{
RetrieveNavigationPropertyPaths(subNavProperty, currentPath);
if (CanFilter(subNavProperty))
{
RetrieveNavigationPropertyPaths(subNavProperty, currentPath);
}
}
}
@ -160,11 +244,6 @@ namespace Microsoft.OpenApi.OData.Edm
currentPath.Pop();
}
/// <summary>
/// Test the navigation property should be expanded or not.
/// </summary>
/// <param name="navigationProperty">The navigation property.</param>
/// <param name="currentPath">The current OData path.</param>
private bool ShouldExpandNavigationProperty(IEdmNavigationProperty navigationProperty, ODataPath currentPath)
{
Debug.Assert(navigationProperty != null);
@ -188,34 +267,25 @@ namespace Microsoft.OpenApi.OData.Edm
// check whether the navigation type used to define a navigation source.
// if so, not expand it.
return !_allNavigationSource.Value.ContainsKey(navEntityType);
return !_allNavigationSources.ContainsKey(navEntityType);
}
/// <summary>
/// Retrieve all bounding <see cref="IEdmOperation"/>.
/// </summary>
private void RetrieveBoundOperationPaths()
{
IList<ODataPath> npPaths = _allODataPaths.Where(p => p.Kind == ODataPathKind.NavigationProperty).ToList();
IDictionary<IEdmEntityType, IList<ODataPath>> allNavigationPropertyPaths = new Dictionary<IEdmEntityType, IList<ODataPath>>();
foreach(var path in npPaths)
foreach (var edmOperation in _model.SchemaElements.OfType<IEdmOperation>().Where(e => e.IsBound))
{
ODataNavigationPropertySegment npSegment = path.Segments.Last(s => s is ODataNavigationPropertySegment) as ODataNavigationPropertySegment;
IEdmEntityType navPropertyEntityType = npSegment.NavigationProperty.ToEntityType();
if (!allNavigationPropertyPaths.TryGetValue(navPropertyEntityType, out IList<ODataPath> value))
if (!CanFilter(edmOperation))
{
value = new List<ODataPath>();
allNavigationPropertyPaths[navPropertyEntityType] = value;
continue;
}
value.Add(path);
}
foreach (var edmOperation in Model.SchemaElements.OfType<IEdmOperation>().Where(e => e.IsBound))
{
IEdmOperationParameter bindingParameter = edmOperation.Parameters.First();
IEdmTypeReference bindingType = bindingParameter.Type;
bool isCollection = bindingType.IsCollection();
if (isCollection)
{
bindingType = bindingType.AsCollection().ElementType();
@ -224,59 +294,58 @@ namespace Microsoft.OpenApi.OData.Edm
{
continue;
}
IEdmEntityType bindingEntityType = bindingType.AsEntity().EntityDefinition();
bool found = false;
// 1. Search for correspoinding navigation source
if (_allNavigationSource.Value.TryGetValue(bindingEntityType, out IList<IEdmNavigationSource> correspondingNavigationSources))
{
foreach(var ns in correspondingNavigationSources)
{
if (isCollection)
{
if (ns is IEdmEntitySet)
{
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataOperationSegment(edmOperation));
_allODataPaths.Add(newPath);
found = true;
}
}
else
{
if (ns is IEdmSingleton)
{
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataOperationSegment(edmOperation));
_allODataPaths.Add(newPath);
found = true;
}
else
{
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataKeySegment(ns.EntityType()),
new ODataOperationSegment(edmOperation));
_allODataPaths.Add(newPath);
found = true;
}
}
}
}
if (found)
// 1. Search for corresponding navigation source path
if (AppendBoundOperationOnNavigationSourcePath(edmOperation, isCollection, bindingEntityType))
{
continue;
}
// 2. Search for generated navigation property
/*
foreach (var path in npPaths)
if (AppendBoundOperationOnNavigationPropertyPath(edmOperation, isCollection, bindingEntityType))
{
continue;
}
// 3. Search for derived
if (AppendBoundOperationOnDerived(edmOperation, isCollection, bindingEntityType))
{
continue;
}
}
}
private bool AppendBoundOperationOnNavigationSourcePath(IEdmOperation edmOperation, bool isCollection, IEdmEntityType bindingEntityType)
{
bool found = false;
if (_allNavigationSourcePaths.TryGetValue(bindingEntityType, out IList<ODataPath> value))
{
foreach (var subPath in value)
{
if ((isCollection && subPath.Kind == ODataPathKind.EntitySet) ||
(!isCollection && subPath.Kind != ODataPathKind.EntitySet))
{
ODataPath newPath = subPath.Clone();
newPath.Push(new ODataOperationSegment(edmOperation));
AppendPath(newPath);
found = true;
}
}
}
return found;
}
private bool AppendBoundOperationOnNavigationPropertyPath(IEdmOperation edmOperation, bool isCollection, IEdmEntityType bindingEntityType)
{
bool found = false;
if (_allNavigationPropertyPaths.TryGetValue(bindingEntityType, out IList<ODataPath> value))
{
foreach (var path in value)
{
ODataNavigationPropertySegment npSegment = path.Segments.Last(s => s is ODataNavigationPropertySegment) as ODataNavigationPropertySegment;
IEdmEntityType navPropertyEntityType = npSegment.NavigationProperty.ToEntityType();
if (navPropertyEntityType != bindingEntityType)
{
continue;
}
bool isLastKeySegment = path.LastSegment is ODataKeySegment;
@ -302,87 +371,58 @@ namespace Microsoft.OpenApi.OData.Edm
ODataPath newPath = path.Clone();
newPath.Push(new ODataOperationSegment(edmOperation));
_allODataPaths.Add(newPath);
AppendPath(newPath);
found = true;
}*/
}
}
if (allNavigationPropertyPaths.TryGetValue(bindingEntityType, out IList<ODataPath> value))
return found;
}
private bool AppendBoundOperationOnDerived(IEdmOperation edmOperation, bool isCollection, IEdmEntityType bindingEntityType)
{
bool found = false;
foreach (var baseType in bindingEntityType.FindAllBaseTypes())
{
if (_allNavigationSources.TryGetValue(baseType, out IList<IEdmNavigationSource> baseNavigationSource))
{
foreach(var path in value)
foreach (var ns in baseNavigationSource)
{
ODataNavigationPropertySegment npSegment = path.Segments.Last(s => s is ODataNavigationPropertySegment) as ODataNavigationPropertySegment;
bool isLastKeySegment = path.LastSegment is ODataKeySegment;
if (isCollection)
{
if (isLastKeySegment)
if (ns is IEdmEntitySet)
{
continue;
}
if (npSegment.NavigationProperty.TargetMultiplicity() != EdmMultiplicity.Many)
{
continue;
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataTypeCastSegment(bindingEntityType),
new ODataOperationSegment(edmOperation));
AppendPath(newPath);
found = true;
}
}
else
{
if (!isLastKeySegment && npSegment.NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
if (ns is IEdmSingleton)
{
continue;
}
}
ODataPath newPath = path.Clone();
newPath.Push(new ODataOperationSegment(edmOperation));
_allODataPaths.Add(newPath);
found = true;
}
}
if (found)
{
continue;
}
// 3. Search for derived
foreach(var baseType in bindingEntityType.FindAllBaseTypes())
{
if (_allNavigationSource.Value.TryGetValue(baseType, out IList<IEdmNavigationSource> baseNavigationSource))
{
foreach (var ns in baseNavigationSource)
{
if (isCollection)
{
if (ns is IEdmEntitySet)
{
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataTypeCastSegment(bindingEntityType),
new ODataOperationSegment(edmOperation));
_allODataPaths.Add(newPath);
found = true;
}
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataTypeCastSegment(bindingEntityType),
new ODataOperationSegment(edmOperation));
AppendPath(newPath);
found = true;
}
else
{
if (ns is IEdmSingleton)
{
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataTypeCastSegment(bindingEntityType), new ODataOperationSegment(edmOperation));
_allODataPaths.Add(newPath);
found = true;
}
else
{
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataKeySegment(ns.EntityType()), new ODataTypeCastSegment(bindingEntityType),
new ODataOperationSegment(edmOperation));
_allODataPaths.Add(newPath);
found = true;
}
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataKeySegment(ns.EntityType()),
new ODataTypeCastSegment(bindingEntityType),
new ODataOperationSegment(edmOperation));
AppendPath(newPath);
found = true;
}
}
}
}
}
return found;
}
}
}

View file

@ -4,8 +4,13 @@
// ------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Csdl;
using Microsoft.OData.Edm.Validation;
using Microsoft.OpenApi.OData.Tests;
using Xunit;
@ -14,21 +19,14 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
public class ODataPathProviderTests
{
[Fact]
public void CtorThrowArgumentNullModel()
{
// Arrange & Act & Assert
Assert.Throws<ArgumentNullException>("model", () => new ODataPathProvider(model: null));
}
[Fact]
public void CreatePathsForEmptyEdmModelReturnsEmptyPaths()
public void GetPathsForEmptyEdmModelReturnsEmptyPaths()
{
// Arrange
IEdmModel model = new EdmModel();
ODataPathProvider provider = new ODataPathProvider(model);
ODataPathProvider provider = new ODataPathProvider();
// Act
var paths = provider.CreatePaths();
var paths = provider.GetPaths(model);
// Assert
Assert.NotNull(paths);
@ -36,18 +34,172 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
}
[Fact]
public void CreatePathsForGraphBetaModelReturnsAllPaths()
public void GetPathsForGraphBetaModelReturnsAllPaths()
{
// Arrange
IEdmModel model = EdmModelHelper.GraphBetaModel;
ODataPathProvider provider = new ODataPathProvider(model);
ODataPathProvider provider = new ODataPathProvider();
// Act
var paths = provider.CreatePaths();
var paths = provider.GetPaths(model);
// Assert
Assert.NotNull(paths);
Assert.Equal(4399, paths.Count());
}
[Fact]
public void GetPathsForSingleEntitySetWorks()
{
// Arrange
IEdmModel model = GetEdmModel("", "");
ODataPathProvider provider = new ODataPathProvider();
// Act
var paths = provider.GetPaths(model);
// Assert
Assert.NotNull(paths);
Assert.Equal(2, paths.Count());
Assert.Equal(new[] { "/Customers", "/Customers({ID})" }, paths.Select(p => p.GetPathItemName()));
}
[Fact]
public void GetPathsWithSingletonWorks()
{
// Arrange
IEdmModel model = GetEdmModel("", @"<Singleton Name=""Me"" Type=""NS.Customer"" />");
ODataPathProvider provider = new ODataPathProvider();
// Act
var paths = provider.GetPaths(model);
// Assert
Assert.NotNull(paths);
Assert.Equal(3, paths.Count());
Assert.Contains("/Me", paths.Select(p => p.GetPathItemName()));
}
[Fact]
public void GetPathsWithBoundFunctionOperationWorks()
{
// Arrange
string boundFunction =
@"<Function Name=""delta"" IsBound=""true"">
<Parameter Name=""bindingParameter"" Type=""Collection(NS.Customer)"" />
<ReturnType Type=""Collection(NS.Customer)"" />
</Function>";
IEdmModel model = GetEdmModel(boundFunction, "");
ODataPathProvider provider = new ODataPathProvider();
// Act
var paths = provider.GetPaths(model);
// Assert
Assert.NotNull(paths);
Assert.Equal(3, paths.Count());
Assert.Contains("/Customers/NS.delta()", paths.Select(p => p.GetPathItemName()));
}
[Fact]
public void GetPathsWithBoundActionOperationWorks()
{
// Arrange
string boundAction =
@"<Action Name=""renew"" IsBound=""true"">
<Parameter Name=""bindingParameter"" Type=""NS.Customer"" />
<ReturnType Type=""Edm.Boolean"" />
</Action>";
IEdmModel model = GetEdmModel(boundAction, "");
ODataPathProvider provider = new ODataPathProvider();
// Act
var paths = provider.GetPaths(model);
// Assert
Assert.NotNull(paths);
Assert.Equal(3, paths.Count());
Assert.Contains("/Customers({ID})/NS.renew", paths.Select(p => p.GetPathItemName()));
}
[Fact]
public void GetPathsWithUnboundOperationImportWorks()
{
// Arrange
string boundAction =
@"<Function Name=""GetNearestCustomers"">
<ReturnType Type=""NS.Customer"" />
</Function >
<Action Name=""ResetDataSource"" />";
string unbounds = @"
<FunctionImport Name=""GetNearestCustomers"" Function=""NS.GetNearestCustomers"" EntitySet=""Customers"" />
<ActionImport Name =""ResetDataSource"" Action =""NS.ResetDataSource"" />";
IEdmModel model = GetEdmModel(boundAction, unbounds);
ODataPathProvider provider = new ODataPathProvider();
// Act
var paths = provider.GetPaths(model);
// Assert
Assert.NotNull(paths);
Assert.Equal(4, paths.Count());
Assert.Contains("/GetNearestCustomers()", paths.Select(p => p.GetPathItemName()));
Assert.Contains("/ResetDataSource", paths.Select(p => p.GetPathItemName()));
}
[Fact]
public void GetPathsWithNavigationPropertytWorks()
{
// Arrange
string entityType =
@"<EntityType Name=""Order"">
<Key>
<PropertyRef Name=""id"" />
</Key>
<NavigationProperty Name=""MultipleCustomers"" Type=""Collection(NS.Customer)"" />
<NavigationProperty Name=""SingleCustomers"" Type=""NS.Customer"" />
</EntityType>";
string entitySet = @"<EntitySet Name=""Orders"" EntityType=""NS.Order"" />";
IEdmModel model = GetEdmModel(entityType, entitySet);
ODataPathProvider provider = new ODataPathProvider();
// Act
var paths = provider.GetPaths(model);
// Assert
Assert.NotNull(paths);
Assert.Equal(7, paths.Count());
Assert.Contains("/Orders({id})/MultipleCustomers", paths.Select(p => p.GetPathItemName()));
Assert.Contains("/Orders({id})/MultipleCustomers({ID})", paths.Select(p => p.GetPathItemName()));
Assert.Contains("/Orders({id})/SingleCustomers", paths.Select(p => p.GetPathItemName()));
}
private static IEdmModel GetEdmModel(string schemaElement, string containerElement)
{
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>
{0}
<EntityContainer Name =""Default"">
<EntitySet Name=""Customers"" EntityType=""NS.Customer"" />
{1}
</EntityContainer>
</Schema>";
string schema = String.Format(template, schemaElement, containerElement);
IEdmModel parsedModel;
IEnumerable<EdmError> errors;
bool parsed = SchemaReader.TryParse(new XmlReader[] { XmlReader.Create(new StringReader(schema)) }, out parsedModel, out errors);
Assert.True(parsed);
return parsedModel;
}
}
}

View file

@ -91,8 +91,8 @@ namespace UpdateDocs
string output = @"E:\work\OpenApi\metadata_withRequestTerms.json";
File.WriteAllText(output, document.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0));
return false;*/
ODataPathProvider provider = new ODataPathProvider(model);
var paths = provider.CreatePaths();
ODataPathProvider provider = new ODataPathProvider();
var paths = provider.GetPaths(model);
using (StreamWriter file = new StreamWriter(@"e:\work\openapi\Graph.Beta.OData.AllPath5.txt"))
{