modify the ODataPath provider

This commit is contained in:
Sam Xu 2018-09-14 17:00:21 -07:00
parent b0cc50d59f
commit be14a752d3
5 changed files with 120 additions and 69 deletions

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.Collections.Generic;
using System.Linq;
using Microsoft.OData.Edm;
@ -15,6 +16,36 @@ namespace Microsoft.OpenApi.OData.Edm
/// </summary>
public static class EdmModelExtensions
{
public static IDictionary<IEdmEntityType, IList<IEdmNavigationSource>> LoadAllNavigationSources(this IEdmModel model)
{
var navigationSourceDic = new Dictionary<IEdmEntityType, IList<IEdmNavigationSource>>();
if (model != null && model.EntityContainer != null)
{
Action<IEdmNavigationSource, IDictionary<IEdmEntityType, IList<IEdmNavigationSource>>> action = (ns, dic) =>
{
if (!dic.TryGetValue(ns.EntityType(), out IList<IEdmNavigationSource> value))
{
value = new List<IEdmNavigationSource>();
dic[ns.EntityType()] = value;
}
value.Add(ns);
};
foreach (var entitySet in model.EntityContainer.EntitySets())
{
action(entitySet, navigationSourceDic);
}
foreach (var singelton in model.EntityContainer.Singletons())
{
action(singelton, navigationSourceDic);
}
}
return navigationSourceDic;
}
public static IEnumerable<IEdmEntityType> FindAllBaseTypes(this IEdmEntityType entityType)
{
if (entityType == null)

View file

@ -27,7 +27,7 @@ namespace Microsoft.OpenApi.OData.Edm
private IDictionary<IEdmTypeReference, IEdmOperation> _boundOperations;
private bool _keyAsSegmentSupported = false;
private IList<OpenApiTag> _tags = new List<OpenApiTag>();
private ODataPathHandler _pathHandler;
private ODataPathProvider _pathProvider;
public HttpRequestProvider _httpRequestProvider;
public AuthorizationProvider _authorizationProvider;
@ -54,7 +54,7 @@ namespace Microsoft.OpenApi.OData.Edm
visitor.Visit(model);
IsSpatialTypeUsed = visitor.IsSpatialTypeUsed;
_pathHandler = new ODataPathHandler(this);
_pathProvider = new ODataPathProvider(model);
OperationHanderProvider = new OperationHandlerProvider();
PathItemHanderProvider = new PathItemHandlerProvider();
@ -104,7 +104,7 @@ namespace Microsoft.OpenApi.OData.Edm
/// <summary>
/// Gets the <see cref="ODataPath"/>s.
/// </summary>
public IList<ODataPath> Paths => _pathHandler.Paths;
public IEnumerable<ODataPath> Paths => _pathProvider.CreatePaths();
/// <summary>
/// Gets the boolean value indicating to support key as segment.

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.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@ -15,59 +16,80 @@ namespace Microsoft.OpenApi.OData.Edm
/// </summary>
public class ODataPathProvider
{
private List<ODataPath> _allODataPaths;
private Lazy<IDictionary<IEdmEntityType, IList<IEdmNavigationSource>>> _allNavigationSource;
/// <summary>
/// Generate the list of <see cref="ODataPath"/> with the given <see cref="IEdmModel"/>.
/// Gets the Edm model.
/// </summary>
/// <param name="model">The given Edm model.</param>
/// <returns>The collection of build <see cref="ODataPath"/>.</returns>
public static IEnumerable<ODataPath> CreatePaths(IEdmModel model)
public IEdmModel Model { get; }
/// <summary>
/// Initializes a new instance of <see cref="AuthorizationProvider"/> class.
/// </summary>
/// <param name="model">The Edm model.</param>
public ODataPathProvider(IEdmModel model)
{
if (model == null || model.EntityContainer == null)
Model = model ?? throw new ArgumentNullException(nameof(model));
// if the entity container is null or empty. assign the path as empty.
if (model.EntityContainer == null)
{
return Enumerable.Empty<ODataPath>();
_allODataPaths = new List<ODataPath>();
}
IList<ODataPath> paths = new List<ODataPath>();
_allNavigationSource = new Lazy<IDictionary<IEdmEntityType, IList<IEdmNavigationSource>>>(
() => model.LoadAllNavigationSources(), false);
}
/// <summary>
/// Generate the list of <see cref="ODataPath"/>.
/// </summary>
/// <returns>The collection of build <see cref="ODataPath"/>.</returns>
public virtual IEnumerable<ODataPath> CreatePaths()
{
if (_allODataPaths != null)
{
return _allODataPaths;
}
_allODataPaths = new List<ODataPath>();
// entity set
foreach (IEdmEntitySet entitySet in model.EntityContainer.EntitySets())
foreach (IEdmEntitySet entitySet in Model.EntityContainer.EntitySets())
{
RetrieveNavigationSourcePaths(model, entitySet, paths);
RetrieveNavigationSourcePaths(entitySet);
}
// singleton
foreach (IEdmSingleton singleton in model.EntityContainer.Singletons())
foreach (IEdmSingleton singleton in Model.EntityContainer.Singletons())
{
RetrieveNavigationSourcePaths(model, singleton, paths);
RetrieveNavigationSourcePaths(singleton);
}
// bound operations
RetrieveBoundOperationPaths(model, paths);
RetrieveBoundOperationPaths();
// unbound operations
foreach (IEdmOperationImport import in model.EntityContainer.OperationImports())
foreach (IEdmOperationImport import in Model.EntityContainer.OperationImports())
{
paths.Add(new ODataPath(new ODataOperationImportSegment(import)));
_allODataPaths.Add(new ODataPath(new ODataOperationImportSegment(import)));
}
return paths;
return _allODataPaths;
}
/// <summary>
/// Retrieve the paths for <see cref="IEdmNavigationSource"/>.
/// </summary>
/// <param name="model">The Edm model.</param>
/// <param name="navigationSource">The navigation source.</param>
/// <param name="paths">The existing paths.</param>
private static void RetrieveNavigationSourcePaths(IEdmModel model, IEdmNavigationSource navigationSource, IList<ODataPath> paths)
private void RetrieveNavigationSourcePaths(IEdmNavigationSource navigationSource)
{
Debug.Assert(paths != null);
Debug.Assert(model != null);
Debug.Assert(navigationSource != null);
// navigation source itself
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(navigationSource));
paths.Add(path);
_allODataPaths.Add(path);
IEdmEntitySet entitySet = navigationSource as IEdmEntitySet;
IEdmEntityType entityType = navigationSource.EntityType();
@ -76,19 +98,20 @@ namespace Microsoft.OpenApi.OData.Edm
if (entitySet != null)
{
path.Push(new ODataKeySegment(entityType));
paths.Add(path.Clone());
_allODataPaths.Add(path.Clone());
}
// navigation property
foreach (IEdmNavigationProperty np in entityType.DeclaredNavigationProperties())
{
RetrieveNavigationPropertyPaths(model, np, path, paths);
RetrieveNavigationPropertyPaths(np, path);
}
if (entitySet != null)
{
path.Pop(); // end of entity
}
path.Pop(); // end of navigation source.
Debug.Assert(path.Any() == false);
}
@ -98,30 +121,32 @@ namespace Microsoft.OpenApi.OData.Edm
/// </summary>
/// <param name="navigationProperty">The navigation property.</param>
/// <param name="currentPath">The current OData path.</param>
private static void RetrieveNavigationPropertyPaths(IEdmModel model, IEdmNavigationProperty navigationProperty, ODataPath currentPath, IList<ODataPath> paths)
private void RetrieveNavigationPropertyPaths(IEdmNavigationProperty navigationProperty, ODataPath currentPath)
{
Debug.Assert(navigationProperty != null);
Debug.Assert(currentPath != null);
bool shouldExpand = ShouldExpandNavigationProperty(model, navigationProperty, currentPath);
// test ahead for the navigation expandable.
bool shouldExpand = ShouldExpandNavigationProperty(navigationProperty, currentPath);
// append a navigation property.
currentPath.Push(new ODataNavigationPropertySegment(navigationProperty));
paths.Add(currentPath.Clone());
_allODataPaths.Add(currentPath.Clone());
// append a navigation property key.
IEdmEntityType navEntityType = navigationProperty.ToEntityType();
if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
{
currentPath.Push(new ODataKeySegment(navEntityType));
paths.Add(currentPath.Clone());
_allODataPaths.Add(currentPath.Clone());
}
if (shouldExpand)
{
// expand to sub navigation properties
foreach (IEdmNavigationProperty subNavProperty in navEntityType.DeclaredNavigationProperties())
{
RetrieveNavigationPropertyPaths(model, subNavProperty, currentPath, paths);
RetrieveNavigationPropertyPaths(subNavProperty, currentPath);
}
}
@ -133,14 +158,23 @@ namespace Microsoft.OpenApi.OData.Edm
currentPath.Pop();
}
private static bool ShouldExpandNavigationProperty(IEdmModel model, IEdmNavigationProperty navigationProperty, ODataPath currentPath)
/// <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)
{
// only expand for the containment.
Debug.Assert(navigationProperty != null);
Debug.Assert(currentPath != null);
// not expand for the non-containment.
if (!navigationProperty.ContainsTarget)
{
return false;
}
// check the type is visited before, if visited, not expand it.
IEdmEntityType navEntityType = navigationProperty.ToEntityType();
foreach (ODataSegment segment in currentPath)
{
@ -150,22 +184,16 @@ namespace Microsoft.OpenApi.OData.Edm
}
}
if (FindNavigationSource(model, navEntityType).Any())
{
return false;
}
return true;
// check whether the navigation type used to define a navigation source.
// if so, not expand it.
return !_allNavigationSource.Value.ContainsKey(navEntityType);
}
private static void RetrieveBoundOperationPaths(IEdmModel model, IList<ODataPath> paths)
private void RetrieveBoundOperationPaths()
{
IList<ODataPath> npPaths = paths.Where(p => p.Kind == ODataPathKind.NavigationProperty).ToList();
IList<ODataPath> npPaths = _allODataPaths.Where(p => p.Kind == ODataPathKind.NavigationProperty).ToList();
var navigationSourceDic = model.EntityContainer.EntitySets().ToDictionary(o => o.EntityType(), o => o as IEdmNavigationSource);
foreach (var edmOperation in model.SchemaElements.OfType<IEdmOperation>().Where(e => e.IsBound))
foreach (var edmOperation in Model.SchemaElements.OfType<IEdmOperation>().Where(e => e.IsBound))
{
IEdmOperationParameter bindingParameter = edmOperation.Parameters.First();
IEdmTypeReference bindingType = bindingParameter.Type;
@ -184,18 +212,18 @@ namespace Microsoft.OpenApi.OData.Edm
IEdmEntityType bindingEntityType = bindingType.AsEntity().EntityDefinition();
bool found = false;
// 1. Search for correspoinding navigation source
var correspondingNavigationSource = FindNavigationSource(model, bindingEntityType);
if (correspondingNavigationSource.Any())
if (_allNavigationSource.Value.TryGetValue(bindingEntityType, out IList<IEdmNavigationSource> correspondingNavigationSources))
{
foreach(var ns in correspondingNavigationSource)
foreach(var ns in correspondingNavigationSources)
{
if (isCollection)
{
if (ns is IEdmEntitySet)
{
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataOperationSegment(edmOperation));
paths.Add(newPath);
_allODataPaths.Add(newPath);
found = true;
}
}
@ -204,14 +232,14 @@ namespace Microsoft.OpenApi.OData.Edm
if (ns is IEdmSingleton)
{
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataOperationSegment(edmOperation));
paths.Add(newPath);
_allODataPaths.Add(newPath);
found = true;
}
else
{
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataKeySegment(ns.EntityType()),
new ODataOperationSegment(edmOperation));
paths.Add(newPath);
_allODataPaths.Add(newPath);
found = true;
}
}
@ -224,7 +252,6 @@ namespace Microsoft.OpenApi.OData.Edm
}
// 2. Search for generated navigation property
// IList<ODataPath> npPaths = paths.Where(p => p.Kind == ODataPathKind.NavigationProperty).ToList();
foreach(var path in npPaths)
{
ODataNavigationPropertySegment npSegment = path.Segments.Last(s => s is ODataNavigationPropertySegment) as ODataNavigationPropertySegment;
@ -239,7 +266,7 @@ namespace Microsoft.OpenApi.OData.Edm
{
ODataPath newPath = path.Clone();
newPath.Push(new ODataOperationSegment(edmOperation));
paths.Add(newPath);
_allODataPaths.Add(newPath);
found = true;
}
}
@ -252,8 +279,7 @@ namespace Microsoft.OpenApi.OData.Edm
// 3. Search for derived
foreach(var baseType in bindingEntityType.FindAllBaseTypes())
{
var baseNavigationSource = FindNavigationSource(model, baseType);
if (baseNavigationSource.Any())
if (_allNavigationSource.Value.TryGetValue(baseType, out IList<IEdmNavigationSource> baseNavigationSource))
{
foreach (var ns in baseNavigationSource)
{
@ -263,7 +289,7 @@ namespace Microsoft.OpenApi.OData.Edm
{
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataTypeCastSegment(bindingEntityType),
new ODataOperationSegment(edmOperation));
paths.Add(newPath);
_allODataPaths.Add(newPath);
found = true;
}
}
@ -272,14 +298,14 @@ namespace Microsoft.OpenApi.OData.Edm
if (ns is IEdmSingleton)
{
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataTypeCastSegment(bindingEntityType), new ODataOperationSegment(edmOperation));
paths.Add(newPath);
_allODataPaths.Add(newPath);
found = true;
}
else
{
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataKeySegment(ns.EntityType()), new ODataTypeCastSegment(bindingEntityType),
new ODataOperationSegment(edmOperation));
paths.Add(newPath);
_allODataPaths.Add(newPath);
found = true;
}
}
@ -288,12 +314,5 @@ namespace Microsoft.OpenApi.OData.Edm
}
}
}
private static IEnumerable<IEdmNavigationSource> FindNavigationSource(IEdmModel model, IEdmEntityType entityType)
{
IEnumerable<IEdmNavigationSource> returnEnumerable1 = model.EntityContainer.EntitySets().Where(e => e.EntityType() == entityType);
IEnumerable<IEdmNavigationSource> returnEnumerable2 = model.EntityContainer.Singletons().Where(e => e.EntityType() == entityType);
return returnEnumerable1.Concat(returnEnumerable2);
}
}
}

View file

@ -34,9 +34,9 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
IEdmModel model = EdmModelHelper.GraphBetaModel;
//ODataContext context = new ODataContext(model);
var paths = ODataPathProvider.CreatePaths(model);
ODataPathProvider provider = new ODataPathProvider(model);
var paths = provider.CreatePaths();
Assert.NotNull(paths);
}

View file

@ -91,9 +91,10 @@ 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();
var paths = ODataPathProvider.CreatePaths(model);
using (StreamWriter file = new StreamWriter(@"e:\work\openapi\Graph.Beta.OData.AllPath4.txt"))
using (StreamWriter file = new StreamWriter(@"e:\work\openapi\Graph.Beta.OData.AllPath5.txt"))
{
foreach (var path in paths)
{