public void TargetOnEntitySetReturnsCorrectNavigationRestrictionsValue(EdmVocabularyAnnotationSerializationLocation location)
        {
            // Arrange
            const string template = @"
                <Annotations Target=""NS.Default/Calendars"">
                  {0}
                </Annotations>";

            IEdmModel model = GetEdmModel(template, location);

            Assert.NotNull(model); // guard

            IEdmEntitySet calendars = model.EntityContainer.FindEntitySet("Calendars");

            // Act
            NavigationRestrictionsType navigation = model.GetRecord <NavigationRestrictionsType>(calendars);

            // Assert
            VerifyNavigationRestrictions(navigation);

            NavigationPropertyRestriction navRestriction = navigation.RestrictedProperties.Last();

            Assert.NotNull(navRestriction.Navigability);
            Assert.Equal(NavigationType.None, navRestriction.Navigability.Value);
            Assert.Equal("xyz", navRestriction.NavigationProperty);
            Assert.True(navigation.IsRestrictedProperty("xyz"));
        }
        public void TargetOnEntitySetReturnsCorrectNavigationRestrictionsValue(EdmVocabularyAnnotationSerializationLocation location)
        {
            // Arrange
            const string outOfLineTemplate = @"
                <Annotations Target=""NS.Default/Calendars"">
                  {0}
                </Annotations>";

            string navigationAnnotation = @"
                <Annotation Term=""Org.OData.Capabilities.V1.NavigationRestrictions"" >
                  <Record>
                    <PropertyValue Property=""Navigability"" >
                      <EnumMember>Org.OData.Capabilities.V1.NavigationType/Recursive</EnumMember>
                    </PropertyValue>
                    <PropertyValue Property=""RestrictedProperties"" >
                      <Collection>
                        <Record>
                          <PropertyValue Property=""Navigability"" >
                            <EnumMember>Org.OData.Capabilities.V1.NavigationType/Single</EnumMember>
                          </PropertyValue>
                          <PropertyValue Property=""NavigationProperty"" NavigationPropertyPath=""abc"" />
                         </Record>
                         <Record>
                          <PropertyValue Property=""Navigability"" >
                            <EnumMember>Org.OData.Capabilities.V1.NavigationType/None</EnumMember>
                          </PropertyValue>
                          <PropertyValue Property=""NavigationProperty"" NavigationPropertyPath=""xyz"" />
                         </Record>
                       </Collection>
                     </PropertyValue>
                   </Record>
                 </Annotation>";

            IEdmModel model;
            if (location == EdmVocabularyAnnotationSerializationLocation.OutOfLine)
            {
                navigationAnnotation = string.Format(outOfLineTemplate, navigationAnnotation);
                model = CapabilitiesModelHelper.GetEdmModelOutline(navigationAnnotation);
            }
            else
            {
                model = CapabilitiesModelHelper.GetEdmModelTypeInline(navigationAnnotation);
            }

            IEdmEntitySet calendars = model.EntityContainer.FindEntitySet("Calendars");

            // Act
            NavigationRestrictions navigation = new NavigationRestrictions();
            bool result = navigation.Load(model, calendars);

            // Assert
            Assert.True(result);
            VerifyNavigationRestrictions(navigation);

            NavigationPropertyRestriction navRestriction = navigation.RestrictedProperties.Last();
            Assert.NotNull(navRestriction.Navigability);
            Assert.Equal(NavigationType.None, navRestriction.Navigability.Value);
            Assert.Equal("xyz", navRestriction.NavigationProperty);
            Assert.True(navigation.IsRestrictedProperty("xyz"));
        }
