コード例 #1
0
        public void GetTemplatesWorksForODataPathWithDollarRefOnSingleNavigation()
        {
            // Arrange
            EdmEntityType customer = new EdmEntityType("NS", "Customer");

            customer.AddKeys(customer.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32));
            EdmEntityContainer container = new EdmEntityContainer("NS", "Default");
            var entitySet  = container.AddEntitySet("Customers", customer);
            var navigation = customer.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo
            {
                TargetMultiplicity = EdmMultiplicity.One,
                Name   = "SubCustomer",
                Target = customer
            });

            ODataPathTemplate template = new ODataPathTemplate(
                new EntitySetSegmentTemplate(entitySet),
                KeySegmentTemplate.CreateKeySegment(customer, entitySet),
                new NavigationLinkSegmentTemplate(navigation, entitySet));

            // Act
            IEnumerable <(string, string)> actual = template.GetTemplates();

            // Assert
            Assert.Equal(2, actual.Count());
            Assert.Equal(new[]
            {
                "Customers({key})/SubCustomer/$ref",
                "Customers/{key}/SubCustomer/$ref"
            }, actual.Select(a => a.Item1));
        }
コード例 #2
0
        /// <summary>
        /// Adds the OData selector model to the action.
        /// </summary>
        /// <param name="action">The given action model.</param>
        /// <param name="httpMethods">The supported http methods, if mulitple, using ',' to separate.</param>
        /// <param name="prefix">The prefix.</param>
        /// <param name="model">The Edm model.</param>
        /// <param name="path">The OData path template.</param>
        /// <param name="options">The route build options.</param>
        public static void AddSelector(this ActionModel action, string httpMethods, string prefix, IEdmModel model, ODataPathTemplate path, ODataRouteOptions options = null)
        {
            if (action == null)
            {
                throw Error.ArgumentNull(nameof(action));
            }

            if (string.IsNullOrEmpty(httpMethods))
            {
                throw Error.ArgumentNullOrEmpty(nameof(httpMethods));
            }

            if (model == null)
            {
                throw Error.ArgumentNull(nameof(model));
            }

            if (path == null)
            {
                throw Error.ArgumentNull(nameof(path));
            }

            string[] methods = httpMethods.Split(',');
            foreach (string template in path.GetTemplates(options))
            {
                // Be noted: https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/ApplicationModels/ActionAttributeRouteModel.cs#L74-L75
                // No matter whether the action selector model is absolute route template, the controller's attribute will apply automatically
                // So, let's only create/update the action selector model
                SelectorModel selectorModel = action.Selectors.FirstOrDefault(s => s.AttributeRouteModel == null);
                if (selectorModel == null)
                {
                    // Create a new selector model.
                    selectorModel = CreateSelectorModel(action, methods);
                    action.Selectors.Add(selectorModel);
                }
                else
                {
                    // Update the existing non attribute routing selector model.
                    selectorModel = UpdateSelectorModel(selectorModel, methods);
                }

                ODataRoutingMetadata odataMetadata = new ODataRoutingMetadata(prefix, model, path);
                selectorModel.EndpointMetadata.Add(odataMetadata);

                string templateStr = string.IsNullOrEmpty(prefix) ? template : $"{prefix}/{template}";

                selectorModel.AttributeRouteModel = new AttributeRouteModel
                {
                    // OData convention route template doesn't get combined with the route template applied to the controller.
                    // Route templates applied to an action that begin with / or ~/ don't get combined with route templates applied to the controller.
                    Template = $"/{templateStr}",
                    Name     = templateStr // do we need this?
                };

                // Check with .NET Team whether the "Endpoint name metadata" needed?
                selectorModel.EndpointMetadata.Add(new EndpointNameMetadata(Guid.NewGuid().ToString())); // Do we need this?
            }
        }
コード例 #3
0
        public void GetTemplatesReturnsCorrectWithMetadataSegment()
        {
            // Arrange
            ODataPathTemplate path = new ODataPathTemplate(MetadataSegmentTemplate.Instance);

            // Act
            IEnumerable <string> templates = path.GetTemplates();

            // Assert
            var template = Assert.Single(templates);

            Assert.Equal("$metadata", template);
        }
