diff --git a/src/Microsoft.OpenApi.OData.Reader/Edm/ODataDollarCountSegment.cs b/src/Microsoft.OpenApi.OData.Reader/Edm/ODataDollarCountSegment.cs index 2109dad..5cad240 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Edm/ODataDollarCountSegment.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Edm/ODataDollarCountSegment.cs @@ -12,6 +12,10 @@ namespace Microsoft.OpenApi.OData.Edm /// public class ODataDollarCountSegment : ODataSegment { + /// + /// Get the static instance of $count segment. + /// + internal static ODataDollarCountSegment Instance = new(); /// public override ODataSegmentKind Kind => ODataSegmentKind.DollarCount; diff --git a/src/Microsoft.OpenApi.OData.Reader/Edm/ODataPathProvider.cs b/src/Microsoft.OpenApi.OData.Reader/Edm/ODataPathProvider.cs index 580d27a..2885827 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Edm/ODataPathProvider.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Edm/ODataPathProvider.cs @@ -102,7 +102,7 @@ namespace Microsoft.OpenApi.OData.Edm private IEnumerable MergePaths() { - List allODataPaths = new List(); + List allODataPaths = new(); foreach (var item in _allNavigationSourcePaths.Values) { allODataPaths.AddRange(item); @@ -127,6 +127,7 @@ namespace Microsoft.OpenApi.OData.Edm ODataPathKind kind = path.Kind; switch(kind) { + case ODataPathKind.DollarCount: case ODataPathKind.Entity: case ODataPathKind.EntitySet: case ODataPathKind.Singleton: @@ -143,8 +144,7 @@ namespace Microsoft.OpenApi.OData.Edm case ODataPathKind.NavigationProperty: case ODataPathKind.Ref: - ODataNavigationPropertySegment navigationPropertySegment = path.Last(p => p is ODataNavigationPropertySegment) - as ODataNavigationPropertySegment; + ODataNavigationPropertySegment navigationPropertySegment = path.OfType().Last(); if (!_allNavigationPropertyPaths.TryGetValue(navigationPropertySegment.EntityType, out IList npList)) { @@ -174,15 +174,17 @@ namespace Microsoft.OpenApi.OData.Edm Debug.Assert(navigationSource != null); // navigation source itself - ODataPath path = new ODataPath(new ODataNavigationSourceSegment(navigationSource)); + ODataPath path = new(new ODataNavigationSourceSegment(navigationSource)); AppendPath(path.Clone()); IEdmEntitySet entitySet = navigationSource as IEdmEntitySet; IEdmEntityType entityType = navigationSource.EntityType(); - // for entity set, create a path with key + // for entity set, create a path with key and a $count path if (entitySet != null) { + CreateCountPath(path); + path.Push(new ODataKeySegment(entityType)); AppendPath(path.Clone()); } @@ -275,6 +277,13 @@ namespace Microsoft.OpenApi.OData.Edm if (restriction == null || restriction.IndexableByKey == true) { IEdmEntityType navEntityType = navigationProperty.ToEntityType(); + var targetsMany = navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many; + + if (targetsMany) + { + // ~/entityset/{key}/collection-valued-Nav/$count + CreateCountPath(currentPath); + } if (!navigationProperty.ContainsTarget) { @@ -283,7 +292,7 @@ namespace Microsoft.OpenApi.OData.Edm // Collection-valued: ~/entityset/{key}/collection-valued-Nav/$ref?$id ={navKey} CreateRefPath(currentPath); - if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) + if (targetsMany) { // Collection-valued: DELETE ~/entityset/{key}/collection-valued-Nav/{key}/$ref currentPath.Push(new ODataKeySegment(navEntityType)); @@ -296,7 +305,7 @@ namespace Microsoft.OpenApi.OData.Edm else { // append a navigation property key. - if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) + if (targetsMany) { currentPath.Push(new ODataKeySegment(navEntityType)); AppendPath(currentPath.Clone()); @@ -318,7 +327,7 @@ namespace Microsoft.OpenApi.OData.Edm } } - if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) + if (targetsMany) { currentPath.Pop(); } @@ -360,6 +369,16 @@ namespace Microsoft.OpenApi.OData.Edm newPath.Push(ODataRefSegment.Instance); // $ref AppendPath(newPath); } + /// + /// Create $count paths. + /// + /// The current OData path. + private void CreateCountPath(ODataPath currentPath) { + if(currentPath == null) throw new ArgumentNullException(nameof(currentPath)); + var countPath = currentPath.Clone(); + countPath.Push(ODataDollarCountSegment.Instance); + AppendPath(countPath); + } /// /// Retrieve all bounding . @@ -436,7 +455,11 @@ namespace Microsoft.OpenApi.OData.Edm } } } - + private static readonly HashSet _oDataPathKindsToSkipForOperations = new HashSet() { + ODataPathKind.EntitySet, + ODataPathKind.MediaEntity, + ODataPathKind.DollarCount + }; private bool AppendBoundOperationOnNavigationSourcePath(IEdmOperation edmOperation, bool isCollection, IEdmEntityType bindingEntityType) { bool found = false; @@ -448,8 +471,7 @@ namespace Microsoft.OpenApi.OData.Edm foreach (var subPath in value) { if ((isCollection && subPath.Kind == ODataPathKind.EntitySet) || - (!isCollection && subPath.Kind != ODataPathKind.EntitySet && - subPath.Kind != ODataPathKind.MediaEntity)) + (!isCollection && !_oDataPathKindsToSkipForOperations.Contains(subPath.Kind))) { ODataPath newPath = subPath.Clone(); newPath.Push(new ODataOperationSegment(edmOperation, isEscapedFunction)); @@ -461,7 +483,9 @@ namespace Microsoft.OpenApi.OData.Edm return found; } - + private static readonly HashSet _pathKindToSkipForNavigationProperties = new () { + ODataPathKind.Ref, + }; private bool AppendBoundOperationOnNavigationPropertyPath(IEdmOperation edmOperation, bool isCollection, IEdmEntityType bindingEntityType) { bool found = false; @@ -469,13 +493,8 @@ namespace Microsoft.OpenApi.OData.Edm if (_allNavigationPropertyPaths.TryGetValue(bindingEntityType, out IList value)) { - foreach (var path in value) + foreach (var path in value.Where(x => !_pathKindToSkipForNavigationProperties.Contains(x.Kind))) { - if (path.Kind == ODataPathKind.Ref) - { - continue; - } - ODataNavigationPropertySegment npSegment = path.Segments.Last(s => s is ODataNavigationPropertySegment) as ODataNavigationPropertySegment; if (!npSegment.NavigationProperty.ContainsTarget) @@ -596,15 +615,9 @@ namespace Microsoft.OpenApi.OData.Edm { if (_allNavigationPropertyPaths.TryGetValue(baseType, out IList paths)) { - foreach (var path in paths) + foreach (var path in paths.Where(x => !_pathKindToSkipForNavigationProperties.Contains(x.Kind))) { - if (path.Kind == ODataPathKind.Ref) - { - continue; - } - - var npSegment = path.Segments.Last(s => s is ODataNavigationPropertySegment) - as ODataNavigationPropertySegment; + var npSegment = path.Segments.OfType().LastOrDefault(); if (npSegment == null) { continue;