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