コード例 #4
0
        public void GetTemplatesReturnsCorrectWithEmptySegments()
        {
            // Arrange
            ODataPathTemplate path = new ODataPathTemplate();

            // Act
            IEnumerable <string> templates = path.GetTemplates();

            // Assert
            var template = Assert.Single(templates);

            Assert.Equal("", template);
        }
コード例 #5
0
        /// <summary>
        /// Adds the OData selector model to the action.
        /// </summary>
        /// <param name="action">The given action model.</param>
        /// <param name="httpMethod">The supported http methods, if mulitple, using ',' to separate.</param>
        /// <param name="prefix">The prefix.</param>
        /// <param name="model">The Edm model.</param>
        /// <param name="path">The OData path template.</param>
        /// <param name="options">The route build options.</param>
        public static void AddSelector(this ActionModel action, string httpMethod, string prefix, IEdmModel model, ODataPathTemplate path, ODataRouteOptions options = null)
        {
            if (action == null)
            {
                throw Error.ArgumentNull(nameof(action));
            }

            if (model == null)
            {
                throw Error.ArgumentNull(nameof(model));
            }

            if (path == null)
            {
                throw Error.ArgumentNull(nameof(path));
            }

            foreach ((string template, string display) in path.GetTemplates(options))
            {
                // We have to check the selector model on controller?
                SelectorModel selectorModel = action.Selectors.FirstOrDefault(s => s.AttributeRouteModel == null);
                if (selectorModel == null)
                {
                    selectorModel = CreateSelectorModel(action.Attributes);
                    action.Selectors.Add(selectorModel);
                }

                ODataRoutingMetadata odataMetadata = new ODataRoutingMetadata(prefix, model, path)
                {
                    TemplateDisplayName = string.IsNullOrEmpty(prefix) ? display : $"{prefix}/{display}"
                };

                AddHttpMethod(odataMetadata, httpMethod);

                selectorModel.EndpointMetadata.Add(odataMetadata);

                string templateStr = string.IsNullOrEmpty(prefix) ? template : $"{prefix}/{template}";

                // OData convention route template doesn't get combined with the route template applied to the controller.
                // Route templates applied to an action that begin with / or ~/ don't get combined with route templates applied to the controller.
                templateStr = "/" + templateStr;

                selectorModel.AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(templateStr)
                {
                    Name = templateStr
                });

                // Check with .NET Team whether the "Endpoint name metadata"
                selectorModel.EndpointMetadata.Add(new EndpointNameMetadata(Guid.NewGuid().ToString()));
            }
        }
