/// <summary>
        /// Enumerates the segments in a path and calls a corresponding delegate verifier on each segment.
        /// Do not overuse this method: most test cases don't need to over-baseline what the expected segments are.
        /// </summary>
        public static void VerifyPath(ODataPath path, Action<ODataPathSegment>[] segmentVerifiers)
        {
            path.Count().Should().Be(segmentVerifiers.Count());

            var i = 0;
            foreach (var segment in path)
            {
                segmentVerifiers[i++](segment);
            }
        }
Exemple #2
0
 /// <summary>
 /// Create a new ODataUri. This contains the semantic meaning of the 
 /// entire uri.
 /// </summary>
 /// <param name="parameterAliasValueAccessor">The ParameterAliasValueAccessor.</param>
 /// <param name="path">The top level path for this uri.</param>
 /// <param name="customQueryOptions">Any custom query options for this uri. Can be null.</param>
 /// <param name="selectAndExpand">Any $select or $expand option for this uri. Can be null.</param>
 /// <param name="filter">Any $filter option for this uri. Can be null.</param>
 /// <param name="orderby">Any $orderby option for this uri. Can be null</param>
 /// <param name="search">Any $search option for this uri. Can be null</param>
 /// <param name="apply">Any $apply option for this uri. Can be null</param>
 /// <param name="skip">Any $skip option for this uri. Can be null.</param>
 /// <param name="top">Any $top option for this uri. Can be null.</param>
 /// <param name="queryCount">Any query $count option for this uri. Can be null.</param>
 internal ODataUri(
     ParameterAliasValueAccessor parameterAliasValueAccessor,
     ODataPath path,
     IEnumerable<QueryNode> customQueryOptions,
     SelectExpandClause selectAndExpand,
     FilterClause filter,
     OrderByClause orderby,
     SearchClause search,
     ApplyClause apply,
     long? skip,
     long? top,
     bool? queryCount)
 {
     this.ParameterAliasValueAccessor = parameterAliasValueAccessor;
     this.Path = path;
     this.CustomQueryOptions = new ReadOnlyCollection<QueryNode>(customQueryOptions.ToList());
     this.SelectAndExpand = selectAndExpand;
     this.Filter = filter;
     this.OrderBy = orderby;
     this.Search = search;
     this.Apply = apply;
     this.Skip = skip;
     this.Top = top;
     this.QueryCount = queryCount;
 }
        public void SelectAction_Returns_ExpectedMethodOnBaseType(string method, string[] methodsInController,
            string expectedSelectedAction)
        {
            // Arrange
            string key = "42";
            CustomersModelWithInheritance model = new CustomersModelWithInheritance();
            var ordersProperty = model.Customer.FindProperty("Orders") as IEdmNavigationProperty;

            ODataPath odataPath = new ODataPath(new EntitySetPathSegment(model.Customers), new KeyValuePathSegment(key),
                new LinksPathSegment(), new NavigationPathSegment(ordersProperty));

            HttpControllerContext controllerContext = CreateControllerContext(method);
            var actionMap = GetMockActionMap(methodsInController);

            // Act
            string selectedAction = new LinksRoutingConvention().SelectAction(odataPath, controllerContext, actionMap);

            // Assert
            Assert.Equal(expectedSelectedAction, selectedAction);
            if (expectedSelectedAction == null)
            {
                Assert.Empty(controllerContext.RouteData.Values);
            }
            else
            {
                Assert.Equal(2, controllerContext.RouteData.Values.Count);
                Assert.Equal(key, controllerContext.RouteData.Values["key"]);
                Assert.Equal(ordersProperty.Name, controllerContext.RouteData.Values["navigationProperty"]);
            }
        }
Exemple #4
0
        /// <summary>
        /// Creates a <see cref="RangeVariable"/> for an implicit parameter ($it) from an <see cref="ODataPath"/>.
        /// </summary>
        /// <param name="path"><see cref="ODataPath"/> that the range variable is iterating over.</param>
        /// <returns>A new <see cref="RangeVariable"/>.</returns>
        internal static RangeVariable CreateImplicitRangeVariable(ODataPath path)
        {
            ExceptionUtils.CheckArgumentNotNull(path, "path");
            IEdmTypeReference elementType = path.EdmType();

            if (elementType == null)
            {
                // This case if for something like a void service operation
                // This is pretty ugly; if pratice we shouldn't be creating a parameter node for this case I think
                return null;
            }

            if (elementType.IsCollection())
            {
                elementType = elementType.AsCollection().ElementType();
            }

            if (elementType.IsEntity())
            {
                IEdmEntityTypeReference entityTypeReference = elementType as IEdmEntityTypeReference;
                return new EntityRangeVariable(ExpressionConstants.It, entityTypeReference, path.NavigationSource());
            }

            return new NonentityRangeVariable(ExpressionConstants.It, elementType, null);
        }
 private static void AddLinkInfoToRouteData(IHttpRouteData routeData, ODataPath odataPath)
 {
     KeyValuePathSegment keyValueSegment = odataPath.Segments[1] as KeyValuePathSegment;
     routeData.Values[ODataRouteConstants.Key] = keyValueSegment.Value;
     NavigationPathSegment navigationSegment = odataPath.Segments[3] as NavigationPathSegment;
     routeData.Values[ODataRouteConstants.NavigationProperty] = navigationSegment.NavigationProperty.Name;
 }
        private static IEdmProperty GetProperty(ODataPath odataPath, HttpMethod method)
        {
            PropertyAccessPathSegment segment = null;
            if (method == HttpMethod.Get)
            {
                if (odataPath.PathTemplate == "~/entityset/key/property" ||
                    odataPath.PathTemplate == "~/entityset/key/cast/property" ||
                    odataPath.PathTemplate == "~/singleton/property" ||
                    odataPath.PathTemplate == "~/singleton/cast/property")
                {
                    segment = odataPath.Segments[odataPath.Segments.Count - 1] as PropertyAccessPathSegment;
                }
                else if (odataPath.PathTemplate == "~/entityset/key/property/$value" ||
                    odataPath.PathTemplate == "~/entityset/key/cast/property/$value" ||
                    odataPath.PathTemplate == "~/singleton/property/$value" ||
                    odataPath.PathTemplate == "~/singleton/cast/property/$value" ||
                    odataPath.PathTemplate == "~/entityset/key/property/$count" ||
                    odataPath.PathTemplate == "~/entityset/key/cast/property/$count" ||
                    odataPath.PathTemplate == "~/singleton/property/$count" ||
                    odataPath.PathTemplate == "~/singleton/cast/property/$count")
                {
                    segment = odataPath.Segments[odataPath.Segments.Count - 2] as PropertyAccessPathSegment;
                }
            }

            return segment == null ? null : segment.Property;
        }
