/// <summary> /// Retrieve the paths for <see cref="IEdmNavigationSource"/>. /// </summary> /// <param name="navigationSource">The navigation source.</param> /// <param name="convertSettings">The settings for the current conversion.</param> private void RetrieveNavigationSourcePaths(IEdmNavigationSource navigationSource, OpenApiConvertSettings convertSettings) { Debug.Assert(navigationSource != null); // navigation source itself ODataPath path = new(new ODataNavigationSourceSegment(navigationSource)); AppendPath(path.Clone()); IEdmEntitySet entitySet = navigationSource as IEdmEntitySet; IEdmEntityType entityType = navigationSource.EntityType(); CountRestrictionsType count = null; // for entity set, create a path with key and a $count path if (entitySet != null) { count = _model.GetRecord <CountRestrictionsType>(entitySet, CapabilitiesConstants.CountRestrictions); if (count?.Countable ?? true) // ~/entitySet/$count { CreateCountPath(path, convertSettings); } CreateTypeCastPaths(path, convertSettings, entityType, entitySet, true); // ~/entitySet/subType path.Push(new ODataKeySegment(entityType)); AppendPath(path.Clone()); CreateTypeCastPaths(path, convertSettings, entityType, entitySet, false); // ~/entitySet/{id}/subType } else if (navigationSource is IEdmSingleton singleton) { // ~/singleton/subType CreateTypeCastPaths(path, convertSettings, entityType, singleton, false); } // media entity RetrieveMediaEntityStreamPaths(entityType, path); // properties of type complex RetrieveComplexPropertyPaths(entityType, path, convertSettings); // navigation property foreach (IEdmNavigationProperty np in entityType.DeclaredNavigationProperties()) { if (CanFilter(np)) { RetrieveNavigationPropertyPaths(np, count, path, convertSettings); } } if (entitySet != null) { path.Pop(); // end of entity } path.Pop(); // end of navigation source. Debug.Assert(path.Any() == false); }
public void GetRecordWorksForRecord() { // Arrange string qualifiedName = "Org.OData.Capabilities.V1.CountRestrictions"; string annotation = $@" <Annotation Term=""{qualifiedName}"" > <Record> <PropertyValue Property=""Countable"" Bool=""true"" /> <PropertyValue Property=""NonCountableProperties"" > <Collection> <PropertyPath>Emails</PropertyPath> <PropertyPath>mij</PropertyPath> </Collection> </PropertyValue> <PropertyValue Property=""NonCountableNavigationProperties"" > <Collection> <NavigationPropertyPath>RelatedEvents</NavigationPropertyPath> <NavigationPropertyPath>abc</NavigationPropertyPath> </Collection> </PropertyValue> </Record> </Annotation>"; IEdmModel model = GetEdmModel(annotation); Assert.NotNull(model); // guard // Act CountRestrictionsType count1 = model.GetRecord <CountRestrictionsType>(model.EntityContainer, qualifiedName); CountRestrictionsType count2 = model.GetRecord <CountRestrictionsType>(model.EntityContainer, qualifiedName); // Assert Assert.NotNull(count1); Assert.NotNull(count2); System.Action <CountRestrictionsType> verifyCount = count => { // Countable Assert.NotNull(count.Countable); Assert.True(count.Countable.Value); // NonCountableProperties Assert.NotNull(count.NonCountableProperties); Assert.Equal(new[] { "Emails", "mij" }, count.NonCountableProperties); // NonCountableNavigationProperties Assert.NotNull(count.NonCountableNavigationProperties); Assert.Equal(new[] { "RelatedEvents", "abc" }, count.NonCountableNavigationProperties); }; verifyCount(count1); verifyCount(count2); }
/// <summary> /// Create the $count parameter. /// </summary> /// <param name="context">The OData context.</param> /// <param name="target">The Edm annotation target.</param> /// <returns>The created <see cref="OpenApiParameter"/> or null.</returns> public static OpenApiParameter CreateCount(this ODataContext context, IEdmVocabularyAnnotatable target) { Utils.CheckArgumentNull(context, nameof(context)); Utils.CheckArgumentNull(target, nameof(target)); CountRestrictionsType count = context.Model.GetRecord <CountRestrictionsType>(target, CapabilitiesConstants.CountRestrictions); if (count == null || count.IsCountable) { return(new OpenApiParameter { Reference = new OpenApiReference { Type = ReferenceType.Parameter, Id = "count" } }); } return(null); }
private static void VerifyCountRestrictions(CountRestrictionsType count) { Assert.NotNull(count); Assert.NotNull(count.Countable); Assert.False(count.Countable.Value); Assert.False(count.IsCountable); Assert.NotNull(count.NonCountableProperties); Assert.Equal(2, count.NonCountableProperties.Count); Assert.Equal("Emails|mij", String.Join("|", count.NonCountableProperties)); Assert.NotNull(count.NonCountableNavigationProperties); Assert.Equal(2, count.NonCountableNavigationProperties.Count); Assert.Equal("RelatedEvents,abc", String.Join(",", count.NonCountableNavigationProperties)); Assert.False(count.IsNonCountableProperty("id")); Assert.True(count.IsNonCountableProperty("Emails")); Assert.True(count.IsNonCountableNavigationProperty("RelatedEvents")); }
public void InitializeCountRestrictionsTypeWithRecordSuccess() { // Assert IEdmRecordExpression record = new EdmRecordExpression( new EdmPropertyConstructor("Countable", new EdmBooleanConstant(false)), new EdmPropertyConstructor("NonCountableProperties", new EdmCollectionExpression( new EdmPropertyPathExpression("Emails"), new EdmPropertyPathExpression("mij"))), new EdmPropertyConstructor("NonCountableNavigationProperties", new EdmCollectionExpression( new EdmNavigationPropertyPathExpression("RelatedEvents"), new EdmNavigationPropertyPathExpression("abc"))) ); // Act CountRestrictionsType count = new CountRestrictionsType(); count.Initialize(record); // Assert VerifyCountRestrictions(count); }
public void InitializeCountRestrictionsWorksWithCsdl() { // Arrange string countAnnotation = @" <Annotation Term=""Org.OData.Capabilities.V1.CountRestrictions"" > <Record> <PropertyValue Property=""Countable"" Bool=""false"" /> <PropertyValue Property=""NonCountableProperties""> <Collection> <PropertyPath>Emails</PropertyPath> <PropertyPath>mij</PropertyPath> </Collection> </PropertyValue> <PropertyValue Property=""NonCountableNavigationProperties"" > <Collection> <NavigationPropertyPath>RelatedEvents</NavigationPropertyPath> <NavigationPropertyPath>abc</NavigationPropertyPath> </Collection> </PropertyValue> </Record> </Annotation>"; IEdmModel model = CapabilitiesModelHelper.GetEdmModelSetInline(countAnnotation); Assert.NotNull(model); // guard IEdmEntitySet calendars = model.EntityContainer.FindEntitySet("Calendars"); Assert.NotNull(calendars); // guard // Act CountRestrictionsType count = model.GetRecord <CountRestrictionsType>(calendars); // Assert VerifyCountRestrictions(count); }
public void GetGenericCollectionWorksForCollectionOfGenericRecord() { // Arrange string qualifiedName = "NS.MyCollectionCountRestrictions"; string annotation = $@" <Annotation Term=""{qualifiedName}"" > <Collection> <Record> <PropertyValue Property=""Countable"" Bool=""true"" /> <PropertyValue Property=""NonCountableProperties"" > <Collection> <PropertyPath>123</PropertyPath> <PropertyPath>abc</PropertyPath> </Collection> </PropertyValue> <PropertyValue Property=""NonCountableNavigationProperties"" > <Collection> <NavigationPropertyPath>234</NavigationPropertyPath> <NavigationPropertyPath>xyz</NavigationPropertyPath> </Collection> </PropertyValue> </Record> <Record> <PropertyValue Property=""Countable"" Bool=""false"" /> <PropertyValue Property=""NonCountableProperties"" > <Collection> <PropertyPath>567</PropertyPath> <PropertyPath>mij</PropertyPath> </Collection> </PropertyValue> <PropertyValue Property=""NonCountableNavigationProperties"" > <Collection> <NavigationPropertyPath>789</NavigationPropertyPath> <NavigationPropertyPath>rst</NavigationPropertyPath> </Collection> </PropertyValue> </Record> </Collection> </Annotation>"; IEdmModel model = GetEdmModel(annotation); Assert.NotNull(model); // guard // Act IEnumerable <CountRestrictionsType> counts1 = model.GetCollection <CountRestrictionsType>(model.EntityContainer, qualifiedName); IEnumerable <CountRestrictionsType> counts2 = model.GetCollection <CountRestrictionsType>(model.EntityContainer, qualifiedName); // Assert Assert.NotNull(counts1); Assert.NotNull(counts2); Assert.Equal(2, counts1.Count()); foreach (var countItem in new[] { counts1, counts2 }) { CountRestrictionsType count = countItem.First(); // Countable Assert.NotNull(count.Countable); Assert.True(count.Countable.Value); // NonCountableProperties Assert.NotNull(count.NonCountableProperties); Assert.Equal(new[] { "123", "abc" }, count.NonCountableProperties); // NonCountableNavigationProperties Assert.NotNull(count.NonCountableNavigationProperties); Assert.Equal(new[] { "234", "xyz" }, count.NonCountableNavigationProperties); // #2 count = countItem.Last(); // Countable Assert.NotNull(count.Countable); Assert.False(count.Countable.Value); // NonCountableProperties Assert.NotNull(count.NonCountableProperties); Assert.Equal(new[] { "567", "mij" }, count.NonCountableProperties); // NonCountableNavigationProperties Assert.NotNull(count.NonCountableNavigationProperties); Assert.Equal(new[] { "789", "rst" }, count.NonCountableNavigationProperties); } }
/// <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(); }