コード例 #6
0
        public void GetTemplatesWorksForPathWithTypeCastAndFunction()
        {
            // Arrange
            EdmEntityType customer = new EdmEntityType("NS", "Customer");

            customer.AddKeys(customer.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32));

            // VipCustomer
            EdmEntityType vipCustomer = new EdmEntityType("NS", "VipCustomer", customer);

            EdmEntityContainer container = new EdmEntityContainer("NS", "Default");
            var entitySet = container.AddEntitySet("Customers", customer);

            // function with optional parameters
            EdmFunction getSalaray = new EdmFunction("NS", "GetWholeSalary", IntType, isBound: true, entitySetPathExpression: null, isComposable: false);

            getSalaray.AddParameter("entityset", new EdmEntityTypeReference(vipCustomer, false));
            getSalaray.AddParameter("salary", IntType);
            getSalaray.AddOptionalParameter("minSalary", IntType);
            getSalaray.AddOptionalParameter("maxSalary", IntType, "129");

            ODataPathTemplate template = new ODataPathTemplate(
                new EntitySetSegmentTemplate(entitySet),
                KeySegmentTemplate.CreateKeySegment(customer, entitySet),
                new CastSegmentTemplate(vipCustomer, customer, entitySet),
                new FunctionSegmentTemplate(getSalaray, null));

            // Act
            IEnumerable <(string, string)> actual = template.GetTemplates();

            Assert.Equal(4, actual.Count());
            Assert.Equal(new[]
            {
                "Customers({key})/NS.VipCustomer/NS.GetWholeSalary({salary;minSalary;maxSalary})",
                "Customers({key})/NS.VipCustomer/GetWholeSalary({salary;minSalary;maxSalary})",
                "Customers/{key}/NS.VipCustomer/NS.GetWholeSalary({salary;minSalary;maxSalary})",
                "Customers/{key}/NS.VipCustomer/GetWholeSalary({salary;minSalary;maxSalary})",
            }, actual.Select(a => a.Item1));

            Assert.Equal(new[]
            {
                "Customers({key})/NS.VipCustomer/NS.GetWholeSalary(salary={salary},minSalary={minSalary},maxSalary={maxSalary})",
                "Customers({key})/NS.VipCustomer/GetWholeSalary(salary={salary},minSalary={minSalary},maxSalary={maxSalary})",
                "Customers/{key}/NS.VipCustomer/NS.GetWholeSalary(salary={salary},minSalary={minSalary},maxSalary={maxSalary})",
                "Customers/{key}/NS.VipCustomer/GetWholeSalary(salary={salary},minSalary={minSalary},maxSalary={maxSalary})",
            }, actual.Select(a => a.Item2));
        }
コード例 #7
0
        /// <summary>
        /// Adds the OData selector model to the action.
        /// </summary>
        /// <param name="action">The given action model.</param>
        /// <param name="httpMethod">The supported http methods, if mulitple, using ',' to separate.</param>
        /// <param name="prefix">The prefix.</param>
        /// <param name="model">The Edm model.</param>
        /// <param name="path">The OData path template.</param>
        /// <param name="options">The route build options.</param>
        public static void AddSelector(this ActionModel action, string httpMethod, string prefix, IEdmModel model, ODataPathTemplate path, ODataRouteOptions options = null)
        {
            if (action == null)
            {
                throw Error.ArgumentNull(nameof(action));
            }

            if (model == null)
            {
                throw Error.ArgumentNull(nameof(model));
            }

            if (path == null)
            {
                throw Error.ArgumentNull(nameof(path));
            }

            foreach ((string template, string display) in path.GetTemplates(options))
            {
                SelectorModel selectorModel = action.Selectors.FirstOrDefault(s => s.AttributeRouteModel == null);
                if (selectorModel == null)
                {
                    selectorModel = CreateSelectorModel(action.Attributes);
                    action.Selectors.Add(selectorModel);
                }

                ODataRoutingMetadata odataMetadata = new ODataRoutingMetadata(prefix, model, path)
                {
                    TemplateDisplayName = string.IsNullOrEmpty(prefix) ? display : $"{prefix}/{display}"
                };

                AddHttpMethod(odataMetadata, httpMethod);

                selectorModel.EndpointMetadata.Add(odataMetadata);

                string templateStr = string.IsNullOrEmpty(prefix) ? template : $"{prefix}/{template}";

                selectorModel.AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(templateStr)
                {
                    Name = templateStr
                });

                // Check with .NET Team whether the "Endpoint name metadata"
                selectorModel.EndpointMetadata.Add(new EndpointNameMetadata(Guid.NewGuid().ToString()));
            }
        }