Exemple #7
0
        private void ProcessUpdateEntityReference(IODataRequestMessage requestMessage, IODataResponseMessage responseMessage, ODataPath odataPath)
        {
            // This is for change the reference in single-valued navigation property
            // PUT ~/Person(0)/Parent/$ref
            // {
            //     "@odata.context": "http://host/service/$metadata#$ref",
            //     "@odata.id": "Orders(10643)"
            // }

            if (this.HttpMethod == HttpMethod.PATCH)
            {
                throw Utility.BuildException(HttpStatusCode.MethodNotAllowed, "PATCH on a reference link is not supported.", null);
            }

            // Get the parent first
            var level = this.QueryContext.QueryPath.Count - 2;
            var parent = this.QueryContext.ResolveQuery(this.DataSource, level);

            var navigationPropertyName = ((NavigationPropertyLinkSegment)odataPath.LastSegment).NavigationProperty.Name;

            using (var messageReader = new ODataMessageReader(requestMessage, this.GetReaderSettings(), this.DataSource.Model))
            {
                var referenceLink = messageReader.ReadEntityReferenceLink();
                var queryContext = new QueryContext(this.ServiceRootUri, referenceLink.Url, this.DataSource.Model);
                var target = queryContext.ResolveQuery(this.DataSource);

                this.DataSource.UpdateProvider.UpdateLink(parent, navigationPropertyName, target);
                this.DataSource.UpdateProvider.SaveChanges();
            }

            ResponseWriter.WriteEmptyResponse(responseMessage);
        }
        /// <summary>
        /// Selects the action for OData requests.
        /// </summary>
        /// <param name="odataPath">The OData path.</param>
        /// <param name="controllerContext">The controller context.</param>
        /// <param name="actionMap">The action map.</param>
        /// <returns>
        ///   <c>null</c> if the request isn't handled by this convention; otherwise, the name of the selected action
        /// </returns>
        public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap)
        {
            if (odataPath == null)
            {
                throw Error.ArgumentNull("odataPath");
            }

            if (controllerContext == null)
            {
                throw Error.ArgumentNull("controllerContext");
            }

            if (actionMap == null)
            {
                throw Error.ArgumentNull("actionMap");
            }

            if (odataPath.PathTemplate == "~/entityset/key" ||
                odataPath.PathTemplate == "~/entityset/key/cast")
            {
                HttpMethod httpMethod = controllerContext.Request.Method;
                string httpMethodName;

                switch (httpMethod.ToString().ToUpperInvariant())
                {
                    case "GET":
                        httpMethodName = "Get";
                        break;
                    case "PUT":
                        httpMethodName = "Put";
                        break;
                    case "PATCH":
                    case "MERGE":
                        httpMethodName = "Patch";
                        break;
                    case "DELETE":
                        httpMethodName = "Delete";
                        break;
                    default:
                        return null;
                }

                Contract.Assert(httpMethodName != null);

                IEdmEntityType entityType = odataPath.EdmType as IEdmEntityType;

                // e.g. Try GetCustomer first, then fallback on Get action name
                string actionName = actionMap.FindMatchingAction(
                    httpMethodName + entityType.Name,
                    httpMethodName);

                if (actionName != null)
                {
                    KeyValuePathSegment keyValueSegment = odataPath.Segments[1] as KeyValuePathSegment;
                    controllerContext.RouteData.Values[ODataRouteConstants.Key] = keyValueSegment.Value;
                    return actionName;
                }
            }
            return null;
        }
        /// <summary>
        /// Selects the action for OData requests.
        /// </summary>
        /// <param name="odataPath">The OData path.</param>
        /// <param name="controllerContext">The controller context.</param>
        /// <param name="actionMap">The action map.</param>
        /// <returns>
        ///   <c>null</c> if the request isn't handled by this convention; otherwise, the name of the selected action
        /// </returns>
        public string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap)
        {
            if (odataPath == null)
            {
                throw Error.ArgumentNull("odataPath");
            }

            if (controllerContext == null)
            {
                throw Error.ArgumentNull("controllerContext");
            }

            if (actionMap == null)
            {
                throw Error.ArgumentNull("actionMap");
            }

            if (odataPath.PathTemplate == "~")
            {
                return "GetServiceDocument";
            }

            if (odataPath.PathTemplate == "~/$metadata")
            {
                return "GetMetadata";
            }

            return null;
        }
        /// <summary>
        /// Selects the controller for OData requests.
        /// </summary>
        /// <param name="odataPath">The OData path.</param>
        /// <param name="request">The request.</param>
        /// <returns>
        ///   <c>null</c> if the request isn't handled by this convention; otherwise, the name of the selected controller
        /// </returns>
        public virtual string SelectController(ODataPath odataPath, HttpRequestMessage request)
        {
            if (odataPath == null)
            {
                throw Error.ArgumentNull("odataPath");
            }

            if (request == null)
            {
                throw Error.ArgumentNull("request");
            }

            // entity set
            EntitySetPathSegment entitySetSegment = odataPath.Segments.FirstOrDefault() as EntitySetPathSegment;
            if (entitySetSegment != null)
            {
                return entitySetSegment.EntitySetName;
            }

            // singleton
            SingletonPathSegment singletonSegment = odataPath.Segments.FirstOrDefault() as SingletonPathSegment;
            if (singletonSegment != null)
            {
                return singletonSegment.SingletonName;
            }

            return null;
        }
 /// <summary>
 /// Build a segment representing a navigation property.
 /// </summary>
 /// <param name="path">Path to perform the computation on.</param>
 /// <param name="navigationProperty">The navigation property this segment represents.</param>
 /// <param name="navigationSource">The navigation source of the entities targetted by this navigation property. This can be null.</param>
 /// <returns>The ODataPath with navigation property appended in the end in the end</returns>
 public static ODataPath AppendNavigationPropertySegment(this ODataPath path, IEdmNavigationProperty navigationProperty, IEdmNavigationSource navigationSource)
 {
     var newPath = new ODataPath(path);
     NavigationPropertySegment np = new NavigationPropertySegment(navigationProperty, navigationSource);
     newPath.Add(np);
     return newPath;
 }
 /// <summary>
 /// Binds a collection of <paramref name="segments"/> to metadata, creating a semantic ODataPath object.
 /// </summary>
 /// <param name="segments">Collection of path segments.</param>
 /// <param name="configuration">The configuration to use when binding the path.</param>
 /// <returns>A semantic <see cref="ODataPath"/> object to describe the path.</returns>
 internal static ODataPath BindPath(ICollection<string> segments, ODataUriParserConfiguration configuration)
 {
     ODataPathParser semanticPathParser = new ODataPathParser(configuration);
     var intermediateSegments = semanticPathParser.ParsePath(segments);
     ODataPath path = new ODataPath(intermediateSegments);
     return path;
 }
        /// <inheritdoc/>
        public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap)
        {
            if (odataPath == null)
            {
                throw Error.ArgumentNull("odataPath");
            }

            if (controllerContext == null)
            {
                throw Error.ArgumentNull("controllerContext");
            }

            if (actionMap == null)
            {
                throw Error.ArgumentNull("actionMap");
            }

            string prefix;
            ComplexCastPathSegment cast;
            IEdmProperty property = GetProperty(odataPath, controllerContext.Request.Method, out prefix, out cast);
            IEdmEntityType declaringType = property == null ? null : property.DeclaringType as IEdmEntityType;

            if (declaringType != null)
            {
                string actionName;
                if (cast == null)
                {
                    actionName = actionMap.FindMatchingAction(
                        prefix + property.Name + "From" + declaringType.Name,
                        prefix + property.Name);
                }
                else
                {
                    // for example: GetCityOfSubAddressFromVipCustomer or GetCityOfSubAddress
                    actionName = actionMap.FindMatchingAction(
                        prefix + property.Name + "Of" + cast.CastType.Name + "From" + declaringType.Name,
                        prefix + property.Name + "Of" + cast.CastType.Name);
                }

                if (actionName != null)
                {
                    if (odataPath.PathTemplate.StartsWith("~/entityset/key", StringComparison.Ordinal))
                    {
                        EntitySetPathSegment entitySetPathSegment = (EntitySetPathSegment)odataPath.Segments.First();
                        IEdmEntityType edmEntityType = entitySetPathSegment.EntitySetBase.EntityType();
                        KeyValuePathSegment keyValueSegment = (KeyValuePathSegment)odataPath.Segments[1];

                        controllerContext.AddKeyValueToRouteData(keyValueSegment, edmEntityType, ODataRouteConstants.Key);
                    }

                    return actionName;
                }
            }

            return null;
        }
        public void SelectController_ThrowsArgmentNull_IfMissRequest()
        {
            // Arrange
            ODataPath odataPath = new ODataPath(new ODataPathSegment[] { new EntitySetPathSegment("Customers") });

            // Act & Assert
            Assert.ThrowsArgumentNull(
                () => new MockNavigationSourceRoutingConvention().SelectController(odataPath, null),
                "request");
        }
        public void TypeComputedForEntitySetSegment()
        {
            var entitySet = mbh.BuildValidEntitySet();
            var path = new ODataPath(new ODataPathSegment[]
            {
                new EntitySetSegment(entitySet)
            });

            path.EdmType().Should().BeSameAs(entitySet);
        }
        public void SelectAction_ThrowsArgumentNull_IfControllerContextIsNull()
        {
            // Arrange
            ODataPath odataPath = new ODataPath();

            // Act & Assert
            Assert.ThrowsArgumentNull(
                () => new ActionRoutingConvention().SelectAction(odataPath, controllerContext: null, actionMap: null),
                "controllerContext");
        }
        public void EntitySetComputedForEntitySetSegment()
        {
            var entitySet = mbh.BuildValidEntitySet();
            var path = new ODataPath(new ODataPathSegment[]
            {
                new EntitySetSegment(entitySet)
            });

            path.NavigationSource().Should().BeSameAs(entitySet);
        }
        public static void AddKeyValueToRouteData(this HttpControllerContext controllerContext, ODataPath odataPath)
        {
            Contract.Assert(controllerContext != null);
            Contract.Assert(odataPath != null);

            KeyValuePathSegment keyValueSegment = odataPath.Segments[1] as KeyValuePathSegment;
            if (keyValueSegment != null)
            {
                controllerContext.RouteData.Values[ODataRouteConstants.Key] = keyValueSegment.Value;
            }
        }
        public void SelectAction_ThrowsArgumentNull_IfActionMapIsNull()
        {
            // Arrange
            ODataPath odataPath = new ODataPath();
            var controllerContext = new Mock<HttpControllerContext>().Object;

            // Act & Assert
            Assert.ThrowsArgumentNull(
                () => new ActionRoutingConvention().SelectAction(odataPath, controllerContext, actionMap: null),
                "actionMap");
        }
        /// <summary>
        /// Selects the action.
        /// </summary>
        /// <param name="odataPath">The odata path.</param>
        /// <param name="controllerContext">The controller context.</param>
        /// <param name="actionMap">The action map.</param>
        /// <returns></returns>
        public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap)
        {
            if (odataPath == null)
            {
                throw Error.ArgumentNull("odataPath");
            }

            if (controllerContext == null)
            {
                throw Error.ArgumentNull("controllerContext");
            }

            if (actionMap == null)
            {
                throw Error.ArgumentNull("actionMap");
            }

            HttpMethod requestMethod = controllerContext.Request.Method;
            IHttpRouteData routeData = controllerContext.RouteData;

            if (odataPath.PathTemplate == "~/entityset/key/$links/navigation" ||
                odataPath.PathTemplate == "~/entityset/key/cast/$links/navigation")
            {
                NavigationPathSegment navigationSegment = odataPath.Segments.Last() as NavigationPathSegment;
                IEdmNavigationProperty navigationProperty = navigationSegment.NavigationProperty;
                IEdmEntityType declaringType = navigationProperty.DeclaringType as IEdmEntityType;

                string linksActionName = FindLinksActionName(actionMap, navigationProperty, declaringType, requestMethod);
                if (linksActionName != null)
                {
                    routeData.Values[ODataRouteConstants.Key] = (odataPath.Segments[1] as KeyValuePathSegment).Value;
                    routeData.Values[ODataRouteConstants.NavigationProperty] = navigationSegment.NavigationProperty.Name;
                    return linksActionName;
                }
            }
            else if ((odataPath.PathTemplate == "~/entityset/key/$links/navigation/key" ||
                odataPath.PathTemplate == "~/entityset/key/cast/$links/navigation/key") && requestMethod == HttpMethod.Delete)
            {
                NavigationPathSegment navigationSegment = odataPath.Segments[odataPath.Segments.Count - 2] as NavigationPathSegment;
                IEdmNavigationProperty navigationProperty = navigationSegment.NavigationProperty;
                IEdmEntityType declaringType = navigationProperty.DeclaringType as IEdmEntityType;

                string linksActionName = FindLinksActionName(actionMap, navigationProperty, declaringType, requestMethod);
                if (linksActionName != null)
                {
                    routeData.Values[ODataRouteConstants.Key] = (odataPath.Segments[1] as KeyValuePathSegment).Value;
                    routeData.Values[ODataRouteConstants.NavigationProperty] = navigationSegment.NavigationProperty.Name;
                    routeData.Values[ODataRouteConstants.RelatedKey] = (odataPath.Segments.Last() as KeyValuePathSegment).Value;
                    return linksActionName;
                }
            }

            return null;
        }
        public void SelectAction_ThrowsArgumentNull_IfMissActionMap()
        {
            // Arrange
            ODataPath odataPath = new ODataPath();
            Mock<HttpControllerContext> controllerContext = new Mock<HttpControllerContext>();

            // Act & Assert
            Assert.ThrowsArgumentNull(
                () => new PropertyRoutingConvention().SelectAction(odataPath, controllerContext.Object, null),
                "actionMap");
        }
        public void SelectAction_ThrowsArgumentNull_IfMissControllerContext()
        {
            // Arrange
            ODataPath odataPath = new ODataPath();
            ILookup<string, HttpActionDescriptor> emptyMap = new HttpActionDescriptor[0].ToLookup(desc => (string)null);

            // Act & Assert
            Assert.ThrowsArgumentNull(
                () => new PropertyRoutingConvention().SelectAction(odataPath, null, emptyMap),
                "controllerContext");
        }
        /// <inheritdoc/>
        public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap)
        {
            if (odataPath == null)
            {
                throw Error.ArgumentNull("odataPath");
            }

            if (controllerContext == null)
            {
                throw Error.ArgumentNull("controllerContext");
            }

            if (actionMap == null)
            {
                throw Error.ArgumentNull("actionMap");
            }

            HttpMethod requestMethod = controllerContext.Request.Method;
            IHttpRouteData routeData = controllerContext.RouteData;

            if (odataPath.PathTemplate == EntitysetKeyNavigationRef ||
                odataPath.PathTemplate == EntitysetKeyCastNavigationRef)
            {
                NavigationPathSegment navigationSegment = (NavigationPathSegment)odataPath.Segments[odataPath.Segments.Count - 2];
                IEdmNavigationProperty navigationProperty = navigationSegment.NavigationProperty;
                IEdmEntityType declaringType = navigationProperty.DeclaringEntityType();

                string refActionName = FindRefActionName(actionMap, navigationProperty, declaringType, requestMethod);
                if (refActionName != null)
                {
                    routeData.Values[ODataRouteConstants.Key] = ((KeyValuePathSegment)odataPath.Segments[1]).Value;
                    routeData.Values[ODataRouteConstants.NavigationProperty] = navigationSegment.NavigationProperty.Name;
                    return refActionName;
                }
            }
            else if ((odataPath.PathTemplate == EntitysetKeyNavigationKeyRef ||
                odataPath.PathTemplate == EntitysetKeyCastNavigationKeyRef) && requestMethod == HttpMethod.Delete)
            {
                NavigationPathSegment navigationSegment = (NavigationPathSegment)odataPath.Segments[odataPath.Segments.Count - 3];
                IEdmNavigationProperty navigationProperty = navigationSegment.NavigationProperty;
                IEdmEntityType declaringType = navigationProperty.DeclaringEntityType();

                string refActionName = FindRefActionName(actionMap, navigationProperty, declaringType, requestMethod);
                if (refActionName != null)
                {
                    routeData.Values[ODataRouteConstants.Key] = ((KeyValuePathSegment)odataPath.Segments[1]).Value;
                    routeData.Values[ODataRouteConstants.NavigationProperty] = navigationSegment.NavigationProperty.Name;
                    routeData.Values[ODataRouteConstants.RelatedKey] = ((KeyValuePathSegment)odataPath.Segments[odataPath.Segments.Count - 2]).Value;
                    return refActionName;
                }
            }

            return null;
        }
        /// <inheritdoc/>
        public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap)
        {
            if (odataPath == null)
            {
                throw Error.ArgumentNull("odataPath");
            }

            if (controllerContext == null)
            {
                throw Error.ArgumentNull("controllerContext");
            }

            if (actionMap == null)
            {
                throw Error.ArgumentNull("actionMap");
            }

            HttpMethod method = controllerContext.Request.Method;

            if ((method == HttpMethod.Get || method == HttpMethod.Post) && (
                odataPath.PathTemplate == "~/entityset/key/navigation" ||
                odataPath.PathTemplate == "~/entityset/key/cast/navigation" ||
                odataPath.PathTemplate == "~/singleton/navigation" ||
                odataPath.PathTemplate == "~/singleton/cast/navigation"))
            {
                NavigationPathSegment navigationSegment = odataPath.Segments.Last() as NavigationPathSegment;
                IEdmNavigationProperty navigationProperty = navigationSegment.NavigationProperty;
                IEdmEntityType declaringType = navigationProperty.DeclaringType as IEdmEntityType;

                if (declaringType != null)
                {
                    string actionNamePrefix = (method == HttpMethod.Get) ? "Get" : "PostTo";

                    // e.g. Try GetNavigationPropertyFromDeclaringType first, then fallback on GetNavigationProperty action name
                    string actionName = actionMap.FindMatchingAction(
                        actionNamePrefix + navigationProperty.Name + "From" + declaringType.Name,
                        actionNamePrefix + navigationProperty.Name);

                    if (actionName != null)
                    {
                        if (odataPath.PathTemplate.StartsWith("~/entityset/key", StringComparison.Ordinal))
                        {
                            KeyValuePathSegment keyValueSegment = odataPath.Segments[1] as KeyValuePathSegment;
                            controllerContext.RouteData.Values[ODataRouteConstants.Key] = keyValueSegment.Value;
                        }

                        return actionName;
                    }
                }
            }

            return null;
        }
        private static IEdmAction GetAction(ODataPath odataPath)
        {
            ODataPathSegment odataSegment = odataPath.Segments.Last();
            IEdmAction action = null;
            BoundActionPathSegment actionSegment = odataSegment as BoundActionPathSegment;
            if (actionSegment != null)
            {
                action = actionSegment.Action;
            }

            return action;
        }
        public void SelectController_RetrunsNull_IfNotNavigationSourceRequest()
        {
            // Arrange
            Mock<HttpRequestMessage> request = new Mock<HttpRequestMessage>();
            ODataPath odataPath = new ODataPath(new ODataPathSegment[] { new RefPathSegment() });

            // Act
            string controller = new MockNavigationSourceRoutingConvention().SelectController(odataPath, request.Object);

            // Assert
            Assert.Null(controller);
        }
        private static IEdmFunction GetFunction(ODataPath odataPath)
        {
            ODataPathSegment odataSegment = odataPath.Segments.Last();
            IEdmFunction function = null;
            BoundFunctionPathSegment functionSegment = odataSegment as BoundFunctionPathSegment;
            if (functionSegment != null)
            {
                function = functionSegment.Function;
            }

            return function;
        }
        public void SelectAction_ThrowsArgumentNull_IfActionMapIsNull()
        {
            // Arrange
            FunctionRoutingConvention functionConvention = new FunctionRoutingConvention();
            ODataPath odataPath = new ODataPath();
            HttpControllerContext controllerContext = new HttpControllerContext();

            // Act & Assert
            Assert.ThrowsArgumentNull(
                () => functionConvention.SelectAction(odataPath, controllerContext, actionMap: null),
                "actionMap");
        }
        public void SelectAction_ReturnsNull_RequestMethodIsNotPost(string requestMethod)
        {
            // Arrange
            ODataPath odataPath = new ODataPath();
            HttpControllerContext controllerContext = new HttpControllerContext();
            controllerContext.Request = new HttpRequestMessage(new HttpMethod(requestMethod), "http://localhost/");
            ILookup<string, HttpActionDescriptor> actionMap = new HttpActionDescriptor[0].ToLookup(desc => (string)null);

            // Act
            string selectedAction = new ActionRoutingConvention().SelectAction(odataPath, controllerContext, actionMap);

            // Assert
            Assert.Null(selectedAction);
        }
        /// <inheritdoc/>
        public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext,
            ILookup<string, HttpActionDescriptor> actionMap)
        {
            if (odataPath == null)
            {
                throw Error.ArgumentNull("odataPath");
            }

            if (controllerContext == null)
            {
                throw Error.ArgumentNull("controllerContext");
            }

            if (actionMap == null)
            {
                throw Error.ArgumentNull("actionMap");
            }

            if (controllerContext.Request.Method == HttpMethod.Get)
            {
                string actionName = null;
                switch (odataPath.PathTemplate)
                {
                    case "~/entityset/key/cast/function":
                    case "~/entityset/key/function":
                        actionName = GetFunction(odataPath).SelectAction(actionMap, isCollection: false);
                        if (actionName != null)
                        {
                            controllerContext.AddKeyValueToRouteData(odataPath);
                        }
                        break;
                    case "~/entityset/cast/function":
                    case "~/entityset/function":
                        actionName = GetFunction(odataPath).SelectAction(actionMap, isCollection: true);
                        break;
                    case "~/singleton/function":
                    case "~/singleton/cast/function":
                        actionName = GetFunction(odataPath).SelectAction(actionMap, isCollection: false);
                        break;
                }

                if (actionName != null)
                {
                    controllerContext.AddFunctionParameterToRouteData(odataPath.Segments.Last() as BoundFunctionPathSegment);
                    return actionName;
                }
            }

            return null;
        }
        public void Can_DeserializePayload_WithPrimitiveParameters(string actionName, IEdmAction expectedAction, ODataPath path)
        {
            // Arrange
            const int    Quantity    = 1;
            const string ProductCode = "PCode";
            string       body        = "{" +
                                       string.Format(@" ""Quantity"": {0} , ""ProductCode"": ""{1}"" , ""Birthday"": ""2015-02-27"", ""BkgColor"": ""Red"", ""InnerColor"": null", Quantity, ProductCode) +
                                       "}";

            ODataMessageWrapper message = new ODataMessageWrapper(GetStringAsStream(body));

            message.SetHeader("Content-Type", "application/json");
            ODataMessageReader       reader  = new ODataMessageReader(message as IODataRequestMessage, new ODataMessageReaderSettings(), _model);
            ODataDeserializerContext context = new ODataDeserializerContext()
            {
                Path = path, Model = _model
            };

            // Act
            ODataActionParameters payload = _deserializer.Read(reader, typeof(ODataActionParameters), context) as ODataActionParameters;
            IEdmAction            action  = ODataActionPayloadDeserializer.GetAction(context);

            // Assert
            Assert.NotNull(actionName);
            Assert.Same(expectedAction, action);
            Assert.NotNull(payload);
            Assert.True(payload.ContainsKey("Quantity"));
            Assert.Equal(Quantity, payload["Quantity"]);
            Assert.True(payload.ContainsKey("ProductCode"));
            Assert.Equal(ProductCode, payload["ProductCode"]);

            Assert.True(payload.ContainsKey("Birthday"));
            Assert.Equal(new Date(2015, 2, 27), payload["Birthday"]);

            Assert.True(payload.ContainsKey("BkgColor"));
            AColor bkgColor = Assert.IsType <AColor>(payload["BkgColor"]);

            Assert.Equal(AColor.Red, bkgColor);

            Assert.True(payload.ContainsKey("InnerColor"));
            Assert.Null(payload["InnerColor"]);
        }
        /// <summary>
        /// Confgiures the http request with OData values.
        /// </summary>
        /// <param name="request">The http request.</param>
        /// <param name="prefix">The prefix.</param>
        /// <param name="model">The Edm model.</param>
        /// <param name="path">The OData path.</param>
        /// <returns></returns>
        public static HttpRequest Configure(this HttpRequest request, string prefix, IEdmModel model, ODataPath path)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            IODataFeature feature = request.ODataFeature();

            feature.PrefixName = prefix;
            feature.Model      = model;
            feature.Path       = path;
            return(request);
        }
        public void Can_DeserializePayload_WithComplexCollectionParameters(string actionName, IEdmAction expectedAction, ODataPath path)
        {
            // Arrange
            const string        Body    = @"{ ""Name"": ""Microsoft"", ""Addresses"": [ { ""StreetAddress"":""1 Microsoft Way"", ""City"": ""Redmond"", ""State"": ""WA"", ""ZipCode"": 98052 } ] }";
            ODataMessageWrapper message = new ODataMessageWrapper(GetStringAsStream(Body));

            message.SetHeader("Content-Type", "application/json");
            ODataMessageReader       reader  = new ODataMessageReader(message as IODataRequestMessage, new ODataMessageReaderSettings(), _model);
            ODataDeserializerContext context = new ODataDeserializerContext {
                Path = path, Model = _model
            };

            // Act
            ODataActionParameters payload = _deserializer.Read(reader, typeof(ODataActionParameters), context) as ODataActionParameters;

            // Assert
            Assert.NotNull(actionName);
            Assert.NotNull(expectedAction);
            Assert.NotNull(payload);
            Assert.True(payload.ContainsKey("Name"));
            Assert.Equal("Microsoft", payload["Name"]);
            Assert.True(payload.ContainsKey("Addresses"));
            IList <MyAddress> addresses = (payload["Addresses"] as IEnumerable <MyAddress>).ToList();

            Assert.NotNull(addresses);
            Assert.Single(addresses);
            MyAddress address = addresses[0];

            Assert.NotNull(address);
            Assert.Equal("1 Microsoft Way", address.StreetAddress);
            Assert.Equal("Redmond", address.City);
            Assert.Equal("WA", address.State);
            Assert.Equal(98052, address.ZipCode);
        }
        public void Can_DeserializePayload_WithEntityCollectionParameters_InUntypedMode(IEdmAction expectedAction, ODataPath path)
        {
            // Arrange
            ODataMessageWrapper message = new ODataMessageWrapper(GetStringAsStream(EntityCollectionPayload));

            message.SetHeader("Content-Type", "application/json");
            ODataMessageReader       reader  = new ODataMessageReader(message as IODataRequestMessage, new ODataMessageReaderSettings(), _model);
            ODataDeserializerContext context = new ODataDeserializerContext {
                Path = path, Model = _model, ResourceType = typeof(ODataUntypedActionParameters)
            };

            // Act
            ODataUntypedActionParameters payload = _deserializer.Read(reader, typeof(ODataUntypedActionParameters), context) as ODataUntypedActionParameters;

            // Assert
            Assert.Same(expectedAction, payload.Action);
            Assert.NotNull(payload);
            Assert.True(payload.ContainsKey("Id"));
            Assert.Equal(1, payload["Id"]);

            IEnumerable <IEdmObject> customers = payload["Customers"] as EdmEntityObjectCollection;

            Assert.Equal(2, customers.Count());
            dynamic customer = customers.First();

            Assert.NotNull(customer);
            Assert.Equal(109, customer.Id);
            Assert.Equal("Avatar", customer.Name);

            customer = customers.Last();
            Assert.NotNull(customer);
            Assert.Equal(901, customer.Id);
            Assert.Equal("Robot", customer.Name);
        }
