public void InitializNavigationRestrictionsTypeWithRecordSuccess()
        {
            // Assert
            EdmModel     model          = new EdmModel();
            IEdmEnumType navigationType = model.FindType("Org.OData.Capabilities.V1.NavigationType") as IEdmEnumType;

            Assert.NotNull(navigationType);

            IEdmRecordExpression restriction1 = new EdmRecordExpression(
                new EdmPropertyConstructor("NavigationProperty", new EdmNavigationPropertyPathExpression("abc")),
                new EdmPropertyConstructor("Navigability", new EdmEnumMemberExpression(navigationType.Members.First(c => c.Name == "Single"))),
                new EdmPropertyConstructor("SkipSupported", new EdmBooleanConstant(false)));

            IEdmRecordExpression restriction2 = new EdmRecordExpression(
                new EdmPropertyConstructor("NavigationProperty", new EdmNavigationPropertyPathExpression("xyz")),
                new EdmPropertyConstructor("Navigability", new EdmEnumMemberExpression(navigationType.Members.First(c => c.Name == "None"))),
                new EdmPropertyConstructor("OptimisticConcurrencyControl", new EdmBooleanConstant(true)));

            IEdmRecordExpression record = new EdmRecordExpression(
                new EdmPropertyConstructor("Navigability", new EdmEnumMemberExpression(navigationType.Members.First(c => c.Name == "Recursive"))),
                new EdmPropertyConstructor("RestrictedProperties", new EdmCollectionExpression(restriction1, restriction2))
                );

            // Act
            NavigationRestrictionsType navigation = new NavigationRestrictionsType();

            navigation.Initialize(record);

            // Assert
            VerifyNavigationRestrictions(navigation);
        }
        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"));
        }
Beispiel #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);
            }
        }
        /// <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);
        }
        public static OpenApiParameter CreateSelect(this ODataContext context, IEdmVocabularyAnnotatable target, IEdmStructuredType structuredType)
        {
            Utils.CheckArgumentNull(context, nameof(context));
            Utils.CheckArgumentNull(target, nameof(target));
            Utils.CheckArgumentNull(structuredType, nameof(structuredType));

            NavigationRestrictionsType navigation = context.Model.GetRecord <NavigationRestrictionsType>(target, CapabilitiesConstants.NavigationRestrictions);

            if (navigation != null && !navigation.IsNavigable)
            {
                return(null);
            }

            IList <IOpenApiAny> selectItems = new List <IOpenApiAny>();

            foreach (var property in structuredType.StructuralProperties())
            {
                selectItems.Add(new OpenApiString(property.Name));
            }

            foreach (var property in structuredType.NavigationProperties())
            {
                if (navigation != null && navigation.IsRestrictedProperty(property.Name))
                {
                    continue;
                }

                selectItems.Add(new OpenApiString(property.Name));
            }

            return(new OpenApiParameter
            {
                Name = "$select",
                In = ParameterLocation.Query,
                Description = "Select properties to be returned",
                Schema = new OpenApiSchema
                {
                    Type = "array",
                    UniqueItems = true,
                    Items = new OpenApiSchema
                    {
                        Type = "string",
                        Enum = selectItems
                    }
                },
                Style = ParameterStyle.Form,
                Explode = false
            });
        }
        /// <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);
        }
        public void TargetOnEntityTypeReturnsCorrectNavigationRestrictionsValue(EdmVocabularyAnnotationSerializationLocation location)
        {
            // Arrange
            const string template = @"
                <Annotations Target=""NS.Calendar"">
                  {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);
        }
        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);
        }
        public void TargetWithUnknownEnumMemberDoesnotReturnsNavigationRestrictionsValue()
        {
            // Arrange
            const string navigationAnnotation = @"
              <Annotations Target=""NS.Calendar"" >
                  <Annotation Term=""Org.OData.Capabilities.V1.NavigationRestrictions"">
                    <Record>
                      <PropertyValue Property=""Navigability"" >
                        <EnumMember>Org.OData.Capabilities.V1.NavigationType/Unknown</EnumMember>
                    </PropertyValue>
                    </Record>
                  </Annotation>
              </Annotations>";

            IEdmModel      model    = CapabilitiesModelHelper.GetEdmModelOutline(navigationAnnotation);
            IEdmEntityType calendar = model.SchemaElements.OfType <IEdmEntityType>().First(c => c.Name == "Calendar");

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

            // Assert
            Assert.Null(navigation.Navigability);
            Assert.Null(navigation.RestrictedProperties);
        }
        /// <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);
                    }
                }
            }
        }
Beispiel #11
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);
                }
            }
        }
        /// <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();
        }