コード例 #8
0
        /// <summary>
        /// Adds the OData selector model to the action.
        /// </summary>
        /// <param name="action">The given action model.</param>
        /// <param name="prefix">The prefix.</param>
        /// <param name="model">The Edm model.</param>
        /// <param name="path">The OData path template.</param>
        public static void AddSelector(this ActionModel action, string prefix, IEdmModel model, ODataPathTemplate path)
        {
            if (action == null)
            {
                throw Error.ArgumentNull(nameof(action));
            }

            if (model == null)
            {
                throw Error.ArgumentNull(nameof(model));
            }

            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }

            var httpMethods = action.GetSupportedHttpMethods();

            foreach (var template in path.GetTemplates())
            {
                SelectorModel selectorModel = action.Selectors.FirstOrDefault(s => s.AttributeRouteModel == null);
                if (selectorModel == null)
                {
                    selectorModel = new SelectorModel();
                    action.Selectors.Add(selectorModel);
                }

                string templateStr = string.IsNullOrEmpty(prefix) ? template : $"{prefix}/{template}";

                selectorModel.AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(templateStr)
                {
                    Name = templateStr
                });

                ODataRoutingMetadata odataMetadata = new ODataRoutingMetadata(prefix, model, path);
                selectorModel.EndpointMetadata.Add(odataMetadata);

                // Check with .NET Team whether the "Endpoint name metadata"
                // selectorModel.EndpointMetadata.Add(new EndpointNameMetadata(templateStr));
                foreach (var httpMethod in httpMethods)
                {
                    odataMetadata.HttpMethods.Add(httpMethod);
                }
            }
        }
コード例 #9
0
        public void GetTemplatesWorksForBasicPath()
        {
            // Arrange
            EdmEntityType customer = new EdmEntityType("NS", "Customer");

            customer.AddKeys(customer.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32));
            EdmEntityContainer container = new EdmEntityContainer("NS", "Default");
            var entitySet = container.AddEntitySet("Customers", customer);

            ODataPathTemplate template = new ODataPathTemplate(
                new EntitySetSegmentTemplate(entitySet),
                KeySegmentTemplate.CreateKeySegment(customer, entitySet));

            // Act
            IEnumerable <(string, string)> actual = template.GetTemplates();

            // Assert
            Assert.Equal(2, actual.Count());
            Assert.Equal(new[] { "Customers({key})", "Customers/{key}" }, actual.Select(a => a.Item1));
        }
コード例 #10
0
        public void GetTemplatesWorksForODataPathWithDollarRefOnCollectionNavigation()
        {
            // Arrange
            EdmEntityType customer = new EdmEntityType("NS", "Customer");

            customer.AddKeys(customer.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32));
            EdmEntityContainer container = new EdmEntityContainer("NS", "Default");
            var entitySet  = container.AddEntitySet("Customers", customer);
            var navigation = customer.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo
            {
                TargetMultiplicity = EdmMultiplicity.Many,
                Name   = "SubCustomers",
                Target = customer
            });

            KeyValuePair <string, object>[] keys = new KeyValuePair <string, object>[]
            {
                new KeyValuePair <string, object>("Id", "{nextKey}")
            };
            KeySegment        keySegment = new KeySegment(keys, customer, entitySet);
            ODataPathTemplate template   = new ODataPathTemplate(
                new EntitySetSegmentTemplate(entitySet),
                KeySegmentTemplate.CreateKeySegment(customer, entitySet),
                new NavigationLinkSegmentTemplate(navigation, entitySet)
            {
                Key = new KeySegmentTemplate(keySegment)
            });

            // Act
            IEnumerable <string> actual = template.GetTemplates();

            // Assert
            Assert.Equal(4, actual.Count());
            Assert.Equal(new[]
            {
                "Customers({key})/SubCustomers({nextKey})/$ref",
                "Customers({key})/SubCustomers/{nextKey}/$ref",
                "Customers/{key}/SubCustomers({nextKey})/$ref",
                "Customers/{key}/SubCustomers/{nextKey}/$ref"
            }, actual);
        }
コード例 #11
0
        public void GetTemplatesReturnsCorrectWithTwoSegments()
        {
            // Arrange
            EdmEntityType      entityType = new EdmEntityType("NS", "entity");
            EdmEntityContainer container  = new EdmEntityContainer("NS", "default");
            EdmEntitySet       entitySet  = new EdmEntitySet(container, "set", entityType);
            EdmAction          action     = new EdmAction("NS", "action", null, true, null);
            ODataPathTemplate  path       = new ODataPathTemplate(new EntitySetSegmentTemplate(entitySet),
                                                                  new ActionSegmentTemplate(action, null));

            // Act
            IEnumerable <string> templates = path.GetTemplates();

            // Act & Assert
            Assert.Collection(templates,
                              e =>
            {
                Assert.Equal("set/NS.action", e);
            },
                              e =>
            {
                Assert.Equal("set/action", e);
            });
        }