Пример #3
0
        /// <inheritdoc/>
        protected override void SetOperations(OpenApiPathItem item)
        {
            IEdmEntitySet             entitySet = NavigationSource as IEdmEntitySet;
            IEdmVocabularyAnnotatable target    = entitySet;

            if (target == null)
            {
                target = NavigationSource as IEdmSingleton;
            }

            string navigationPropertyPath = String.Join("/",
                                                        Path.Segments.Where(s => !(s is ODataKeySegment || s is ODataNavigationSourceSegment)).Select(e => e.Identifier));

            NavigationRestrictionsType    navigation  = Context.Model.GetRecord <NavigationRestrictionsType>(target, CapabilitiesConstants.NavigationRestrictions);
            NavigationPropertyRestriction restriction = navigation?.RestrictedProperties?.FirstOrDefault(r => r.NavigationProperty == navigationPropertyPath);

            // verify using individual first
            if (restriction != null && restriction.Navigability != null && restriction.Navigability.Value == NavigationType.None)
            {
                return;
            }

            if (restriction == null || restriction.Navigability == null)
            {
                // if the individual has not navigability setting, use the global navigability setting
                if (navigation != null && navigation.Navigability != null && navigation.Navigability.Value == NavigationType.None)
                {
                    // Default navigability for all navigation properties of the annotation target.
                    // Individual navigation properties can override this value via `RestrictedProperties/Navigability`.
                    return;
                }
            }

            // So far, we only consider the non-containment
            Debug.Assert(!NavigationProperty.ContainsTarget);

            // Create the ref
            if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
            {
                ODataSegment penultimateSegment = Path.Segments.Reverse().Skip(1).First();
                if (penultimateSegment is ODataKeySegment)
                {
                    // Collection-valued: DELETE ~/entityset/{key}/collection-valued-Nav/{key}/$ref
                    AddDeleteOperation(item, restriction);
                }
                else
                {
                    AddReadOperation(item, restriction);
                    AddInsertOperation(item, restriction);
                }
            }
            else
            {
                AddReadOperation(item, restriction);
                AddUpdateOperation(item, restriction);
                AddDeleteOperation(item, restriction);
            }
        }
Пример #4
0
        private void AddDeleteOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction)
        {
            DeleteRestrictionsType delete = restriction?.DeleteRestrictions;

            if (delete == null || delete.IsDeletable)
            {
                AddOperation(item, OperationType.Delete);
            }
        }
Пример #5
0
        private void AddUpdateOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction)
        {
            UpdateRestrictionsType update = restriction?.UpdateRestrictions;

            if (update == null || update.IsUpdatable)
            {
                AddOperation(item, OperationType.Put);
            }
        }
Пример #6
0
        private void AddInsertOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction)
        {
            InsertRestrictionsType insert = restriction?.InsertRestrictions;

            if (insert == null || insert.IsInsertable)
            {
                AddOperation(item, OperationType.Post);
            }
        }
        /// <inheritdoc/>
        protected override void SetOperations(OpenApiPathItem item)
        {
            IEdmEntitySet             entitySet = NavigationSource as IEdmEntitySet;
            IEdmVocabularyAnnotatable target    = entitySet;

            if (target == null)
            {
                target = NavigationSource as IEdmSingleton;
            }

            NavigationRestrictionsType navSourceRestrictionType = Context.Model.GetRecord <NavigationRestrictionsType>(target, CapabilitiesConstants.NavigationRestrictions);
            NavigationRestrictionsType navPropRestrictionType   = Context.Model.GetRecord <NavigationRestrictionsType>(NavigationProperty, CapabilitiesConstants.NavigationRestrictions);

            NavigationPropertyRestriction restriction = navSourceRestrictionType?.RestrictedProperties?
                                                        .FirstOrDefault(r => r.NavigationProperty == Path.NavigationPropertyPath())
                                                        ?? navPropRestrictionType?.RestrictedProperties?.FirstOrDefault();

            // Check whether the navigation property should be part of the path
            if (EdmModelHelper.NavigationRestrictionsAllowsNavigability(navSourceRestrictionType, restriction) == false ||
                EdmModelHelper.NavigationRestrictionsAllowsNavigability(navPropRestrictionType, restriction) == false)
            {
                return;
            }

            // containment: Get / (Post - Collection | Patch - Single)
            // non-containment: Get
            AddGetOperation(item, restriction);

            if (NavigationProperty.ContainsTarget)
            {
                if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
                {
                    if (LastSegmentIsKeySegment)
                    {
                        UpdateRestrictionsType updateEntity = Context.Model.GetRecord <UpdateRestrictionsType>(_entityType);
                        if (updateEntity?.IsUpdatable ?? true)
                        {
                            AddUpdateOperation(item, restriction);
                        }
                    }
                    else
                    {
                        InsertRestrictionsType insert = restriction?.InsertRestrictions;
                        if (insert?.IsInsertable ?? true)
                        {
                            AddOperation(item, OperationType.Post);
                        }
                    }
                }
                else
                {
                    AddUpdateOperation(item, restriction);
                }
            }

            AddDeleteOperation(item, restriction);
        }
