/// <summary> /// Compare between two ODataPath using its path item name. /// </summary> /// <param name="other">The compare to ODataPath.</param> /// <returns>true/false</returns> public int CompareTo(ODataPath other) { return(GetPathItemName().CompareTo(other.GetPathItemName())); }
private bool ShouldExpandNavigationProperty(IEdmNavigationProperty navigationProperty, ODataPath currentPath) { 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) { if (navEntityType.IsAssignableFrom(segment.EntityType)) { return(false); } } // check whether the navigation type used to define a navigation source. // if so, not expand it. return(!_allNavigationSources.ContainsKey(navEntityType)); }
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="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(); }
/// <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()); // append a navigation property key. IEdmEntityType navEntityType = navigationProperty.ToEntityType(); if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { currentPath.Push(new ODataKeySegment(navEntityType)); AppendPath(currentPath.Clone()); } if (shouldExpand) { // expand to sub navigation properties foreach (IEdmNavigationProperty subNavProperty in navEntityType.DeclaredNavigationProperties()) { if (CanFilter(subNavProperty)) { RetrieveNavigationPropertyPaths(subNavProperty, currentPath); } } } if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { currentPath.Pop(); } currentPath.Pop(); }
/// <summary> /// Profides a suffix for the operation id based on the operation path. /// </summary> /// <param name="path">Path to use to deduplicate.</param> /// <param name="settings">The settings.</param> ///<returns>The suffix.</returns> public string GetPathHash(OpenApiConvertSettings settings, ODataPath path = default) { var suffix = string.Join("/", path?.Segments.Select(x => x.Identifier).Distinct() ?? Enumerable.Empty <string>()); return((GetPathItemName(settings) + suffix).GetHashSHA256().Substring(0, 4)); }
/// <summary> /// Create OData type cast paths. /// </summary> /// <param name="currentPath">The current OData path.</param> /// <param name="convertSettings">The settings for the current conversion.</param> /// <param name="structuredType">The type that is being inherited from to which this method will add downcast path segments.</param> /// <param name="annotable">The annotable navigation source to read cast annotations from.</param> /// <param name="targetsMany">Whether the annotable navigation source targets many entities.</param> private void CreateTypeCastPaths(ODataPath currentPath, OpenApiConvertSettings convertSettings, IEdmStructuredType structuredType, IEdmVocabularyAnnotatable annotable, bool targetsMany) { if (currentPath == null) { throw Error.ArgumentNull(nameof(currentPath)); } if (convertSettings == null) { throw Error.ArgumentNull(nameof(convertSettings)); } if (structuredType == null) { throw Error.ArgumentNull(nameof(structuredType)); } if (annotable == null) { throw Error.ArgumentNull(nameof(annotable)); } if (!convertSettings.EnableODataTypeCast) { return; } var annotedTypeNames = GetDerivedTypeConstaintTypeNames(annotable); if (!annotedTypeNames.Any() && convertSettings.RequireDerivedTypesConstraintForODataTypeCastSegments) { return; // we don't want to generate any downcast path item if there is no type cast annotation. } var annotedTypeNamesSet = new HashSet <string>(annotedTypeNames, StringComparer.OrdinalIgnoreCase); bool filter(IEdmStructuredType x) => convertSettings.RequireDerivedTypesConstraintForODataTypeCastSegments && annotedTypeNames.Contains(x.FullTypeName()) || !convertSettings.RequireDerivedTypesConstraintForODataTypeCastSegments && ( !annotedTypeNames.Any() || annotedTypeNames.Contains(x.FullTypeName()) ); var targetTypes = _model .FindAllDerivedTypes(structuredType) .Where(x => (x.TypeKind == EdmTypeKind.Entity || x.TypeKind == EdmTypeKind.Complex) && filter(x)) .OfType <IEdmStructuredType>() .ToArray(); foreach (var targetType in targetTypes) { var castPath = currentPath.Clone(); var targetTypeSegment = new ODataTypeCastSegment(targetType); castPath.Push(targetTypeSegment); AppendPath(castPath); if (targetsMany) { CreateCountPath(castPath, convertSettings); } else { if (convertSettings.ExpandDerivedTypesNavigationProperties) { foreach (var declaredNavigationProperty in targetType.DeclaredNavigationProperties()) { RetrieveNavigationPropertyPaths(declaredNavigationProperty, null, castPath, convertSettings); } } } } }
/// <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(); }