コード例 #12
0
        /// <summary>
        /// Adds the OData selector model to the action.
        /// </summary>
        /// <param name="action">The given action model.</param>
        /// <param name="httpMethods">The supported http methods, if multiple, using ',' to separate.</param>
        /// <param name="prefix">The prefix.</param>
        /// <param name="model">The Edm model.</param>
        /// <param name="path">The OData path template.</param>
        /// <param name="options">The route build options.</param>
        public static void AddSelector(this ActionModel action, string httpMethods, string prefix, IEdmModel model, ODataPathTemplate path, ODataRouteOptions options = null)
        {
            if (action == null)
            {
                throw Error.ArgumentNull(nameof(action));
            }

            if (string.IsNullOrEmpty(httpMethods))
            {
                throw Error.ArgumentNullOrEmpty(nameof(httpMethods));
            }

            if (model == null)
            {
                throw Error.ArgumentNull(nameof(model));
            }

            if (path == null)
            {
                throw Error.ArgumentNull(nameof(path));
            }

            // if the controller has attribute route decorated, for example:
            // [Route("api/[controller]")]
            // public class CustomersController : Controller
            // {}
            // let's always create new selector model for action.
            // Since the new created selector model is absolute attribute route, the controller attribute route doesn't apply to this selector model.
            bool hasAttributeRouteOnController = action.Controller.Selectors.Any(s => s.AttributeRouteModel != null);

            // If the methods have different case sensitive, for example, "get", "Get", in the ASP.NET Core 3.1,
            // It will throw "An item with the same key has already been added. Key: GET", in
            // HttpMethodMatcherPolicy.BuildJumpTable(Int32 exitDestination, IReadOnlyList`1 edges)
            // Another root cause is that in attribute routing, we reuse the HttpMethodMetadata, the method name is always "upper" case.
            // Therefore, we upper the http method name always.
            string[] methods = httpMethods.ToUpperInvariant().Split(',');
            foreach (string template in path.GetTemplates(options))
            {
                // Be noted: https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/ApplicationModels/ActionAttributeRouteModel.cs#L74-L75
                // No matter whether the action selector model is absolute route template, the controller's attribute will apply automatically
                // So, let's only create/update the action selector model
                SelectorModel selectorModel = action.Selectors.FirstOrDefault(s => s.AttributeRouteModel == null);
                if (hasAttributeRouteOnController || selectorModel == null)
                {
                    // Create a new selector model.
                    selectorModel = CreateSelectorModel(action, methods);
                    action.Selectors.Add(selectorModel);
                }
                else
                {
                    // Update the existing non attribute routing selector model.
                    selectorModel = UpdateSelectorModel(selectorModel, methods);
                }

                ODataRoutingMetadata odataMetadata = new ODataRoutingMetadata(prefix, model, path);
                selectorModel.EndpointMetadata.Add(odataMetadata);

                string templateStr = string.IsNullOrEmpty(prefix) ? template : $"{prefix}/{template}";

                selectorModel.AttributeRouteModel = new AttributeRouteModel
                {
                    // OData convention route template doesn't get combined with the route template applied to the controller.
                    // Route templates applied to an action that begin with / or ~/ don't get combined with route templates applied to the controller.
                    Template = $"/{templateStr}",
                    Name     = templateStr // do we need this?
                };

                // Check with .NET Team whether the "Endpoint name metadata" needed?
                selectorModel.EndpointMetadata.Add(new EndpointNameMetadata(Guid.NewGuid().ToString())); // Do we need this?
            }
        }
コード例 #13
0
            public static void AddedODataSelector(ILogger logger, ActionModel action, ODataPathTemplate template)
            {
                string message = action.DisplayName + ": " + template.GetTemplates().FirstOrDefault();

                _addedODataSelector(logger, message, null);
            }