Exemple #35
0
        public void CreateNavigationPuthOperationReturnsSecurityForUpdateRestrictions(bool enableAnnotation)
        {
            string annotation = @"<Annotation Term=""Org.OData.Capabilities.V1.NavigationRestrictions"">
  <Record>
   <PropertyValue Property=""RestrictedProperties"" >
      <Collection>
        <Record>
          <PropertyValue Property=""NavigationProperty"" NavigationPropertyPath=""Orders"" />
          <PropertyValue Property=""UpdateRestrictions"" >
            <Record>
              <PropertyValue Property=""Permissions"">
                <Collection>
                  <Record>
                    <PropertyValue Property=""SchemeName"" String=""Delegated (work or school account)"" />
                    <PropertyValue Property=""Scopes"">
                      <Collection>
                        <Record>
                          <PropertyValue Property=""Scope"" String=""User.ReadBasic.All"" />
                        </Record>
                        <Record>
                          <PropertyValue Property=""Scope"" String=""User.Read.All"" />
                        </Record>
                      </Collection>
                    </PropertyValue>
                  </Record>
                  <Record>
                    <PropertyValue Property=""SchemeName"" String=""Application"" />
                    <PropertyValue Property=""Scopes"">
                      <Collection>
                        <Record>
                          <PropertyValue Property=""Scope"" String=""User.Read.All"" />
                        </Record>
                        <Record>
                          <PropertyValue Property=""Scope"" String=""Directory.Read.All"" />
                        </Record>
                      </Collection>
                    </PropertyValue>
                  </Record>
                </Collection>
              </PropertyValue>
              <PropertyValue Property=""Description"" String=""A brief description of GET '/me' request."" />
              <PropertyValue Property=""CustomHeaders"">
                <Collection>
                  <Record>
                    <PropertyValue Property=""Name"" String=""odata-debug"" />
                    <PropertyValue Property=""Description"" String=""Debug support for OData services"" />
                    <PropertyValue Property=""Required"" Bool=""false"" />
                    <PropertyValue Property=""ExampleValues"">
                      <Collection>
                        <Record>
                          <PropertyValue Property=""Value"" String=""html"" />
                          <PropertyValue Property=""Description"" String=""Service responds with self-contained..."" />
                        </Record>
                        <Record>
                          <PropertyValue Property=""Value"" String=""json"" />
                          <PropertyValue Property=""Description"" String=""Service responds with JSON document..."" />
                        </Record>
                      </Collection>
                    </PropertyValue>
                  </Record>
                </Collection>
              </PropertyValue>
            </Record>
          </PropertyValue>
        </Record>
      </Collection>
    </PropertyValue>
  </Record>
</Annotation>";

            // Arrange
            IEdmModel edmModel = NavigationPropertyPathItemHandlerTest.GetEdmModel(enableAnnotation ? annotation : "");

            Assert.NotNull(edmModel);
            ODataContext  context   = new ODataContext(edmModel);
            IEdmEntitySet entitySet = edmModel.EntityContainer.FindEntitySet("Customers");

            Assert.NotNull(entitySet); // guard
            IEdmEntityType entityType = entitySet.EntityType();

            IEdmNavigationProperty property = entityType.DeclaredNavigationProperties().FirstOrDefault(c => c.Name == "Orders");

            Assert.NotNull(property);

            ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet),
                                           new ODataKeySegment(entityType),
                                           new ODataNavigationPropertySegment(property),
                                           new ODataKeySegment(property.DeclaringEntityType()));

            // Act
            var operation = _operationHandler.CreateOperation(context, path);

            // Assert
            Assert.NotNull(operation);
            Assert.NotNull(operation.Security);

            if (enableAnnotation)
            {
                Assert.Equal(2, operation.Security.Count);

                string json = operation.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
                Assert.Contains(@"
  ""security"": [
    {
      ""Delegated (work or school account)"": [
        ""User.ReadBasic.All"",
        ""User.Read.All""
      ]
    },
    {
      ""Application"": [
        ""User.Read.All"",
        ""Directory.Read.All""
      ]
    }
  ],".ChangeLineBreaks(), json);

                // with custom header
                Assert.Contains(@"
    {
      ""name"": ""odata-debug"",
      ""in"": ""header"",
      ""description"": ""Debug support for OData services"",
      ""schema"": {
        ""type"": ""string""
      },
      ""examples"": {
        ""example-1"": {
          ""description"": ""Service responds with self-contained..."",
          ""value"": ""html""
        },
        ""example-2"": {
          ""description"": ""Service responds with JSON document..."",
          ""value"": ""json""
        }
      }
    }".ChangeLineBreaks(), json);
            }
            else
            {
                Assert.Empty(operation.Security);
            }
        }
 public virtual string SelectController(ODataPath odataPath, HttpRequestMessage request)
 {
     Arg.NotNull(odataPath, nameof(odataPath));
     Arg.NotNull(request, nameof(request));
     return(odataPath.PathTemplate == "~" || odataPath.PathTemplate == "~/$metadata" ? "VersionedMetadata" : null);
 }
        /// <inheritdoc/>
        public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup <string, HttpActionDescriptor> actionMap)
        {
            if (odataPath == null)
            {
                throw Error.ArgumentNull("odataPath");
            }

            if (controllerContext == null)
            {
                throw Error.ArgumentNull("controllerContext");
            }

            if (actionMap == null)
            {
                throw Error.ArgumentNull("actionMap");
            }

            if (odataPath.PathTemplate == "~/entityset/key" ||
                odataPath.PathTemplate == "~/entityset/key/cast")
            {
                HttpMethod httpMethod = controllerContext.Request.Method;
                string     httpMethodName;

                switch (httpMethod.ToString().ToUpperInvariant())
                {
                case "GET":
                    httpMethodName = "Get";
                    break;

                case "PUT":
                    httpMethodName = "Put";
                    break;

                case "PATCH":
                case "MERGE":
                    httpMethodName = "Patch";
                    break;

                case "DELETE":
                    httpMethodName = "Delete";
                    break;

                default:
                    return(null);
                }

                Contract.Assert(httpMethodName != null);

                IEdmEntityType entityType = (IEdmEntityType)odataPath.EdmType;

                // e.g. Try GetCustomer first, then fallback on Get action name
                string actionName = actionMap.FindMatchingAction(
                    httpMethodName + entityType.Name,
                    httpMethodName);

                if (actionName != null)
                {
                    KeySegment keySegment = (KeySegment)odataPath.Segments[1];
                    controllerContext.AddKeyValueToRouteData(keySegment);
                    return(actionName);
                }
            }
            return(null);
        }
 public virtual HttpResponseMessage HandleUnmappedRequest(ODataPath odataPath)
 {
     throw EntitySetControllerHelpers.UnmappedRequestResponse(this, odataPath);
 }