Пример #8
0
        private void AddReadOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction)
        {
            ReadRestrictionsType read = restriction?.ReadRestrictions;

            if (read == null || read.IsReadable)
            {
                AddOperation(item, OperationType.Get);
            }
        }
Пример #9
0
        /// <summary>
        /// Verifies whether the provided navigation restrictions allow for navigability of a navigation property.
        /// </summary>
        /// <param name="restrictionType">The <see cref="NavigationRestrictionsType"/>.</param>
        /// <param name="restrictionProperty">The <see cref="NavigationPropertyRestriction"/>.</param>
        /// <returns></returns>
        internal static bool NavigationRestrictionsAllowsNavigability(
            NavigationRestrictionsType restrictionType,
            NavigationPropertyRestriction restrictionProperty)
        {
            // Verify using individual navigation restriction first
            if (restrictionProperty?.Navigability != null && restrictionProperty.Navigability.Value == NavigationType.None)
            {
                return(false);
            }

            // if the individual has no navigability setting, use the global navigability setting
            // Default navigability for all navigation properties of the annotation target.
            // Individual navigation properties can override this value via `RestrictedProperties/Navigability`.
            return(restrictionProperty?.Navigability != null || restrictionType == null || restrictionType.IsNavigable);
        }
        private void AddGetOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction)
        {
            ReadRestrictionsType read = restriction?.ReadRestrictions;

            if (read == null)
            {
                AddOperation(item, OperationType.Get);
                return;
            }

            if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
            {
                // TODO: $ref also supports Get ?
                if (LastSegmentIsKeySegment)
                {
                    if (read.ReadByKeyRestrictions != null && read.ReadByKeyRestrictions.Readable != null)
                    {
                        if (read.ReadByKeyRestrictions.Readable.Value)
                        {
                            AddOperation(item, OperationType.Get);
                        }
                    }
                    else
                    {
                        ReadRestrictionsType readEntity = Context.Model.GetRecord <ReadRestrictionsType>(_entityType);
                        if (readEntity?.IsReadable ?? true)
                        {
                            AddOperation(item, OperationType.Get);
                        }
                    }
                }
                else
                {
                    if (read.IsReadable)
                    {
                        AddOperation(item, OperationType.Get);
                    }
                }
            }
            else
            {
                Debug.Assert(LastSegmentIsKeySegment == false);
                if (read.IsReadable)
                {
                    AddOperation(item, OperationType.Get);
                }
            }
        }
        private static void VerifyNavigationRestrictions(NavigationRestrictions navigation)
        {
            Assert.NotNull(navigation);
            Assert.True(navigation.IsNavigable);

            Assert.NotNull(navigation.Navigability);
            Assert.Equal(NavigationType.Recursive, navigation.Navigability.Value);

            Assert.NotNull(navigation.RestrictedProperties);
            Assert.Equal(2, navigation.RestrictedProperties.Count);

            NavigationPropertyRestriction navRestriction = navigation.RestrictedProperties.First();
            Assert.NotNull(navRestriction.Navigability);
            Assert.Equal(NavigationType.Single, navRestriction.Navigability.Value);
            Assert.Equal("abc", navRestriction.NavigationProperty);
            Assert.False(navigation.IsRestrictedProperty("abc"));
        }
        private void AddGetOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction)
        {
            ReadRestrictionsType read = restriction?.ReadRestrictions;

            if (read == null)
            {
                AddOperation(item, OperationType.Get);
                return;
            }

            if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
            {
                if (LastSegmentIsKeySegment)
                {
                    if (read.ReadByKeyRestrictions != null && read.ReadByKeyRestrictions.Readable != null)
                    {
                        if (read.ReadByKeyRestrictions.Readable.Value)
                        {
                            AddOperation(item, OperationType.Get);
                        }
                    }
                    else
                    {
                        if (read.IsReadable)
                        {
                            AddOperation(item, OperationType.Get);
                        }
                    }
                }
                else
                {
                    if (read.IsReadable)
                    {
                        AddOperation(item, OperationType.Get);
                    }
                }
            }
            else
            {
                Debug.Assert(LastSegmentIsKeySegment == false);
                if (read.IsReadable)
                {
                    AddOperation(item, OperationType.Get);
                }
            }
        }
        private void AddDeleteOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction)
        {
            Debug.Assert(!LastSegmentIsRefSegment);

            if (!NavigationProperty.ContainsTarget)
            {
                return;
            }

            DeleteRestrictionsType delete = restriction?.DeleteRestrictions;

            if (delete == null || delete.IsDeletable)
            {
                if (NavigationProperty.TargetMultiplicity() != EdmMultiplicity.Many || LastSegmentIsKeySegment)
                {
                    AddOperation(item, OperationType.Delete);
                }

                return;
            }
        }
        private static void VerifyNavigationRestrictions(NavigationRestrictionsType navigation)
        {
            Assert.NotNull(navigation);
            Assert.True(navigation.IsNavigable);

            Assert.NotNull(navigation.Navigability);
            Assert.Equal(NavigationType.Recursive, navigation.Navigability.Value);

            Assert.NotNull(navigation.RestrictedProperties);
            Assert.Equal(2, navigation.RestrictedProperties.Count);

            Assert.False(navigation.IsRestrictedProperty("abc"));
            Assert.True(navigation.IsRestrictedProperty("xyz"));

            // #1
            NavigationPropertyRestriction navRestriction = navigation.RestrictedProperties.First();

            Assert.NotNull(navRestriction.Navigability);
            Assert.Equal(NavigationType.Single, navRestriction.Navigability.Value);
            Assert.Equal("abc", navRestriction.NavigationProperty);

            Assert.NotNull(navRestriction.SkipSupported);
            Assert.False(navRestriction.SkipSupported.Value);

            Assert.Null(navRestriction.TopSupported);
            Assert.Null(navRestriction.OptimisticConcurrencyControl);

            // #2
            navRestriction = navigation.RestrictedProperties.Last();
            Assert.NotNull(navRestriction.Navigability);
            Assert.Equal(NavigationType.None, navRestriction.Navigability.Value);
            Assert.Equal("xyz", navRestriction.NavigationProperty);

            Assert.Null(navRestriction.SkipSupported);
            Assert.Null(navRestriction.TopSupported);

            Assert.NotNull(navRestriction.OptimisticConcurrencyControl);
            Assert.True(navRestriction.OptimisticConcurrencyControl.Value);
        }
        private void AddDeleteOperation(OpenApiPathItem item, NavigationPropertyRestriction restriction)
        {
            Debug.Assert(!LastSegmentIsRefSegment);

            if (!NavigationProperty.ContainsTarget)
            {
                return;
            }

            DeleteRestrictionsType delete       = restriction?.DeleteRestrictions;
            DeleteRestrictionsType deleteEntity = Context.Model.GetRecord <DeleteRestrictionsType>(_entityType);
            bool isDeletableDefault             = delete == null && deleteEntity == null;

            if (isDeletableDefault ||
                (delete?.IsDeletable ?? false) ||
                (deleteEntity?.IsDeletable ?? false))
            {
                if (NavigationProperty.TargetMultiplicity() != EdmMultiplicity.Many || LastSegmentIsKeySegment)
                {
                    AddOperation(item, OperationType.Delete);
                }
                return;
            }
        }
