Пример #1
0
        /// <summary>
        /// Retrieves the paths for a media entity stream.
        /// </summary>
        /// <param name="entityType">The entity type.</param>
        /// <param name="currentPath">The current OData path.</param>
        private void RetrieveMediaEntityStreamPaths(IEdmEntityType entityType, ODataPath currentPath)
        {
            Debug.Assert(entityType != null);
            Debug.Assert(currentPath != null);

            bool createValuePath = true;

            foreach (IEdmStructuralProperty sp in entityType.DeclaredStructuralProperties())
            {
                if (sp.Type.AsPrimitive().IsStream())
                {
                    currentPath.Push(new ODataStreamPropertySegment(sp.Name));
                    AppendPath(currentPath.Clone());
                    currentPath.Pop();
                }

                if (sp.Name.Equals("content", System.StringComparison.OrdinalIgnoreCase))
                {
                    createValuePath = false;
                }
            }

            /* Create a /$value path only if entity has stream and
             * does not contain a structural property named Content
             */
            if (createValuePath && entityType.HasStream)
            {
                currentPath.Push(new ODataStreamContentSegment());
                AppendPath(currentPath.Clone());
                currentPath.Pop();
            }
        }
Пример #2
0
        /// <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);
            }

            // append a navigation property key.
            IEdmEntityType navEntityType = navigationProperty.ToEntityType();

            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);
                    }
                }
            }

            if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
            {
                currentPath.Pop();
            }

            currentPath.Pop();
        }
        /// <summary>
        /// Create $ref paths.
        /// </summary>
        /// <param name="currentPath">The current OData path.</param>
        private void CreateRefPath(ODataPath currentPath)
        {
            ODataPath newPath = currentPath.Clone();

            newPath.Push(ODataRefSegment.Instance); // $ref
            AppendPath(newPath);
        }
        /// <summary>
        /// Retrieves the paths for properties of type complex type from entities
        /// </summary>
        /// <param name="entityType">The entity type.</param>
        /// <param name="currentPath">The current path.</param>
        /// <param name="convertSettings">The settings for the current conversion.</param>
        private void RetrieveComplexPropertyPaths(IEdmEntityType entityType, ODataPath currentPath, OpenApiConvertSettings convertSettings)
        {
            Debug.Assert(entityType != null);
            Debug.Assert(currentPath != null);
            Debug.Assert(convertSettings != null);

            if (!convertSettings.EnableNavigationPropertyPath)
            {
                return;
            }

            foreach (IEdmStructuralProperty sp in entityType.StructuralProperties()
                     .Where(x => x.Type.IsComplex() ||
                            x.Type.IsCollection() && x.Type.Definition.AsElementType() is IEdmComplexType))
            {
                if (!ShouldCreateComplexPropertyPaths(sp, convertSettings))
                {
                    continue;
                }

                currentPath.Push(new ODataComplexPropertySegment(sp));
                AppendPath(currentPath.Clone());


                if (sp.Type.IsCollection())
                {
                    CreateTypeCastPaths(currentPath, convertSettings, sp.Type.Definition.AsElementType() as IEdmComplexType, sp, true);
                    var count = _model.GetRecord <CountRestrictionsType>(sp, CapabilitiesConstants.CountRestrictions);
                    if (count?.IsCountable ?? true)
                    {
                        CreateCountPath(currentPath, convertSettings);
                    }
                }
                else
                {
                    var complexTypeReference = sp.Type.AsComplex();
                    var definition           = complexTypeReference.ComplexDefinition();

                    CreateTypeCastPaths(currentPath, convertSettings, definition, sp, false);
                    foreach (IEdmNavigationProperty np in complexTypeReference
                             .DeclaredNavigationProperties()
                             .Union(definition
                                    .FindAllBaseTypes()
                                    .SelectMany(x => x.DeclaredNavigationProperties()))
                             .Distinct()
                             .Where(CanFilter))
                    {
                        var count = _model.GetRecord <CountRestrictionsType>(np, CapabilitiesConstants.CountRestrictions);
                        RetrieveNavigationPropertyPaths(np, count, currentPath, convertSettings);
                    }
                }
                currentPath.Pop();
            }
        }
Пример #5
0
        /// <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();
        }
Пример #6
0
        /// <summary>
        /// Retrieve the paths for <see cref="IEdmNavigationSource"/>.
        /// </summary>
        /// <param name="navigationSource">The navigation source.</param>
        private void RetrieveNavigationSourcePaths(IEdmNavigationSource navigationSource)
        {
            Debug.Assert(navigationSource != null);

            // navigation source itself
            ODataPath path = new ODataPath(new ODataNavigationSourceSegment(navigationSource));

            AppendPath(path.Clone());

            IEdmEntitySet  entitySet  = navigationSource as IEdmEntitySet;
            IEdmEntityType entityType = navigationSource.EntityType();

            // for entity set, create a path with key
            if (entitySet != null)
            {
                path.Push(new ODataKeySegment(entityType));
                AppendPath(path.Clone());
            }

            // media entity
            RetrieveMediaEntityStreamPaths(entityType, path);

            // navigation property
            foreach (IEdmNavigationProperty np in entityType.DeclaredNavigationProperties())
            {
                if (CanFilter(np))
                {
                    RetrieveNavigationPropertyPaths(np, path);
                }
            }

            if (entitySet != null)
            {
                path.Pop(); // end of entity
            }

            path.Pop(); // end of navigation source.
            Debug.Assert(path.Any() == false);
        }
        /// <summary>
        /// Create $count paths.
        /// </summary>
        /// <param name="currentPath">The current OData path.</param>
        /// <param name="convertSettings">The settings for the current conversion.</param>
        private void CreateCountPath(ODataPath currentPath, OpenApiConvertSettings convertSettings)
        {
            if (currentPath == null)
            {
                throw new ArgumentNullException(nameof(currentPath));
            }
            if (convertSettings == null)
            {
                throw new ArgumentNullException(nameof(convertSettings));
            }
            if (!convertSettings.EnableDollarCountPath)
            {
                return;
            }
            var countPath = currentPath.Clone();

            countPath.Push(ODataDollarCountSegment.Instance);
            AppendPath(countPath);
        }
        /// <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();
        }