Exemple #39
0
        private static IEdmNavigationSource GetNavigationSource(IEdmModel model, IEdmType elementType, ODataPath odataPath)
        {
            Contract.Assert(model != null);
            Contract.Assert(elementType != null);

            IEdmNavigationSource navigationSource = (odataPath != null) ? odataPath.NavigationSource : null;

            if (navigationSource != null)
            {
                return(navigationSource);
            }

            IEdmEntityContainer entityContainer = model.EntityContainer;

            if (entityContainer == null)
            {
                return(null);
            }

            List <IEdmEntitySet> matchedNavigationSources =
                entityContainer.EntitySets().Where(e => e.EntityType() == elementType).ToList();

            return((matchedNavigationSources.Count != 1) ? null : matchedNavigationSources[0]);
        }
Exemple #40
0
        public void CreatePathItemForNavigationPropertyAndUpdateMethodUpdateRestrictions(bool updateMethod, string navigationPropertyPath)
        {
            // Arrange
            string annotation = String.Format(@"
<Annotation Term=""Org.OData.Capabilities.V1.NavigationRestrictions"">
  <Record>
    <PropertyValue Property=""RestrictedProperties"" >
      <Collection>
        <Record>
          <PropertyValue Property=""NavigationProperty"" NavigationPropertyPath=""{0}"" />
          <PropertyValue Property=""UpdateRestrictions"" >
            <Record>
              <PropertyValue Property=""UpdateMethod"">
                <EnumMember>Org.OData.Capabilities.V1.HttpMethod/PUT</EnumMember>
              </PropertyValue>
            </Record>
          </PropertyValue>
        </Record>
      </Collection>
    </PropertyValue>
  </Record>
</Annotation>", navigationPropertyPath);

            IEdmModel     model     = GetEdmModel(updateMethod ? annotation : "");
            ODataContext  context   = new ODataContext(model);
            IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");

            Assert.NotNull(entitySet); // guard

            ODataPath path = CreatePath(entitySet, navigationPropertyPath, true);

            // Act
            var pathItem = _pathItemHandler.CreatePathItem(context, path);

            // Assert
            Assert.NotNull(pathItem);
            Assert.NotNull(pathItem.Operations);
            Assert.NotEmpty(pathItem.Operations);

            var  navigationProperty = path.Segments.OfType <ODataNavigationPropertySegment>().Last().NavigationProperty;
            bool isContainment      = navigationProperty.ContainsTarget;
            bool isCollection       = navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many;

            OperationType[] expected;
            if (isContainment)
            {
                if (updateMethod)
                {
                    expected = new[] { OperationType.Get, OperationType.Put, OperationType.Delete };
                }
                else
                {
                    expected = new[] { OperationType.Get, OperationType.Patch, OperationType.Delete };
                }
            }
            else
            {
                expected = new[] { OperationType.Get };
            }

            Assert.Equal(expected, pathItem.Operations.Select(o => o.Key));
        }
        /// <summary>
        /// Creates a set of transformed route values that will be used to select an action.
        /// </summary>
        /// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
        /// <param name="values">The route values associated with the current match.</param>
        /// <returns>A task which asynchronously returns a set of route values.</returns>
        public override ValueTask <RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
        {
            if (httpContext == null)
            {
                throw Error.ArgumentNull(nameof(httpContext));
            }

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

            if (httpContext.ODataFeature().Path != null)
            {
                // Noted: if there's a route mapping with ODataPrefix == null,
                // for example: router.MapODataRoute(routeName: "odata", routePrefix: null, ...)
                // This route will match all requests.
                // Therefore, this route will be a candidate and its tranformer will be called.
                // So, we use the ODataPath setting to verify whether the request is transformed or not.
                // Maybe we have a better solution later.
                return(new ValueTask <RouteValueDictionary>(result: null));
            }

            (string routeName, object oDataPathValue) = values.GetODataRouteInfo();
            if (routeName != null)
            {
                HttpRequest request   = httpContext.Request;
                string      oDataPath = oDataPathValue as string;
                // Create request container
                request.CreateRequestContainer(routeName);

                // Check whether the request is a POST targeted at a resource path ending in /$query
                if (request.IsQueryRequest(oDataPath))
                {
                    request.TransformQueryRequest();

                    oDataPath = oDataPath.Substring(0, oDataPath.LastIndexOf('/' + ODataRouteConstants.QuerySegment, StringComparison.OrdinalIgnoreCase));
                    values[ODataRouteConstants.ODataPath] = oDataPath;
                }

                // We need to call Uri.GetLeftPart(), which returns an encoded Url.
                // The ODL parser does not like raw values.
                Uri    requestUri      = new Uri(request.GetEncodedUrl());
                string requestLeftPart = requestUri.GetLeftPart(UriPartial.Path);
                string queryString     = request.QueryString.HasValue ? request.QueryString.ToString() : null;

                // Call ODL to parse the Request URI.
                ODataPath path = ODataPathRouteConstraint.GetODataPath(oDataPath, requestLeftPart, queryString, () => request.GetRequestContainer());
                if (path != null)
                {
                    // Set all the properties we need for routing, querying, formatting
                    IODataFeature odataFeature = httpContext.ODataFeature();
                    odataFeature.Path              = path;
                    odataFeature.RouteName         = routeName;
                    odataFeature.IsEndpointRouting = true; // mark as Endpoint routing

                    // Noted: we inject the ActionSelector and use it to select the best OData action.
                    // In .NET 5 or later, this maybe change.
                    RouteContext routeContext     = new RouteContext(httpContext);
                    var          condidates       = _selector.SelectCandidates(routeContext);
                    var          actionDescriptor = _selector.SelectBestCandidate(routeContext, condidates);
                    ControllerActionDescriptor controllerActionDescriptor = actionDescriptor as ControllerActionDescriptor;
                    if (controllerActionDescriptor != null)
                    {
                        RouteValueDictionary newValues = new RouteValueDictionary();
                        foreach (var item in values)
                        {
                            newValues.Add(item.Key, item.Value);
                        }

                        foreach (var item in routeContext.RouteData.Values)
                        {
                            newValues[item.Key] = item.Value;
                        }

                        newValues["controller"] = controllerActionDescriptor.ControllerName;
                        newValues["action"]     = controllerActionDescriptor.ActionName;
                        newValues["odataPath"]  = oDataPathValue;

                        // Noted, here's a working around for mulitiple actions in same controller.
                        // For example, we have two "Get" methods in same controller, in order to help "EndpointSelector"
                        // to select the correct Endpoint, we save the ActionDescriptor value into ODataFeature.
                        odataFeature.ActionDescriptor = controllerActionDescriptor;
                        // Add handler to handle options calls. The routing criteria has been patched to allow endpoint discovery using the correct cors headers
                        if (request.Method.Equals("OPTIONS", StringComparison.OrdinalIgnoreCase))
                        {
                            var metadata = actionDescriptor.EndpointMetadata;
                            // For option request can set this as the action will be handled by the cors middleware
                            var metadataCollection = metadata?.Any() == true
                                ? new EndpointMetadataCollection(metadata)
                                : EndpointMetadataCollection.Empty;
                            // This workaround allows the default cors middleware to read the annotations if the user has them enabling fine-grained cors access control with endpoints
                            var endpoint = new Endpoint(null, metadataCollection, controllerActionDescriptor.ActionName);
                            httpContext.SetEndpoint(endpoint);
                        }

                        return(new ValueTask <RouteValueDictionary>(newValues));
                    }
                }
                else
                {
                    // The request doesn't match this route so dispose the request container.
                    request.DeleteRequestContainer(true);
                }
            }

            return(new ValueTask <RouteValueDictionary>(result: null));
        }
        private void VerifyMediaEntityPutOperation(string annotation, bool enableOperationId)
        {
            // Arrange
            IEdmModel model = MediaEntityGetOperationHandlerTests.GetEdmModel(annotation);
            OpenApiConvertSettings settings = new OpenApiConvertSettings
            {
                EnableOperationId = enableOperationId
            };

            ODataContext  context = new ODataContext(model, settings);
            IEdmEntitySet todos   = model.EntityContainer.FindEntitySet("Todos");
            IEdmSingleton me      = model.EntityContainer.FindSingleton("me");

            Assert.NotNull(todos);

            IEdmEntityType         todo = model.SchemaElements.OfType <IEdmEntityType>().First(c => c.Name == "Todo");
            IEdmStructuralProperty sp   = todo.StructuralProperties().First(c => c.Name == "Logo");
            ODataPath path = new ODataPath(new ODataNavigationSourceSegment(todos),
                                           new ODataKeySegment(todos.EntityType()),
                                           new ODataStreamPropertySegment(sp.Name));

            IEdmEntityType         user        = model.SchemaElements.OfType <IEdmEntityType>().First(c => c.Name == "user");
            IEdmNavigationProperty navProperty = user.NavigationProperties().First(c => c.Name == "photo");
            ODataPath path2 = new ODataPath(new ODataNavigationSourceSegment(me),
                                            new ODataNavigationPropertySegment(navProperty),
                                            new ODataStreamContentSegment());

            // Act
            var putOperation  = _operationalHandler.CreateOperation(context, path);
            var putOperation2 = _operationalHandler.CreateOperation(context, path2);

            // Assert
            Assert.NotNull(putOperation);
            Assert.NotNull(putOperation2);
            Assert.Equal("Update Logo for Todo in Todos", putOperation.Summary);
            Assert.Equal("Update media content for the navigation property photo in me", putOperation2.Summary);
            Assert.NotNull(putOperation.Tags);
            Assert.NotNull(putOperation2.Tags);

            var tag  = Assert.Single(putOperation.Tags);
            var tag2 = Assert.Single(putOperation2.Tags);

            Assert.Equal("Todos.Todo", tag.Name);
            Assert.Equal("me.profilePhoto", tag2.Name);

            Assert.NotNull(putOperation.Responses);
            Assert.NotNull(putOperation2.Responses);
            Assert.Equal(2, putOperation.Responses.Count);
            Assert.Equal(2, putOperation2.Responses.Count);
            Assert.Equal(new[] { "204", "default" }, putOperation.Responses.Select(r => r.Key));
            Assert.Equal(new[] { "204", "default" }, putOperation2.Responses.Select(r => r.Key));

            if (!string.IsNullOrEmpty(annotation))
            {
                Assert.Equal(2, putOperation.RequestBody.Content.Keys.Count);
                Assert.True(putOperation.RequestBody.Content.ContainsKey("image/png"));
                Assert.True(putOperation.RequestBody.Content.ContainsKey("image/jpeg"));
                Assert.Equal("The logo image.", putOperation.Description);

                Assert.Equal(1, putOperation2.RequestBody.Content.Keys.Count);
                Assert.True(putOperation2.RequestBody.Content.ContainsKey(Constants.ApplicationOctetStreamMediaType));
            }
            else
            {
                Assert.Equal(1, putOperation.RequestBody.Content.Keys.Count);
                Assert.Equal(1, putOperation2.RequestBody.Content.Keys.Count);
                Assert.True(putOperation.RequestBody.Content.ContainsKey(Constants.ApplicationOctetStreamMediaType));
                Assert.True(putOperation2.RequestBody.Content.ContainsKey(Constants.ApplicationOctetStreamMediaType));
            }

            if (enableOperationId)
            {
                Assert.Equal("Todos.Todo.UpdateLogo", putOperation.OperationId);
                Assert.Equal("me.UpdatePhotoContent", putOperation2.OperationId);
            }
            else
            {
                Assert.Null(putOperation.OperationId);
                Assert.Null(putOperation2.OperationId);
            }
        }
Exemple #43
0
        /// <inheritdoc/>
        public override ActionDescriptor SelectAction(RouteContext routeContext, IEnumerable <ControllerActionDescriptor> actionDescriptors)
        {
            if (routeContext == null)
            {
                throw Error.ArgumentNull("routeContext");
            }

            ODataPath   odataPath = routeContext.HttpContext.Request.ODataFeature().Path;
            HttpRequest request   = routeContext.HttpContext.Request;

            if (odataPath.PathTemplate == "~/entityset/key" ||
                odataPath.PathTemplate == "~/entityset/key/cast")
            {
                string httpMethod = request.Method.ToUpperInvariant();
                string httpMethodName;
                switch (httpMethod)
                {
                case ODataRouteConstants.HttpGet:
                    httpMethodName = "Get";
                    break;

                case ODataRouteConstants.HttpPut:
                    httpMethodName = "Put";
                    break;

                case ODataRouteConstants.HttpPatch:
                    httpMethodName = "Patch";
                    break;

                case ODataRouteConstants.HttpDelete:
                    httpMethodName = "Delete";
                    break;

                default:
                    return(null);
                }

                Contract.Assert(httpMethodName != null);

                IEdmEntityType entityType = (IEdmEntityType)odataPath.EdmType;

                // e.g. Try GetCustomer first, then fallback on Get action name
                var actions = actionDescriptors.FindMatchingActions(
                    httpMethodName + entityType.Name,
                    httpMethodName);
                // TODO: Cache these results
                var keyNames         = entityType.Key().Select(k => k.Type.AsTypeDefinition().ShortQualifiedName()).ToArray();
                var actionDescriptor = actions.FirstOrDefault(action =>
                {
                    if (action.Parameters.Count == keyNames.Length &&
                        action.Parameters.All(
                            p => keyNames.Contains(p.ParameterType.EdmName()) || p.ParameterType == typeof(KeyValue)))
                    {
                        return(true);
                    }
                    if (action.Parameters.Count == 1 &&
                        typeof(IEnumerable <object>).IsAssignableFrom(action.Parameters[0].ParameterType))
                    {
                        return(true);
                    }
                    return(false);
                });
                if (actionDescriptor != null)
                {
                    KeySegment keySegment = (KeySegment)odataPath.Segments[1];
                    routeContext.AddKeyValueToRouteData(keySegment, actionDescriptor);
                    return(actionDescriptor);
                }
            }

            return(null);
        }
        public void AddSkipConstant(ConstantExpression skipConstant, ODataPath path)
        {
            ConstantNode skipNode = UriCompare.OeODataUriComparerParameterValues.CreateSkipConstantNode((int)skipConstant.Value, path);

            AddConstant(skipConstant, skipNode);
        }
Exemple #45
0
        internal static string SelectActionImpl(ODataPath odataPath, IWebApiControllerContext controllerContext,
                                                IWebApiActionMap actionMap)
        {
            string             actionName            = null;
            DynamicPathSegment dynamicPropertSegment = null;

            switch (odataPath.PathTemplate)
            {
            case "~/entityset/key/dynamicproperty":
            case "~/entityset/key/cast/dynamicproperty":
            case "~/singleton/dynamicproperty":
            case "~/singleton/cast/dynamicproperty":
                dynamicPropertSegment = odataPath.Segments.Last() as DynamicPathSegment;
                if (dynamicPropertSegment == null)
                {
                    return(null);
                }

                if (ODataRequestMethod.Get == controllerContext.Request.GetRequestMethodOrPreflightMethod())
                {
                    string actionNamePrefix = String.Format(CultureInfo.InvariantCulture, "Get{0}", ActionName);
                    actionName = actionMap.FindMatchingAction(actionNamePrefix);
                }
                break;

            case "~/entityset/key/property/dynamicproperty":
            case "~/entityset/key/cast/property/dynamicproperty":
            case "~/singleton/property/dynamicproperty":
            case "~/singleton/cast/property/dynamicproperty":
                dynamicPropertSegment = odataPath.Segments.Last() as DynamicPathSegment;
                if (dynamicPropertSegment == null)
                {
                    return(null);
                }

                PropertySegment propertyAccessSegment = odataPath.Segments[odataPath.Segments.Count - 2]
                                                        as PropertySegment;
                if (propertyAccessSegment == null)
                {
                    return(null);
                }

                EdmComplexType complexType = propertyAccessSegment.Property.Type.Definition as EdmComplexType;
                if (complexType == null)
                {
                    return(null);
                }

                if (ODataRequestMethod.Get == controllerContext.Request.GetRequestMethodOrPreflightMethod())
                {
                    string actionNamePrefix = String.Format(CultureInfo.InvariantCulture, "Get{0}", ActionName);
                    actionName = actionMap.FindMatchingAction(actionNamePrefix + "From" + propertyAccessSegment.Property.Name);
                }
                break;

            default: break;
            }

            if (actionName != null)
            {
                if (odataPath.PathTemplate.StartsWith("~/entityset/key", StringComparison.Ordinal))
                {
                    KeySegment keyValueSegment = (KeySegment)odataPath.Segments[1];
                    controllerContext.AddKeyValueToRouteData(keyValueSegment);
                }

                controllerContext.RouteData.Add(ODataRouteConstants.DynamicProperty, dynamicPropertSegment.Identifier);
                var key   = ODataParameterValue.ParameterValuePrefix + ODataRouteConstants.DynamicProperty;
                var value = new ODataParameterValue(dynamicPropertSegment.Identifier, EdmLibHelpers.GetEdmPrimitiveTypeReferenceOrNull(typeof(string)));
                controllerContext.RouteData.Add(key, value);
                controllerContext.Request.Context.RoutingConventionsStore.Add(key, value);
                return(actionName);
            }
            return(null);
        }
        public void AddTopConstant(ConstantExpression topConstant, ODataPath path)
        {
            ConstantNode topNode = UriCompare.OeODataUriComparerParameterValues.CreateTopConstantNode((int)topConstant.Value, path);

            AddConstant(topConstant, topNode);
        }
        public void Throws_ODataException_When_Parameter_Notfound(string actionName, IEdmAction expectedAction, ODataPath path)
        {
            // Arrange
            const string        Body    = @"{ ""Quantity"": 1 , ""ProductCode"": ""PCode"", ""MissingParameter"": 1 }";
            ODataMessageWrapper message = new ODataMessageWrapper(GetStringAsStream(Body));

            message.SetHeader("Content-Type", "application/json");
            ODataMessageReader       reader  = new ODataMessageReader(message as IODataRequestMessage, new ODataMessageReaderSettings(), _model);
            ODataDeserializerContext context = new ODataDeserializerContext {
                Path = path, Model = _model
            };

            // Act & Assert
            Assert.NotNull(expectedAction);
            ExceptionAssert.Throws <ODataException>(() =>
            {
                ODataActionParameters payload = _deserializer.Read(reader, typeof(ODataActionParameters), context) as ODataActionParameters;
            }, "The parameter 'MissingParameter' in the request payload is not a valid parameter for the operation '" + actionName + "'.");
        }
        public void Can_DeserializePayload_WithPrimitiveCollectionParameters(string actionName, IEdmAction expectedAction, ODataPath path)
        {
            // Arrange
            const string Body =
                @"{ ""Name"": ""Avatar"", ""Ratings"": [ 5, 5, 3, 4, 5, 5, 4, 5, 5, 4 ], ""Time"": [""01:02:03.0040000"", ""12:13:14.1150000""], ""Colors"": [ ""Red"", null, ""Green""] }";

            int[] expectedRatings = new int[] { 5, 5, 3, 4, 5, 5, 4, 5, 5, 4 };

            ODataMessageWrapper message = new ODataMessageWrapper(GetStringAsStream(Body));

            message.SetHeader("Content-Type", "application/json");
            ODataMessageReader reader = new ODataMessageReader(message as IODataRequestMessage, new ODataMessageReaderSettings(), _model);

            ODataDeserializerContext context = new ODataDeserializerContext {
                Path = path, Model = _model
            };

            // Act
            ODataActionParameters payload = _deserializer.Read(reader, typeof(ODataActionParameters), context) as ODataActionParameters;
            IEdmAction            action  = ODataActionPayloadDeserializer.GetAction(context);

            // Assert
            Assert.NotNull(actionName);
            Assert.NotNull(payload);
            Assert.Same(expectedAction, action);
            Assert.True(payload.ContainsKey("Name"));
            Assert.Equal("Avatar", payload["Name"]);
            Assert.True(payload.ContainsKey("Ratings"));
            IEnumerable <int> ratings = payload["Ratings"] as IEnumerable <int>;

            Assert.Equal(10, ratings.Count());
            Assert.True(expectedRatings.Zip(ratings, (expected, actual) => expected - actual).All(diff => diff == 0));

            Assert.True(payload.ContainsKey("Time"));
            IEnumerable <TimeOfDay> times = payload["Time"] as IEnumerable <TimeOfDay>;

            Assert.Equal(2, times.Count());
            Assert.Equal(new[] { new TimeOfDay(1, 2, 3, 4), new TimeOfDay(12, 13, 14, 115) }, times.ToList());

            Assert.True(payload.ContainsKey("Colors"));
            IEnumerable <AColor?> colors = payload["Colors"] as IEnumerable <AColor?>;

            Assert.Equal("Red|null|Green", String.Join("|", colors.Select(e => e == null ? "null" : e.ToString())));
        }
        public void Can_DeserializePayload_WithEntityCollectionParameters(IEdmAction expectedAction, ODataPath path)
        {
            // Arrange
            ODataMessageWrapper message = new ODataMessageWrapper(GetStringAsStream(EntityCollectionPayload));

            message.SetHeader("Content-Type", "application/json");
            ODataMessageReader       reader  = new ODataMessageReader(message as IODataRequestMessage, new ODataMessageReaderSettings(), _model);
            ODataDeserializerContext context = new ODataDeserializerContext()
            {
                Path = path, Model = _model
            };

            // Act
            ODataActionParameters payload = _deserializer.Read(reader, typeof(ODataActionParameters), context) as ODataActionParameters;
            IEdmAction            action  = ODataActionPayloadDeserializer.GetAction(context);

            // Assert
            Assert.Same(expectedAction, action);
            Assert.NotNull(payload);
            Assert.True(payload.ContainsKey("Id"));
            Assert.Equal(1, payload["Id"]);

            IList <Customer> customers = (payload["Customers"] as IEnumerable <Customer>).ToList();

            Assert.NotNull(customers);
            Assert.Equal(2, customers.Count);
            Customer customer = customers[0];

            Assert.NotNull(customer);
            Assert.Equal(109, customer.Id);
            Assert.Equal("Avatar", customer.Name);

            customer = customers[1];
            Assert.NotNull(customer);
            Assert.Equal(901, customer.Id);
            Assert.Equal("Robot", customer.Name);
        }
        public void Can_DeserializePayload_WithEnumParameters_InUntypedMode(string actionName, IEdmAction expectedAction, ODataPath path)
        {
            // Arrange
            const string        Body    = @"{ ""Color"": ""Red""}";
            ODataMessageWrapper message = new ODataMessageWrapper(GetStringAsStream(Body));

            message.SetHeader("Content-Type", "application/json");

            ODataMessageReaderSettings settings = new ODataMessageReaderSettings();
            ODataMessageReader         reader   = new ODataMessageReader(message as IODataRequestMessage, settings, _model);

            ODataDeserializerContext context = new ODataDeserializerContext {
                Path = path, Model = _model, ResourceType = typeof(ODataUntypedActionParameters)
            };

            // Act
            ODataUntypedActionParameters payload = _deserializer.Read(reader, typeof(ODataUntypedActionParameters), context) as ODataUntypedActionParameters;

            // Assert
            Assert.NotNull(actionName);
            Assert.NotNull(payload);
            Assert.Same(expectedAction, payload.Action);
            Assert.True(payload.ContainsKey("Color"));
            EdmEnumObject color = payload["Color"] as EdmEnumObject;

            Assert.IsType <EdmEnumObject>(color);
            Assert.Equal("Red", color.Value);
        }
 private static IEdmEntitySet GetEntitySet(ODataPath path)
 {
     Contract.Assert(path != null);
     return(path.EntitySet);
 }
        private static IEdmProperty GetProperty(ODataPath odataPath, HttpMethod method, out string prefix,
                                                out ComplexCastPathSegment cast)
        {
            prefix = String.Empty;
            cast   = null;
            PropertyAccessPathSegment segment = null;

            if (odataPath.PathTemplate == "~/entityset/key/property" ||
                odataPath.PathTemplate == "~/entityset/key/cast/property" ||
                odataPath.PathTemplate == "~/singleton/property" ||
                odataPath.PathTemplate == "~/singleton/cast/property")
            {
                PropertyAccessPathSegment tempSegment =
                    (PropertyAccessPathSegment)odataPath.Segments[odataPath.Segments.Count - 1];

                switch (method.Method.ToUpperInvariant())
                {
                case "GET":
                    prefix  = "Get";
                    segment = tempSegment;
                    break;

                case "PUT":
                    prefix  = "PutTo";
                    segment = tempSegment;
                    break;

                case "PATCH":
                    // OData Spec: PATCH is not supported for collection properties.
                    if (!tempSegment.Property.Type.IsCollection())
                    {
                        prefix  = "PatchTo";
                        segment = tempSegment;
                    }
                    break;

                case "DELETE":
                    // OData spec: A successful DELETE request to the edit URL for a structural property, ... sets the property to null.
                    // The request body is ignored and should be empty.
                    // DELETE request to a non-nullable value MUST fail and the service respond with 400 Bad Request or other appropriate error.
                    if (tempSegment.Property.Type.IsNullable)
                    {
                        prefix  = "DeleteTo";
                        segment = tempSegment;
                    }
                    break;
                }
            }
            else if (odataPath.PathTemplate == "~/entityset/key/property/complexcast" ||
                     odataPath.PathTemplate == "~/entityset/key/cast/property/complexcast" ||
                     odataPath.PathTemplate == "~/singleton/property/complexcast" ||
                     odataPath.PathTemplate == "~/singleton/cast/property/complexcast")
            {
                PropertyAccessPathSegment tempSegment =
                    (PropertyAccessPathSegment)odataPath.Segments[odataPath.Segments.Count - 2];
                ComplexCastPathSegment tempCast = (ComplexCastPathSegment)odataPath.Segments.Last();
                switch (method.Method.ToUpperInvariant())
                {
                case "GET":
                    prefix  = "Get";
                    segment = tempSegment;
                    cast    = tempCast;
                    break;

                case "PUT":
                    prefix  = "PutTo";
                    segment = tempSegment;
                    cast    = tempCast;
                    break;

                case "PATCH":
                    // PATCH is not supported for collection properties.
                    if (!tempSegment.Property.Type.IsCollection())
                    {
                        prefix  = "PatchTo";
                        segment = tempSegment;
                        cast    = tempCast;
                    }
                    break;
                }
            }
            else if (odataPath.PathTemplate == "~/entityset/key/property/$value" ||
                     odataPath.PathTemplate == "~/entityset/key/cast/property/$value" ||
                     odataPath.PathTemplate == "~/singleton/property/$value" ||
                     odataPath.PathTemplate == "~/singleton/cast/property/$value" ||
                     odataPath.PathTemplate == "~/entityset/key/property/$count" ||
                     odataPath.PathTemplate == "~/entityset/key/cast/property/$count" ||
                     odataPath.PathTemplate == "~/singleton/property/$count" ||
                     odataPath.PathTemplate == "~/singleton/cast/property/$count")
            {
                PropertyAccessPathSegment tempSegment = (PropertyAccessPathSegment)odataPath.Segments[odataPath.Segments.Count - 2];
                switch (method.Method.ToUpperInvariant())
                {
                case "GET":
                    prefix  = "Get";
                    segment = tempSegment;
                    break;
                }
            }

            return(segment == null ? null : segment.Property);
        }
        public Task ApplyAsync(HttpContext httpContext, CandidateSet candidates)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

            IODataFeature odataFeature = httpContext.ODataFeature();

            if (odataFeature.Path != null)
            {
                // If we have the OData path setting, it means there's some Policy working.
                // Let's skip this default OData matcher policy.
                return(Task.CompletedTask);
            }

            // The goal of this method is to perform the final matching:
            // Map between route values matched by the template and the ones we want to expose to the action for binding.
            // (tweaking the route values is fine here)
            // Invalidating the candidate if the key/function values are not valid/missing.
            // Perform overload resolution for functions by looking at the candidates and their metadata.
            for (var i = 0; i < candidates.Count; i++)
            {
                ref CandidateState candidate = ref candidates[i];
                if (!candidates.IsValidCandidate(i))
                {
                    continue;
                }

                IODataRoutingMetadata metadata = candidate.Endpoint.Metadata.OfType <IODataRoutingMetadata>().FirstOrDefault();
                if (metadata == null)
                {
                    continue;
                }

                IEdmModel model = GetEdmModel(candidate.Values);
                if (model == null)
                {
                    continue;
                }

                ODataTemplateTranslateContext translatorContext = new ODataTemplateTranslateContext(httpContext, candidate.Values, model);

                try
                {
                    ODataPath odataPath = _translator.Translate(metadata.Template, translatorContext);
                    if (odataPath != null)
                    {
                        odataFeature.PrefixName = metadata.Prefix;
                        odataFeature.Model      = model;
                        odataFeature.Path       = odataPath;

                        MergeRouteValues(translatorContext.UpdatedValues, candidate.Values);
                    }
                    else
                    {
                        candidates.SetValidity(i, false);
                    }
                }
                catch
                {
                }
            }
 internal static bool IsCountRequest(ODataPath path)
 {
     return(path != null && path.LastSegment is CountSegment);
 }
        public void Can_DeserializePayload_WithComplexParameters_InUntypedMode(string actionName, IEdmAction expectedAction, ODataPath path)
        {
            // Arrange
            const string        Body    = @"{ ""Quantity"": 1 , ""Address"": { ""StreetAddress"":""1 Microsoft Way"", ""City"": ""Redmond"", ""State"": ""WA"", ""ZipCode"": 98052 } }";
            ODataMessageWrapper message = new ODataMessageWrapper(GetStringAsStream(Body));

            message.SetHeader("Content-Type", "application/json");

            ODataMessageReaderSettings settings = new ODataMessageReaderSettings();
            ODataMessageReader         reader   = new ODataMessageReader(message as IODataRequestMessage, settings, _model);

            ODataDeserializerContext context = new ODataDeserializerContext {
                Path = path, Model = _model, ResourceType = typeof(ODataUntypedActionParameters)
            };

            // Act
            ODataUntypedActionParameters payload = _deserializer.Read(reader, typeof(ODataUntypedActionParameters), context) as ODataUntypedActionParameters;

            // Assert
            Assert.NotNull(actionName);
            Assert.NotNull(payload);
            Assert.Same(expectedAction, payload.Action);
            Assert.True(payload.ContainsKey("Quantity"));
            Assert.Equal(1, payload["Quantity"]);
            Assert.True(payload.ContainsKey("Address"));
            dynamic address = payload["Address"] as EdmComplexObject;

            Assert.IsType <EdmComplexObject>(address);
            Assert.Equal("1 Microsoft Way", address.StreetAddress);
            Assert.Equal("Redmond", address.City);
            Assert.Equal("WA", address.State);
            Assert.Equal(98052, address.ZipCode);
        }
        /// <summary>
        /// Selects the action for OData requests.
        /// </summary>
        /// <param name="odataPath">The OData path.</param>
        /// <param name="controllerContext">The controller context.</param>
        /// <param name="actionMap">The action map.</param>
        /// <returns>
        ///   <c>null</c> if the request isn't handled by this convention; otherwise, the name of the selected action
        /// </returns>
        internal static string SelectActionImpl(ODataPath odataPath, IWebApiControllerContext controllerContext, IWebApiActionMap actionMap)
        {
            if (odataPath.PathTemplate == "~/entityset")
            {
                EntitySetSegment  entitySetSegment = (EntitySetSegment)odataPath.Segments[0];
                IEdmEntitySetBase entitySet        = entitySetSegment.EntitySet;

                if (ODataRequestMethod.Get == controllerContext.Request.GetRequestMethodOrPreflightMethod())
                {
                    // e.g. Try GetCustomers first, then fall back to Get action name
                    return(actionMap.FindMatchingAction(
                               "Get" + entitySet.Name,
                               "Get"));
                }
                else if (ODataRequestMethod.Post == controllerContext.Request.GetRequestMethodOrPreflightMethod())
                {
                    // e.g. Try PostCustomer first, then fall back to Post action name
                    return(actionMap.FindMatchingAction(
                               "Post" + entitySet.EntityType().Name,
                               "Post"));
                }
            }
            else if (odataPath.PathTemplate == "~/entityset/$count" &&
                     ODataRequestMethod.Get == controllerContext.Request.GetRequestMethodOrPreflightMethod())
            {
                EntitySetSegment  entitySetSegment = (EntitySetSegment)odataPath.Segments[0];
                IEdmEntitySetBase entitySet        = entitySetSegment.EntitySet;

                // e.g. Try GetCustomers first, then fall back to Get action name
                return(actionMap.FindMatchingAction(
                           "Get" + entitySet.Name,
                           "Get"));
            }
            else if (odataPath.PathTemplate == "~/entityset/cast")
            {
                EntitySetSegment   entitySetSegment = (EntitySetSegment)odataPath.Segments[0];
                IEdmEntitySetBase  entitySet        = entitySetSegment.EntitySet;
                IEdmCollectionType collectionType   = (IEdmCollectionType)odataPath.EdmType;
                IEdmEntityType     entityType       = (IEdmEntityType)collectionType.ElementType.Definition;

                if (ODataRequestMethod.Get == controllerContext.Request.GetRequestMethodOrPreflightMethod())
                {
                    // e.g. Try GetCustomersFromSpecialCustomer first, then fall back to GetFromSpecialCustomer
                    return(actionMap.FindMatchingAction(
                               "Get" + entitySet.Name + "From" + entityType.Name,
                               "GetFrom" + entityType.Name));
                }
                else if (ODataRequestMethod.Post == controllerContext.Request.GetRequestMethodOrPreflightMethod())
                {
                    // e.g. Try PostCustomerFromSpecialCustomer first, then fall back to PostFromSpecialCustomer
                    return(actionMap.FindMatchingAction(
                               "Post" + entitySet.EntityType().Name + "From" + entityType.Name,
                               "PostFrom" + entityType.Name));
                }
            }
            else if (odataPath.PathTemplate == "~/entityset/cast/$count" &&
                     ODataRequestMethod.Get == controllerContext.Request.GetRequestMethodOrPreflightMethod())
            {
                EntitySetSegment   entitySetSegment = (EntitySetSegment)odataPath.Segments[0];
                IEdmEntitySetBase  entitySet        = entitySetSegment.EntitySet;
                IEdmCollectionType collectionType   = (IEdmCollectionType)odataPath.Segments[1].EdmType;
                IEdmEntityType     entityType       = (IEdmEntityType)collectionType.ElementType.Definition;

                // e.g. Try GetCustomersFromSpecialCustomer first, then fall back to GetFromSpecialCustomer
                return(actionMap.FindMatchingAction(
                           "Get" + entitySet.Name + "From" + entityType.Name,
                           "GetFrom" + entityType.Name));
            }

            return(null);
        }
        public string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup <string, HttpActionDescriptor> actionMap)
        {
            var retValue = _delegateRoutingConvention.SelectAction(odataPath, controllerContext, actionMap);

            return(retValue);
        }
 public string HandleUnmappedRequest(ODataPath path)
 {
     return(path.PathTemplate);
 }
        public override string SelectAction(ODataPath odataPath, HttpControllerContext context,
                                            ILookup <string, HttpActionDescriptor> actionMap)
        {
            if (context.Request.Method == HttpMethod.Get &&
                odataPath.PathTemplate == "~/entityset/key/navigation/key")
            {
                string navigationEntityName = model.FindDeclaredEntitySet((odataPath.Segments[2] as NavigationPathSegment).NavigationPropertyName).EntityType().Name;

                string actionName = "Get" + navigationEntityName + "ByRelatedKey";
                if (actionMap.Contains(actionName))
                {
                    // Add keys to route data, so they will bind to action parameters.
                    KeyValuePathSegment keyValueSegment = odataPath.Segments[1] as KeyValuePathSegment;
                    context.RouteData.Values[ODataRouteConstants.Key] = keyValueSegment.Value;

                    KeyValuePathSegment relatedKeySegment = odataPath.Segments[3] as KeyValuePathSegment;
                    context.RouteData.Values[ODataRouteConstants.RelatedKey] = relatedKeySegment.Value;

                    return(actionName);
                }
            }

            if (context.Request.Method == HttpMethod.Put &&
                odataPath.PathTemplate == "~/entityset/key/navigation/key")
            {
                string navigationPropertyName = (odataPath.Segments[2] as NavigationPathSegment).NavigationPropertyName;
                string actionName             = "Update" + navigationPropertyName;

                if (actionMap.Contains(actionName))
                {
                    // Add keys to route data, so they will bind to action parameters.
                    KeyValuePathSegment keyValueSegment = odataPath.Segments[1] as KeyValuePathSegment;
                    context.RouteData.Values[ODataRouteConstants.Key] = keyValueSegment.Value;

                    KeyValuePathSegment relatedKeySegment = odataPath.Segments[3] as KeyValuePathSegment;
                    context.RouteData.Values[ODataRouteConstants.RelatedKey] = relatedKeySegment.Value;

                    return(actionName);
                }
            }

            if (context.Request.Method == HttpMethod.Put &&
                odataPath.PathTemplate == "~/entityset/key/navigation")
            {
                string navigationPropertyName = (odataPath.Segments[2] as NavigationPathSegment).NavigationPropertyName;
                string actionName             = "Update" + navigationPropertyName;

                if (actionMap.Contains(actionName))
                {
                    // Add keys to route data, so they will bind to action parameters.
                    KeyValuePathSegment keyValueSegment = odataPath.Segments[1] as KeyValuePathSegment;
                    context.RouteData.Values[ODataRouteConstants.Key] = keyValueSegment.Value;

                    return(actionName);
                }
            }

            if (context.Request.Method == HttpMethod.Post &&
                odataPath.PathTemplate == "~/entityset/key/navigation")
            {
                NavigationPathSegment segment = (odataPath.Segments[2] as NavigationPathSegment);
                string navigationPropertyName = segment.NavigationPropertyName;
                string actionPrefix           = "Create";

                var type = segment.NavigationProperty.Type.Definition.TypeKind;
                if (type == EdmTypeKind.Collection)
                {
                    actionPrefix = "AddTo";
                }

                string actionName = actionPrefix + navigationPropertyName;

                if (actionMap.Contains(actionName))
                {
                    // Add keys to route data, so they will bind to action parameters.
                    KeyValuePathSegment keyValueSegment = odataPath.Segments[1] as KeyValuePathSegment;
                    context.RouteData.Values[ODataRouteConstants.Key] = keyValueSegment.Value;

                    return(actionName);
                }
            }

            // Not a match.
            return(null);
        }
        /// <inheritdoc/>
        public override ActionDescriptor SelectAction(RouteContext routeContext, IEnumerable <ControllerActionDescriptor> actionDescriptors)
        {
            if (routeContext == null)
            {
                throw Error.ArgumentNull("routeContext");
            }

            ODataPath   odataPath  = routeContext.HttpContext.Request.ODataFeature().Path;
            HttpRequest request    = routeContext.HttpContext.Request;
            string      httpMethod = request.Method.ToUpperInvariant();

            if (httpMethod == ODataRouteConstants.HttpGet)
            {
                ActionDescriptor actionDescriptor = null;
                OperationSegment function         = null;
                switch (odataPath.PathTemplate)
                {
                case "~/entityset/key/cast/function":
                case "~/entityset/key/function":
                    function         = odataPath.Segments.Last() as OperationSegment;
                    actionDescriptor = GetFunction(function).SelectAction(actionDescriptors, isCollection: false);
                    if (actionDescriptor != null)
                    {
                        routeContext.AddKeyValueToRouteData((KeySegment)odataPath.Segments[1]);
                    }
                    break;

                case "~/entityset/key/cast/function/$count":
                case "~/entityset/key/function/$count":
                    function         = odataPath.Segments[odataPath.Segments.Count - 2] as OperationSegment;
                    actionDescriptor = GetFunction(function).SelectAction(actionDescriptors, isCollection: false);
                    if (actionDescriptor != null)
                    {
                        routeContext.AddKeyValueToRouteData((KeySegment)odataPath.Segments[1]);
                    }
                    break;

                case "~/entityset/cast/function":
                case "~/entityset/function":
                    function         = odataPath.Segments.Last() as OperationSegment;
                    actionDescriptor = GetFunction(function).SelectAction(actionDescriptors, isCollection: true);
                    break;

                case "~/entityset/cast/function/$count":
                case "~/entityset/function/$count":
                    function         = odataPath.Segments[odataPath.Segments.Count - 2] as OperationSegment;
                    actionDescriptor = GetFunction(function).SelectAction(actionDescriptors, isCollection: true);
                    break;

                case "~/singleton/function":
                case "~/singleton/cast/function":
                    function         = odataPath.Segments.Last() as OperationSegment;
                    actionDescriptor = GetFunction(function).SelectAction(actionDescriptors, isCollection: false);
                    break;

                case "~/singleton/function/$count":
                case "~/singleton/cast/function/$count":
                    function         = odataPath.Segments[odataPath.Segments.Count - 2] as OperationSegment;
                    actionDescriptor = GetFunction(function).SelectAction(actionDescriptors, isCollection: false);
                    break;
                }

                if (actionDescriptor != null)
                {
                    routeContext.AddFunctionParameterToRouteData(function);
                    return(actionDescriptor);
                }
            }

            return(null);
        }