Пример #16
0
        /// <inheritdoc/>
        protected override void SetOperations(OpenApiPathItem item)
        {
            IEdmEntitySet             entitySet = NavigationSource as IEdmEntitySet;
            IEdmVocabularyAnnotatable target    = entitySet;

            if (target == null)
            {
                target = NavigationSource as IEdmSingleton;
            }

            string navigationPropertyPath = String.Join("/",
                                                        Path.Segments.Where(s => !(s is ODataKeySegment || s is ODataNavigationSourceSegment)).Select(e => e.Identifier));

            NavigationRestrictionsType    navigation  = Context.Model.GetRecord <NavigationRestrictionsType>(target, CapabilitiesConstants.NavigationRestrictions);
            NavigationPropertyRestriction restriction = navigation?.RestrictedProperties?.FirstOrDefault(r => r.NavigationProperty == navigationPropertyPath);

            // verify using individual first
            if (restriction != null && restriction.Navigability != null && restriction.Navigability.Value == NavigationType.None)
            {
                return;
            }

            if (restriction == null || restriction.Navigability == null)
            {
                // if the individual has not navigability setting, use the global navigability setting
                if (navigation != null && navigation.Navigability != null && navigation.Navigability.Value == NavigationType.None)
                {
                    // Default navigability for all navigation properties of the annotation target.
                    // Individual navigation properties can override this value via `RestrictedProperties/Navigability`.
                    return;
                }
            }

            // So far, we only consider the non-containment
            Debug.Assert(!NavigationProperty.ContainsTarget);

            // It seems OData supports to "GetRef, DeleteRef",
            // Here at this time,let's only consider the "delete"
            ReadRestrictionsType read = restriction?.ReadRestrictions;

            if (read == null || read.IsReadable)
            {
                AddOperation(item, OperationType.Get);
            }

            // Create the ref
            if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
            {
                InsertRestrictionsType insert = restriction?.InsertRestrictions;
                if (insert == null || insert.IsInsertable)
                {
                    AddOperation(item, OperationType.Post);
                }
            }
            else
            {
                UpdateRestrictionsType update = restriction?.UpdateRestrictions;
                if (update == null || update.IsUpdatable)
                {
                    AddOperation(item, OperationType.Put);
                }

                // delete the link
                DeleteRestrictionsType delete = restriction?.DeleteRestrictions;
                if (delete == null || delete.IsDeletable)
                {
                    AddOperation(item, OperationType.Delete);
                }
            }
        }
        /// <inheritdoc/>
        protected override void SetOperations(OpenApiPathItem item)
        {
            IEdmEntitySet             entitySet = NavigationSource as IEdmEntitySet;
            IEdmVocabularyAnnotatable target    = entitySet;

            if (target == null)
            {
                target = NavigationSource as IEdmSingleton;
            }

            string navigationPropertyPath = String.Join("/",
                                                        Path.Segments.Where(s => !(s is ODataKeySegment || s is ODataNavigationSourceSegment)).Select(e => e.Identifier));

            NavigationRestrictionsType    navigation  = Context.Model.GetRecord <NavigationRestrictionsType>(target, CapabilitiesConstants.NavigationRestrictions);
            NavigationPropertyRestriction restriction = navigation?.RestrictedProperties?.FirstOrDefault(r => r.NavigationProperty == navigationPropertyPath);

            // verify using individual first
            if (restriction != null && restriction.Navigability != null && restriction.Navigability.Value == NavigationType.None)
            {
                return;
            }

            if (restriction == null || restriction.Navigability == null)
            {
                // if the individual has not navigability setting, use the global navigability setting
                if (navigation != null && navigation.Navigability != null && navigation.Navigability.Value == NavigationType.None)
                {
                    // Default navigability for all navigation properties of the annotation target.
                    // Individual navigation properties can override this value via `RestrictedProperties/Navigability`.
                    return;
                }
            }

            // how about delete?
            // contaiment: Get / (Post - Collection | Patch - Single)
            // non-containment: only Get
            AddGetOperation(item, restriction);

            if (NavigationProperty.ContainsTarget)
            {
                if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
                {
                    if (LastSegmentIsKeySegment)
                    {
                        // Need to check this scenario is valid or not?
                        UpdateRestrictionsType update = restriction?.UpdateRestrictions;
                        if (update == null || update.IsUpdatable)
                        {
                            AddOperation(item, OperationType.Patch);
                        }
                    }
                    else
                    {
                        InsertRestrictionsType insert = restriction?.InsertRestrictions;
                        if (insert == null || insert.IsInsertable)
                        {
                            AddOperation(item, OperationType.Post);
                        }
                    }
                }
                else
                {
                    UpdateRestrictionsType update = restriction?.UpdateRestrictions;
                    if (update == null || update.IsUpdatable)
                    {
                        AddOperation(item, OperationType.Patch);
                    }
                }
            }
        }
        /// <summary>
        /// Retrieve the path for <see cref="IEdmNavigationProperty"/>.
        /// </summary>
        /// <param name="navigationProperty">The navigation property.</param>
        /// <param name="count">The count restrictions.</param>
        /// <param name="currentPath">The current OData path.</param>
        /// <param name="convertSettings">The settings for the current conversion.</param>
        /// <param name="visitedNavigationProperties">A stack that holds the visited navigation properties in the <paramref name="currentPath"/>.</param>
        private void RetrieveNavigationPropertyPaths(
            IEdmNavigationProperty navigationProperty,
            CountRestrictionsType count,
            ODataPath currentPath,
            OpenApiConvertSettings convertSettings,
            Stack <string> visitedNavigationProperties = null)
        {
            Debug.Assert(navigationProperty != null);
            Debug.Assert(currentPath != null);

            if (visitedNavigationProperties == null)
            {
                visitedNavigationProperties = new();
            }

            string navPropFullyQualifiedName = $"{navigationProperty.DeclaringType.FullTypeName()}/{navigationProperty.Name}";

            // Check whether the navigation property has already been navigated in the path.
            if (visitedNavigationProperties.Contains(navPropFullyQualifiedName))
            {
                return;
            }

            // Get the annotatable navigation source for this navigation property.
            IEdmVocabularyAnnotatable  annotatableNavigationSource = (currentPath.FirstSegment as ODataNavigationSourceSegment)?.NavigationSource as IEdmVocabularyAnnotatable;
            NavigationRestrictionsType navSourceRestrictionType    = null;
            NavigationRestrictionsType navPropRestrictionType      = null;

            // Get the NavigationRestrictions referenced by this navigation property: Can be defined in the navigation source or in-lined in the navigation property.
            if (annotatableNavigationSource != null)
            {
                navSourceRestrictionType = _model.GetRecord <NavigationRestrictionsType>(annotatableNavigationSource, CapabilitiesConstants.NavigationRestrictions);
                navPropRestrictionType   = _model.GetRecord <NavigationRestrictionsType>(navigationProperty, CapabilitiesConstants.NavigationRestrictions);
            }

            NavigationPropertyRestriction restriction = navSourceRestrictionType?.RestrictedProperties?
                                                        .FirstOrDefault(r => r.NavigationProperty == currentPath.NavigationPropertyPath(navigationProperty.Name))
                                                        ?? navPropRestrictionType?.RestrictedProperties?.FirstOrDefault();

            // Check whether the navigation property should be part of the path
            if (!EdmModelHelper.NavigationRestrictionsAllowsNavigability(navSourceRestrictionType, restriction) ||
                !EdmModelHelper.NavigationRestrictionsAllowsNavigability(navPropRestrictionType, restriction))
            {
                return;
            }

            // Whether to expand the navigation property.
            bool shouldExpand = navigationProperty.ContainsTarget;

            // Append a navigation property.
            currentPath.Push(new ODataNavigationPropertySegment(navigationProperty));
            AppendPath(currentPath.Clone());
            visitedNavigationProperties.Push(navPropFullyQualifiedName);

            // Check whether a collection-valued navigation property should be indexed by key value(s).
            bool indexableByKey = restriction?.IndexableByKey ?? true;

            if (indexableByKey)
            {
                IEdmEntityType navEntityType       = navigationProperty.ToEntityType();
                var            targetsMany         = navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many;
                var            propertyPath        = navigationProperty.GetPartnerPath()?.Path;
                var            propertyPathIsEmpty = string.IsNullOrEmpty(propertyPath);

                if (targetsMany)
                {
                    if (propertyPathIsEmpty ||
                        (count?.IsNonCountableNavigationProperty(propertyPath) ?? true))
                    {
                        // ~/entityset/{key}/collection-valued-Nav/$count
                        CreateCountPath(currentPath, convertSettings);
                    }
                }
                // ~/entityset/{key}/collection-valued-Nav/subtype
                // ~/entityset/{key}/single-valued-Nav/subtype
                CreateTypeCastPaths(currentPath, convertSettings, navEntityType, navigationProperty, targetsMany);

                if ((navSourceRestrictionType?.Referenceable ?? false) ||
                    (navPropRestrictionType?.Referenceable ?? false))
                {
                    // Referenceable navigation properties
                    // Single-Valued: ~/entityset/{key}/single-valued-Nav/$ref
                    // Collection-valued: ~/entityset/{key}/collection-valued-Nav/$ref?$id='{navKey}'
                    CreateRefPath(currentPath);

                    if (targetsMany)
                    {
                        // Collection-valued: DELETE ~/entityset/{key}/collection-valued-Nav/{key}/$ref
                        currentPath.Push(new ODataKeySegment(navEntityType));
                        CreateRefPath(currentPath);

                        CreateTypeCastPaths(currentPath, convertSettings, navEntityType, navigationProperty, false); // ~/entityset/{key}/collection-valued-Nav/{id}/subtype
                    }

                    // Get possible stream paths for the navigation entity type
                    RetrieveMediaEntityStreamPaths(navEntityType, currentPath);

                    // Get the paths for the navigation property entity type properties of type complex
                    RetrieveComplexPropertyPaths(navEntityType, currentPath, convertSettings);
                }
                else
                {
                    // append a navigation property key.
                    if (targetsMany)
                    {
                        currentPath.Push(new ODataKeySegment(navEntityType));
                        AppendPath(currentPath.Clone());

                        CreateTypeCastPaths(currentPath, convertSettings, navEntityType, navigationProperty, false); // ~/entityset/{key}/collection-valued-Nav/{id}/subtype
                    }

                    // Get possible stream paths for the navigation entity type
                    RetrieveMediaEntityStreamPaths(navEntityType, currentPath);

                    // Get the paths for the navigation property entity type properties of type complex
                    RetrieveComplexPropertyPaths(navEntityType, currentPath, convertSettings);

                    if (shouldExpand)
                    {
                        // expand to sub navigation properties
                        foreach (IEdmNavigationProperty subNavProperty in navEntityType.DeclaredNavigationProperties())
                        {
                            if (CanFilter(subNavProperty))
                            {
                                RetrieveNavigationPropertyPaths(subNavProperty, count, currentPath, convertSettings, visitedNavigationProperties);
                            }
                        }
                    }
                }

                if (targetsMany)
                {
                    currentPath.Pop();
                }
            }
            currentPath.Pop();
            visitedNavigationProperties.Pop();
        }