/// <summary> /// Retrieve the path for <see cref="IEdmNavigationProperty"/>. /// </summary> /// <param name="navigationProperty">The navigation property.</param> /// <param name="currentPath">The current OData path.</param> private void RetrieveNavigationPropertyPaths(IEdmNavigationProperty navigationProperty, ODataPath currentPath) { Debug.Assert(navigationProperty != null); Debug.Assert(currentPath != null); // test the expandable for the navigation property. bool shouldExpand = ShouldExpandNavigationProperty(navigationProperty, currentPath); // append a navigation property. currentPath.Push(new ODataNavigationPropertySegment(navigationProperty)); AppendPath(currentPath.Clone()); if (!navigationProperty.ContainsTarget) { // Non-Contained // Single-Valued: DELETE ~/entityset/{key}/single-valued-Nav/$ref // collection-valued: DELETE ~/entityset/{key}/collection-valued-Nav/$ref?$id ={ navKey} ODataPath newPath = currentPath.Clone(); newPath.Push(ODataRefSegment.Instance); // $ref AppendPath(newPath); } else { IEdmEntityType navEntityType = navigationProperty.ToEntityType(); // append a navigation property key. if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { currentPath.Push(new ODataKeySegment(navEntityType)); AppendPath(currentPath.Clone()); if (!navigationProperty.ContainsTarget) { // TODO: Shall we add "$ref" after {key}, and only support delete? // ODataPath newPath = currentPath.Clone(); // newPath.Push(ODataRefSegment.Instance); // $ref // AppendPath(newPath); } } if (shouldExpand) { // expand to sub navigation properties foreach (IEdmNavigationProperty subNavProperty in navEntityType.DeclaredNavigationProperties()) { if (CanFilter(subNavProperty)) { RetrieveNavigationPropertyPaths(subNavProperty, currentPath); } } } // Get possible navigation property stream paths RetrieveMediaEntityStreamPaths(navEntityType, currentPath); if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { currentPath.Pop(); } } currentPath.Pop(); }
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); }
/// <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(); }