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")); }
/// <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); } } } }
/// <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(); }